GpgFrontend的前世今生:我与一个开源加密工具的成长之路

缘起:偶遇 GnuPG(GPG) 我在 2021 年 4 月左右第一次接触 GPG。那时我还只是个对计算机世界充满好奇的学生, 对密码学的理解也仅停留在“密码就是加密字符串”这样的表层概念。但当我第一次读到非对 称加密的原理——一个公钥可以让所有人加密信息,而只有私钥拥有者才能解密——我被深 深震撼了。这种“数学意义上的信任”让我着迷。它优雅、严谨、不可逆。那一刻,我意识 到,这种技术的美感并不亚于任何艺术创作。于是我开始学习 GPG 命令行的各种操作,从 密钥生成到签名验证,反复实验,琢磨其中的逻辑。 但与此同时,我也发现了问题:**命令行的门槛实在太高。**特别是有的时候,想快点做一 些简单操作却要输入一串命令,让我感觉“不爽”。对非技术用户来说,GPG 的命令行是一片 晦涩的荒原,几乎挡住了所有想尝试的人。我开始想,如果能为 GPG 做一个简单易用的图 形界面,让它像一台看得见摸得着的“密码机机”那样可视化与便捷;如果每个人都可以简单 地拥有者与一台耐用且小巧的“密码机”,也许会有更多人愿意尝试。这个想法当时看起来微 不足道,但正是它,开启了我后来整整数年的技术旅程。 “密码机”让人立刻联想到某种坚固、可靠的小设备,就像早期的机械密码机(如 Enigma)那样充满仪式感。它承载的不仅是技术,更是一种对安全的信念。这种机器功能 不求繁多,却必须精准可靠。用户按下一个按钮,期望它执行的动作必须**可预期且安全 **。这种对“可控性”的追求,就是“密码机”的灵魂。 我开始寻找现成的解决方案。那个时候已经有一些 GUI 工具,比如 Kleopatra。但它们的 很多都界面复杂、功能太多,甚至还引入了 X.509 证书体系,这在当时一下子劝退了我。 我希望的是一个更轻、更纯粹的东西。后来我发现了一个叫 gpg4usb 的项目,它非常接近我理 想中的形态——轻量、可携带、跨平台。然而遗憾的是,它早已停止更新,只支持 GPG 1.4 版本。那意味着它与现代系统的兼容性问题几乎无法回避,也无法支持子密钥机制。 我开始琢磨:**能不能在它的基础上重写一部分,让它重新焕发生命?**在 5 月份左右, 我花了大约半个月的时间,在宿舍里读它的源码,摸索 GPG 的接口调用方式,研究 Qt 框 架与跨平台兼容。那是一个孤独却极其充实的过程。每当我让一个新功能成功运行时,心里 的成就感都难以言喻。当我终于让它在 GPG2 环境下运行起来,并修复了不少原有的 Bug 后,我决定给这个焕然一新的项目一个新名 字:GpgFrontend——GPG 的图形前端,也是我人 生中的第一个完整开源项目。 亮相:从宿舍代码到 GitHub 上的光点 我将第一个版本上传到 GitHub 后,并没有期待太多关注。毕竟这是个极小众的领域,GPG 本身就不是大众常用的东西。但几天后,我收到了第一个 issue。那是一位用户,提到程序 在某个操作系统上无法运行。他不仅提供了日志,还提出了修改建议。那一刻,我第一次感 受到某种力量,愿意继续为这一个项目投入时间。 ...

2025年11月12日

二零二三年四月第二周技术周报

