基本使用及配置
以下配置及使用均基于Spring Boot工程,默认POM父类为spring-boot-starter-parent,dependecyManagement里包含spring-cloud-dependencies的配置。
根据配置文件为RestTemplate负载均衡
配置及使用
引入以下JAR包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <span class="hljs-tag" style="box-sizing: border-box; color: navy; font-weight: 400;"><<span class="hljs-title" style="box-sizing: border-box; color: navy; font-weight: 400;">dependency</span>></span> <span class="hljs-tag" style="box-sizing: border-box; color: navy; font-weight: 400;"><<span class="hljs-title" style="box-sizing: border-box; color: navy; font-weight: 400;">groupId</span>></span>org.springframework.cloud<span class="hljs-tag" style="box-sizing: border-box; color: navy; font-weight: 400;"></<span class="hljs-title" style="box-sizing: border-box; color: navy; font-weight: 400;">groupId</span>></span> <span class="hljs-tag" style="box-sizing: border-box; color: navy; font-weight: 400;"><<span class="hljs-title" style="box-sizing: border-box; color: navy; font-weight: 400;">artifactId</span>></span>spring-cloud-starter-ribbon<span class="hljs-tag" style="box-sizing: border-box; color: navy; font-weight: 400;"></<span class="hljs-title" style="box-sizing: border-box; color: navy; font-weight: 400;">artifactId</span>></span> <span class="hljs-tag" style="box-sizing: border-box; color: navy; font-weight: 400;"></<span class="hljs-title" style="box-sizing: border-box; color: navy; font-weight: 400;">dependency</span>></span></code></pre> spring-boot-starter-web提供RestTemplate实现; spring-cloud-starter-ribbon提供Ribbon实现。 在配置文件中配置应用及其对应实例 service-a.ribbon.listOfServers=localhost:80,localhost:81 service-b.ribbon.listOfServers=localhost:83,localhost:82 service-a,service-b代表应用名 ribbon这个字段固定不变 ribbon后面代表某个特定某个特定应用名后的参数 其他更多的参数可参见DefaultClientConfigImpl这个类 创建RestTemplate及使用 创建RestTemplate这个Bean @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } 加上@LoadBalanced表示创建的这个RestTemplate需要使用Ribbon进行负载均衡。 通过注入获得restTemplate: @Resource private RestTemplate restTemplate; 使用RestTemplate restTemplate.getForObject("http://service-a/hello-world?parameter={params}", String.class,paramObject); 如上直接使用service-a即可自动负载均衡到两个实例:localhost:80,localhost:81中 默认负载均衡策略 IClientConfig:客户端配置读取形式,默认使用DefaultClientConfigImpl IRule:具体服务选择策略,默认使用ZoneAvoidanceRule,其会根据ZONE的健康情况过滤特定ZONE的所有实例,并根据实例自身的并发请求数量及断路状态进行过滤。过滤完后进行线性轮询 IPing:服务实例存活检测策略,默认NoOpPing,即不主动进行服务器存活检测 ServerList:获取应用对应的服务实例列表。默认ConfigurationBasedServerList,其根据客户端的配置获取特定应用的服务器列表 ServerListFilter:服务实例过滤策略,默认ZonePreferenceServerListFilter,其优先筛选出相同ZONE的Server列表 ILoadBalancer:ZoneAwareLoadBalancer,该实现具有区域感知能力,能对区域层次的调用信息进行统计,供具体负载均衡实例使用 调用重试:默认不会调用重试,除非引入了了Spring-retry的包 若不需重试,请注意别不小心引入该包,或者显式配置关闭重试:spring.cloud.loadbalancer.retry.enabled=false 此处的重试跟IRule的重试不是一回事 建议不启用重试 ServerStat:其中包含默认访问断路逻辑,当某一个Server连续失败N次时(默认是3次)则将其断路若干时间,断路时间随着连续失败次数的增长而按2为底的指数级增长,断路时间存在一个上限,默认为30秒 关于以上各个类及名词的详细作用及解析请参考后续章节——《原理、主要类、配置详解》 注意事项 配置了@LoadBalanced注解的RestTemplate默认不能再通过 原生的域名或者IP 完成访问。 需要访问原生域名/IP时,最简单的方法就是重新创建一个RestTemplate 若不想创建多个RestTemplate请参考后续章节,请顺序往后阅读 使用Eureka的配置 配置及使用 配置 在上述配置的基础上添加Eureka的POM依赖及填写对应的Eureka服务器地址即可。具体请参考注册中心配置部分。 如果有多个区域的服务,若希望优先使用本区的服务则需要配置区域信息 eureka.instance.metadataMap.zone=shanghai 使用 其使用方法与依赖于配置文件的负载均衡使用形式一致。 默认负载均衡策略 其主要特性与基于配置的一致,以下描述不同点 IPing:实现将替换为NIWSDiscoveryPing。其判断Server存活的依据就是之前从Eureka服务器拉下来的Server的状态,也就是说客户端不作实质的存活检测,交由Eureka进行。 ServerList:实现替换为DomainExtractingServerList,其主要操作实质委托给DiscoveryEnabledNIWSServerList,也就是委托给Eureka处理 注意事项 引入Eureka后,原来基于配置的应用访问列表随即失效,但其他配置依然有效。 同样,添加了@LoadBalanced注解的RestTemplate访问原生域名/IP也会失败。 后续会介绍兼容 基于配置服务器负载均衡、基于Eureka服务器负载均衡以及基于DNS服务器列表负载均衡的 @LoadBalanced RestTemplate的配置形式 回退到配置形式 如果遇到某些特殊情况,如Eureka服务器不可用,可以手工回退到配置模式 ribbon.eureka.enabled=false 因此建议前期上线的基于Ribbon及Eureka做客户端负载均衡的最好也配上listOfServers这个参数。以便出现Eureka服务器异常时,也能降级使用 整合Eureka、配置及DNS访问(Beta) 使用本实现前请先阅读《原理、主要类、配置详解》一节,理解相关背景知识,本案例仅供参。优先推荐使用多个RestTemplte实例来解决同时存在域名访问及RIBBON访问的问题 有的时候我们希望使用一个@LoadBalanced RestTemplate解决所有的访问类型,包括域名访问、基于配置的RIBBON访问、基于Eureka的RIBBON访问,这样注入的时候不需要刻意选择特定的RestTemplate。 实现这个需求需要我们自行定制一个ServerList以替换原生的ServerList。以下扩展基于Eureka的Ribbon访问配置。 定义一个CompositeServerList类,其使用Composite模式组装多个ServerList实现 public class CompositeServerList implements ServerList<Server> { @Autowired <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">List</span><ServerList<span class="hljs-preprocessor" style="box-sizing: border-box; color: #999999; font-weight: bold;"><?</span> extends Server>> listServerList; <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">static</span> Logger LOG = LoggerFactory.getLogger(CompositeServerList.class); @SuppressWarnings(<span class="hljs-string" style="box-sizing: border-box; color: #dd1144;">"unchecked"</span>) @Override <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">List</span><Server> getInitialListOfServers() { <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">for</span>(ServerList<span class="hljs-preprocessor" style="box-sizing: border-box; color: #999999; font-weight: bold;"><?</span> extends Server> serverList:listServerList){ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">List</span><Server> initialListOfServers = <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">null</span>; <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">try</span> { initialListOfServers = (<span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">List</span><Server>) serverList.getInitialListOfServers(); } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">catch</span> (<span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">Exception</span> e) { LOG.error(<span class="hljs-string" style="box-sizing: border-box; color: #dd1144;">"server list resolved failed"</span>,e); } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">if</span>(initialListOfServers == <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">null</span> || initialListOfServers.size() == <span class="hljs-number" style="box-sizing: border-box; color: teal;">0</span>){ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">continue</span>; } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> initialListOfServers; } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> ArrayList<>(); } @SuppressWarnings(<span class="hljs-string" style="box-sizing: border-box; color: #dd1144;">"unchecked"</span>) @Override <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">List</span><Server> getUpdatedListOfServers() { <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">for</span>(ServerList<span class="hljs-preprocessor" style="box-sizing: border-box; color: #999999; font-weight: bold;"><?</span> extends Server> serverList:listServerList){ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">List</span><Server> updatedListOfServers = <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">null</span>; <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">try</span> { updatedListOfServers = (<span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">List</span><Server>) serverList.getUpdatedListOfServers(); } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">catch</span> (<span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">Exception</span> e) { LOG.error(<span class="hljs-string" style="box-sizing: border-box; color: #dd1144;">"server list resolved failed"</span>,e); } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">if</span>(updatedListOfServers == <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">null</span> || updatedListOfServers.size() == <span class="hljs-number" style="box-sizing: border-box; color: teal;">0</span>){ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">continue</span>; } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> updatedListOfServers; } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> ArrayList<>(); } } 创建一个Spring Configuration类,但不必带上@Configuartion标签(避免在主ApplicationContext加载了这些Configuration,这些配置是需要在application/host对应的子ApplicationContext加载的,具体请参考后续章节) public class OverrideRibbonConfiguration { <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">static</span> Logger LOG = LoggerFactory.getLogger(OverrideRibbonConfiguration.class); <span class="hljs-annotation" style="box-sizing: border-box;">@Value</span>(<span class="hljs-string" style="box-sizing: border-box; color: #dd1144;">"${ribbon.eureka.approximateZoneFromHostname:false}"</span>) <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">boolean</span> approximateZoneFromHostname = <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">false</span>; <span class="hljs-annotation" style="box-sizing: border-box;">@Value</span>(<span class="hljs-string" style="box-sizing: border-box; color: #dd1144;">"${ribbon.client.name}"</span>) <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">private</span> String ribbonClientName; <span class="hljs-annotation" style="box-sizing: border-box;">@Value</span>(<span class="hljs-string" style="box-sizing: border-box; color: #dd1144;">"${ribbon.dns.resolvedServerPort:80}"</span>) <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">int</span> dnsResolvedServerPort; <span class="hljs-annotation" style="box-sizing: border-box;">@Bean</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> IClientConfig <span class="hljs-title" style="box-sizing: border-box; color: #990000; font-weight: bold;">ribbonClientConfig</span><span class="hljs-params" style="box-sizing: border-box;">()</span> </span>{ DefaultClientConfigImpl config = <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> DefaultClientConfigImpl(); config.loadProperties(<span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">this</span>.ribbonClientName); <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> config; } <span class="hljs-annotation" style="box-sizing: border-box;">@Bean</span> <span class="hljs-annotation" style="box-sizing: border-box;">@Primary</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> ServerList<Server> <span class="hljs-title" style="box-sizing: border-box; color: #990000; font-weight: bold;">serverList</span><span class="hljs-params" style="box-sizing: border-box;">()</span></span>{ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> CompositeServerList(); } <span class="hljs-annotation" style="box-sizing: border-box;">@Bean</span> <span class="hljs-annotation" style="box-sizing: border-box;">@Order</span>(<span class="hljs-number" style="box-sizing: border-box; color: teal;">0</span>) <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> DomainExtractingServerList <span class="hljs-title" style="box-sizing: border-box; color: #990000; font-weight: bold;">eurekaServerList</span><span class="hljs-params" style="box-sizing: border-box;">(IClientConfig config,Provider<EurekaClient> eurekaClientProvider)</span></span>{ DiscoveryEnabledNIWSServerList discoveryServerList = <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> DiscoveryEnabledNIWSServerList(config, eurekaClientProvider); DomainExtractingServerList serverList = <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> DomainExtractingServerList(discoveryServerList, config, <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">this</span>.approximateZoneFromHostname); <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> serverList; } <span class="hljs-annotation" style="box-sizing: border-box;">@Bean</span> <span class="hljs-annotation" style="box-sizing: border-box;">@Order</span>(<span class="hljs-number" style="box-sizing: border-box; color: teal;">1</span>) <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> ConfigurationBasedServerList <span class="hljs-title" style="box-sizing: border-box; color: #990000; font-weight: bold;">cfgServerList</span><span class="hljs-params" style="box-sizing: border-box;">(IClientConfig config)</span></span>{ ConfigurationBasedServerList cfgServerList = <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> ConfigurationBasedServerList(); cfgServerList.initWithNiwsConfig(config); <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> cfgServerList; } <span class="hljs-annotation" style="box-sizing: border-box;">@Bean</span> <span class="hljs-annotation" style="box-sizing: border-box;">@Order</span>(<span class="hljs-number" style="box-sizing: border-box; color: teal;">2</span>) <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> DnsResolvedServerList <span class="hljs-title" style="box-sizing: border-box; color: #990000; font-weight: bold;">dnsServerList</span><span class="hljs-params" style="box-sizing: border-box;">()</span></span>{ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> DnsResolvedServerList(ribbonClientName, dnsResolvedServerPort); } public static class DnsResolvedServerList implements ServerList<Server>{ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">private</span> String ribbonClientName; <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">private</span> <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">int</span> dnsResolvedServerPort; <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> <span class="hljs-title" style="box-sizing: border-box; color: #990000; font-weight: bold;">DnsResolvedServerList</span><span class="hljs-params" style="box-sizing: border-box;">(String ribbonClientName, <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">int</span> dnsResolvedServerPort)</span> </span>{ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">super</span>(); <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">this</span>.ribbonClientName = ribbonClientName; <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">this</span>.dnsResolvedServerPort = dnsResolvedServerPort; } <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">private</span> List<Server> <span class="hljs-title" style="box-sizing: border-box; color: #990000; font-weight: bold;">getHostAddress</span><span class="hljs-params" style="box-sizing: border-box;">()</span></span>{ ArrayList<Server> result = <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> ArrayList<>(); <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">try</span> { InetAddress[] address = InetAddress.getAllByName(ribbonClientName); <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">for</span>(InetAddress addr :address){ result.add(<span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">new</span> Server(addr.getHostAddress(),dnsResolvedServerPort)); } } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">catch</span> (UnknownHostException e) { LOG.warn(<span class="hljs-string" style="box-sizing: border-box; color: #dd1144;">"can not resolve by DNS:"</span> + ribbonClientName); } <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> result; } <span class="hljs-annotation" style="box-sizing: border-box;">@Override</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> List<Server> <span class="hljs-title" style="box-sizing: border-box; color: #990000; font-weight: bold;">getInitialListOfServers</span><span class="hljs-params" style="box-sizing: border-box;">()</span> </span>{ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> getHostAddress(); } <span class="hljs-annotation" style="box-sizing: border-box;">@Override</span> <span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">public</span> List<Server> <span class="hljs-title" style="box-sizing: border-box; color: #990000; font-weight: bold;">getUpdatedListOfServers</span><span class="hljs-params" style="box-sizing: border-box;">()</span> </span>{ <span class="hljs-keyword" style="box-sizing: border-box; color: #333333; font-weight: bold;">return</span> getHostAddress(); } } } 以上类主要的功能是,创建了3个ServerList,然后再注入到CompositeServerList。 但本实现存在缺陷,就是DNS的解析实现时,并没有Server的端口信息,只能解析到域名对应的IP列表,因此若要使用本实现,需要在本地配置好不同 域名对应的服务端口。若一个域名有多个端口提供服务,则无解,只能另起途径解决本问题。 原理、主要类、配置详解 获取所有加了@LoadBalanced注解的RestTemplate,并为其应用LoadBalancerInterceptor这个Inteceptor。 其对于http请求的传入要访问的HostName做解析,解析到对应IP及端口列表,并根据相关负载均衡的逻辑选择一个IP及端口作为host替换掉原来的hostName LoadBalancerInterceptor 本类在RestTemplate与Ribbon整合时嵌入到RestTemplate的Inteceptor中,根据hostName获取对应的服务器列表,并替换hostName再执行发送请求后返回结果。 后续的inteceptor会被忽略掉。 对于每个hostName都会以当前ApplicationContext为父Context创建一个新的ApplicationContext,并在其中初始化对应的ILoadBalancer相关的一系列Bean,其目的主要是为了简化不同hostName都有相同类型的Bean的情况,该ApplicationContext初始化的设置由SpringClientFactory.configurations、defaultConfigType及PropertyPlaceholderAutoConfiguration.Class确定 若要调整默认或者特定hostName对应的ILoadBalancer的生成,可通过定义RibbonClientSpecification这个Bean或者更改defaultConfigType实现 ILoadBalancer 负载均衡器 初始化/新增 Server列表 根据一个KEY选择一个Server(Key通常是应用名,但LoadBalancer不负责选择应用,只选择应用下的Server。一个Application对应一个ILoadBalancer,由外层控制) 标志某一个Server已经DOWN了,供本地客户程序使用,可动态去掉不可用服务器 还有其他很多层次标志服务器已经DOWN的实现,如通过IPING接口检测到已经DOWN的,Eureka检测到已经DOWN的,不过ILoadBalancer这个层次的抽象不Care 获取所有Server列表 获取所有可用Server AbstractLoadBalancer 定义方法可获取已经DOWN了的服务器的列表 无参选择一个服务器的方法(比较少用) Server统计数据对象的获取 该对象负责统计、更新、对外提供 server层次/ZONE层次的 响应时间(平均,最大)、活跃调用数、保持的连接数、是否处于断路状态、断路时间配置、 BaseLoadBalancer 引入IRule用于定制选择可用Server的方法 其包含一个LoadBalancer实例,设计有点挫,主要是为了供具体的IRule逻辑获取各种类型服务器列表 引入IPing用于检测特定Server是否健康运作 引入IPingStrategy用于指定执行IPing的策略 如果IPing及IPingStrategy都存在则默认10秒执行一遍PING 上次PING正在执行则跳过 引入发布订阅模式,对外发布 Server列表变更事件、Server状态变更事件 引入统一的配置读取接口 DynamicServerListLoadBalancer 引入ServerList类,用于获取初始服务器列表,以及获取更新的服务器列表 “获取更新服务器列表”这个功能将会被ServerListUpdater视情况调用,其有两个实现 定时(默认), 默认是按时30秒调用一次 Eureka相关事件触发 ServerList在Eureka中的实现,并不一定是全量远程获取所有的?????TBD 引入ServerListFilter,用于按需过滤ServerList类获取到的服务器列表。 该过滤功能与ServerList类的获取更新服务器列表问题 一样,被ServerListUpdater调用 过滤后的ServerList会被全量应用到Loadbalancer中 主要实现的ServerListFilter都与Zone相关,优先筛选出本ZONE对应的Server,若本ZONE出现问题,才会返回其他ZONE的Server ZoneAwareLoadBalancer 这个类改写了选择父类的ChooseServer方法,父类方法会直接将经过ServerListFilter过滤后的服务器列表直接传递给IRule进行一个特定服务器的选择 而这个类对ZONE数量大于1时,将会对Server按照Zone进行分组并创建一个对应的BaseLoadBalancer,同时根据Zone的一些统计数据,剔除一些不健康的ZONE,对于剩下的ZONE,将随机选择一个,调用BaseLoadBalancer,将请求委托给IRule. IRule 服务器选择规则 其包含一个选择服务器的方法 Server Choose(Object) 其可以获取或者设置IRule归属的ILoadBalancer,用于获取服务列表等信息 AbstractLoadBalancerRole 实现设置、获取 LoadBalancer的方法 RandomRule 当服务器列表中存在服务器时,随机选择一个状态为UP状态的返回,若没有选择成功则循环返回(不推荐使用本方法,个人觉得这个实现有BUG) RetryRule Decorator模式,其可修饰一个特定的其他IRule,使其增加在发生获取Server失败的情况下,在一定超时范围内尝试重试获取Server的逻辑。 RoundRobin 线性地从ServerList中获取Server,如果获取的服务器 不可用 或者 没有 准备好提供服务,则尝试获取下一个,最多重试10次 WeightedResponseTimeRule 根据统计数据计算出每个Server的权重,平均响应时间越短的Sever其权重越大,权重越大则在随机选择的过程中命中几率更大 ClientConfigEnabledRoundRobinRule 使用组合模式,组合了RoundRobinRule的功能,这便于本类的继承类自由的实现其他Rule形式,而无需考虑父类实现,当其他形式失败时,则回退到roundRobin形式 BestAvailableRule 继承自 ClientConfigEnabledRoundRobinRule,根据统计信息,选择可用且请求数量最少的一个Server返回,若统计信息尚未生成完善,则使用线性轮训机制 PredicateBaseRule 继承自 ClientConfigEnabledRoundRobinRule,可自定义Server过滤规则,过滤完后再线性轮训(可通过继承修改) AvailabilityFilteringRule 继承自PredicateBaseRule,线性轮询Sever,过滤掉处于断路状态或者并发请求数超过某个限定值的请求 ZoneAvoidanceRule 其继承自PredicateBaseRule,该Rule集成了2个Predict的筛选条件,其中有一个是主筛选条件:ZoneAvoidancePredict主要根据ZONE的健康情况,过滤不健康的ZONE的实例,当主条件过滤完一遍后,使用从过滤条件过滤:AvailablitilyPredicate,其过滤结束条件是——全部都过滤了一遍或者过滤剩下的Server小于要求的个数或者小于特定的比例 改写默认策略 配置形式 hello-service.ribbon.listOfServers=localhost:8080,localhost:8081 ribbon.ConnectionTimeout=250 第一条表示改写hostName/applicationId 为 hello-service的配置 第二条表示改写全局默认配置 ribbon后面的配置项,可参见DefaultClientConfigImpl,常见的有: NFLoadBalancerClassName NFLoadBalancerRuleClassName NFLoadBalancerPingClassName NIWSServerListClassName NIWSServerListFilterClassName 注解形式 使用@RibbonClient实现,只能覆盖特定HostName的配置 若要覆盖默认实现,则直接创建默认实现对应的Bean即可 RibbonClientSpecification形式 创建该Bean的对应实例,可以调整具体的ILoadBalancer的实现
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于