欢迎光临
我们一直在努力

培训与开发的作用(1.开发的特点及特点分析-苏州安嘉开发)

一、发展背景

为了满足业务端对公有云平台的高并发需求,决定利用二次开发的方式,将nginx的高性能、高可靠、高并发机制引入其中,实现通过+多进程的七层转发功能。

2

2.1 简介

是一个快速的单线程代理,支持 ASCII 协议和较新的 Redis 协议。它全部用 C 语言编写并在 2.0 下获得许可。支持以下功能:

i) 快

ii) 轻量级

iii) 保持持久的服务器连接

iii) 启用请求和响应管道

2.2 缓存集群拓扑

图 1 缓存集群拓扑

如上图所示,在实际应用中多核服务器,业务程序通过轮询不同的来提高qps,同时实现负载均衡。

注意:没有官方集群版本和持久化功能。集群版本和持久化功能是我们自己开发的。

2.3 原生瓶颈

如今,凭借其高性能的优势,已被众多互联网公司广泛使用,并占据了不可动摇的地位。但在实际生产环境中,存在以下缺陷,如下:

i) 单进程单线程,无法充分发挥服务器多核CPU的性能

ii) 当qps短连接达到8000时,CPU消耗超过70%多核服务器,延迟急剧增加。

iii) 流量大导致IO阻塞,无法处理更多请求,qps上不去,服务延迟飙升

iii) 维护成本高。要想充分发挥服务器的所有资源,包括cpu、网络io等,必须建立多个实例,维护成本高

iii) 扩展升级不便

原生进程呈现如下现象:一个人工作,多人观看。多核服务器只有一个 cpu 工作,资源没有得到充分利用。

3.Nginx

Nginx 是由俄罗斯软件工程师 Igor 开发的免费开源 Web 服务器软件。它专注于高性能、高并发和低内存消耗。因此,它成为了业界公认的高性能服务器,并逐渐成为业界主流的Web服务器。主要特点是:

i) 异步操作完全由epoll机制实现,避免阻塞。

ii) 重用现有服务器的多核资源。

)+多进程模式,确保进程可靠运行。当某个进程出现故障时,可以快速拉起新的子进程来提供服务。

) 内存池和连接池的详细设计保证了低内存消耗。

) 热部署支持,与流程设计模式分离,使其可热部署。

) 易于升级,升级过程不会对业务造成任何伤害。

Nginx多进程服务流程如下图所示:

4 Nginx+多进程机制在中的应用

4.1 为什么选择nginx多进程机制作为参考?

nginx和nginx都是网络io密集型应用,都属于七层转发应用,延迟要求高,应用场景基本一致。

Nginx充分利用多核CPU资源,性能好,延迟低。

4.2 – 多进程机制原理

– 进程机制使用一个进程管理多个进程。每个进程都很忙,它们实际上是在提供服务,而进程很“空闲”,只负责监控和管理进程,包括:接收外界的信号,向各个进程发送信号,监控进程的运行状态进程,当进程退出后(异常),会自动重启一个新进程。

进程负责处理客户端的网络请求,多个进程同时处理来自客户端的不同请求,进程数可以配置。

4.3 多进程的关键性能问题

– 多进程模式下需要解决的主要问题有:

i) 低版本的linux内核(低于2.6版本),“ herd”问题

ii) linux内核版本低(2.6以下),负载均衡问题

iii) 如何使用Linux内核高版本(3.9及以上版本)的新特性

iii) 如何确保进程看到高度可靠的通信

iiii) 如何减少不同CPU之间进程切换的开销

iii) 进程如何聚合每个工作进程的监控数据

) 进程异常,如何快速恢复

4.3.1 低版本Linux内核的关键技术问题

由于Linux低内核版本的缺陷,存在“震群”和负载不均的问题。解决方案完全依赖于应用层代码的保护。

4.3.1.1 如何解决“惊群”问题