原文经过 AI 润色,非本人文风。 在全国范围(如广州、深圳、上海、北京)部署大量客户端的配置下发服务中,面对节假 日、活动上线等高并发场景,传统的单地数据中心架构难以支撑。本文分享我当属在“配置 下发服务”层面进行的多地部署、数据层优化及缓存机制升级,阐述其技术背景、方案设 计、落地实践与收获。 背景 我服务的客户端分布在广州、深圳、上海、北京等全国多个城市,通常需要根据运营策略获 取各种动态或静态配置,比如节日活动、功能开关、故障临时屏蔽等。这些配置由服务端统 一下发,客户端根据自身属性(地域、版本等)拉取对应内容。 随着客户端规模扩大,特别是在活动上线、客户端批量启动的场景中,配置拉取请求会在短 时间内剧增,导致服务端QPS暴涨,严重时甚至会拖垮主服务节点。我虽在容器层实现了全 国多地的 Kubernetes Workload 部署,但数据层(包括数据库和 Redis)仍集中在广州。 所有请求即便从北京或成都的容器发出,最终还是要跨地域访问广州的数据中心,延迟高、 压力集中、体验差。 此外,虽然服务中使用了容器内缓存机制,但考虑到容器本身应保持无状态,其运行内存不 适合长时间、海量地持有配置缓存。一旦配置数据规模扩大至几百兆甚至上 GB 级别,运维 和资源成本都急剧上升。 为了解决这些问题,我决定从数据层和缓存层入手,进行更彻底的多地部署改造。 设计思路 基于上述挑战,我设计的方案主要包括: 主数据库实例(写 + 读)部署在广州,运营人员在这个实例上进行配置管理、写入 操作。 各地部署 只读副本(Read‑Only)数据库实例,例如上海、北京、成都等地。各地 容器连接对应地区的只读实例以拉取配置。 Redis 亦做多地部署:每个地域有自己的 Redis 实例,容器拉取缓存优先从本地 Redis;若缓存未命中,再访问本地只读数据库或主数据库。 在配置下发平台中,对不同 Kubernetes Workload 打标签(如地域、版本、标签等), 客户端根据标签拉取对应配置。运营人员仍在统一平台管理配置。配置平台写入主数据 库,然后同步至只读副本,各地容器读取对应地域的副本。 架构优势 读请求分散到各地只读副本,减轻主数据库压力。 客户端拉配置网络路径变短、延迟下降、体验提升。 配置写入集中在主实例,确保配置管理统一、操作简便。 架构符合“写少、读多”的典型场景:操作多为配置读取,写入(新增活动、屏蔽功能等) 相对少。 实施 根据上面的设计,我继续保留广州作为主写中心,同时在全国多个区域(如北京、上海、成 都)部署只读副本,配合就近的 Redis 实例,实现配置服务的分布式读请求调度。主数据 库仍由运营人员维护和写入更新,通过异步同步机制,将数据分发到各地只读副本。各地的 容器服务则连接本地副本,从而避免跨地域拉取配置数据。 Redis 也采用相似策略,在每个部署地域都设立实例,服务端优先从 Redis 缓存中获取所 需配置,未命中时才回源数据库。这样既减轻数据库压力,也提升了配置拉取效率。为了让 客户端能快速命中就近服务节点,我基于 DNS 做了地域级别的接入调度,同时结合 Kubernetes Workload 的标签机制,自动下发对应配置。所有运营配置仍在部署在广州的 “统一平台”进行集中管理,通过主数据库推送同步,容器按标签识别自身身份,拉取所需数 据。 ...

2025年11月10日

先行动而非先推理

