Spring Cloud负载均衡
- 理论知识
- 自定义负载均衡
- Spring Cloud LoadBalanced实现
- Netflix Ribbon实现
Spring Cloud负载均衡
版本信息
Spring Cloud : Hoxton.SR1
Spring Boot : 2.2.2.RELEASE
Zookeeper : 3.5.6 (注册中心使用)
理论知识
负载均衡分为4层负载和7层负载(4层和7层负载是针对网络协议),还可以分为客户端负载和服务端负载
- 4层负载 一般来说是硬件负载,比如F5
- 7层负载 一般来说是软件负载,比如Nginx
OSI模型(Open System Interconnection Reference Model开放式系统互联通信参考模型)
- 物理层
- 数据链路层
- 网络层
- 传输层
- 会话层
- 表达层
- 应用层
常见负载均衡算法
- Round Robin 轮询 
- Random 随机 
- Hash 哈希 
- Least Connections 最小连接数 
- Least Time 最少时间 
- Least Response Times 最小响应时间 
自定义负载均衡
服务端
一个简单的对外提供服务,基于
Zookeeper注册中心
- 添加 - pom依赖- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14- <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>
 <!--zookeeper 客户端-->
 <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
 </dependency>
- 简单的对外提供服务端点 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20- /**
 * 服务提供端点
 *
 * @author FelixFly <chenglinxu@yeah.net>
 * @date 2020/2/4
 */
 @RestController
 public class EchoController {
 @Autowired
 private Environment environment;
 @GetMapping("/echo")
 public String echo(String message) {
 // 由于采用的是随机端口,这地方必须采用这个方式获取端口
 String port = environment.getProperty("local.server.port");
 return "ECHO(" + port + "):" + message;
 }
 }
- 配置文件 - application.yml- 1 
 2
 3
 4
 5
 6
 7
 8- spring:
 application:
 name: load-balance-server
 cloud:
 zookeeper:
 connect-string: 127.0.0.1:2181
 server:
 port: 0
- 服务启动程序类 - LoadBalanceServerApplication- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13- /**
 * 负载均衡服务端启动程序
 *
 * @author FelixFly <chenglinxu@yeah.net>
 * @date 2020/2/4
 */
 @SpringBootApplication
 public class LoadBalanceServerApplication {
 public static void main(String[] args) {
 SpringApplication.run(LoadBalanceServerApplication.class, args);
 }
 }- 启动服务,根据启动日志查看本地的随机端口,此次端口是 - 50939- http://127.0.0.1:50939/echo?message=Hello返回信息ECHO(50939):Hello
客户端
了解
RestTemplate核心api
org.springframework.web.client.RestTemplateRest客户端模板
org.springframework.http.client.ClientHttpRequestFactoryhttp客户端请求工厂
org.springframework.http.client.SimpleClientHttpRequestFactory简单实现(JDK)
org.springframework.http.client.HttpComponentsClientHttpRequestFactoryapache HTTP组件实现
org.springframework.http.client.OkHttp3ClientHttpRequestFactoryOkHttp实现
org.springframework.http.client.ClientHttpRequesthttp客户端请求
org.springframework.http.HttpRequesthttp请求信息
org.springframework.http.HttpMessagehttp请求头信息
org.springframework.http.HttpOutputMessagehttp返回信息
org.springframework.http.HttpMessagehttp请求头信息
org.springframework.http.client.ClientHttpResponsehttp客户端返回信息序列化与反序列化是通过
Spring MVC组件进行转换
org.springframework.http.converter.HttpMessageConverterhttp信息转换
- 添加 - pom依赖- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14- <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>
 <!--zookeeper 客户端-->
 <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
 </dependency>
- 客户端提供服务端点 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18- /**
 * 服务提供端点
 *
 * @author FelixFly <chenglinxu@yeah.net>
 * @date 2020/2/4
 */
 @RestController
 public class EchoController {
 @Autowired
 private RestTemplate restTemplate;
 @GetMapping("/{serveName}/echo/{message}")
 public String echo(@PathVariable String serveName,@PathVariable String message) {
 return restTemplate.getForObject("http://"+serveName+"/echo?message=" + message, String.class);
 }
 
 }