当客户端发起连接时,由于所有子进程都在监听同一个端口,内核协议栈在检测到客户端连接后会激活所有休眠的子进程。最后,只有一个子进程会成功建立新的连接。所有其他子进程都将失败。

失败的子进程不应该被内核唤醒,因为它们的唤醒操作是多余的,占用了不应该占用的系统资源,造成不必要的进程上下文切换,增加了系统开销,还影响了终端连接的客户延迟。

“震群”问题是多个子进程同时监听同一个端口造成的,所以解决方法是让一个子进程同时监听服务器端口,这样新的连接事件只会唤醒正在侦听端口的唯一子进程。

因此,“冲击组”问题通过非阻塞锁实现进程互斥()。原理是:在进程的主循环中非阻塞获取锁,如果成功,进程会将监听端口对应的fd添加到()本进程的空闲epoll事件集中;如果失败,则从该进程对应的epoll事件集中清除监控fd。

Nginx 实现了两组互斥锁:基于原子操作和信号量的互斥锁,以及基于文件锁封装的互斥锁。考虑到锁的平台可移植性和通用性,在改造选择时,选择文件锁实现。

如果成功获取锁的进程占用锁的时间过长,其他空闲进程在这段时间内无法获取锁,从而无法接受新的连接。最后客户端连接的相应时间变长,qps低,负载严重不平衡。为了解决这个问题,选择了post事件队列的方式来提高性能,并且成功获取锁的过程,工作流程如下:

1、获取锁成功

2、通过获取所有事件信息,将所有被监控的事件信息添加到列表中,将现有连接触发的读写事件信息添加到列表中。

3.执行列表中的所有事件

4.锁定

5. 执行列表中的事件。

工艺主循环流程图如下:

从上图可以看出,该流程使用epoll来实现网络的异步收发。当客户端连接时,进程循环检测客户端的各种网络事件和后端的网络事件,并进行相应的处理。

各进程整体网络I/O处理流程图如下:

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

当多个子进程争相处理同一个新的连接事件时,只有一个子进程最终必须成功建立连接,然后它会继续处理连接,直到连接关闭。这样一来,如果某些子进程“侥幸”,就急于建立并处理大部分连接,而其他子进程只能处理少量连接,这对于多核CPU架构下的应用是非常不利的. 理想情况下,每个子进程应该是平等的,每个子进程应该大致平等地处理客户端连接请求。如果子进程负载不均衡,势必会影响整体服务的性能。

Nginx 通过连接阈值机制实现负载均衡。原理如下:每个进程都有自己的最大连接阈值、当前连接阈值、当前连接阈值。进程每收到一个新的连接,就加一,连接断开。打开后,减去一个。超过则不获取锁,将机会留给其他进程,同时减1,这样下次就有机会获取锁,接收客户端连接。

在实际业务应用中,有些业务使用长连接,建立连接,最大连接数可能是上百个连接。如果阈值设置过大,如果同时按下多个连接,很容易导致所有连接都被同一个进程获取。导致不平衡。

为了尽量减少负载不平衡,在实际应用中,增加了一个超时配置选项,将超时时间设置为较短的时间,减少了互联网上空闲进程的等待事件,使得对应的客户端连接可以更快可以有效避免负载。不平衡。

4.3.2 如何在更高版本中使用Linux内核的TCP特性

4.3.2.1 什么是?

是一种套接字复用机制,允许您将多个套接字绑定到同一个 IP 地址/端口对,以便可以建立多个服务来接受到同一个端口的连接。

4.3.2.2 支持和不支持的区别

如果 Linux 内核版本低于 3.9,则不支持(注:部分发行版已在较低版本打补丁,因此部分较低的 Linux 发行版也支持此功能)。

对于不支持该特性的内核,一个ip+port的组合只能被监听和绑定一次。这样,在多核环境中,往往只能有一个线程(或进程),即只有一个进程或线程可以同时进行处理。在高并发的情况下,这往往是性能瓶颈。其网络模型如下:

在Linux 3.9带来的特性,可以解决以上问题,网络模型如下:

