网站优化

网站优化

Products

当前位置:首页 > 网站优化 >

如何通过Nginx实现多进程高并发下的低时延、高可靠?

GG网络技术分享 2025-11-23 10:16 3


nginx是俄罗斯软件工程师Igor Sysoev开发的免费开源web服务器软件, 聚焦于高性Neng,高并发和低内存消耗问题,所以呢成为业界公认的高性Neng服务器,并逐渐成为业内主流的web服务器。iii)如何确保进程见高可靠通信。如上图所示, 实际应用中业务程序通过轮询不同的twemproxy来提高qps,一边实现负载均衡,拉倒吧...。

为了适应公有云平台上业务方的高并发需求, 所以呢决定借助于twemproxy来Zuo二次开发,把nginx的高性Neng、高可靠高并发机制引入到twemproxy中,通过master+多worker进程来实现七层转发功Neng。Nginx多进程提供服务过程如下图所示:.iii)如何确保进程见高可靠通信。

开发背景

现有开源缓存代理中间件有twemproxy、 codis等,其中twemproxy为单进程单线程模型,只支持memcache单机版和redis单机版,dou不支持集群版功Neng。

由于twemproxy无法利用多核特性, 所以呢性Neng低下短连接QPS大约为3W,长连接QPS大约为13W; codis起几十个线程, 他破防了。 短连接qps不超过10万;一边某些场景这些开源软件时延抖动厉害。

Twemproxy

Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII协议和geng新的Redis协议。它全部用C写成,使用Apache License授权。支持以下特性:

  • 速度快
  • 轻量级
  • 维护持久的服务器连接
  • 启用请求和响应的管道
  • 支持代理到多个后端缓存服务器
  • 一边支持多个服务器池
  • 多个服务器自动分享数据
  • 可一边连接后端多个缓存集群
  • 实现了完整的 memcached ascii 和 redis 协议.
  • 服务器池配置简单, 通过一个 YAML 文件即可
  • 一致性hash
  • 详细的监控统计信息
  • 支持 Linux, *BSD, OS X and Solaris
  • 支持设置HashTag
  • 连接复用,内存复用,提高效率

滴云memcache缓存集群拓扑结构

如上图所示,实际应用中业务程序通过轮询不同的twemproxy来提高qps,一边实现负载均衡。

推特原生twemproxy瓶颈

嚯... 如今twemproxy凭借其高性Neng的优势, 存在以下缺陷,如下:

  • i)单进程单线程,无法充分发挥服务器多核cpu的性Neng
  • ii)当twemproxy qps短连接达到8000后消耗cpu超过70%,时延陡增。
  • iii)大流量下造成IO阻塞, 无法处理geng多请求,qps上不去,业务时延飙升
  • iiii)维护成本高,Ru果想要充分发挥服务器的所有资源包括cpu、网络io等,就必须建立多个twemproxy实例,维护成本高
  • iiiii)扩容、升级不便

Nginx

nginx

nginx是俄罗斯软件工程师Igor Sysoev开发的免费开源web服务器软件,聚焦于高性Neng,高并发和低内存消耗问题,所以呢成为业界公认的高性Neng服务器,并逐渐成为业内主流的web服务器。主要特点有:,啥玩意儿?

  • i)完全借助epoll机制实现异步操作,避免阻塞。
  • ii)重复利用现有服务器的多核资源。
  • iii)充分利用CPU 亲和性, 把每个进程与固定CPU绑定在一起,给定的CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性,减少进程调度开销。
  • iiii)请求响应快
  • iiiii)支持模块化开发, 性好
  • iiiii)Master+多worker进程方式,确保worker进程可靠工作。当worker进程出错时Ke以快速拉起新的worker子进程来提供服务。
  • iiiiii)内存池、连接池等细节设计保障低内存消耗。
  • iiiiii)热部署支持, master与worker进程分离设计模式,使其具有热部署功Neng。
  • iiiiiii)升级方便,升级过程不会对业务造成任何伤害。

Nginx master+worker多进程机制在twemproxy中的应用

为什么选择nginx多进程机制Zuo为参考,看好你哦!?

Twemproxy和nginxdou属于网络io密集型应用, dou属于七层转发应用,时延要求较高,应用场景基本相同。

Nginx充分利用了多核cpu资源,性Neng好,时延低,是不是?。

Master-worker多进程机制原理

Master-worker进程机制采用一个master进程来管理多个worker进程。每一个worker进程dou是繁忙的, 它们在真正地提供服务,master进程则hen“清闲”,只负责监控管理worker进程, 包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后会自动重新启动新的worker进程,在我看来...。

worker进程负责处理客户端的网络请求, 多个worker进程一边处理来自客户端的不同请求,worker进程数可配置,很棒。。

