高并发系统设计(二十一):【负载均衡】怎样提升系统的横向扩展能力?
负载均衡服务大体上可以分为两大类:一类是代理类的负载均衡服务;另一类是客户端负载均衡服务。
代理类的负载均衡服务,以单独的服务方式部署,所有的请求都要先经过负载均衡服务,在负载均衡服务中,选出一个合适的服务节点后,再由负载均衡服务,调用这个服务节点来实现流量的分发。
由于这类服务需要承担全量的请求,所以对于性能的要求极高。代理类的负载均衡服务有很多开源实现,比较著名的有LVS,Nginx等等。LVS在OSI网络模型中的第四层,传输层工作,所以LVS又可以称为四层负载;而Nginx运行在OSI网络模型中的第七层,应用层,所以又可以称它为七层负载。
在项目的架构中,一般会同时部署LVS和Nginx来做HTTP应用服务的负载均衡。也就是说,在入口处部署LVS,将流量分发到多个Nginx服务器上,再由Nginx服务器分发到应用服务器上:
因为:主要和LVS和Nginx的特点有关,LVS是在网络栈的四层做请求包的转发,请求包转发之后,由客户端和后端服务直接建立连接,后续的响应包不会再经过LVS服务器,所以相比Nginx,性能会更高,也能够承担更高的并发。
可LVS缺陷是工作在四层,而请求的URL是七层的概念,不能针对URL做更细致地请求分发,而且LVS也没有提供探测后端服务是否存活的机制;而Nginx虽然比LVS的性能差很多,但也可以承担每秒几万次的请求,并且它在配置上更加灵活,还可以感知后端服务是否出现问题。
因此,LVS适合在入口处,承担大流量的请求分发,而Nginx要部署在业务服务器之前做更细维度的请求分发。建议如果你的QPS在十万以内,那么可以考虑不引入LVS而直接使用Nginx作为唯一的负载均衡服务器,这样少维护一个组件,也会减少系统的维护成本。
不过这两个负载均衡服务适用于普通的Web服务,对于微服务架构来说,它们是不合适的。因为微服务架构中的服务节点存储在注册中心里,使用LVS就很难和注册中心交互,获取全量的服务节点列表。另外,一般微服务架构中,使用的是RPC协议而不是HTTP协议,所以Nginx也不能满足要求。
所以,我们会使用另一类的负载均衡服务,客户端负载均衡服务,也就是把负载均衡的服务内嵌在RPC客户端中。
它一般和客户端应用,部署在一个进程中,提供多种选择节点的策略,最终为客户端应用提供一个最佳的,可用的服务端节点。这类服务一般会结合注册中心来使用,注册中心提供服务节点的完整列表,客户端拿到列表之后使用负载均衡服务的策略选取一个合适的节点,然后将请求发到这个节点上。
负载均衡策略从大体上来看可以分为两类:
- 一类是静态策略,也就是说负载均衡服务器在选择服务节点时,不会参考后端服务的实际运行的状态。如,轮询的策略。轮询和带有权重的轮询策略,能够将请求尽量平均地分配到后端服务节点上,也就能够做到对于负载的均衡分配,在没有更好的动态策略之前,应该优先使用这两种策略,比如Nginx就会优先使用轮询的策略。
- 一类是动态策略,也就是说负载均衡服务器会依据后端服务的一些负载特性,来决定要选择哪一个服务节点。在负载均衡服务器上会收集对后端服务的调用信息,比如从负载均衡端到后端服务的活跃连接数,或者是调用的响应时间,然后从中选择连接数最少的服务,或者响应时间最短的后端服务
如何检测节点是否故障
淘宝开源的Nginx模块nginx_upstream_check_module了,这个模块可以让Nginx定期地探测后端服务的一个指定的接口,然后根据返回的状态码,来判断服务是否还存活。当探测不存活的次数达到一定阈值时,就自动将这个后端服务从负载均衡服务器中摘除。
在服务刚刚启动时,可以初始化默认的HTTP状态码是500,这样Nginx就不会很快将这个服务节点标记为可用,也就可以等待服务中,依赖的资源初始化完成,避免服务初始启动时的波动。
在完全初始化之后,再将HTTP状态码变更为200,Nginx经过两次探测后,就会标记服务为可用。在服务关闭时,也应该先将HTTP状态码变更为500,等待Nginx探测将服务标记为不可用后,前端的流量也就不会继续发往这个服务节点。在等待服务正在处理的请求全部处理完毕之后,再对服务做重启,可以避免直接重启导致正在处理的请求失败的问题。这是启动和关闭线上Web服务时的标准姿势,你可以在项目中参考使用。