它支持多个进程或线程绑定到同一个端口,提高了服务器程序的吞吐性能。其优势体现在以下几个方面:

i) 允许多个套接字绑定()/() 同一个 TCP/UDP 端口

ii) 每个线程都有自己的服务器套接字

iii) 服务器套接字上没有锁争用,因为每个进程有一个服务器套接字

iii) 内核级别的负载均衡

) 在安全级别上,监听同一端口的套接字只能位于同一用户下

4.3.3 进程如何相互通信?

由于进程需要实时获取进程的工作状态并实时汇总进程的各种统计信息,因此选择可靠的进程间通信方式至关重要。

在改造过程中,直接参考nginx的信号量机制和机制(依赖)来实现父子进程之间的通信。进程使用信号量机制检测子进程是否异常,从而快速直接响应;另外,通过封装接口完成父子进程之间的异步通信,进程依靠这种机制统计子进程的各种统计信息并汇总,通过获取汇总来判断整个中间件的稳定性和可靠性从中得到的信息。

配置下发流程:主进程接收实时配置信息,然后通过某种机制发送给所有进程,每个进程收到配置信息后响应工作进程。过程如下:

获取监控信息的过程与下发配置的过程基本相同。进程收到各个工作进程的响应后,会做一个统一的汇总发送给客户端。

4.3.4 如何减少不同CPU之间进程切换的开销

CPU () 是进程在给定 CPU 上尽可能长时间运行而不迁移到其他处理器的倾向。

Linux 内核进程调度程序固有地具有称为软 CPU 亲和性 ( ) 的特性,这意味着进程通常不会在处理器之间频繁迁移。这种状态正是我们想要的,因为更少的进程迁移意味着更少的负载。具体参考函数。

4.3.5 流程异常如何降低对业务的影响?

在实际在线环境中,经常会出现多线程服务运行几个月后,进程不知什么原因挂掉,最终整个服务不可用的情况。

这时候+more的多进程模型就体现了它的优势。如果代码中存在不易触发的隐藏bug,如果某个请求在某个时间触发了这个bug,那么处理该请求的进程就会以段错误退出。. 但是,其他进程不会受到任何影响,也就是说,如果一个转换后的进程启动了 20 个进程,并且在某个时候某个请求触发了一个隐藏的 bug,那么只有处理该请求的进程才会出现 异常,而其他19个这个过程不会受到任何影响,隐藏bug触发后的影响面只有5%。如果是多线程模型,则冲击面为 100%。

如果某个进程挂了,父进程会感知到这个信号,然后重新启动一个进程,实现无感知的即时“拉”恢复。下面是模拟触发异常分段故障的过程:

如上图所示,杀死31420进程后,该进程会立即拉起一个31451工作进程,实现快速恢复。

多进程异常,自动“拉取”功能源码,可以参考以下demo:

//-code-of-nginx-1.9.2/blob//nginx-1.9.2/src/demo.c

5 网络优化

5.1 网卡多队列

实际上线后发现软中断太高,而且大多集中在一个或几个CPU上,严重影响客户端连接和数据转发。

RSS(Side)是网卡的一个硬件特性,它实现了多个队列,可以将不同的流分发到不同的CPU。支持RSS的网卡,通过多队列技术,每个队列对应一个中断号,通过绑定每个中断,可以将网卡中断分配到cpu多核上,最终达到负载均衡的效果.

5.2 可怕的 40ms

在线运行的过程中,发现延迟波动很大。发现部分数据包响应延迟约40ms,增加了整体延迟。抓包如下(借助工具):

解决方法如下:recv系统调用后,调用一次函数并设置。代码修改如下:

6 改造前后性能对比(延迟、qps对比)

6.1 线上真实流量延迟对比

6.1.1 重构前在线集群延迟

在线集群完全采用开源作为代理,架构如下:

前线+集群不修改,qps=5000~6000,长连接,客户端延迟分布如下图所示:

在机器上使用被监控的网卡的延迟如下:

从上面两张图可以看出,使用,延迟高,同时抖动严重。

6.1.2 参考nginx改造后的延迟

线上集群一是采用官方原生,二是改造后的集群。改造后的配置进程数为1,与原生开源进程数一致。架构如下:

更换在线集群中的两个代理之一后(影响50%的流量),长连接,qps=5000~6000,客户端监控延迟分布如下:

更换两个代理之一后,使用代理集群查看两个代理的延迟分布如下:

本机节点机器上的延迟分布:

另一台修改节点机器上的延迟分布:

总结:更换两个在线代理之一后,客户端时间翻倍。如果将在线集群中的两个代理都替换为修改后的代理,预计客户端监控延迟将再次翻倍,整体延迟降低约 3 倍。

此外,从监测中可以看出,改造后的延迟更低,更稳定,没有任何波动。

6.2 参考nginx多进程改造后的离线压测结果(启用功能)

监听同一个端口,数据长度为100字节,压测结果如下:

Linux内核版本:linux-3.10

物理机型号:M10(48 cpu)

多个进程监听同一个端口,数据长度为150字节,压测结果如下:

Linux内核版本:linux-3.10

物理机型号:TS60(24 cpu)

7 总结

7.1 多进程多线程机制的选择

选择参考nginx多进程机制而不是多线程实现的主要原因如下:

1)多进程机制无锁操作,更容易实现

2)多进程代理,整个进程没有任何锁操作,性能更好

3)如果是多线程的,如果代码有bug段故障,整个进程就会挂掉,整个服务不可用。如果是多进程模式,因为一个bug触发了进程段故障异常,其他进程不会受到影响。如果有 20 个进程触发了异常,那么只有 1/20 的流量会同时受到影响。而如果是多线程模式,100%的流量都会受到影响。

4)进程异常退出后,该进程立即感知并拉起一个新的进程提供服务,更加可靠。

5)更容易配置热加载和程序热升级功能

7.2 参考nginx改造后的特点

它几乎支持了nginx所有的优秀特性,还根据自己的实际情况添加了自己的特性:

1) +多进程机制

2)适配所有linux内核版本,避免支持低版本内核

3) 支持

4) 适配支持

5)进程异常,进程自动拉起功能支持

6) 90%、95%、98%、100%平均延迟统计功能支持

7) 单机版、集群版支持

8)Redis单机版、集群版支持

9) 支持二进制协议和文本协议

10)Redis,集群在线扩容、缩容,支持数据迁移,扩缩容过程,数据迁移对业务没有影响。

11) 多租户支持,一个agent可以连接多个redis集群,支持co-。

12) 优化mget、gets、sets等批处理命令

13) 慢响应记录功能支持

14) 支持实时修改内存参数

15) 详细的集群监控统计功能

16)CPU亲和性的自加

17)动态实时修改内存配置

7.3 后期规划

添加以下功能:

i) 配置文件热加载支持。

ii) 代码热升级功能支持。

7.4 长期规划展望

抽象一个类似于nginx的高性能代理软件,nginx支持http协议,我们的代理支持tcp协议,涵盖了nginx的所有功能,包括上面提到的所有功能,并且支持模块化开发。这样,很多tcp协议代理不需要关心网络架构的底层实现,只需要根据需要开发相应的协议解析模块,以及自己关心的统计和审计等功能,降低开发成本. 现有的开源中间件很大一部分是tcp,并且有自己的私有tcp协议。如果把这个抽象出来,开发成本会更低。

对nginx感兴趣的可以参考源码分析:

//-code-of-nginx-1.9.2

内核网卡延迟分析工具:

//

源码分析:

//-和–.4.1

内核协议栈延迟确认机制:

//-和–linux—-堆栈

教你如何开发中间件、分布式存储、高性能服务器开发等:

//

赞(0) 打赏
未经允许不得转载:艾飞特资源网 » 培训与开发的作用(1.开发的特点及特点分析-苏州安嘉开发)
分享到

登录

找回密码

注册