很多时候,我在做事前都会去考虑很久利弊得失。就比如:我去做了这件事会遇到哪些情 况?这些情况下我该怎么办?那些情况下会不如人意?我用的这些个方法真的好吗?等等。 不得不说,这伴随了我的很多的决策,特别是在一些比较大的决策下,这些事先的思考真的 有用处。所以,我在做很多事情之前,都会去想一下这些问题。如果我的结论是:这件事情 目前条件不成熟,或者这件事情很麻烦,亦或者这件事情目前前置条件没达到。那么我就不 会去做这件事情,以免投入了很多,却收效甚微。但是,最近我就发现,我这样将这个方法 扩大化并不好。因为很多事情,对我来说是新的,在我现有的经历和经验下,对这些事情的 认知还处于比较幼稚的阶段。这种情况下,我无法正确地去推理上面说的这些问题,从而去 得出正确的结论。 具体来说,利用常识是真的能枚举一些可以预见的情况的,理论上,这些情况会发生。但 是,实际上,一件事情,或者一个项目,这些理论上会发生的事情都有它们会发生的概率。 这些概率又由许许多多的前置变量决定,如果对这件事情的认知不足,很难说对这些变量抓 大放小,找出主要。这样就容易放大一些变量的影响,最终导致预测的事情其实并不会发 生。另外,在很多情况下,我也会对一件事物的性质和反应对出某种推断,特别是对一类人 的推断,比如说我觉得某些人不友善,某些人友善这样。但实际上,在你真正地接触这些人 后,你才能知道他们是否真如你想得那样友善。或者说,他们对于你和他们接触的真正反 应,只有你去接触后才知道。这些事情是无法进行预先推断的,除非建立在你经常和别人打 交道的基础上。所以,在很多时候,我对于某些事物,某些人群的判断,是先入为主的,是 从别人那里听来的,模糊的,加入了自己想象的一种判断。大多数时候,这些判断并不准 确。如果我想让这些判断准确,那么我就要先行动起来,去做这些事情。 先行动,也就是说在对事情有个轮廓,模糊的前提下,优先行动起来,放低姿态去尝试,然 后再根据反馈的结果看看是否可行。如果反馈良好,完全可以继续行动。如果反馈不好,也 不必立即收手,可以分析一下原因,或者拿着这些具体的案例问问其他过来人。不论哪一种 情况,都比先不动,然后想一大堆,这样如何那样如何,来得具体、实在与真切。很多我们 认为实践中会遇到的比较大的问题,往往会在真正的实践中找到思路。反之,在实践中真正 遇到的大的问题,在想法中往往预料不到。所以,先激活自己,去迈出第一步,在对事情认 知比较模糊的情况下,比什么都重要。 先推理,我认为是建立在对事情已经有一个比较成熟的把握的情况下,特别是在这个事情所 述的领域,我并不是一个纯粹的新手。而推理的准确度,建立在我在这个领域的学习与实践 的成果的叠加。也就是说,推理需要真真切切的样本,这些样本需要自己得到,而不是纯粹 从别人那里“听来”,更重要地是自己通过实践“寻来”。这种情况下,有了这些样本作为基 础,对事情的推理预测的把握才能更大,对于事物的认知才能更加准确。这样,后续就能不 断靠推理来规避一些在这该领域下的陷阱,从而让自己能够跑得更快更稳。 如果综合上面的论述,我可以发现,总是应该先去行动,因为推理没有行动的结果作为支 撑,是摇摇欲坠的。先行动而非先推理,确实是可以作为我在对于一些自己并不熟悉的领域 的一种行事的习惯,从而避免先入为主,然后想太多,最后什么都没做,白白错过了可能的 机遇。

2025年11月10日

二零二三年四月第一周技术周报

看了一下当时的周报,值得说的事情也就是关于日志整理的事情,然后就是关于一个配置下 发服务的多地部署。关于多地部署的事情,下一篇技术周报再详细说明。 首先来说说,日志方面的事情。现在的话,线上有很多服务。然后目前 DEBUG 的日志,是 用了腾讯云的 CLS。而目前存在一些问题,这些服务的日志有些分散,不太好找。如果一个 请求跨越了多个服务,有可能需要研发去跨越几个日志集然后来定位问题。然后就是,这些 服务技术栈并不统一,并没有一个统一的日志监控框架来监控这些服务的状态。目前追踪跨 服务的请求使用 TraceID,TraceID 是在请求进入网关服务或者入口服务的时候自动生成 的。TraceID 有可能是纯数字,也有可能是数字带字母。后续的服务之间的请求基本上会带 有 TraceID 这个字段,用于追踪请求。然而,也还有很多请求内容里不带 TraceID,导致 服务没办法从请求本身来拿到 TraceID,只能索性重新生成一个,所以这些请求在各个服务 之间的 TraceID 都会不同。总而言之,理论上,正常情况下,一个 TraceID 将伴随一个请 求,从进入追踪范围到出追踪范围为止。 另外还有个问题,就是这些个日志集中,日志内带有的字段也不太统一,有写日志集的请求 内容的字段叫 X,有些又叫 Y。有些字段日志的结构上有,实际输出的日志中却没有填写。 如此这些,都增加了新的研发上手定位 BUG 原因的学习成本。上述这些问题,都很让人头 疼。 还有一个就是降本增效的问题,在目前的已有日志的基础上需要看看还有哪些日志可以精简 优化的。整个部门,每个月的单单存储和整理日志成本都十多万。而我负责的这一块的业务 量又占大头,日志量也是非常大的,每天大概有 540 亿条日志。所以,我需要在这一块多 下功夫。 对于当前的的 TraceID 问题,我采取先把同样技术栈服务的 TraceID 的生成方式和传递方 式都改为统一的。对于 Java 技术栈的服务,用的无非都是 Spring Boot 框架。所以我对 每个服务都增加了一个统一的拦截器,来拦截请求和响应。对于请求,这些 Java 服务处理 的都是文本类型的请求,基本上都是 JSON 格式编码的。所以,无外乎就是检查 JSON 字段 中是否有相应的 traceId 字段,如果有的话,那就拿来用。具体就是把 TraceID 放置到线 程私有化存储中(MDC),伴随着这个线程处理。这个方法也有局限性,有些请求会在某些 步骤上进行异步处理,转移到其他线程池的线程上处理了。这样,TraceID 的追踪就丢失 了。我的办法是写一个包装类,接管所有异步处理的需求,参数与调用方式和原来一模一 样,然后在其中检查上一个线程中是否含有 TraceID 字段,然后把 TraceID 字段先放到包 装类产生的对象中。切换到另一个线程后,这个线程先看看包装类对象中是否有上一个线程 存下的 TraceID,如果有就提取这个 TraceID 然后记录到自己的私有化存储中。这样 TraceID 的追踪就不会中断了。 ...

