首页 > 其他分享 >高并发系统设计——负载均衡技术选型

高并发系统设计——负载均衡技术选型

时间:2023-04-04 14:02:35浏览次数:36  
标签:负载 服务 请求 Nginx 并发 选型 均衡 节点


摘要

高并发系统设计的三个通用方法:缓存、异步和横向扩展,不过在实际的工作中,你经常使用的负载均衡的组件应该算是 Nginx,它的作用是承接前端的 HTTP 请求,然后将它们按照多种策略,分发给后端的多个业务服务器上。这样,我们可以随时通过扩容业务服务器的方式,来抵挡突发的流量高峰。与 DNS 不同的是,Nginx 可以在域名和请求 URL 地址的层面做更细致的流量分配,也提供更复杂的负载均衡策略。你可能会想到,在微服务架构中,我们也会启动多个服务节点,来承接从用户端到应用服务器的请求,自然会需要一个负载均衡服务器,作为流量的入口,实现流量的分发。那么在微服务架构中,如何使用负载均衡服务器呢?

一、负载均衡服务器的种类

负载均衡的含义是:将负载(访问的请求)“均衡”地分配到多个处理节点上。这样可以减少单个处理节点的请求量,提升整体系统的性能。同时,负载均衡服务器作为流量入口,可以对请求方屏蔽服务节点的部署细节,实现对于业务方无感知的扩容。它就像交通警察,不断地疏散交通,将汽车引入合适的道路上。负载均衡服务大体上可以分为两大类:一类是代理类的负载均衡服务;另一类是客户端负载均衡服务。

代理类的负载均衡服务,以单独的服务方式部署,所有的请求都要先经过负载均衡服务,在负载均衡服务中,选出一个合适的服务节点后,再由负载均衡服务,调用这个服务节点来实现流量的分发。

高并发系统设计——负载均衡技术选型_Nginx

由于这类服务需要承担全量的请求,所以对于性能的要求极高。代理类的负载均衡服务有很多开源实现,比较著名的有 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_02

二、常见的负载均衡策略

负载均衡策略从大体上来看可以分为两类:

  • 一类是静态策略,也就是说负载均衡服务器在选择服务节点时,不会参考后端服务的实际运行的状态。
  • 一类是动态策略,也就是说负载均衡服务器会依据后端服务的一些负载特性,来决定要选择哪一个服务节点。

常见的静态策略有几种,其中使用最广泛的是轮询的策略(RoundRobin,RR)这种策略会记录上次请求后端服务的地址或者序号,然后在请求时,按照服务列表的顺序,请求下一个后端服务节点。

AtomicInteger lastCounter = getLastCounter();// 获取上次请求的服务节点的序号 

List<String> serverList = getServerList(); // 获取服务列表

int currentIndex = lastCounter.addAndGet(); // 增加序列号

if(currentIndex >= serverList.size()) {

  currentIndex = 0;

}

setLastCounter(currentIndex);

return serverList.get(currentIndex);

它其实是一种通用的策略,基本上,大部分的负载均衡服务器都支持。轮询的策略可以做到将请求尽量平均地分配到所有服务节点上,但是,它没有考虑服务节点的具体配置情况。比如,你有三个服务节点,其中一个服务节点的配置是 8 核 8G,另外两个节点的配置是 4 核 4G,那么如果使用轮询的方式来平均分配请求的话,8 核 8G 的节点分到的请求数量和 4 核 4G 的一样多,就不能发挥性能上的优势了。

所以,我们考虑给节点加上权重值,比如给 8 核 8G 的机器配置权重为 2,那么就会给它分配双倍的流量,这种策略就是带有权重的轮询策略。

除了这两种策略之外,目前开源的负载均衡服务还提供了很多静态策略:

Nginx 提供了 ip_hash 和 url_hash 算法;

LVS 提供了按照请求的源地址,和目的地址做 hash 的策略;

Dubbo 也提供了随机选取策略,以及一致性 hash 的策略。

轮询和带有权重的轮询策略,能够将请求尽量平均地分配到后端服务节点上,也就能够做到对于负载的均衡分配,在没有更好的动态策略之前,应该优先使用这两种策略,比如 Nginx 就会优先使用轮询的策略。

