目录

  • 背景
  • 现状
  • 挑战
  • 设计
  • 实测
  • 不足
  • 总结

背景

旧系统随着节点数量增多,平均TPS处理能力越来越低下, 延迟越来越高,在业务不断扩张的同时,旧系统越来越难以满足需求

现状

低可用

旧系统在设计之初主要是满足于100台以内集群规模,随着业务增长,目前集群规模已经上升到1000多台,量级增长了10多倍,旧系统架构没有经过良好的设计,在规模线形增长的同时,节点管理方式没有良好的适配,这就导致集群的伸缩性不能及时响应,节点上下线感知非常滞后,节点异常后自恢复和处理措施几乎没有,这就导致集群的可利用率不断下降

低性能

随着集群规模的增长,系统的TPS(单位时间处理请求数)并没有增长,同时由于异常节点的存在,请求命中死链的机率更高,给用户的直观感受是经常卡死,业务在高并发下,请求成功率反而下降了

低扩展

旧系统只支持http,https的代理,socks5, websocket等,暂时没有办法支持,另外更下层协议tcp,udp等也不支持,没有办法满足某些业务的需求

无网关

在很多情况下代理需要指定特定区域或者指定,旧系统使用是反向随机代理模式,无法定向出口,并且请求上没有进行用户鉴权,只是通过IP白名单来限制,这样每增加一个用户(调用方IP),需要所有节点都更新相关IP安全配置(为正向代理做准备),操作极其繁琐,同时缺少资源隔离,使用方互相争抢资源,导致了系统不稳定,在这样的背景下,迫切需要一个高性能的网关来治理

挑战

节点机器都是非常低配的,大多数的节点机器CPU都在0.5核 ~ 1核左右,内存在0.5G ~ 2G ,带宽在1M ~ 10M不等, 另外由于业务需求,机器经常需要拨号,节点服务上下线非常频繁,在这样的前提之下,如何才能构建一套高健壮,高性能,高扩展的代理系统?

设计

我们首先看看旧系统的设计

旧系统请求生命周期相对简单:

  • 用户选择合适出口服务器
  • 服务器随机选择合适的节点服务器
  • 出口nginx进程与节点服务squid进程建立连接,转发流量

在流量不高时,这套系统都能稳定运行,在大流量和高洪峰时,这套系统就无法满足了,针对旧架构和背景,我们重新设计了系统
new

相对于之前nginx直达squid的单点转发模式,我们将系统切分为三层

  • gateway - 网关层,承接用户请求,对请求做鉴权,路由,限流,负载等常规的操作
  • server - 隧道起点层,与gateway服务在同一局域网内,与client是一对多的关系,暴露公网接口,心跳保活,下发指令给client
  • client - 隧道终点层,负责server建立隧道链接,保持会话,接收server端指令,解析流量报文,完成相关流量操作后返回给server端

client与server的会话建立,client上下线状态,都会时时同步至KV服务发现集群,同时使用go做为开发语言,基于它的语言特性,在并发,维护,跨平台等方面,都有非常好的表现,整体用户接口协议这里,将之前的7层流量入口,改为4层流量入口,既兼容旧系统主流协议,也留下了应用层接口,提高了系统扩展性

功能

正向连接 vs 反向连接

旧系统每增加一个用户,就需要在节点IP安全策略上更新一次,同时还要验证是否生效,还没有算上机器异常导致更新不成功情况,基于此,我们考虑应该采用反向的操作,让client主动与server建立安全隧道,保持长连接,这样就没有冗余的安全加白操作了,而且变向增加了client的安全,因为client不用开放任何端口到公网了

无缓存 vs 连接池

旧系统采用即时建立连接传输模式,建立连接的时间虽然非常短,不过在一天上亿请求量的背景下,也会消耗不少的时间在这里,新系统在client向server端注册通信时,会预先建立N条隧道的连接池,这样在网关下发网络请求时,可以快速进行流量转发,减少等待时间,用空间换时间

tcp vs udp

在设计这套架构时,刚好正在学习网络协议,里面有提到了新一代的传输协议quic,在对比当下使用场景时,发现非常适合这种:短,平,快的请求,旧系统的squid客户端是没有办法集成这种新协议,新系统客户端使用社区相对成熟的quic-go,轻松集成

实测

废话不多说,直接放一下新旧平台http压测情况

成功率

新平台在高并发下,还能保持稳定的成功率,成功率提升了至少20%+, 再来看看高并下的耗时(性能) success

性能

可以看到新平台的耗时整体趋于稳定,整体性能提升了200%+
high

top 95%

更进一步看看统计分布状况
top95

top 99%

top99

到这里,我们新系统不论是性能,稳定,扩展性都达到了当初的设计目标,当然,随着网关上线,系统可以轻松对用户请求鉴权,而且旧系统指定客户端(比如某些区域,城市,特定机器)的大难题,在新系统里面得到了很好的解决

不足

冗余操作

我们可以看到,一次用户请求存在两次拆包的:网关,客户端

  • 网关:解析proxy-auth报文
  • client: 解析http host报文,与目标host建立连接

冗余部署

针对不同的协议,我们需要起不同的实例和配置,还是存在冗余部署的情况

总结

在新系统架构和开发的过程中,构架语言和生态选型是非常切合当下的业务需求的,正是基于go丰富的网络生态和语言特性,极大的降低了开发成本和迁移成本,具体的业务场景下,选择特定的语言模型是非常重要的