2024年11月17日

二零二三年三月第四周技术周报

本周有个插入的事项,就是说我们目前在使用某云服务商的 API,对于其中的 API 鉴权, 那边有更安全的方案。原先,我们应用调取云产品的功能时,是通过一个 AccessKey 和 AccessSecret 来的。在调用的时候,需要在 SDK 中提供这两个值,然后就可以在应用中使 用 SDK 提供的云产品 API 了。原先的安全要求是不能在代码中存储这两个值,公司会有某 种自动扫描机制,通过这个机制可以检测代码仓库中的敏感信息,这两个鉴权用的字段也当 然囊括在内。所以,我们一般都会把这两个值写在配置文件中,安全性会更好一些,也方便 更改。 而现在他们说这两个值是静态的,无法在泄露的情况下快速进行更换。毕竟我们服务一般都 有几十上百个容器,而目前来说,启动配置虽然已经集中于各种配置中心里了,但是在技术 上是无法实现秒级别的动态更改的,必须在修改配置后重新启动容器。而容器的启动是无法 通过一次性重启所有容器来完成,必须进行分批重启以保证服务的平稳,没有十几分钟甚至 几十分钟是搞不定的。 所以云提供商的技术团队提出了一个新的方案,通过向我们提供一些必要的而且泄露后不容 易造成直接影响的凭据(好像是一个密钥文件),然后通过在线认证的机制下发动态的验证 信息。这个过程比我上文提到的验证机制复杂许多,特别是我们需要在容器中纳入这样一个 配置文件,或者直接在 JAR 包中纳入。然后我们需要引入一个 SDK,来专门做这个动态验 证信息的获取,然后将这个信息通过某种方式传递给云产品的 SDK,最后才能实现云产品 API 的调用。 这个具体的过程并不是我来跟进,我把这些问题交给其他同事了,由他们来具体执行。我只 是作为云账号权限的控制者,为他们提供密钥文件并给他们提供正确的方向即可。

2024年4月15日

二零二三年三月第三周技术周报