而目前开源的负载均衡服务中,也会提供一些动态策略,我强调一下它们的原理。在负载均衡服务器上会收集对后端服务的调用信息,比如从负载均衡端到后端服务的活跃连接数,或者是调用的响应时间,然后从中选择连接数最少的服务,或者响应时间最短的后端服务。

Dubbo 提供的 LeastAcive 策略,就是优先选择活跃连接数最少的服务;

Spring Cloud 全家桶中的 Ribbon 提供了 WeightedResponseTimeRule 是使用响应时间,给每个服务节点计算一个权重,然后依据这个权重,来给调用方分配服务节点。

这些策略的思考点是从调用方的角度出发,选择负载最小、资源最空闲的服务来调用,以期望能得到更高的服务调用性能,也就能最大化地使用服务器的空闲资源,请求也会响应地更迅速,**所以,我建议你,**在实际开发中,优先考虑使用动态的策略。

到目前为止,你已经可以根据上面的分析,选择适合自己的负载均衡策略,并选择一个最优的服务节点,**那么问题来了:**你怎么保证选择出来的这个节点,一定是一个可以正常服务的节点呢?如果你采用的是轮询的策略,选择出来的,是一个故障节点又要怎么办呢?所以,为了降低请求被分配到一个故障节点的几率,有些负载均衡服务器,还提供了对服务节点的故障检测功能。

三、检测节点是否故障

在微服务化架构中,服务节点会定期地向注册中心发送心跳包,这样注册中心就能够知晓服务节点是否故障,也就可以确认传递给负载均衡服务的节点,一定是可用的。

但对于 Nginx 来说,我们要如何保证配置的服务节点是可用的呢?

这就要感谢淘宝开源的 Nginx 模块nginx_upstream_check_module了,这个模块可以让 Nginx 定期地探测后端服务的一个指定的接口,然后根据返回的状态码,来判断服务是否还存活。当探测不存活的次数达到一定阈值时,就自动将这个后端服务从负载均衡服务器中摘除。它的配置样例如下:

upstream server {

        server 192.168.1.1:8080;

        server 192.168.1.2:8080;

        check interval=3000 rise=2 fall=5 timeout=1000 type=http default_down=true;// 检测间隔为 3 秒,检测超时时间是 1 秒,使用 http 协议。如果连续失败次数达到 5 次就认为服务不可用;如果连续连续成功次数达到 2 次,则认为服务可用。后端服务刚启动时状态是不可用的

        check_http_send "GET /health_check HTTP/1.0\r\n\r\n"; // 检测 URL

        check_http_expect_alive http_2xx; // 检测返回状态码为 200 时认为检测成功

}

Nginx 按照上面的方式配置之后,你的业务服务器也要实现一个“/health_check”的接口,在这个接口中返回的 HTTP 状态码,这个返回的状态码可以存储在配置中心中,这样在变更状态码时,就不需要重启服务了。

节点检测的功能,还能够帮助我们实现 Web 服务的优雅关闭。服务的优雅关闭需要先切除流量再关闭服务,使用了注册中心之后,就可以先从注册中心中摘除节点,再重启服务,以便达到优雅关闭的目的。那么 Web 服务要如何实现优雅关闭呢?接下来,我来给你了解一下,有了节点检测功能之后,服务是如何启动和关闭的。

在服务刚刚启动时,可以初始化默认的 HTTP 状态码是 500,这样 Nginx 就不会很快将这个服务节点标记为可用,也就可以等待服务中,依赖的资源初始化完成,避免服务初始启动时的波动。在完全初始化之后,再将 HTTP 状态码变更为 200,Nginx 经过两次探测后,就会标记服务为可用。在服务关闭时,也应该先将 HTTP 状态码变更为 500,等待 Nginx 探测将服务标记为不可用后,前端的流量也就不会继续发往这个服务节点。在等待服务正在处理的请求全部处理完毕之后,再对服务做重启,可以避免直接重启导致正在处理的请求失败的问题。这是启动和关闭线上 Web 服务时的标准姿势,你可以在项目中参考使用。

博文参考

标签:负载,服务,请求,Nginx,并发,选型,均衡,节点
From: https://blog.51cto.com/u_13643065/6168627

