强平jraft阻塞和运维问题-试验对比计划

背景介绍

  • 强平的初级目标是30秒钟1万个流程,远期目标是3秒钟1万个强平流程,

  • 强平流程中大部分耗时都是发生在外部rpc调用的阻塞和jraft本地落盘和jraft广播到副节点同步数据时的阻塞

  • jraft通过半强一致性协议防止内存数据丢失需要提前落盘持久化,需要广播给其他节点。假如生产环境jraft集群有5个节点,1秒钟10万次更改,那么除了主节点需要写入本地硬盘10万次(同步阻塞),还需要40万次网络写入(20万次同步阻塞),总共50万次IO操作,这些IO操作耗时既严重影响了单次更改的耗时,也影响了整体吞吐量。

  • jraft把应用程序变成了有状态,直接限制了应用程序的伸缩性,同一个强平基本上要同一台机器完成,瓶颈在单机上,机器内一处发生异常全盘完蛋。如果出现这种情况,停机修复期间将会造成重大损失。

  • jraft的业务代码夹杂了很多跟业务本身无关的代码,开发和维护成本高。运行经常出错,稳定性较差。缺乏易用的监控组件,定位问题困难,运维成本高。数据保存位置特殊,复现问题极其困难,解决问题耗时长。

  • 全内存有状态设计更适合计算型的应用,比如撮合服务,运行中途不需要跟外部系统交互,cpu满负荷运转。无状态应用,把数据状态保存在mysql/redis/kafka等中间件,适合IO型的应用,比如交易服务。

  • 强平服务虽然需要计算当前风险水位和最大亏损值,但跟永续期权交易资产运营撮合等服务的交互等待对比,计算耗时占比不超过1%。整体看来强平服务属于IO应用型而非计算型应用。

  • 基于现有环境,可采用mysql(margin-db)作为强平服务保存数据的替代方案。从性能看,mysql在100万以下数据的增删改查速度是非常快(平时说的mysql慢是千万级别时才会出现或者大量join查询才会出现)。如果是批量提交,10个字段以内,一秒钟提交10万行都是轻而易举。

  • 从伸缩性和分布式处理看,强平服务的数据保存在数据库后,强平服务可以根据需要随时伸缩,业务高峰期加机器,业务低谷时缩减机器动态调整。同时一个强平流程可分发到任何机器处理,甚至强平流程的每个环节每种状态都可以由不同机器完成,真正意义上分布式协助处理(当前jraft集群节点的副节点主要是为了容灾而非分布式处理)。

  • 从整体网络流量看,强平服务的数据保存在数据库后,强平流程的每1次状态更改只需要向mysql发送1次网络写入请求,而不需要像jraft集群一样除了写入本地文件还需要分发到多个副节点,mysql更节省流量。

  • 从整体同步阻塞看,强平服务的数据保存在数据库后,强平的每一次状态更改只需要一次同步阻塞,发生在mysql持久化时那一刻。jraft集群如果有5个节点,写入本地文件是阻塞,另外4个网络请求也是阻塞,jraft阻塞的次数远大于mysql。

  • 从单次写入速度看,jraft由java语言开发,mysql由c语言开发,c语言的操作速度更加优秀,单次写入速度大概率还是myql更优。

  • 强平流程运行时,对历史强平记录并不敏感,比如它不依赖于24小时前的数据。这些历史强平数据可以迁移到hbase等地方永久存储(后续大数据分析报表统计),保持强平数据库margin-db的单表数据永远在100万数据以下,每时每刻增删改查都高速运转。

  • 强平服务的数据保存在数据库后,开发成本,运维监控成本,定位问题的成本,解决问题的成本,都会大幅度降低。

  • 口说无凭,实践才是检验真理的唯一标准,不妨设计一个实验对比

实验目的

  • 分别测试jraft和mysql作为容灾持久化方式时,错误率,稳定性

  • 分别测试jraft和mysql作为容灾持久化方式时,吞吐量

  • 分别测试jraft和mysql作为容灾持久化方式时,平均处理时间

  • 分别测试jraft和mysql作为容灾持久化方式时,最大处理时间