本周值得注意的事项就是,测试人员那边反馈在测试环境下,某些账号突然无法正常使用的 问题。目前整个账号的体系还是在沿用老的系统。而在当前老系统中,账号信息使用某种特 殊的数据存储分成多个模块存储,每个模块相当于一张表。每张表都各有侧重,有些侧重于 与其他账号之间的关联关系,有些侧重于查询等等。这些账号无法使用的问题在于,其中最 重要的一张表也就是主表,这个用户的记录消失了。在其他表中该用户的相关记录还是存在 的。 这就引发了我的各种猜想,是否是测试人员测试的过程中的环境弄混了?或者是某个服务的 配置有问题导致了部分应该写在测试环境的数据写到了生产环境去了?说代码写得有问题导 致了某些极端情况下,用户的主表信息写入失败?通过调查发现,最终要的查询表中,数据 结构还是完整的,而且能够反推出用户注册时候的微信账号。查询表只有在账号创建的时候 才会去写,后续基本不会再修改了。而主表中的数据应该是和查询表在用户注册的时候一块 写入。 对于这个问题,后续推断应该是数据模块发生了某种问题导致数据丢失。而在我咨询数据模 块相关维护人员的时候,我还没说清楚问题,他们就说是测试环境并不保证服务的可用性, 可能发生任何问题。那么这个问题可能就无解了,因为如果是数据模块这层问题,我作为使 用方基本没有任何能力来自己解决。除非我能替换掉这个数据模块,使用更加稳定可靠的解 决方案。那对于这个问题,我只能够然同事手动清理查询表中的索引关系,然后在让用户重 新进入应用。当用户重新进入应用的时候,由于没有索引就相当于没有这个用户,此时就会 重新创建用户。后续几个月遇到了十几例相同的问题,没有办法,让同事添加了一段自动处 理这个问题的业务代码,以免去每次手动处理的麻烦。 这个问题第一次出现将近一年后,由于某个重要客户反馈了这个问题。而又正好这个重要客 户当时半个月之内反馈的我们的各种问题有点多,领导对此十分困扰,所以对于我这个问题 他特别重视。领导后续找到我让我推动解决这件事,我同意执行,因为我也想知道问题具体 是什么。现在能够确定的是,问题出现在数据存储层,而这一层又不是我们直接控制,我们 只是使用这样一个产品。这个产品已经很老了,维护人员很少了,而且还有其他事情。他们 不愿意来主动寻找问题,也是情有可原的。所以想要推动他们的维护人员来解决,就只能抓 住具体且直接的证据来证明他们的问题在哪里。 我以当时的了解,知道数据层分为缓存和落盘数据库,而且经过一年在各种问题的解决过程 中学习,我也能很熟练调取分析落盘数据库里的数据了。我调取了落盘数据库中的用户 ID 的倒序情况,发现从某个用户 ID 开始,后面就没有了。最近一年,用户报问题的用户 ID 以及新注册的用户 ID 是远远大于这个 ID 的。我又去日志中获取了一个最新注册的用户 ID,查询数据层,发现是有的。说明缓存中是有正常数据的,但是由于某种原因数据并没有 落到数据库中,而是在缓存满后直接消失了。 这已经能够说明问题了。于是我将这些调查记录截图发给数据层产品的维护人员,并提了一 个工单,推动他们来解决这个问题。该问题的脉络我已经提前分析地很清楚明了了,而且又 有直接的证据,他们也开始认真对待这个问题。后面确实,他们重启某个模块后,这个问题 就好了。而且最终他们承认问题与缓存层落库的机制有关系。

2024年4月15日

二零二三年三月第二周技术周报

本周相关的一些事项,我认为最值得讲得就是某个数据库接入层服务的给每一条数据加上键 过期时间。这个数据库接入层是一个 Java 服务,本质上做的工作就是将 Redis 与数据库 结合起来。对上的接口遵循某一个公司内部的特定协议,而对下则使用开源且很多人使用的 Java 包来访问 Redis 或者数据库。访问 Redis 使用的是 lettuce,而访问数据库用的就 是 mybatis。原来访问 Redis 其实用的是 Jedis,后面之所以改用 lettuce,是因为它在 高并发下的性能/资源表现更好一些。我认为具体地主要体现在多路复用连接,使得不需要 在连接池中维护很多连接,总之就是在连接上的开销不会很大。 然后对于数据库来说,并非直接写入,而是在消息队列中写入一条插入指令并附上一些上下 文。这样消息中包含了写入数据库需要的所有信息了。然后这条消息队列也是自己在消费, 消费的时候将数据写入数据库,如果成功就 ACK 消息,失败了就让这条消息 5 分钟后再回 来。 这个服务总的读写策略就是,如果一条数据查询来了,Redis 中没有数据时就将数据库中的 数据拉到 Redis。然后返回 Redis 中的数据。Redis 中有数据则直接返回。对于写入数据 来说,这条数据会双写 Redis 和数据库。修改数据的时候,操作复杂得多。因为这个 Redis 中数据是使用 HSET 的方式进行存储的,然后一条修改指令可能只是修改 HSET 中的 一部分字段。这个就需要对一些具体的情况进行讨论了,数据库指令也是根据具体情况来构 造。 值得一提的事写数据库的时候,目前看前人升级了策略,不直接拿消息中的数据来写,而是 消费到消息时,直接从 Redis 中读数据(这个时候 Redis 是早就被双写了的),然后直接 将 Redis 中读区到的数据写入数据库。我认为这样做的好处是,让数据库中的信息尽快更 新。因为从消息投递到消息队列再到消费的过程中,这一条数据可能已经修改了好几次了。 在这几周我发现这个服务在 Redis 中的缓存并未设置过期时间,而是依赖 Redis 实例中设 置的统一过期策略来进行数据淘汰。这个策略是在 Redis 数据存满的时候才会执行的,这 就会引入一个风险。根据我们云上分布式 Redis 的设计,如果 Redis 存储一直都是满的然 后这个时候有大量的数据写入操作(某种特殊的业务高峰期),这样云上分布式 Redis 为 了一次性腾出足够的空间会集中进行淘汰操作。这个时候无法处理任何请求,这种情况将持 续几分钟。这对于业务来说,存在较大影响,因为这个时候上游业务服务将无法读或者写入 数据了。而现实情况就是,这个服务的 Redis 一直处于满的状态。 ...