相关文章

  • Tomcat 9.0.26 高并发场景下DeadLock问题排查与修复
    vivo互联网技术微信公众号 作者:黄卫兵、陈锦霞一、Tomcat容器9.0.26版本Deadlock问题1.1问题现象1.1.1 发生Deadlock的背景某接口/get.do压测,3分钟后,成功事务数TPS由1W骤降至0。1.1.2 Tomcat服务器出现大量的CLOSE_WAIT被压测服务器,出现TCPCLOSE_WAIT状态个数在200~......
  • 高并发系统设计——注册中心技术选型
    摘要服务注册和发现不是一个新的概念,你在之前的实际项目中也一定了解过,只是你可能没怎么注意罢了。比如说,你知道Nginx是一个反向代理组件,那么Nginx需要知道,应用服务器的地址是什么,这样才能够将流量透传到应用服务器上,这就是服务发现的过程。那么Nginx是怎么实现的呢?它是把应......
  • 高并发系统设计——系统架构的微服务化选型
    摘要现在,你的系统运行稳定,好评不断,每天高峰期的流量,已经达到了10000/s请求,DAU也涨到了几十万。CEO非常高兴,打算继续完善产品功能,以便进行新一轮的运营推广,争取在下个双十一可以将DAU冲击过百万。这时,你开始考虑,怎么通过技术上的优化改造,来支撑更高的并发流量,比如支撑过百万的......
  • 高并发系统设计——RPC框架的技术选型
    摘要服务拆分单独部署后,引入的服务跨网络通信的问题;在拆分成多个小服务之后,服务如何治理的问题。如果想要解决这两方面问题,你需要了解,微服务化所需要的中间件的基本原理,和使用技巧,那么本节课,我会带你掌握,解决第一点问题的核心组件:RPC框架。你的垂直电商系统的QPS已经达到了每秒......
  • 高并发系统设计——数据库技术选型
    摘要我们用池化技术解决了数据库连接复用的问题,这时,你的垂直电商系统虽然整体架构上没有变化,但是和数据库交互的过程有了变化,在你的Web工程和数据库之间增加了数据库连接池,减少了频繁创建连接的成本,从上节课的测试来看性能上可以提升80%。现在的架构图如下所示:此时,你的数据库还......
  • 高并发系统设计——“三高”解决方案
    摘要提到互联网系统设计,你可能听到最多的词儿就是“三高”,也就是“高并发”“高性能”“高可用”,它们是互联网系统架构设计永恒的主题。在前两节课中,我带你了解了高并发系统设计的含义,意义以及分层设计原则,接下来,我想带你整体了解一下高并发系统设计的目标,然后在此基础上,进入我们今......
  • Taro、uni-app、Mpx选型对比
    选型对比框架技术栈案例微信小程序支付宝小程序百度小程序头条小程序H5AppTaroReact丰富√√√√√√uni-appVue丰富√√√√√√MpxVue少√√√√××社区生态taro官方发布了taro-ui库,awesome里三方组件不太多。uni-app官方发布了uni-ui库,还有个插件市场,里面轮子很多。mpx提供了完......
  • 分布式系统——并发条件下如何保证缓存与DB数据一致性
    什么是数据一致性我们常说的数据一致性指的是在程序运行过程中本地缓存、分布式缓存、数据库三者之间的数据一致性常见的本地缓存有hashmap、currenthashmap、guavacache、caffeine分布式缓存常见的有redis、memcache常见数据不一致常见有:本地缓存与mysql不一致redis......
  • 如何理解MySQL的MVCC多版本并发控制
    前言我们知道在mysql中存在四种隔离级别(读未提交、读已提交、可重复读、序列化),它默认的就是隔离级别就是可重复读,它能够解决脏读、不可重复读问题,并且在innodb引擎下能部分解决幻读问题。在mysqlinnodb存储引擎下RC(读已提交),RR(可重复读)基于MVCC(多版本并发控制)进行并发事务控......
  • 【Java 并发】【八】【Atomic】【二】AtomicInteger、AtomicBoolean原理
    1 前言这节我们从AtomicInteger这个比较简单的原子类开始,来看看AtomicInteger的底层原理。2  实测样例对比线程安全性在说AtomicInteger的底层原理之前呢,我们先来看个例子感受下原子类:static修饰的共享变量,我们开启两个线程对共享变量进行10000次+1的操作2.1  Integer......