整体概念
Ribbon是Netflix开发的客户端负载均衡器,为Ribbon配置服务提供者地址列表后,Ribbon就可以基于某种负载均衡策略算法,自动地帮助服务消费者去请求 提供者。Ribbon默认为我们提供了很多负载均衡算法,例如轮询、随机等。我们也可以实现自定义负载均衡算法。
实现负载均衡
- 准备一个 client 服务,两个 order 服务,全部注册到同一个注册中心中,通过 client 服务调用order服务达到负载均衡
- 注册中心 application.properties
#是否将自己注册到其他Eureka Server,默认为true
eureka.client.register-with-eureka=false
#是否从eureka server获取注册信息
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://eureka-service1.com:7901/eureka/
- client 服务 application.properties
#是否将自己注册到其他Eureka Server,默认为true
eureka.client.register-with-eureka=true
#是否从eureka server获取注册信息
eureka.client.fetch-registry=true
#设置服务注册中心的URL
eureka.client.serviceUrl.defaultZone=http://eureka-service1.com:7901/eureka/
eureka.instance.appname=client
#web端口,服务是由这个端口处理rest请求的
server.port=8080
- order1 application.properties
#是否将自己注册到其他Eureka Server
eureka.client.register-with-eureka=true
#是否从eureka server获取注册信息
eureka.client.fetch-registry=true
#设置服务注册中心的URL
eureka.client.serviceUrl.defaultZone=http://eureka-service1.com:7901/eureka/
#实例名
eureka.instance.appname=order
#主机名
eureka.instance.hostname=eureka-order1.com
#应用名(loadBalancerClient.choose 方法的参数)
spring.application.name=order
#web端口,服务是由这个端口处理rest请求的
server.port=8081
- order2 application.properties
#是否将自己注册到其他Eureka Server,默认为true 需要
eureka.client.register-with-eureka=true
#是否从eureka server获取注册信息, 需要
eureka.client.fetch-registry=true
#设置服务注册中心的URL,用于client和server端交流
eureka.client.serviceUrl.defaultZone=http://eureka-service1.com:7901/eureka/
#实例名
eureka.instance.appname=order
#主机名
eureka.instance.hostname=eureka-order2.com
#应用名(loadBalancerClient.choose 方法的参数)
spring.application.name=order
#web端口,服务是由这个端口处理rest请求的
server.port=8082
- 配置中心界面
- client 代码
package com.example.eurekaclient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class BalancerController {
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/choseOrder")
public String choseOrder() {
// spring.application.name 的值
String serviceName = "order";
for (int i = 0; i < 10; i++) {
ServiceInstance si = loadBalancerClient.choose(serviceName);
System.out.println("order节点信息:url:" + si.getHost() + ",port:" + si.getPort());
}
return "SUCCESS";
}
}
- 访问 http://localhost:8080/choseOrder
@LoadBalanced 实现
- 两个 order 服务代码
package com.example.eurekaorder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class BalancerController {
@Value("${eureka.instance.hostname}")
private String hostName;
@Value("${server.port}")
private Integer port;
@GetMapping("/getHostAndPort")
public String getHostAndPort() {
return "host:" + hostName + ",port:" + port;
}
}
- client 代码
package com.example.eurekaclient;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class Config {
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
}
package com.example.eurekaclient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class LoadBalancedController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/loadBalanced")
public String loadBalanced() {
String url = "http://order/getHostAndPort";
for (int i = 0; i < 10; i++) {
System.out.println(restTemplate.getForObject(url, String.class));
}
return "SUCCESS";
}
}
- 访问 http://localhost:8080/loadBalanced
自定义负载均衡策略
java代码定义
@Configuration
@RibbonClient(name = "service-order",configuration = RibbonConfiguration.class)
属性定义
针对服务定ribbon策略:
service-order:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
给所有服务定ribbon策略:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
脱离Eureka使用
service-order:
ribbon:
eureka:
# 将Eureka关闭,则Ribbon无法从Eureka中获取服务端列表信息
enabled: false
# listOfServers可以设置服务端列表
listOfServers:localhost:8081,localhost:8082
负载均衡算法
ZoneAvoidanceRule(区域权衡策略):复合判断Server所在区域的性能和Server的可用性,轮询选择服务器。
BestAvailableRule(最低并发策略):会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。逐个找服务,如果断路器打开,则忽略。
RoundRobinRule(轮询策略):以简单轮询选择一个服务器。按顺序循环选择一个server。
RandomRule(随机策略):随机选择一个服务器。
AvailabilityFilteringRule(可用过滤策略):会先过滤掉多次访问故障而处于断路器跳闸状态的服务和过滤并发的连接数量超过阀值得服务,然后对剩余的服务列表安装轮询策略进行访问。
WeightedResponseTimeRule(响应时间加权策略):据平均响应时间计算所有的服务的权重,响应时间越快服务权重越大,容易被选中的概率就越高。刚启动时,如果统计信息不中,则使用RoundRobinRule(轮询)策略,等统计的信息足够了会自动的切换到WeightedResponseTimeRule。响应时间长,权重低,被选择的概率低。反之,同样道理。此策略综合了各种因素(网络,磁盘,IO等),这些因素直接影响响应时间。
RetryRule(重试策略):先按照RoundRobinRule(轮询)的策略获取服务,如果获取的服务失败则在指定的时间会进行重试,进行获取可用的服务。如多次获取某个服务失败,就不会再次获取该服务。主要是在一个时间段内,如果选择一个服务不成功,就继续找可用的服务,直到超时。
原理
- 拦截请求。
- 获取url。
- 通过url中 serviceName 获取 List。
- 通过负载均衡算法选取一个ServiceInstance。
- 替换请求,将原来url中的 serviceName换成ip+port。