作者:梁宏达
问题背景
资源投放系统在线服务采用三层缓存架构设计,最大限度保证服务高可用和效率。在资源投放系统上线后,接入的业务方越来越多,将来会更多,数据量上升明显,三层缓存设计完全可以满足现在和将来的数据量,但是底层缓存层会比早期有明显“滞后”现象,导致用户的体验较差,也会使Dubbo线程请求慢,影响后续的请求。解决方案并不是优化代码能解决的,而是底层缓存使用更高效、更可靠的解决方案。
解决方案
分片方案
解决方案采用的是Redis Sentinel集群方案,且Redis Sentinel集群部署在两台物理机上,物理机互成主备。线上创建了32个Master实例,且有32个Slave,它们一起构成Redis集群,另外有三台机器构建成Sentinel哨兵监控集群,对Redis集群进行监控,主要体现在主备切换。
分片方案采用的是客户端分片,这也是目前网易推崇的分片方案,稳定且可以快速上线使用,与Sentinel的锲合度也较高,另外对于哈希到Master实例上的数据也可以均匀分布,对于后续的实例扩充也有明确的解决方案。对于代理分片和服务端分片,网易内部没有任何成功上线的案例,且对于异常或故障的处理比较耗费人力,选择客户端分片就不言而喻了。
客户端分片采用的是一致性哈希算法,一致性哈希的实现是MurMurHash算法,可以最大限度的数据分散性,提高Redis集群的负载。下面截取几个Master实例的key总量来体现分散性(截图key总量即为Master中key的数量):
...
主备切换
线上Master出现故障当然是谁都不愿意看到的,但是出现了也有办法解决,上面提到的Sentinel集群会不时地监控Redis的运行状况。Sentinel每秒一次向所有的实例发送PING命令,等待回复判断实例是否在线。
主备进行切换主要过程:
1).Sentinel发现故障Master实例
2).Sentinel先做主观下线,然后再做客观下线
3).Sentinel切换完成向+switch-master发送消息
4).客户端监听+switch-master,接收到特定的切换消息进行客户端连接池初始化
5).主备切换结束
数据迁移
Redis两种数据落地方案:
1).RDB
2).LOF
这两种形式都满足补了现在的需求,因为落地后的数据需要分片到32个Master实例上。数据迁移没有落地,因为资源投放的数据的TTL基本上是以"天"为单位,且有较多的定时任务从DB中同步过来,但是前期也是做了挺多的调研。有3方式做迁移:
1).市面上的同步工具大部分都是基于代理分片实现的,但是分配的原理和resource-app中的客户端分片会有差异,所以同步数据和分片必须是同一套分片方案才行。自己配置线上NCR【自动保存间隔】 500 1表示500秒内修改了一次;线上 NCR file.rdb上传至NOS上,地址更新到Disconf上;读取 file.rdb文件,通过客户端分片到Redis HA集群;如果同步数据期间有大量访问,可以再dump一次。a.运营使用使用频率很低;b.外部用户请求量小,最根本还是要保证基础缓存和组合缓存写入量很少使用rdb形式做数据迁移,不可避免的出现几分钟的数据丢失,这些丢失的数据只能通过访问数据库进行弥补尽量做到源节点和目标节点数据保持一致性;这样线上就会有两个数据源,此时先部署一台具有分片的服务,观察线上日志、App显示是否和访问单节点NCR的数据展示是否一致和访问耗时时长是否正常;正常后即可全部部署服务,继续观察;若出现问题,回滚后,项目启动Kschedule会自动刷新单节点NCR缓存最后,以上步骤现在测试环境NCR节点测试,然后实施上线
2).对于只有部分重要的数据,可以通过keys命令配合DBA直接拉去然后分片到Redis HA集群上(该方案是与DBA商议出来的)
3).如果所有的数据都可以从DB中读取,可以不用做迁移
mget实现
第一版的实现是遍历所有的key,遍历一个执行一个,效率及其低。
原始是线上32个实例不能直接使用mget命令,但是每个单独的Master实例可以使用mget命令,基于这个想法,对每次请求key列表先做归并处理,归并结束后,最多32个归并集合,然后再异步去请求,性能压测后提升比较明显。
可能存在的问题:
1).归并后的key的异步处理的线程池,请求量过大会有影响
2).查询的key过于分散的场景,性能会一般
线上切换
由于代码变动太大,merge到master分支很难回归到所有的风险点,按照如下全部上线(所有的步骤都为hotfix分支):
1).测试环境功能测试;
2).压测环境物理机压测;
3).预发环境功能回归;
4).Beta环境模拟线上环境演练,导办公网和外部1%的流量观察1天;
5).继续上线一台线上机器,观察1-2天;
6).hotfix合并到master分支,以上步骤再执行一遍;
7).master分支全部部署,切换物理机结束
优化结果
在数据量为27KB的情况下,单台resource-app的ResourceFacade.getCurrentSchedules接口可支撑1000+的TPS;
APP端首页在平均响应长度为53KB的情况下,单台resource-app可支撑500左右的TPS,因数据量大后端耗时点主要在于online的数据处理上,resource-app的dubbo线程池已满;
压测过程中物理机CPU消耗不超过2%,性能表现可接受。
上线有两三个月了,后续持续关注Redis集群的运行状况,还要不断跟踪。