多进程关键性Neng问题点

master-worker多进程模式需要解决的问题主要有:,从头再来。

  • i)linux内核低版本, “惊群”问题
  • ii) linux内核低版本,负载均衡问题
  • iii)linux内核高版本新特性如何利用
  • iii)如何确保进程见高可靠通信
  • iiii)如何减少worker进程在不同cpu切换的开销
  • iiiii)master进程如何汇总各个工作进程的监控数据
  • iiiiii)worker进程异常, 如何快速恢复

linux内核低版本关键技术问题

由于linux低内核版本缺陷,所以呢存在”惊群”、负载不均问题,解决办法完全依赖应用层代码保障,靠谱。。

如何解决“惊群”问题

复盘一下。 当客户端发起连接后 由于所有的worker子进程dou监听着同一个端口,内核协议栈在检测到客户端连接后会激活所有休眠的worker子进程,到头来只会有一个子进程成功建立新连接,其他子进程dou会accept失败。

Accept失败的子进程是不应该被内核唤醒的, 主要原因是它们被唤醒的操作是多余的,占用本不应该被占用的系统资源,引起不必要的进程上下文切换,增加了系统开销,一边也影响了客户端连接的时延。

礼貌吗? “惊群”问题是多个子进程一边监听同一个端口引起的, 所以呢解决的方法是同一时刻只让一个子进程监听服务器端口,这样新连接事件只会唤醒唯一正在监听端口的子进程。

所以呢“惊群”问题通过非阻塞的accept锁来实现进程互斥accept, 其原理是:在worker进程主循环中非阻塞trylock获取accept锁,Ru果trylock成功,则此进程把监听端口对应的fd通过epoll_ctl加入到本进程自由的epoll事件集;Ru果trylock失败,则把监听fd从本进程对应的epoll事件集中清除。

如何解决“负载均衡“问题

复盘一下。 在多个子进程争抢处理同一个新连接事件时 一定只有一个worker子进程到头来会成功建立连接,接着,它会一直处理这个连接直到连接关闭。这样, Ru果有的子进程“运气”hen好,它们抢着建立并处理了大部分连接,其他子进程就只Neng处理少量连接,这对多核cpu架构下的应用hen不利。理想情况下每个子进程应该是平等的,每个worker子进程应该大致平均的处理客户端连接请求。Ru果worker子进程负载不均衡,必然影响整体服务的性Neng。

nginx通过连接阈值机制来实现负载均衡, 其原理如下:每个进程dou有各自的大连接数阈值max_threshold和当前连接阈值数local_threshold,和当前连接数阈值,进程每接收一个新的连接,local_threshold增一,连接断开后local_threashold减一。Ru果local_threshold超过max_threshold, 则不去获取accept锁,把accept机会留给其他进程,一边把local_threshold减1,这样下次就有机会获取accept锁,接收客户端连接了。

在实际业务应用中, 有的业务采用长连接和twemproxy建立连接,连接数大可Neng就几百连接,Ru果设置max_threshold阈值过大, 是不是? 多个连接Ru果一边压到twemproxy,则hen容易引起所有连接被同一个进程获取从而造成不均衡。

为了尽量减少负载不均衡, 在实际应用中,新增了epoll_wait超时时间配置选项,把该超时时间设短,这样减少空闲进程在epoll_wait上的等待事件,从而Ke以geng快相应客户端连接,并有效避免负载不均衡,不错。。

Linux内核高版本TCP REUSEPORT特性如何利用

什么是reuseport?

reuseport是一种套接字复用机制, 它允许你将多个套接字bind在同一个IP地址/端口对上,这样一来就Ke以建立多个服务来接受到同一个端口的连接,换个角度。。

支持reuseport和不支持reuseport的区别

我直接起飞。 Ru果linux内核版本小于3.,则不支持reuseport。

不支持该特性的内核,一个ip+port组合,只Neng被监听bind一次。这样在多核环境下 往往只Neng有一个线程是listener,也就是同一时刻只Neng由一个进程或者线程Zuoaccept处理,在高并发情况下往往这就是性Neng瓶颈。其网络模型如下:

在Linux kernel .9带来了reuseport特性, 它Ke以解决上面的问题,其网络模型如下:,完善一下。

reuseport是支持多个进程或者线程绑定到同一端口,提高服务器程序的吞吐性Neng,其优点体现在如下几个方面:,这玩意儿...

  • i)允许多个套接字 bind/listen 同一个TCP/UDP端口
  • ii)每一个线程拥有自己的服务器套接字
  • iii)在服务器套接字上没有了锁的竞争,主要原因是每个进程一个服务器套接字
  • iiii)内核层面实现负载均衡
  • iiiii)平安层面监听同一个端口的套接字只Neng位于同一个用户下面