- 配置文件 - application.yml- 1 
 2
 3
 4
 5
 6
 7
 8- spring:
 application:
 name: load-balance-client
 cloud:
 zookeeper:
 connect-string: 127.0.0.1:2181
 server:
 port: 8080
- RestTemplate拦截器- LoadBalancedRequestInterceptor- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33- /**
 * 自定义实现负载均衡拦截器
 *
 * @author FelixFly <chenglinxu@yeah.net>
 * @date 2020/2/4
 */
 @Component
 public class LoadBalancedRequestInterceptor implements ClientHttpRequestInterceptor {
 @Autowired
 private DiscoveryClient discoveryClient;
 @Override
 public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
 throws IOException {
 URI uri = request.getURI();
 // 主机名
 String host = uri.getHost();
 List<ServiceInstance> instances = this.discoveryClient.getInstances(host);
 // 暂不考虑没有服务的情况
 int size = instances.size();
 // 随机
 int index = new Random().nextInt(size);
 ServiceInstance instance = instances.get(index);
 String url = (instance.isSecure() ? "https://" : "http://") +
 instance.getHost() + ":" + instance.getPort()+uri.getPath()+"?"+uri.getQuery();
 // 客户端调用
 ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
 ClientHttpRequest clientHttpRequest = requestFactory.createRequest(URI.create(url), request.getMethod());
 return clientHttpRequest.execute();
 }
 }
- 服务启动程序类 - LoadBalanceServerApplication- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22- **
 * 负载均衡客户端启动程序
 *
 * @author FelixFly <chenglinxu@yeah.net>
 * @date 2020/2/4
 */
 @SpringBootApplication
 public class LoadBalanceClientApplication {
 public static void main(String[] args) {
 SpringApplication.run(LoadBalanceClientApplication.class, args);
 }
 @Bean
 @Autowired
 public RestTemplate restTemplate(LoadBalancedRequestInterceptor interceptor){
 RestTemplate restTemplate = new RestTemplate();
 restTemplate.setInterceptors(Collections.singletonList(interceptor));
 return restTemplate;
 }
 }- 启动服务, - http://127.0.0.1:8090/load-balance-server/echo/hello返回信息ECHO(51157):hello
Spring Cloud LoadBalanced实现
- Spring
RestTemplateas a Load Balancer Client- Spring
WebClientas a Load Balancer Client- Spring
WebFluxWebClientwithReactorLoadBalancerExchangeFilterFunction
@LoadBalanced注解分析
| 1 |  | 
@Qualifier注解的作用
- 配置value的时候 根据Bean的名称(name)或者别名(alias)进行查找
- 没有配置的时候 标识分类
示例:
| 1 |  | 
打印结果:
| 1 |  | 
从结果可知:Bean b 和Bean c根据@Qualifier进行了分组
@LoadBalanced注解使用
- 客户端注册 - @LoadBalanced的- RestTemplate- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29- /**
 * 负载均衡客户端启动程序
 *
 * @author FelixFly <chenglinxu@yeah.net>
 * @date 2020/2/4
 */
 @SpringBootApplication
 public class LoadBalanceClientApplication {
 public static void main(String[] args) {
 SpringApplication.run(LoadBalanceClientApplication.class, args);
 }
 @Bean
 @Autowired
 public RestTemplate restTemplate(LoadBalancedRequestInterceptor interceptor){
 RestTemplate restTemplate = new RestTemplate();
 restTemplate.setInterceptors(Collections.singletonList(interceptor));
 return restTemplate;
 }
 @Bean
 @LoadBalanced
 public RestTemplate lbRestTemplate(){
 return new RestTemplate();
 }
 }
