在上篇文章中(RestTemplate的逆袭之路,从发送请求到负载均衡)我们完整的分析了RestTemplate的工作过程,在分析的过程中,我们遇到过一个ILoadBalancer接口,这个接口中有一个chooseServer方法是我们选择服务实例的方法,这个也是整个负载均衡中最最核心的部分,那么它到底是采用了什么样的策略从服务提供者列表中选出了一个服务供服务消费者去调用的?这是我们今天要讨论的问题,本文我主要是想基于互联网上公开的资料,来对Spring Cloud中提供的负载均衡器做一个简明扼要的介绍。


本文是Spring Cloud系列的第八篇文章,了解前七篇文章内容有助于更好的理解本文:

1.使用Spring Cloud搭建服务注册中心

2.使用Spring Cloud搭建高可用服务注册中心

3.Spring Cloud中服务的发现与消费

4.Eureka中的核心概念

5.什么是客户端负载均衡

6.Spring RestTemplate中几种常见的请求方式

7.RestTemplate的逆袭之路,从发送请求到负载均衡


负载均衡器

首先我们来看一张上篇文章中的旧图:

这是ILoadBalancer接口的一张类关系图,我们就从这张图里看起吧。

AbstractLoadBalancer

AbstractLoadBalancer类的定义如下:

public abstract class AbstractLoadBalancer implements ILoadBalancer {

    public enum ServerGroup{
ALL,
STATUS_UP,
STATUS_NOT_UP
}
public Server chooseServer() {
return chooseServer(null);
}
public abstract List<Server> getServerList(ServerGroup serverGroup);
public abstract LoadBalancerStats getLoadBalancerStats();
}

关于这个类我说以下几点:

1. AbstractLoadBalancer实现了ILoadBalancer接口,但它是一个抽象类,它里边定义了一个关于服务实例的分组枚举类,包含了三种类型的服务:ALL表示所有服务,STATUS_UP表示正常运行的服务,STATUS_NOT_UP表示下线的服务。

2. chooseServer方法毫无疑问是用来选取一个服务实例,但是要怎么选这里并没有说,我们以后在它的实现类里边寻找选取策略。

3. getServerList方法用来获取某一个分组中所有的的服务实例。

4. getLoadBalancerStats方法用来获取LoadBalancerStats对象,LoadBalancerStats对象中保存了每一个服务的所有细节信息。

BaseLoadBalancer

BaseLoadBalancer是AbstractLoadBalancer的一个实现类,源码比较长我就不贴出来的,我们在这里主要来说说BaseLoadBalancer提供了哪些功能:

1. 首先这个类中有两个List集合中放的Server对象,一个List集合用来保存所有的服务实例,还有一个List集合用来保存当前有效的服务实例。

2. BaseLoadBalancer中定义了一个IPingStrategy,用来描述服务检查策略,IPingStrategy默认实现采用了SerialPingStrategy实现,SerialPingStrategy中的pingServers方法就是遍历所有的服务实例,一个一个发送请求,查看这些服务实例是否还有效,如果网络环境不好的话,这种检查策略效率会很低,如果我们想自定义检查策略的话,可以重写SerialPingStrategy的pingServers方法。

3. 在BaseLoadBalancer的chooseServer方法中(负载均衡的核心方法),我们发现最终调用了IRule中的choose方法来找到一个具体的服务实例,IRule是一个接口,在BaseLoadBalancer它的默认实现是RoundRobinRule类,RoundRobinRule类中采用了最常用的线性负载均衡规则,也就是所有有效的服务端轮流调用。

4. 在BaseLoadBalancer的构造方法中会启动一个PingTask,这个PingTask用来检查Server是否有效,PingTask的默认执行时间间隔为10秒。

5. markServerDown方法用来标记一个服务是否有效,标记方式为调用Server对象的setAlive方法设置isAliveFlag属性为false。

6. getReachableServers方法用来获取所有有效的服务实例列表。

7. getAllServers方法用来获取所有服务的实例列表。

8. addServers方法表示向负载均衡器中添加一个新的服务实例列表。

BaseLoadBalancer的功能大概就这么多。

DynamicServerListLoadBalancer

DynamicServerListLoadBalancer是BaseLoadBalancer的一个子类,在DynamicServerListLoadBalancer中对基础负载均衡器的功能做了进一步的扩展,我们来看看。

