负载均衡介绍
负载均衡(LoadBalancer,简称LB),是高并发,高可用系统中必不可少的关键组件
当服务流量增大的时候,通常会采用增加机器的方式进行扩容,负载均衡就是用来在多个机器或者其他资源中,按照一定的规则合理分配负载
服务多机部署的时候,开发人员都需要考虑负载均衡的实现,所以也出现了一些负载均衡器,来帮助我们实现负载均衡
负载均衡分为服务端负载均衡和客户端负载均衡
服务端负载均衡
在服务端进行负载均衡的算法分配
比较有名的服务端负载均衡器是Nginx,请求先到达Nginx负载均衡器,然后通过负载均衡算法,在多个服务器之间选择一个进行访问

客户端负载均衡
在客户端进行负载均衡的算法分配
把负载均衡的功能以库的方式集成到客户端,而不是由一台指定的负载均衡设备集中提供
比如Spring Cloud中的Ribbon,请求发送到客户端,客户端从注册中心(比如Eureka)获取服务列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问
Ribbon是Spring Cloud 早期的默认实现,由于不维护了,所以最新版本的SpringCloud负载均衡集成的是SpringCloudBalancer(SpringCloud官方维护)

客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置
Spring Cloud LoadBalancer
快速上手
用法:1.添加注解 给RestTemplate这个Bean添加@LoadBalanced注解就可
2.修改远程调用代码,把IP和端口号修改为应用名
负载均衡策略
负载均衡策略是一种思想,无论是哪一种负载均衡器,他们的负载均衡策略都是相似的.SpringCloudLoadBalancer仅支持两种负载均衡策略:轮询策略和随机策略
1.轮询(Round Ribbon) :轮询策略是指服务器轮流处理用户的请求,这是一种最简单,也最常用的策略.(生活中也有类似的场景,比如学校轮流值日)
2.随机选择(Random):随机选择策略是指随机选择一个后端服务器来处理新的请求
自定义负载均衡策略
Spring Cloud LoadBalancer默认负载均衡策略是论轮询策略,实现是RoundRibbonLoadBalancer,如果服务的消费者想采用随机的负载均衡策略,也是非常简单
1.定义随机算法对象,通过@Bean将其加载到Spring容器中
此处SpringCloudLoadBalancer提供的RandomLoadBalancer
2.使用LoadBalancerClient或者@LoadBalancerClients注解
在RestTemplate配置类上方,使用@LoadBalancerClient/@LoadBalancerClients注解,可以对不同的服务提供方配置不同的客户端负载均衡算法策略
@LoadBalancerClient注解说明:
1.name:该负载均衡策略对哪个服务生效(服务提供方)
2.configuration:该负载均衡策略,用哪个负载均衡策略实现
LoadBalancer原理
LoadBalancer的实现,主要是LoadBalancerInterceptor,这个类会对RestTemplate的请求进行拦截,然后会从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到\真实的服务地址信息,替换服务id
我们来看看源码实现:
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
}
可以看到这些Interceptor方法,拦截了用户的HttpRequest请求,然后做了几件事:
1.request.getURI() 从请求中获取uri,也就是http://product-
2.originalUri.getHost() 从uri中获取路径的主机名,也就是服务id,product-service
3.loadBalancer.execute 根据服务id,进行负载均衡,并处理请求
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
String hint = this.getHint(serviceId);
LoadBalancerRequestAdapter<T, TimedRequestContext> lbRequest = new LoadBalancerRequestAdapter(request, this.buildRequestContext(request, hint));
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = this.getSupportedLifecycleProcessors(serviceId);
supportedLifecycleProcessors.forEach((lifecycle) -> {
lifecycle.onStart(lbRequest);
});
//根据serviceId,和负载均衡策略,选择处理的服务
ServiceInstance serviceInstance = this.choose(serviceId, lbRequest);
if (serviceInstance == null) {
supportedLifecycleProcessors.forEach((lifecycle) -> {
lifecycle.onComplete(new CompletionContext(Status.DISCARD, lbRequest, new EmptyResponse()));
});
throw new IllegalStateException("No instances available for " + serviceId);
} else {
return this.execute(serviceId, serviceInstance, lbRequest);
}
//根据serviceId和负载均衡策略,选择处理的服务
public <T> ServiceInstance choose(String serviceId, Request<T> request) {
ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
if (loadBalancer == null) {
return null;
}
Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();
if (loadBalancerResponse == null) {
return null;
}
return loadBalancerResponse.getServer();
}
转载自CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/budingxiaomoli/article/details/160708430