Master进程和worker进程如何通信?

不妨... 由于master进程需要实时获取worker进程的工作状态, 并实时汇总worker进程的各种统计信息,所以选择一种可靠的进程间通信方式必不可少。

在twemproxy改过过程中,直接参考nginx的信号量机制和channel机制来实现父子进程见通信。Master进程子进程是否异常, 从而快速直接的反应出来;还有啊,借助socketpair,封装出channel接口来完成父子进程见异步通信, 踩雷了。 master进程依靠该机制来统计子进程的各种统计信息并汇总,通过获取来自master的汇总信息来判断整个twemproxy中间件的稳定性、可靠性。

简直了。 配置下发过程:主进程接收实时配置信息, 然后通过channel机制发送给所有的worker进程,各个worker进程收到配置信息后应答给工作进程。流程如下:

获取监控信息流程和配置下发流程基本相同, master进程收到各个工作进程的应答后由master进程Zuo统一汇总,然后发送给客户端,栓Q了...。

如何减少worker进程在不同cpu切换的开销

CPU 亲和性 就是进程要在某个给定的 CPU 别纠结... 上尽量长时间地运行而不被迁移到其他处理器的倾向性。

Linux 内核进程调度器天生就具有被称为 软 CPU 亲和性 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们希望的,主要原因是进程迁移的频率小就意味着产生的负载小。具体参考sched_setaffinity函数,等着瞧。。

worker进程异常如何减少对业务的影响?

盘它... 经常出现这样的情况:某个多线程服务跑几个月后主要原因是未知原因进程挂了到头来造成整个服务dou会不可用。

造起来。 这时候, master+多worker的多进程模型就体现了它的优势,Ru果代码有隐藏的并且不容易触发的bug,某个时候Ru果某个请求触发了这个bug,则处理这个请求的worker进程会段错误退出。但是其他worker进程不会收到任何的影响, 也就是说Ru果一个改过后的twemproxy起了20个worker进程,某个时候一个隐藏bug被某个请求触发,则只有处理该请求的进程段错误异常,其他19个进程不会受到任何影响,该隐藏bug触发后影响面仅为5%。Ru果是多线程模型,则影响面会是100%。

摸鱼。 Ru果某个worker进程挂了 master父进程会感知到这个信号,然后重新拉起一个worker进程,实现瞬间无感知”拉起”恢复。以下为模拟触发异常段错误流程:

如上图所示, 杀掉31420 worker进程后maste 呃... r进程会立马在拉起一个31451工作进程,实现了快速恢复。

多进程异常, 自动”拉起”功Neng源码,Ke以参考如下demo:

https://github.com/y123456yz/reading-code-of-nginx-./blob/master/nginx-./src/demo.c

网络优化

网卡多队列

在其实吧线后发现软中断过高,几乎大部分dou集中在一个或者几个CPU上,严重影响客户端连接和数据转发,qps上不去,时延抖动厉害,这家伙...。

RSS是网卡的硬件特性,实现了多队列,Ke以将不同的流分发到不同的CPU上。支持RSS的网卡, 通过多队列技术,每个队列对应一个中断号,通过对每个中断的绑定,Ke以实现网卡中断在cpu多核上的分配,到头来达到负载均衡的作用,我惊呆了。。

可怕的40ms

奥利给! 原生twemproxy在线上跑得过程中, 发现时延波动hen大,抓包发现其中部分数据包应答出现了40ms左右的时延,拉高了整体时延抓包如下:

解决办法如下:在recv系统调用后调用一次setsockopt函数,设置TCP_QUICKACK。代码修改如下:,哈基米!

Twemproxy改过前后性Neng对比

线上真实流量时延对比

改过前线上twemproxy集群时延

线上集群完全采用开源twemproxyZuo代理, 架构如下:

我个人认为... 未改过前线上twemproxy+memcache集群,qps=~,长连接,客户端时延分布如下图所示:

在twemproxy机器上使用tcprstat监控到的网卡时延如下:

从上面两个图Ke以kan出,采用原生twemproxy,时延高,一边抖动厉害。

参照nginx改过后的twemproxy时延

从一个旁观者的角度看... 线上集群一个twemproxy采用官方原生twemproxy,另一个为改过后的twemproxy,其中改过后的twemproxy配置worker进程数为1,保持和原生开源twemproxy进程数一致,架构如下:

开倒车。 替换线上集群两个代理中的一个后长连接,qps=~,客户端埋点监控时延分布如下:

替换两个proxy中的一个后使用tcprstat在代理集群上面查kan两个代理的时延分布如下:,扎心了...

实际上... 原生twemproxy节点机器上的时延分布:

另一个改过后的twemproxy节点机器上的时延分布:


提交需求或反馈

Demand feedback