前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >压测桩设计与思考(一)

压测桩设计与思考(一)

原创
作者头像
tabbyzhou
修改2021-11-29 20:14:25
4340
修改2021-11-29 20:14:25
举报
文章被收录于专栏:设计与思考

最近一段时间我们团队在重构一个系统,这个系统涉及到调用下游的服务,服务提供的协议是下游系统自研的A协议。重构的系统自然是要适配这个协议的。在我们按照我们的认知重构了这个系统后,有两个问题需要解决:

1. 重构的系统性能如何?

2. 如何保证重构的系统和原来的系统所有功能都一致?

对于第一点,方法很简单,压测。下游系统众多,不太可能给我们提供压测的环境。而且这只是验证我们系统,也不必麻烦其他团队。于是外部接口我们计划全用桩。

对于第二点,做全流程对比测试。当相同输入数据同时流经两个系统时,自身系统调用外部系统的参数一致,两套系统外部调用返回的数据一致,同时两套系统的产物一致,就可以保证重构的系统所有功能是一致的。当然还得覆盖全部场景。

本文的重点是第一点,压测桩的实现。后续有时间会写个对比测试的文章,记录一下。

初步方案有了,来看下实际情况和如何实施这个方案。

需求调研

压测和对比测试的优先级我们内部的优先级是先压测,再对比测试,因为已经有手工的功能验证,可以保证整体上没有大的问题,但是性能如果不达标可能会存在方案的变更。一旦出现,可能是更急需解决的问题。

针对压测桩,当时的情况大致是这样的:

  1. 系统调用外部服务存在近20个server,30个接口;
  2. 每个接口在适配某个用例时参数是可变的;
  3. 每个接口使用的协议是A协议,内部传输的数据是pb序列化后的二进制数据;
  4. 后续还会有其他重构系统,外部调用的接口会增加,但协议是一致的,只是业务数据变了;
  5. 开发测试完到压测开始的时间不多,只有2-3天时间。

要在2-3天时间内完成支持近20个server,30个接口的压测桩的开发,难度还是有的。由于系统调用外部A的编解码不是我开发的,这里还存在研究学习A编解码的工作。难归难,任务也得完成。

具体方案制定

首先确定了使用go开发,效率高。虽然公司内有trpc-go,但在那时我对trpc-go了解甚少,光学习trpc-go估计就得花费个2-3天。成本太大了。还是自己搞个简单的server来的快些。

其次找相关开发了解的A协议如何实现的。在研究后发现这个协议主体很简单,这个协议将需要调用的接口映射到cmdid(单系统唯一的一个数值),根据cmdid路由到指定server不同的接口。也就是只要有cmdid,就能知道调用的是当前服务的哪个接口。但在不同的server下这个cmdid不是唯一的。

桩需要提供n个server,m个func。如果针对每个server做单独处理,时间上肯定来不及,而且以后有新接口需要接入还得再次开发。要解决一套代码提供n个server,m个func,有两个问题急需解决:

  1. 多个svr相同cmdid的路由问题。
  2. 每个接口的pb结构体不同,在不硬编码结构体的情况下如何实现支持多pb数据。

对第一个问题,可以用不同端口来解决。每个端口提供一个server,每个server内就可以保证cmdid的唯一,这样路由的问题就解决了。对第二个问题,在查阅了相关资料后发现,pb是可以使用反射动态获取定义在proto源文件中定义的结构的。那这个问题似乎也解决了。就是传入数据是json,通过反射的方式从proto文件中动态生成这个结构体,然后将json数据映射到这个结构体内,再序列化为二进制数据。这样可以不生成固定的结构体,新增接口也不需要改代码,只需要提供相应的proto文件,指定如何路由就可以快速提供新接口的能力。

编码实施

到这一步已经对当前这个需求如何实现有了十分明确的认知,明确了实现这个需求的方案。剩下的就是一些编码工作了,编码工作相对简单,这里只拆解一下步骤。

  1. 底层编解码的实现。
  2. 根据配置路由到具体server的func接口的实现
  3. 每个请求使用一个协程处理。

编码工作还算顺利,用时1.5天,但30多个接口的配置适配调试用了1天。所以这个需求从接手到完成编码工作正好用时3天,在预期范围内。

实践验证

桩的逻辑比较简单,在性能上应该不是什么瓶颈,最耗时的部分应该是使用反射把json转为pb的过程。于是我用go提供的基准测试,简单测试下json转pb对象的过程。在devcloud机器上单核心测试,在json字节长度4k(实际和这个差不多,可能还会有更长的),可以跑到1w/s。考虑到协程的调度和一些其他的逻辑,在8核机器上应该可以达到5w的并发。桩的性能并不是关键,不要太差就可以了。因此也没在这里做更详细的测试。

一切看着都很顺利,时间符合预期,桩的性能也能达到要求。但实际真的会很顺利吗?

一切准备就绪后上压测环境开始压测,刚开始一两分钟都很正常,桩正确返回了数据,但后来开始存在异常了。我们的服务没有正确收到回包,抛了异常。这个情况也不是每个请求必现,而是大约占1%。最开始以为是我们的服务存在问题,在增加一些日志后排查应该不是程序自身的问题。又通过其他方法验证了,不是程序问题,而是桩的问题。

再来细看一下桩的表现。开始一段时间都是正常的,压力上来后99%的调用都是OK的,1%没有正确回包。这说明主要的逻辑没有问题,问题出在当压力过大时,有一些场景没考虑到。再回顾下具体实现:

  1. 底层编解码的实现。
  2. 根据配置路由到具体server的func接口的实现
  3. 每个请求使用一个协程处理。

从表现上看,第二点是没问题的。第三点可能有问题,因为压力大了,协程变多了,在协程到上限后可能会出现一些没考虑到的情况。而第一点的嫌疑最大。因为我们的程序是按照长链接来调用的。但是在实现编解码的解析帧数据时我是按照短连接的方式处理的。从缓冲区中取数据,取完数据处理完后关闭连接。如果一个缓冲区中存在多个帧数据,后面的数据将不会处理,直接丢弃。

问题改进

现在只是怀疑是这两点产生的,但并不能确定,如果解决了后还是存在问题,那就白白浪费了时间。评估了下如何最快修复问题的方案后决定不在这个简单server上修复,而是使用trpc-go框架。在trpc-go上实现A编解码,将上层的逻辑迁移过去。由于我对trpc-go并不熟悉,因此安排了个组内的大佬K来支持trpc-go的A编解码的工作。我负责迁移上层的svr。

trpc-go app的文档稍详细些,我这块的迁移工作很快完成了。但是编解码的文档似乎就少了。只能对着仓库中的其他编解码来实现。实现的过程还是比较曲折的。各种报错,各种尝试。花了1天的时间才完成编解码的工作。

最终验证

这次使用的是trpc-go框架的能力,因此,1和3的问题应该会一并解决。实际情况也确实如此,再次部署压测环境后,一切正常。可正常压测。桩的性能也和预期差不多,在8核机器上可达到5w/s。

后续的桩相关的新增需求与代码的重构会再起一篇文章,下次再写。

参考文献

内部文章暂不列出。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 需求调研
  • 具体方案制定
  • 编码实施
  • 实践验证
  • 问题改进
  • 最终验证
  • 参考文献
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档