2024年4月14日

关于个人对AWS服务的使用感受

原文经过 AI 润色,非本人文风。 从 2022 年 11 月开始,我开始使用 AWS 服务,已经接近 1 年的时间了,而且不是免费套 餐。从一开始对它的使用感到新奇和陌生,到现在对它的了解更加深入,期间经历了很多有 趣的事情。那么,为什么我选择使用 AWS 呢?因为我个人有一些需要在 AWS 上运行的服 务,比如博客网站(Wordpress)、Git 仓库、RSS 订阅等等。当时,我觉得 AWS 在云服务 市场占有最大的份额,所以它的解决方案应该已经非常成熟了。对于 AWS,我还是有一定的 信任,愿意花钱购买适合的服务,以确保我的个人托管业务能够长期稳定运行。 在开始使用 AWS 之前,我也尝试过免费套餐,但是一直没有找到合适的需求场景,所以用 了一段时间后就不再使用了。这样,我的免费套餐周期就白白浪费了。 当我第一次接触 AWS 的时候,我首先尝试了 EC2 产品,并试用了 1 核心 1G 内存的 T2.micro 实例,但后来发现内存不够用,导致机器卡死。因此,我决定换成 T2.small 型 机器,它拥有 1 核心 2G 内存,并且我还增加了 SWAP 分区,每个月大概需要花费 17 美 元。虽然这个价格包括了带宽费用,但我仍然觉得有些贵。然后,我开始考虑是否可以使用 AWS 的保留实例,听说可以减少 60%-70%的费用,看起来非常划算。于是,我下定决心购买 了三年的保留实例,先支付了 170 美元,然后每个月承诺支付 4.75 美元。这样一来,每 个月的费用大约是 8.84 美元,这个价格看起来相对合理。 然后遇到了一个坑,当时我配置了 30G 的系统盘和 10G 的数据盘,以为 30G 的系统盘是 免费的。然而,我又考虑到数据的安全性,因此还增加了定期快照备份,以为快照的费用不 会很多。最后,月度账单一出来,竟然要 20 美元!我很惊讶钱都花在哪里了? ...

2023年11月24日

二零二三年三月第一周周报

