工程上的一些想法

Posted on May 27, 2022

由于过往的各种条件限制,如数据库性能不够、PaaS 平台功能不足、对特定领域的问题理解不充分,线上的服务一定会有很多妥协解决方案。这些问题不会让服务彻底不可用,但一定会困扰团队,容易让人担忧系统的可靠性。特别是提供 toB 服务而言可靠性与准确性是一样重要的。

无论问题有多复杂,问题存在的理由多么充分,还是要解决的。属于重要但不紧急的问题(我相信大部分团队这部分的需求列表都很长)。

这些问题大概率不是一个刚入职的新人能解决的,但也不应回避。最简单的就是持续一段时间接收内外部的反馈信息,统计下反馈最多的是哪个,然后着手去了解下相关的背景、代码、问题的复现流程等。

我觉得正常情况下有一定的经验的工程师是完全可以在半年之内对问题有个充分的认知,并有可能确定出一个解决方案的。问题的解决可能会是一个持续很长时间的过程,需要对所有的细节都足够了解,并有足够的耐心。并且在过程中内外部环境的约束条件会明显影响决策的方向。

Redis 相关

目前工作中维护的项目的建立是 7,8 年前建立的基础,那时候面临的基础设施问题和今天的很不一样。比如 Redis 实例的性能、以及带宽是当时的瓶颈点,不同的业务为了规避这样的问题做了一些在今天看来有些麻烦的做法。

客户端分流

利用 CRC32 算法在客户端分流,将 Key 分配到某个实例的某个 db 上。相关的工作可以参考 升级 rb 到 Python3.7。这个做法在很长的时间里几乎完美得规避了单独的 Redis 节点带宽和性能不够的问题,基本够用。问题是增加节点的时候 Key 不会自动迁移的新节点上,所以这部分都是过期时间比较短的缓存。

攒一段时间数据合并请求

对于部分不是很方便迁移 Key 的业务,是将部分信息在内存里攒,攒到一定数目之后再对 Redis 发起一次请求,降低对 Redis 的压力。但是这个过程中因为另外一些问题,服务会时不时重启,导致部分数据没有被 Flush 过。从结果看任务直接丢失了。

解决方案

在今天这个问题变成了单纯的钱的问题:加钱升级到云服务厂商的 Redis 集群版。带宽和 CPU 就马上都不是问题了。于是乎:客户端分流方案直接废弃,不用操心分流指向哪个 db 了;也不在内存里攒数据了,任务数据不会丢失了。

每月多花一些钱,系统更可靠,人员的心智负担也更小了,其实很值。

限定场景下的自己的轮子比外边的香

某年做了一些和视频处理相关的项目,当时有些加工处理操作,如水印、字幕,不知道怎么去做,就用了一个支持视频处理功能的第三方云服务,并增加一个 Webhook API,在数据处理完毕之后接受事件通知。然后再发起文件下载,存储到自己的对象存储中。中间异步的中转库流程时间比较长,整体比较慢。

后来了解了下 FFmpeg 的命令行参数,稍微封装了一些的参数,总共 50 行不到的文件就提供和之前几乎一样的功能,结果整体的运转效率比之前快了很多。

要控制基础设施复杂度的增长

内部某个服务在早期技术路线决定的时候,一个候选项是使用另一门目前无人会用的编程语言来做,原因是在特定场景下可以和基础设施更紧密得结合起来。

这个方案被否决了,因为那样会导致现有的科学计算的知识用不上,并且要增加维护的基础设施复杂度,新增加另一门虽然成熟但无人会用的编程语言也会让维护成为问题。

不否认在特定领域里用另外一些东西更具优势,但是对历史负担已经很重的小型团队而言不是很好的选择。

性能优化相关

2019 年写过一篇 近期做测试和采样的一些记录 基本总结了当时的性能优化工作。Q1 的时候还写了 Go 在只读高并发场景下的优化 总结了将一部分 Python 服务用 Go 重写的一些结果。

最近另一部分优化工作则是用 Sentry 的 Tracing 功能对很深的调用栈做耗时分析,发现了一部分拖慢了系统响应速度的瓶颈点,而这个瓶颈点是之前的各种采样分析没有发现过的,原因是之前的性能分析不够全:

  1. 粒度不够细,没有单次调用的耗时分析数据,看不到各个环节的耗时
  2. 之前的采样工具得到的是整体的调用栈耗时的百分比,相关的的瓶颈影响到的是 p99 指标,在整体耗时统计中反映不出来
  3. 测试的数据不是真实流量,而是一些开发人员手工录入的 mock 数据,没法测试到边缘情况