1. 首先DynamicServerListLoadBalancer类一开始就声明了一个变量serverListImpl,serverListImpl变量的类型是一个ServerList<T extends Server>,这里的泛型得是Server的子类,ServerList是一个接口,里边定义了两个方法:一个getInitialListOfServers用来获取初始化的服务实例清单;另一个getUpdatedListOfServers用于获取更新的服务实例清单。

2. ServerList接口有很多实现类,DynamicServerListLoadBalancer默认使用了DomainExtractingServerList类作为ServerList的实现,但是在DomainExtractingServerList的构造方法中又传入了DiscoveryEnabledNIWSServerList对象,查看源码发现最终两个清单的获取方式是由DiscoveryEnabledNIWSServerList类来提供的。

3. DomainExtractingServerList类中的obtainServersViaDiscovery方法是用来发现服务实例并获取的,obtainServersViaDiscovery方法的主要逻辑是这样:首先依靠EurekaClient从服务注册中心获取到具体的服务实例InstanceInfo列表,然后对这个列表进行遍历,将状态为UP的实例转换成DiscoveryEnabledServer对象并放到一个集合中,最后将这个集合返回。

4. DynamicServerListLoadBalancer中还定义了一个ServerListUpdater.UpdateAction类型的服务更新器,Spring Cloud提供了两种服务更新策略:一种是PollingServerListUpdater,表示定时更新;另一种是EurekaNotificationServerListUpdater表示由Eureka的事件监听来驱动服务列表的更新操作,默认的实现策略是第一种,即定时更新,定时的方式很简单,创建Runnable,调用DynamicServerListLoadBalancer中updateAction对象的doUpdate方法,Runnable延迟启动时间为1秒,重复周期为30秒。

5. 在更新服务清单的时候,调用了我们在第一点提到的getUpdatedListOfServers方法,拿到实例清单之后,又调用了一个过滤器中的方法进行过滤。过滤器的类型有好几种,默认是DefaultNIWSServerListFilter,这是一个继承自ZoneAffinityServerListFilter的过滤器,具有区域感知功能。即它会对服务提供者所处的Zone和服务消费者所处的Zone进行比较,过滤掉哪些不是同一个区域的实例。

综上,DynamicServerListLoadBalancer主要是实现了服务实例清单在运行期间的动态更新能力,同时提供了对服务实例清单的过滤功能。

ZoneAwareLoadBalancer

ZoneAwareLoadBalancer是DynamicServerListLoadBalancer的子类,ZoneAwareLoadBalancer的出现主要是为了弥补DynamicServerListLoadBalancer的不足。由于DynamicServerListLoadBalancer中并没有重写chooseServer方法,所以DynamicServerListLoadBalancer中负责均衡的策略依然是我们在BaseLoadBalancer中分析出来的线性轮询策略,这种策略不具备区域感知功能,这样当需要跨区域调用时,可能会产生高延迟。ZoneAwareLoadBalancer重写了setServerListForZones方法,该方法在其父类中的功能主要是根据区域Zone分组的实例列表,为负载均衡器中的LoadBalancerStats对象创建ZoneStats并存入集合中,ZoneStats是一个用来存储每个Zone的状态和统计信息。重写之后的setServerListForZones方法主要做了两件事:一件是调用getLoadBalancer方法来创建负载均衡器,同时创建服务选择策略;另一件是对Zone区域中的实例清单进行检查,如果对应的Zone下已经没有实例了,则将Zone区域的实例列表清空,防止节点选择时出现异常。

OK,以上就是我们对负载均衡器的一个简单介绍,下一篇文章我们将继续介绍负载均衡策略

关注公众号【江南一点雨】,专注于 Spring Boot+微服务以及前后端分离等全栈技术,定期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