硬件环境

  • 实验环境仿照准生产环境,5台机器,所有accountId的尾数都为0

数据来源

  • 存在100万accountId结尾为0的用户。

  • 每秒钟有随机1万个用户消息提交到kafka需要处理。

  • 持续运行30分钟,总共消息量30x60x1万=1800万

  • 应用程序监听kafka消息,监听到即处理,处理完则提交offset,jraft架构通过4个轮子持久化,mysql架构则通过提交数据库持久化

  • 处理每个消息中途会停留1毫秒(模拟调用外部rpc)

  • 每个消息处理完毕把json格式的日志打印到日志文件,包括以下字段,当前kafka的offset,请求信息,处理结果(true/false),开始时间,结束时间,总耗时

  • 测试完毕把日志文件同步一份到es通过kibana统计,或者把同步到prometheus通过grafana分析,或者手写java统计

  • 统计的指标主要包括

    总消息数

    总成功处理数

    总失败处理数

    每秒钟处理消息多少个

    每秒钟处理成功消息多少个

    每秒钟处理失败消息多少个

    平均每个消息处理时间

    消息处理时间最长的10%消息,其平均消息处理时间

jraft架构应用代码设计

监听mq消息

幂等判断

处理结果=失败(暂定为失败)

根据accountId查询本地内存
 
try{
    System.out.println("业务处理开始,4个轮子开始");
	模仿rpc休息1毫秒
	System.out.println("业务处理结束,4个轮子结束");	

	处理结果=true(更改为成功)
}catch(业务内部失败,rpc调用失败){  

    如果是业务中途失败,包括jraft同步失败,需要恢复本地内存
    log4j2记录日志消息 	
    重试暂略
  	
}finally{
	提交kafka的offset,如果提交失败,重新消费即可,因为前面有幂等处理
	log4j2记录请求信息,处理结果,开始时间,结束时间,总耗时,以便后续统计吞吐量和平均处理时间
	平常的开始时间,结束时间,总耗时单位为毫秒,在这里暂时使用微妙作为单位,更有说服力
}

mysql架构应用代码设计

监听mq消息

幂等判断

处理结果=失败(暂定为失败)

根据accountId查询guava内存

不存在则从mysql查询(这一步骤和jraft主节点不同)

try{

	System.out.println("业务处理开始");

	模仿rpc休息1毫秒

	手动开启mysql事务(innodb)

	把最新的account写入mysql

	把最新的account写入guava

	提交mysql事务
	
	System.out.println("业务处理结束");

	处理结果=true(更改为成功)
}catch(业务内部失败,rpc调用失败或mysql提交失败){  	

	如果是业务中途失败,包括提交到mysql失败,需要恢复guava
    log4j2记录日志消息
    重试暂略  		
  	
}finally{
	提交kafka的offset,如果提交失败,重新消费即可,因为前面有幂等处理
	log4j2记录请求信息,处理结果,开始时间,结束时间,总耗时,以便后续统计吞吐量和平均处理时间
	平常的开始时间,结束时间,总耗时单位为毫秒,在这里暂时使用微妙作为单位,更有说服力
}

jraft架构交互设计

  • 0号节点作为jraft主节点
  • 1号节点作为jraft从节点
  • 2号节点作为jraft从节点
  • 3号节点作为jraft从节点
  • 4号节点作为jraft从节点
  • 主节点挂掉,重新选主
kafka jraft主节点 外部系统 本地文件 jraft的4个从节点 监听消息 rpc耗时1毫秒 返回响应 阻塞同步最新数据 同步成功 阻塞同步最新数据(并发) 2个返回则同步成功 提交 kafka jraft主节点 外部系统 本地文件 jraft的4个从节点

mysql架构交互设计简易版

  • 0号节点作为应用程序,启动一个有监听消息权限的jvm,还启动了另外一个无监听消息权限的jvm

  • 1号节点空闲,无任何应用程序

  • 2号节点空闲,无任何应用程序

  • 3号节点作为数据库主库

  • 4号节点空闲

  • 0号节点的无监听消息权限的jvm是无状态的,不会处理任何业务,内存数据始终为空,占用资源极低

  • 一旦发生异常,自动切换程序(哨兵),立刻kill掉有监听消息权限的jvm,停止监听消息

  • 自动切换程序(哨兵),对无监听消息权限的jvm,授权,允许其监听消息

