TCP长连接下,在应用层面,定制自己的负载均衡
TCP长连接下,流量负载均衡的做法
- 1 背景
- 2 存在的问题
- 2.1 连接被均分,流量无法被均分
- 2.2 长短信问题
- 2.3 IP鉴权问题
- 2.4 限速问题
- 3 定制自己的服务器负载均衡
- 3.1 解决单点故障
- 3.2 支持平滑升级
- 3.3 序列号问题
- 4 总结
有很多种方式,实现负载均衡。可以通过DNS实现、硬件实现和软件实现。一般我们谈到反向代理和负载均衡时,首先想到的应用场景可能就是网站了,比如使用Nginx作为反向代理服务器,实现HTTP请求的负载均衡。负载均衡的策略可以有轮询、权重、哈希等方式。这种是比较常见的做法。
今天主要讨论的是,在特定场景下,通过软件实现应用层面的「负载均衡」。为什么要加括号呢,因为我觉得它已经不算是通用的负载均衡了, 而是基于特定场景下实现的,其中还会包含一小部分业务逻辑。
1 背景
为了更好地理解该场景,我们以短信群发平台为例。
CMPP(China Mobile Peer to Peer, 中国移动通信互联网短信网关接口协议)是国内短信平台最常用的协议,没有之一。和HTTP协议一样,CMPP也是运行在TCP/IP协议之上。实现CMPP协议时,可以采用长连接,也可以采用短连接。但为了提高效率,实际更多的是采用长连接。
Client先经三次握手与Server连接成功,然后通过发送类型为CMPP_CONNECT的数据包登录Server,Server进行IP鉴权、帐号鉴权后,回复类型为CMPP_CONNECT_RESP的数据包。之后,Client与Server方可进行数据传输,即可以开始愉快地发送短信了。在这之后,除非某一方主动地断开连接,或者由于网络因素导致连接被动断开,连接一直保持,即使没有短信在发送,也会有链路检测CMPP_ACTIVE_TEST数据包在定时传输。
2 存在的问题
2.1 连接被均分,流量无法被均分
Client与Server之间采用的是长连接,每条连接在不同时间段,流量也不一样。使用Nginx的stream模块,只能均分连接,却无法均分流量。
如下图,4个客户端的4条长连接被均分到两个App,但是Client1和Client3流量较大,都被集中到App1,导致App1所在服务器负载较高,而App1会在内存中保存相关数据,所以还会导致App1占用的物理内存暴增,如果没有流控机制,结果就比较悲剧了,而此时App2还处于比较悠闲的状态。
所以,如果采用Nginx的stream模块,当某一时刻,流量较大的连接都分布在同一个App上,就可能会造成内存增加,响应延迟等问题,同时也无法在业务高峰期,高效地利用所有服务器资源。
2.2 长短信问题
同时,采用上述方式,还有另外一个题外话:长短信的处理问题。按惯例,一条中文短信,如果超过70个字,就会按照67字拆分成多个分片,分别提交上来。一条长短信通常最多拆分成10个分片。
正常来讲,一条长短信的10个分片,客户端应该保证都由同一条连接提交到服务器。但实际场景下,有些客户端因量比较大,一个帐号会与服务器建立多条连接,并且,一条长短信的不同分片,是经由不同连接提交上来的。
所以你就会发现,App1收到了长短信的第一个分片,App2收到了长短信的第二个分片,App1和App2还部署在不同的服务器上。这就导致了还必须得有进程间通信机制或者缓存机制来保证长短信分片的合并问题。客户端的这种方式无形中给服务端增加很多业务逻辑和复杂性。
如果Nginx使用按照IP HASH的方式分配客户端连接呢?也不行,因为这会导致一个IP的所有连接都被分配到1个后端App上,存在单点故障、流量不均等问题,而且,万一,一条长短信的不同分片是客户端经由不同IP提交上来的呢。永远不要猜测客户端的行为,因为猜不到。
2.3 IP鉴权问题
在反向代理场景下,Nginx可以通过增加HTTP头域字段X-Real-IP将客户端原始IP透传给后端App,后端App可以进行IP鉴权。而在上述场景下,后端App获取客户端的IP,只能得到Nginx的IP,无法获取客户端原始IP,因为Nginx只能透传客户端的原始数据流。
如果将客户端的IP,配置到Nginx的配置文件中,那么又会面临一个问题:需要reload才会生效。因为Nginx支持平滑升级,在短连接通信场景下,reload不会有任何问题。执行reload指令时,nginx启动新的进程加载新的配置,旧的进程处理完当前请求便退出。对于长连接场景,问题在于,由于连接一直保持着,而且还有数据在传输,旧的nginx会一直处理is shutting down状态,无法退出,虽然有worker_shutdown_timeout参数可以控制,但属于强制结束,对客户端连接不友好。
当然也有其他方式解决这个问题,不过不在本文讨论范围内,这里简单提一下。proxy protocol是HAProxy的作者Willy Tarreau于2010年开发和设计的一个Internet协议,通过为tcp添加一个很小的头信息,来方便的传递客户端信息(协议栈、源IP、目的IP、源端口、目的端口等),在网络情况复杂又需要获取用户真实IP时非常有用。
2.4 限速问题
Nginx的配置项limit_ate来用来限制客户端每秒传输的字节数,但在短信平台实际场景下,速度控制的概念,主要是判断每秒能提交多少条短信,所以无法使用该配置项。
另外,如果一个账号开启多条连接,通过Nginx,这些连接被均分到不同的后端应用,那就只能通过进程间通信机制来计算同一个账号的总速度,会导致一些性能损失。
3 定制自己的服务器负载均衡
问题不在于Nginx,也不在于stream模块,而是因为这种场景与业务高度绑定,使用Nginx已经不太适合了。若要解决上述问题,定制自己的服务器负载均衡,可能是性价比较高的方式,否则就只能针对每个问题各个击破。
如下图,多个客户端,或者一个客户端的多个连接,对接到LB之后,LB可将流量均分到所有的后端App,以实现完全的流量负载均衡。后端App不需要再关注连接层面的问题,比如IP鉴权和短信下发速度控制,也不需要处理棘手的长短信合并问题,这些都很好地被规避掉了。
3.1 解决单点故障
至于负载均衡的单点故障问题,与使用Nginx时一样,可以采用keepalived+VIP方式实现双击热备,正常情况下,虚IP指向主用负载均衡所在服务器,当主用负载均衡宕机时,虚IP漂移至备用负载均衡,方案比较成熟,具体操作方式这里就不再展开讨论了。
3.2 支持平滑升级
虽然大部分情况下,不需要升级负载均衡,但总有需要升级的时刻。如果采用直接重启的方式,虚IP会飘移至备用负载均衡,所以你需要先升级备用负载均衡,否则所有客户都会断连2次(先升级主用时,会断连一次,所有长连接均迁移至备用,再升级备用时,连接又断一次)。
直接重启比较粗暴,可以学习Nginx的方式,升级时,先关闭监听端口,再启动新的负载均衡,此时,新旧负载均衡同时存在。然后旧负载均衡再根据策略,逐步断开所有连接,再退出。
CMPP协议有CMPP_TERMINATE命令,该命令请求拆除双方之间的连接,负载均衡可以先发送该请求,等待客户端主动断开连接。若客户端仍然一直保持连接,那么可以在一段时间之后,强制断开连接。
这种方式需要注意兼容性,比如负载均衡与后端App之间的协议是私有的协议,而此次升级,对该协议做了更改,新增了字段new_field,那么肯定需要先升级后端应用,使其同时支持有new_field和没有new_field字段,因为新旧负载均衡会同时运行一段时间。
3.3 序列号问题
负载均衡主要起到了将数据流重新路由到后端App的作用,这其中,少不了交换相关数据。比如,CMPP协议分为Header和Body两个部分,其中Header包括三个字段中:TotalLength(数据包整体长度,包括Header和Body)、CommondId(消息ID)、SequenceId(消息序列号)。
若负载均衡与每一个后端App保持一个连接,那么当客户端1和客户端2的某一个消息都使用了相同的SequenceId,且都需要转发到后端App1,就会存在SequenceId重复的情况。
有两种方法解决:
- 需要在负载均衡内存中缓存一些数据。负载均衡负责生成新的SequenceId,并将原始连接Id和原始SequenceId,与新的SequenceId做Hash映射。当应答回来之后,根据新的SequenceId,找到原始SequenceId和原始连接Id。
- 需要修改CMPP协议。将客户端的原始连接Id和原始SequenceId,添加到Header中,后端App回复应答时,需要将这两个字段在Header中返回。这样负载均衡可以直接找到原始连接Id,使用原始SequenceId回复客户端。
两种方法各有优缺点。方法一处理起来比方法二麻烦一点,但是对后端App比较友好,后端App不需要改动。方法一处理起来比较直观,而且也不需要在内存缓存数据,但是需要后端App配合修改。
4 总结
由此可见,对于后端分流来说,并不是所有的场景,都适合直接上Nginx,特别是业务比较特殊的情况下,盲目使用Nginx可能会带来很多其他的隐性开发成本,而这些问题,其实都可以避免。
如果感觉本文不错,欢迎转发给身边的好友。
TCP长连接下,在应用层面,定制自己的负载均衡相关推荐
- python使用socket实现协议TCP长连接框架
点击上方↑↑↑蓝字[协议分析与还原]关注我们 " 使用python实现协议中常见的TCP长连接框架." 分析多了协议就会发现,很多的应用,特别是游戏类和IM类应用,它们的协议会使用 ...
- 通da信TCP长连接数据算法分析
点击上方↑↑↑蓝字[协议分析与还原]关注我们 " 分析通da信TCP长连接内部分数据的算法." 作为一款老牌的炒股软件,通da信里面的数据是相当的丰富,免费的也很丰富,准确性也很好 ...
- TCP长连接与短链接
1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次 ...
- TCP长连接和短连接
2019独角兽企业重金招聘Python工程师标准>>> 1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操 ...
- TCP长连接与短连接的区别(转)
1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次 ...
- 应用服务器与数据库之间是长连接,要接收多个 tcp 长连接不断发送的数据并存储,哪些数据库或数据存储方案比较合适?...
在服务器建立服务端,与多个 tcp 连接保持长连接,服务端会根据客户端发送的 token 验证确定是否保持长连接建立"session"缓存, 在某个状态开启时(我称为存储状态),要 ...
- TCP长连接,短连接
1. TCP短连接 我们模拟一下TCP短连接的情况,client向server发起连接请求,server接到请求,然后双方建立连接.client向server 发送消息,server回应client, ...
- tcp长连接和保活时间
tcp长连接和保活时间 TCP协议中有长连接和短连接之分.短连接在数据包发送完成后就会自己断开,长连接在发包完毕后,会在一定的时间内保持连接,即我们通常所说的Keepalive(存活定时器)功能. ...
- TCP长连接和短连接代码及其比较
前言: 最近又看到了关于TCP长连接和短连接的概念,以前也看过Http长连接和短连接的概念,因为Http是建立在TCP协议之上的,所以它其实是依赖TCP的长连接和短连接.所以,我就萌生了一个想法,看看 ...
最新文章
- AddressSanitizer+cmake
- html 乱码_html小坑:网页变成乱码
- 西裤哥的 Hook Api Lib 0.2 For C
- 用户模式与内核模式(2)
- IDEA使用从Eclipse过来的快捷键
- STM32 调试脚上电默认电平
- [C++基础]队列queue中的常用函数
- 5.2 各种类型的Attention: 原理、计算流程
- quartus II 13.1 软件破解
- php date 函数用法,PHP日期时间函数date()使用方法
- bzoj 4874: 筐子放球
- 从零开始学飞塔第一篇:飞塔防火墙基本上网配置(PPPoE拨号固定IP上网)FortiGate Broadband internet access
- android图片做平移动画,Android中用Matrix实现ImageView里的图片平移和缩放动画
- 洛谷——P1348 Couple number(java实现)
- 国内外,网络安全厂商都有哪些?
- Android 高德地图 自己位置的显示与点地图上任意一点的坐标
- 极客日报:2021年年终奖人均水平为2.3万元;消息人士回应华为自研浏览器内核传闻;Linux取消对a.out格式的支持
- 2020-11-23 PTA算法_贪心算法部分习题及代码
- 《奇点临近(The Singularity is Near:When Humans Transcend Biology)》
- 质量管理之代码的圈复杂度
热门文章
- cad详图怎么画_初学CAD如何能画的快,出图迅速?15个小技巧分钟成高手
- P4 | SSPD-based noise cancellation (JSSC-2018-03)
- 想做APP增长,这个基础手段让你事半功倍【黑盒研究内参第13期】
- sql server 获取上个月,上周
- 移通好闹钟源码(校园交互平台微信小程序)
- 知识付费如何二开分销功能
- 美发店预约系统开发的作用_分享美发店预约系统开发步骤
- java jcombobox 样式_JCombobox组合框效果实现
- 机器学习实战中的函数学习记录
- 表彰大会项目经理的辛酸史。数字荣誉墙外场布置大屏触控墙颁奖表彰优秀员工英雄榜单销售经理优秀经销商外场布置跳跃互动tioyo