Spring Cloud Ribbon
简介
Ribbon 是Netflix公司的一个开源的负载均衡项目,是一个客户端/进程内负载均衡器,运行在消费者端。
这里引出一个问题客户端负载均衡和服务端负载均衡的区别是什么呢?
客户端负载均衡和服务端负载均衡最大的不同在于服务清单所在的位置。
客户端负载均衡中,客户端中都维护着自己要访问的服务段清单,
而这些清单都来源于服务注册中心(动态地址,例如配置中心Eureka,Consul,Etcd等)
或者本地(静态地址,可以配置在配置文件中进行维护,不方便)。
但是服务端负载均衡的服务清单是无法自己来维护的,例如Nignx是接收到请求后
把请求转发到系统应用中。
简单使用
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
@Configuration
public class RestTemplateConfiguration {
@LoadBalanced
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping
public String order(){
log.info("begin do order");
String url="http://goods-service/goods";
return goodsInfo=restTemplate.getForObject(url,String.class);
}
}
goods-service.ribbon.listOfServers=\
http://localhost:9090,http://localhost:9093
Ribbon连接数配置
Ribbon可以通过下面的配置项,来限制httpclient连接池的最大连接数量、以及针对不同host的最大连接数量。
Ribbon底层的网络通信,采用的是HttpClient中的PoolingHttpClientConnectionManager连接池,连接池的好处是避免频繁建立连接(针对单个目标地址)带来的性能开销,但是维护过多的链接会对客户端造成内存以及维护上的成本。所以,可以通过MaxTotalConnections 限制总的连接数量,或者通过MaxConnectionsPerHost 限制针对每个host的最大连接数。
ribbon.MaxTotalConnections=200 (默认值:200)
ribbon.MaxConnectionsPerHost=50 (默认值:50)
Ribbon核心之Ping机制
在ribbon负载均衡器中,提供了ping机制,每隔一段时间,就会去ping服务器,由com.netflix.loadbalancer.IPing 接口去实现。
单独使用ribbon,不会激活ping机制,默认采用DummyPing(在RibbonClientConfiguration中实例化),isAlive()方法直接返回true。
ribbon和eureka集成,默认采用NIWSDiscoveryPing (在
EurekaRibbonClientConfiguration中实例化的),只有服务器列表的实例状态为up的时候
才会为Alive。
IPing中默认内置了一些实现方法如下。
- PingUrl: 使用httpClient对目标服务逐个实现Ping操作
- DummyPing: 默认认为对方服务是正常的,直接返回true
- NoOpPing:永远返回true
Ribbon中的负载均衡策略
负载均衡,不管Nginx还是Ribbon都需要其算法的支持,Nginx使用的是轮询和加权轮询算法。而在Ribbon中有更多的负载均衡调度算法,其默认是使用的RoundRobinRule轮询策略。
-
RoundRobinRule:轮询策略。Ribbon默认采用的策略。若经过一轮轮询没有找到可用的provider,其最多轮询 10 轮。若最终还没有找到,则返回 null。
-
RandomRule: 随机策略,从所有可用的 provider 中随机选择一个。
-
RetryRule: 重试策略。先按照 RoundRobinRule 策略获取 provider,若获取失败,则在指定的时限内重试。默认的时限为 500 毫秒。
-
WeightedRespinseTimeRule:权重轮询。该策略是对RoundRobinRule的扩展,增加了根据实例的运行情况来计算权重,并根据权重来挑选实例,已达到更优的分配效果。
-
ClientConfigEnabledRoundRobinRule:该策略较为特殊,我们一般不直接使用它。因为它本身并没有实现什么特殊的处理逻辑,真如代码中所示,在他的内部定义了一个RoundRobinRule策略,而choose函数的实现也正是使用了RoundRobinRule的线性轮询机制,所以它实现的功能实际上RoundeRobinRule相同。
虽然不能直接使用该策略,但是可以通过继承该策略,默认的choose就实现了线性轮询机制,但是可以在子类中实现更高级的策略
-
BestAvailableRule:最低并发策略。该策略通过遍历负载均衡器中维护的所有实例,会过滤调故障的实例,并找出并发请求数最小的一个,所以该策略的特征是选择出最空闲的实例
-
PredicateBaseRule:先通过子类中实现的Predicate逻辑来过滤一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个。至于如何过滤,需要我们在AbstractServerPredicate的子类中实现apply方法来确定具体的实现策略。
-
ZoneAvoidanceRule:区域权衡策略。它是PredicateBaseRule的具体实现类,从它的源码中可以看到它是通过CompositePredicate来进行服务实例清单额过滤的。这是一个组合过滤条件,在其构造函数中,ZoneAvoidancePredicate为主过滤条件,AvailabilityPredicate为次过滤条件初始化了组合过滤条件的实例。综合判断server所在的区域性能和server可用性轮询选择server,并判断AWS zone的运行性能是否可用,剔除不可用的区域中的server。
自定义负载均衡器
自定义负载均衡器需要实现AbstractLoadBalancerRule类
public class CustomerDefinieIpHashRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
public Server choose(ILoadBalancer lb, Object key) {
if(lb==null){
return null;
}
Server server=null;
while(server==null){
List<Server> upList=lb.getReachableServers();
List<Server> allList=lb.getAllServers();
int serverCount=upList.size();
if(serverCount==0){
return null;
}
int index=ipAddressHash(serverCount);
server=upList.get(index);
}
return server;
}
private int ipAddressHash(int serverCount){
ServletRequestAttributes requestAttributes= (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
String remoteAddr=requestAttributes.getRequest().getRemoteAddr();
int code=Math.abs(remoteAddr.hashCode());
return code%serverCount;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(),key);
}
}
goods-service.ribbon.NFLoadBalancerRuleClassName=cn.train.CustomerDefinieIpHashRule