- 调整客户端提供服务端点 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28- /**
 * 服务提供端点
 *
 * @author FelixFly <chenglinxu@yeah.net>
 * @date 2020/2/4
 */
 @RestController
 public class EchoController {
 @Autowired
 private RestTemplate restTemplate;
 @Autowired
 @LoadBalanced
 private RestTemplate lbRestTemplate;
 @GetMapping("/{serveName}/echo/{message}")
 public String echo(@PathVariable String serveName,@PathVariable String message) {
 return restTemplate.getForObject("http://"+serveName+"/echo?message=" + message, String.class);
 }
 @GetMapping("/{serveName}/lb-echo/{message}")
 public String lbEcho(@PathVariable String serveName,@PathVariable String message) {
 return lbRestTemplate.getForObject("http://"+serveName+"/echo?message=" + message, String.class);
 }
 }- 启动服务, - http://127.0.0.1:8090/load-balance-server/lb-echo/hello返回信息ECHO(51157):hello
@LoadBalanced核心api
- org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor负载均衡的- RestTemplate拦截器
- org.springframework.cloud.client.loadbalancer.LoadBalancerClient负载均衡的客户端- org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser服务实例选择器
 - 实现类: - org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient- Spring Cloud LoadBalancer实现
- org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClientRibbon实现类
 
- org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier服务实例列表生成器- 基于 - DiscoveryClient和- ReactiveDiscoveryClient实现:- org.springframework.cloud.loadbalancer.core.DiscoveryClientServiceInstanceListSupplier
- org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer响应式服务实例负载均衡器- org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer响应式负载均衡器- org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer响应式的负载均衡器
 
 - 默认实现是轮询的负载均衡器: - org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer- 仅且仅有这一种实现 
Netflix Ribbon 实现
核心api
Ribbon客户端配置类
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration
- com.netflix.loadbalancer.ILoadBalancer负载均衡接口- 默认实现: - com.netflix.loadbalancer.ZoneAwareLoadBalancer- 父类: - com.netflix.loadbalancer.DynamicServerListLoadBalancer动态服务列表负载均衡器
- com.netflix.client.config.IClientConfig客户端配置- 默认实现: - com.netflix.client.config.DefaultClientConfigImpl
- com.netflix.loadbalancer.IRule负载规则- 默认实现: - com.netflix.loadbalancer.ZoneAvoidanceRule- com.netflix.loadbalancer.RandomRule随机
- com.netflix.loadbalancer.RoundRobinRule轮询
- com.netflix.loadbalancer.WeightedResponseTimeRule响应时间权重
- com.netflix.loadbalancer.BestAvailableRule最大可用
- org.springframework.cloud.zookeeper.discovery.dependency.StickyRule粘性(返回同一个实例)
 
- com.netflix.loadbalancer.IPing存活检测- 默认实现: - com.netflix.loadbalancer.DummyPing一直存活
- com.netflix.loadbalancer.ServerList服务列表- 默认实现: - com.netflix.loadbalancer.ConfigurationBasedServerList配置的服务列表(静态服务列表)- 配置参数: - {severName}.ribbon.listOfServers- 动态服务列表 - com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerListEureka服务列表
- org.springframework.cloud.consul.discovery.ConsulServerListconsul服务列表
- org.springframework.cloud.zookeeper.discovery.ZookeeperServerList- Zookeeper服务列表
- com.alibaba.cloud.nacos.ribbon.NacosServerList- Nacos服务列表
 - 缺少了一种基于 - DiscoveryClient的动态服务列表实现
- com.netflix.loadbalancer.ServerListFilter服务列表过滤器- 默认实现: - org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter
- com.netflix.loadbalancer.ServerListUpdater服务列表更新器- 默认实现: - com.netflix.loadbalancer.PollingServerListUpdater服务列表轮询更新器- com.netflix.niws.loadbalancer.EurekaNotificationServerListUpdaterEureka服务列表更新器
 
备注
As Spring Cloud Ribbon is now under maintenance, we suggest you set
spring.cloud.loadbalancer.ribbon.enabled to false, so that BlockingLoadBalancerClient is used
instead of RibbonLoadBalancerClient.
由于Spring Cloud Ribbon正在维护,官方推荐使用spring.cloud.loadbalancer.ribbon.enabled设置为false(禁用Ribbon),BlockingLoadBalancerClient 替代RibbonLoadBalancerClient。