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.yml1
2
3
4
5
6
7
8spring:
application:
name: load-balance-server
cloud:
zookeeper:
connect-string: 127.0.0.1:2181
server:
port: 0服务启动程序类
LoadBalanceServerApplication1
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);
}
}启动服务,根据启动日志查看本地的随机端口,此次端口是
50939http://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.yml1
2
3
4
5
6
7
8spring:
application:
name: load-balance-client
cloud:
zookeeper:
connect-string: 127.0.0.1:2181
server:
port: 8080RestTemplate拦截器LoadBalancedRequestInterceptor1
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();
}
}服务启动程序类
LoadBalanceServerApplication1
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的RestTemplate1
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.BlockingLoadBalancerClientSpring Cloud LoadBalancer实现org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClientRibbon实现类
org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier服务实例列表生成器基于
DiscoveryClient和ReactiveDiscoveryClient实现:org.springframework.cloud.loadbalancer.core.DiscoveryClientServiceInstanceListSupplierorg.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.DefaultClientConfigImplcom.netflix.loadbalancer.IRule负载规则默认实现:
com.netflix.loadbalancer.ZoneAvoidanceRulecom.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.ZookeeperServerListZookeeper服务列表com.alibaba.cloud.nacos.ribbon.NacosServerListNacos服务列表
缺少了一种基于
DiscoveryClient的动态服务列表实现com.netflix.loadbalancer.ServerListFilter服务列表过滤器默认实现:
org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFiltercom.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。