Web后端系统架构漫谈(1)——负载均衡

在有一定规模的分布式web系统中,负载均衡是必不可少的。负载均衡这四个字已经完美解释了它的含义:将负载(请求/数据)均匀地分配到多个业务系统中。这里要讨论的是web后端架构设计中使用到的负载均衡技术。

DNS轮询

在做域名解析的时候针对一个域名一般都可以添加多条A记录,A记录可以指定一个域名所指向的IP地址,DNS在对一个域名进行解析时,会轮询式地返回其A记录中的任意一个IP地址。以这种方式可以实现DNS级别的负载均衡。

DNS轮询是一种非常简单的负载均衡手段,只需要对域名配置多条A记录即可,很多web系统在最顶层都会使用DNS轮询做负载均衡。但是DNS轮询也有一些缺点,比如它是无脑式轮询,并不会根据每台服务器的负载能力来分配请求。这可能造成负载能力强的服务器只分配到比较少的请求,而负载能力弱的服务器分配到较多请求导致处理不过来。另外由于各大网络运营商都会缓存DNS信息以快速响应DNS解析请求,因此有可能出现某条A记录对应IP的服务器宕机,但DNS缓存还把域名指向这个IP,一些用户无法访问网站的情况。而且就算删除了宕机服务器对应的A记录,DNS信息生效也需要时间,从几分钟到几小时不等,在这段时间内,已经缓存了宕机服务器IP的计算机还是会继续访问这些有问题的服务器,没有缓存DNS信息的计算机可能会从运营商处获取到宕机服务器的IP,同样也是无法访问。

现在很多域名运营商还提供智能DNS轮询,所谓智能,就是返回一个离客户端“最近”的服务器。一般来说,服务器都会部署在各大网络运营商的机房,比如移动机房、联通机房、电信机房。不同运营商之间的网络通信要比同运营商内的网络通信要快(因为不同运营商之间的网络要通信要跨越主干网,更加耗时)。如果识别到客户端是中国移动的,那就返回一个移动机房的服务器IP。这种做法实现起来不难,只要在DNS服务器内部维护一个区域/运营商IP范围列表就可以了。

鉴于DNS轮询的这些缺点,很少有web系统会只使用DNS轮询来作为系统负载均衡的方式。比较常见的组合有DNS轮询+LVS、DNS轮询+Nginx。

接入层负载均衡——Nginx

在接入层一般采用Nginx做反向代理来实现负载均衡,对一个域名配置的A记录指向的IP一般就是接入层Nginx反向代理的外网IP了。在Nginx的配置文件nginx.conf中,可以配置下游服务器的负载均衡方式。一般来说有下列方式:

  1. 给每台下游服务器配置权重然后按照权重来分配请求。
  2. 使用轮询的方式分配请求。
  3. 将请求分配给当前最少连接数的服务器。
  4. 对请求的IP进行哈希计算,对服务器数量取模后路由到相应的服务器。这种方式会使同一个IP的请求都路由到同一台服务器上。

在Nginx服务器的下游,一般是各种web-server,这些web-server一般会调用下游的service来处理请求。

service层负载均衡

在互联网后端架构设计中,web-server的下游是各种service,service层负责对外提供服务,比如用户账户service、商品信息service、订单service、消息推送service等等。在web-server使用服务连接池可以实现web-server层到service层的负载均衡。web-server的服务连接池会建立和下游的service的多个连接,每次随机从服务连接池中取出一个连接来使用,这就实现了负载均衡。

数据层负载均衡

数据层中主要是各种db和cache,在互联网web后端中,单表数据量往往很大,需要对db进行水平切分。一般来说db水平切分有两种方式:

  1. 按id范围切分
  2. 按id哈希切分

举个简单的例子,一个用户表有1亿行数据。

如果按id范围切分,我们可以将其按照每1000W数据一个分库来做水平切分,于是就会有user_0~user_9一共10个分库。然后我们根据请求的id范围来路由到对应的db中。按id范围切分的优点是扩展简单,我们只需要新增一个user_10的库,就可以存放1亿之后的数据,但也有一个问题就是请求可能不均匀,因为一般来说新数据要比老数据活跃。

如果按id哈希切分,先计算id的哈希值,再将这个哈希值对db的个数去模获得对应的db。这种做法的数据均匀性会比较好,但是在扩容的时候稍微麻烦一些,需要数据迁移等操作。

web后端系统架构负载均衡体系图示

说了这么多,还是用一幅图来更直观地说明下:

web后端系统架构负载均衡体系