之前文章我们介绍了如何配置具有Ribbon轮询机制的负载均衡策略的消费者,这次来具体了解一下Ribbon的一些细节,以及如何自定义负载均衡策略等。
说一下Ribbon实现负载均衡的大致思路。它通过用@LoadBalanced修饰RestTemplate,将它交由Ribbon来配置,给RestTemplate定义了一个拦截器,拦截rest请求,交由Ribbon处理后发送出去。Ribbon主要负责选取合适的实例,并构造URL。(具体不分析了,有兴趣的可以看下LoadBalancerAutoConfiguration,LoadBalancerInterceptor,LoadBalancerClient几个类)。
我们来看一下,先了解以下几个我们可能用到的重要接口。
ILoadBalancer:Ribbon的负载均衡器接口,定义了增加服务实例,选择服务实例,标志服务实例下线,取得当前可用实例,取得所有服务实例方法。通过方法定义,我们可以看出,这个类是Ribbon的最主要的管理类。它管理所有实例,并通过获取实例方法的中的负载均衡策略选择合适实例返回。主要涉及到AbstractLoadBalancer实现接口,并定义了一些基础方法。
- BaseLoadBalancer:继承AbstractLoadBalancer,负载均衡器的基础实现类,有几个需要注意的地方:IPing(用于检测实例状态的方式)对象未实现,为空,需要在构造时传入,检查实例状态的默认策是SerialPingStrategy,该策略会使用传入规则,轮询实例检查其状态,在网络不好或实例过多的情况下,会存在性能问题。检查策略也可以自定义传入。定义了IRule对象,该对象主要用于定义负载均衡策略,ILoadBalancer会将自己通过构造函数传入IRule,IRule通过choose函数取得serverList,并返回规则下的某个server。该均衡器默认的IRule为RoundRobinRule,策略为线性轮询服务实例返回。
- NoOpLoadBalancer,这个类继承AbstractLoadBalancer,从名字可以看出,反正就是什么也不做。。。
- DynamicServerlistloadBalancer,继承BaseLoadBalancer,实现了服务清单在运行过程中动态更新的能力,同时具备服务清单过滤的功能。大家可以自己看下,我也没仔细看>_<。
- ZoneAwareLoadBalancer,继承DynamicServerlistloadBalancer,功能上是最完善,最强大的,由于DynamicServerlistloadBalancer使用轮询方式调用实例,在区域不同的情况下,可能存在网络延迟等情况影响性能,所以出现了ZoneAwareLoadBalancer,它具有区域亲和性,会优先选择同区域的实例。它使用ZoneAvoidanceRule作为负载均衡策略。
IRule:负载均衡策略接口
- AbstractloadBalancerRule,实现了一些基本的方法。定义了ILoadBalancer对象。
- RandomRule:随机选择策略。
- RoundRobinRule:线性轮询策略
- RetryRule:内部定义了另一个轮行策略,具备了判断实例选择检测实例是否存活,未存活时,轮询选择其余实例,并判断实例状态,还设置了超时功能,在指定时间内,未能选择出能使用的实例,则返回null。
- WeightedResponseTimeRule:对RoundRobinRule的扩展,里面定义了一个算法计算实例的权重,权重与实例响应时间有关,反正最终效果就是响应时间短的实例更容易被选择到。
- ClientConfigEnabledRoundRobinRule:实现和RoundRobinRule一致,用于其他类通过继承它进行扩展。
- BestAvailableRule继承于ClientConfigEnabledRoundRobinRule,它通过遍历所有实例,过滤故障实例,找出并发请求最少的一个实例返回。谁最闲找谁做事。
- PredicateBasedRule:该规则先实现过滤(某种判断实例是否故障,且较空闲的算法),然后遍历找出一个实例。(这个没怎么懂)。
- AvailabilityFilteringRule:继承了PredicateBasedRule,直接遍历实例,根据规则,找到一个合适的实例就返回。
- ZoneAvoidanceRule:继承PredicateBasedRule,具备区域亲和性的策略,先通过区域过滤出同区域的实例,再选择一个实例。如果没有同区域的实例,则线性遍历其他实例。
IPing:检测实例是否可用的接口
- AbstractLoadBalancerPing:实现IPing,定义了AbstractLoadBalancer。
- NoOpPing:实现IPing,返回true,表示默认所有实例可用。
- DummyPing:默认返回true,并构造了一个initWithNiwsConfig空方法,应该是用来提供给开发者自己继承实现的类。
- NIWSDiscoveryPing:这个是和Eureka结合使用时,以注册中心管理的实例状态为准,状态为UP的实例返回为可用。
- PingConstant:实现IPing,可以通过setConstant来设置当前或所有实例的状态。
- PingUrl:实现IPing,通过发送http请求判断实例返回值的状态码是否为200,判断实例是否存活。默认地址是http或https://ip:端口,可以通过setPingAppendString,往ip后再拼接字符构成地址。
以上三个接口是Ribbon最主要的几个接口,其余接口在这里不做介绍。
下面讲一下Ribbon使用的默认接口,注意,与Eureka整合后,接口有一定变化。
- ILoadBalancer,用的ZoneAwareLoadBalancer
- IRule,用的ZoneAvoidanceRule
- IPing,用的NoOpPing,默认都可以
- ServerList<Server>,用的ConfigurationBasedServerList,以配置实例列表决定,配置方式为:<servername>.ribbon.listOfServers=localhost:8001,localhost:8002, localhost:8003
- ServerListFilter<Server>,默认使用ZonePreferenceServerListFilter,优先过滤同区域的实例。
- IClientConfig,Ribbon的客户端配置,默认使用DefaultClientConfigimpl
在和Eureka整合后,接口实例使用有以下变化:
- ServerList<Server>,由DiscoveryEnabledNIWSServerList替换。该类实现将服务实例清单列表的管理交由Eureka的注册中心维护。表示Ribbon直接从Eureka注册中心获取服务列表。
- IPing被.NIWSDiscoveryPing来替换,表示实例的状态由Eureka注册中心维护,注册中心列表中实例为UP状态则返回该实例可用。
经过上面的介绍之后,我们来看如何自定义这些策略的实现类。自己如果要实现这些接口也行。
通过查看Ribbon源码,找到以下这个类,它的构造函数中定义了这些配置的字段。
public PropertiesFactory() { classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName"); classToProperty.put(IPing.class, "NFLoadBalancerPingClassName"); classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName"); classToProperty.put(ServerList.class, "NIWSServerListClassName"); classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName"); }
我们可以看到,它支持5个接口的实现类配置。如:通过<servername>.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule,配置servername的负载均衡策略为随机选择实例。不加服务名的配置则表示全局生效。好叻,写完了,亲测有效哦,大家可以试着定制自己的负载均衡的配置了!