最近,合规的要求逐渐渗透到技术层面。最近的一段时间,总有产品来找,说要实现这样那 样的合规需求。或者是,做一些什么合规的调查问卷。我感觉,合规也主要就是说对于用户 信息的存储、访问需要规范化,然后用户能够逐渐开始掌控自己的数据。然后还有一些人事 上、组织架构上的变动对于技术层面造成的冲击,比如说一个业务被调度到其他部门去了, 然后我们还在一直和这个业务共享数据库等资源。这个时候费用问题就突出了,就是说钱该 算在那边。虽然是一个公司、一个事业群,但感觉可能是由于公司内部的核算机制问题,对 于这些成本问题还是比较较真的,总会掰扯什么:他们用了我们的数据库,钱还在我们这边 什么的。有时候感觉,这些都是属于内部消耗,并不是出于发展的眼光看待问题。另外,别 的业务可能由于什么需求调整,需要修改一些公用的数据库结构或者接口,就比较麻烦了。 因为这个时候,我们为他们花费工时来做这些事情,并不能得到任何肯定。但是,又会被那 边的人催着,天天轰炸。自己夹在中间,感觉就比较难受。 然后,我上个周报提到的 PHP 网关迁移的问 题,其实前一两个月就开始在做了。其实也早就写好了,但是在后续切换流量到新服务的时 候,总是会遇到这样那样的问题。具体表现就是,测试环境流量切换很久了没有人来找,一 切正式环境这样那样的人就来找了。所这个接口突然用不了,那个接口突然报错什么的。这 种情况下,由于问题发生在线上,只能先把流量切回去再分析问题。一来二回,一个星期就 过去了。有时候,切换了 4、5 天才会有人来找。这样一个流程下来久更久了。 leader 一直说这件事情做了很久如何,但实际的情况就是这样。虽然这个服务的后续具体 的代码编写和维护工作并非我来做了,但是我也知道一个新的服务要能够完全替代就有的服 务必须有一个这样的流程。因为在缺乏文档和测试用例的情况下,你不知道一个接口有什么 暗藏的机制,也无法完全清楚我写的代码是否和原来的等效。所以执行层面,很多事情没有 明确汇报,但是我感觉管理上也要能体会到。虽然过程比较反复、艰难,但最终这个服务的 重构工作在本周还是完成了,所有的流量都迁移到新的服务上了。也就是代表,这个老 PHP 网关的生命周期终结了。 我接手主要的后台业务也快半年了,现在发现刚接手的时候由于 缺乏经验,总感觉别人的技术高深莫测。但是,自己接触久了才知道里面有这样那样不完善 的地方。还存在一些设计缺陷,非常严重的缺陷都有。比如说把 Redis 当作数据库使用, 说白了就是没有设置缓存过期时间,然后 Redis 里面的数据又很重要不能丢失。这对服务 里面还存在着一些缺乏文档、维护人员早已离职的老旧服务,这些老旧服务基本上平时无法 来仔细研究。但这些服务中总有那么一两个依然在被访问,或者早就不能访问了,客户这几 天突然发现然后急着催我们解决。很多安全问题也需要整改,某几个 API 账号有一些高位 的权限需要收敛,但是我并不知道这个几个账号具体是做什么的。这些问题,就是日常频繁 遇到的,很难避免。

2023年11月15日

二零二三年二月第四周技术周报

本周我发现有些服务框架写得并不是很好,特别是某些 Java 框架。当 CPU 占用达到 40% 左右的时候,就会出现大量的超时现象。这些服务,CPU 核心和内存容量不是不多,工作线 程数目也不是不够。但是,就是跑不满 CPU。用 Java 性能工具分析发现,其实发现大部分 工作线程处于 Idel 或者 Waiting 状态。目前综合所有情况分析,还是百思不得其解。NIO 也用上了,还用的是 Netty 框架,但是吞吐量就是上不去。通过对于线程的分析,发现并 没有特别繁忙的业务线程。推断应该是 IO 或者某种等待机制导致了这种低处理效率。 这 次我是准备通过削减节点数量来降本,在执行前并没有考虑特别多的因素。所以我在削减节 点容量的时候,只是通过查看 CPU 的负载来判断节点是否能够承受。当我将北京地域的 workload 平均 CPU 占用率提升到 35%-40%的时候,整个服务出现了大量的超时现象。当时 把我吓了一跳。后面从监控上分析,几乎是整个背景地域的节点都超时了,也就是处于一 种”休克“状态。 本周也继续重构某一个老的 PHP 网关服务,新的网关用 Java 写成。但是我其实不是很赞 成网关用 Java,毕竟 Java 这种语言的执行特性很大程度上决定了它不适合特别高的并 发。并且,我们目前所用的都是 JDK 8,并没有引入轻量级的线程,每个 4c8g 容器内线程 最多也就 800 个,多了线程切换开销就会特别大。所以单个容器的吞吐量有限,承载同样 的流量需要更多的容器。刚开始我是用 Go 重写的,这个框架很不错而且也有专门的团队维 护,但是 leader 还是让我用部门自研的那个 Java 框架。可能人事上的考虑居多吧,无 奈,我还是先写吧。

2023年11月15日