kafka 应用程序0号节点 外部系统 mysql主节点 监听消息 另外两个节点空闲 rpc耗时1毫秒 返回响应 阻塞同步最新数据 同步成功 提交 kafka 应用程序0号节点 外部系统 mysql主节点

mysql架构交互设计升级版

  • mysql架构简易版,有2个节点是空闲没有利用起来,这里利用起来
  • 上游修改,建立3个队列topic0,topic1,topic1
  • 上游发送消息是,accountId对3取模,结果0推送到topic0,结果1推送到topic1,结果2推送到topic2
  • 0号节点作为应用程序,启动一个有监听消息权限的jvm,监听topic0,还启动了另外一个无监听消息权限的jvm,未来监听topic1
  • 1号节点作为应用程序,启动一个有监听消息权限的jvm,监听topic1,还启动了另外一个无监听消息权限的jvm,未来监听topic2
  • 2号节点作为应用程序,启动一个有监听消息权限的jvm,监听topic2,还启动了另外一个无监听消息权限的jvm,未来监听topic0
  • 3号节点作为数据库主库,建立3个分区,0号分区,1号分区,2号分区,分别存储topic0消息处理结果,topic1消息处理结果,topic2消息处理结果
  • 4号节点作为数据库从库
  • 所有节点无监听消息权限的jvm是无状态的,不会处理任何业务,内存数据始终为空,占用资源极低
  • 一旦发生异常,自动切换程序(哨兵),立刻kill掉有监听消息权限的jvm,停止监听消息
  • 自动切换程序(哨兵),对无监听消息权限的jvm,授权,允许其监听消息
上游系统 kafka 应用程序0号节点 外部系统 mysql主节点 发送消息 accountId对3取模 结果0推送到topic0 结果0推送到topic1 结果0推送到topic2 监听0号消息 0号节点开始监听topic0 1号节点开始监听topic1 2号节点开始监听topic2 rpc耗时1毫秒 返回响应 阻塞同步最新数据 mysql主节点进行分区 topic0的消息处理后落入0号分区 topic1的消息处理后落入1号分区 topic2的消息处理后落入2号分区 同步成功 提交 上游系统 kafka 应用程序0号节点 外部系统 mysql主节点

mysql架构交互设计生产机版

  • 如果实验对比后(稳定性,吞吐量,处理时间),mysql架构是合理的,那么可以进一步优化
  • 预生产环境有10个zone,每个zone拥有5个机器,总共50台机器
  • 可以划分32台机器运行jvm,8台机器作为mysql主库(每个库4个分区),8台机器作为mysql从库,另外2台作为自动切换程序(哨兵)
  • 上游消息对32取模,分别落入topic0到topic31
  • 32个有监听权限的jvm分别监听topic0和topic31
  • 消息处理完后,分别写入8*4=32个分区
  • 一旦发生异常,自动切换程序(哨兵),立刻kill掉有监听消息权限的jvm,停止监听消息
  • 自动切换程序(哨兵),对无监听消息权限的jvm,授权,允许其监听消息
  • 如果达到预期的情况下,每个jvm处理的账户量是32分之1,而原来处理的账户量是十分之一
  • 如果原来处理量十分之一能满足需求,由于现在这种方案每个节点的内存使用率只有原来的三分之一, 再增大2倍用户cpu内存也是绰绰有余。

任务排期

任务效果时长
jraft架构应用程序的框架搭建和应用代码编写,日志埋点
mysql架构应用程序的框架搭建和应用代码编写,日志埋点
kafka数据准备,每秒钟推送1万消息,持续30分钟
jraft架构整体测试,调优,及分析稳定性,吞吐量,响应性能
mysq架构简易版测试,调优,及分析稳定性,吞吐量,响应性能
mysq架构升级版测试,调优,及分析稳定性,吞吐量,响应性能
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值