Spring Cloud中负载均衡器概览的更多相关文章

  1. Spring Cloud中的负载均衡策略

    在上篇博客(Spring Cloud中负载均衡器概览)中,我们大致的了解了一下Spring Cloud中有哪些负载均衡器,但是对于负载均衡策略我们并没有去详细了解,我们只是知道在BaseLoadBal ...

  2. Spring Cloud中服务的发现与消费

    之前没注意,微信公众号的图片不能引用到其他地方,本文图片显示不正常,原图在写完博客后已经删了,,,,,,所以本文小伙伴可以移步这里https://mp.weixin.qq.com/s/GoIZdwt5 ...

  3. Spring Cloud中Hystrix、Ribbon及Feign的熔断关系是什么?

    导读 今天和大家聊一聊在Spring Cloud微服务框架实践中,比较核心但是又很容易把人搞得稀里糊涂的一个问题,那就是在Spring Cloud中Hystrix.Ribbon以及Feign它们三者之 ...

  4. Spring Cloud中五大神兽总结(Eureka/Ribbon/Feign/Hystrix/zuul)

    Spring Cloud中五大神兽总结(Eureka/Ribbon/Feign/Hystrix/zuul) 1.Eureka Eureka是Netflix的一个子模块,也是核心模块之一.Eureka是 ...

  5. Spring Cloud中Feign如何统一设置验证token

    代码地址:https://github.com/hbbliyong/springcloud.git 原理是通过每个微服务请求之前都从认证服务获取认证之后的token,然后将token放入到请求头中带过 ...

  6. Spring Cloud中关于Feign的常见问题总结

    一.FeignClient接口,不能使用@GettingMapping 之类的组合注解 代码示例: @FeignClient("microservice-provider-user" ...

  7. Spring Cloud中,Eureka常见问题总结

    Spring Cloud中,Eureka常见问题总结. 1 eureka.environment: 指定环境 参考文档: 1 eureka.datacenter: 指定数据中心 参考文档: 使用配置项 ...

  8. spring Cloud中,解决Feign/Ribbon整合Hystrix第一次请求失败的问题?

    Spring Cloud中,Feign和Ribbon在整合了Hystrix后,可能会出现首次调用失败的问题,要如何解决该问题呢? 造成该问题的原因 Hystrix默认的超时时间是1秒,如果超过这个时间 ...

  9. spring cloud 中Actuator不显示更多信息的处理方式

    spring cloud 中Actuator不显示更多信息的处理方式 直接咨询了周大立,他说 management.security.enabled = false 就可以了: 学习了:http:// ...

随机推荐

  1. Aero问题

    有时候打开电脑会发现自己的桌面有点不一样,没有原来的好看.别着急,这是因为你的Aero没有正常启动.

  2. HDU6055 Regular polygon(计算几何)

    Description On a two-dimensional plane, give you n integer points. Your task is to figure out how ma ...

  3. C# 将数据表导出到Excel通用方法

    DataGrid dg = new DataGrid(); dg.DataSource = dt; dg.DataBind(); Response.Clear(); Response.Buffer = ...

  4. 特殊的ip地址

    特殊IP地址 一.0.0.0.0 严格说来,0.0.0.0已经不是一个真正意义上的IP地址了.它表示的是这样一个集合:所有不清楚的主机和目的网络.这里的“不清楚”是指在本机的路由表里没有特定条目指明如 ...

  5. Java将数据写入word文档(.doc)

    Java可用org.apache.poi包来操作word文档.org.apache.poi包可于官网上下载,解压后各jar作用如下图所示: 可根据需求导入对应的jar. 一.HWPFDocument类 ...

  6. python中的赋值和深浅拷贝

    python中,A object  = B object  是一种赋值操作,赋的值不是一个对象在内存中的空间,而只是这个对象在内存中的位置 . 此时当B对象里面的内容发生更改的时候,A对象也自然而然的 ...

  7. Gitlab CI 自动部署 asp.net core web api 到Docker容器

    为什么要写这个? 在一个系统长大的过程中会经历不断重构升级来满足商业的需求,而一个严谨的商业系统需要高效.稳定.可扩展,有时候还不得不考虑成本的问题.我希望能找到比较完整的开源解决方案来解决持续集成. ...

  8. include包含头文件的语句中,双引号和尖括号的区别

    include包含头文件的语句中,双引号和尖括号的区别   #include <>格式:引用标准库头文件,编译器从标准库目录开始搜索 #incluce ""格式:引用非 ...

  9. iOS - Block底层解析

    Block是iOS开发中一种比较特殊的数据结构,它可以保存一段代码,在合适的地方再调用,具有语法简介.回调方便.编程思路清晰.执行效率高等优点,受到众多猿猿的喜爱.但是Block在使用过程中,如果对B ...

  10. github+hexo搭建自己的博客网站(三)主题之外的一些基本配置(图片位置,文章目录功能)

    使用的yilia主题之后,还需要进行自己的定制配置 1.图片的位置 比如打赏的支付宝二维码图片,是在当前博客的source/assets/img/下 (不是当前主题) 配置:(在yilia主题下文件里 ...