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

本周相关的一些事项,我认为最值得讲得就是某个数据库接入层服务的给每一条数据加上键过期时间。这个数据库接入层是一个 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 一直处于满的状态。 为了降低风险,最好就是为每一条数据设置过期时间。使得 Redis 中存储的始终是较热的数据。而设置过期时间,采取的策略是取一个固定的时间 Y,然后再生成一个均匀分布的浮点随机数因子 a,从 0-1 变化。然后我们对于某一条数据的过期时间就是 Y+aY。这样设置能尽可能避免某些情况下大量数据集中过期造成的某段时间内数据库压力过大问题。 当时 Redis 中有 64GB 的数据,且整个 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 美元!我很惊讶钱都花在哪里了? 我在控制台上查找,又在 Cost Explorer 上进行了进一步的查询,才发现系统盘和数据盘的费用都按照我申请的规格计算了!没想到,AWS 竟然是按照服务器和云硬盘分别计算价格的。而且,我还不知道如何配置第二代 SSD,虽然性能很好,但我用不到这么高的性能,而且价格比第三代 SSD 贵 20%!还有,几个快照居然每个月费用超过了 7 美元。而且,费用查找界面非常复杂,一个选项下可能有很多项目,需要一个一个取消勾选,慢慢查找到底是哪一项花费了多少钱。 ...

2023年11月24日

关于主流道路的一些思考

一般而言,我们总是倾向于捡着他人走过的路来走。一旦偏离这个所谓的“大众路线”,我们很可能就会觉得自己处于悬空状态,从而陷入一种循环地自我怀疑中,总认为自己处于一种脚不着地的危险的状态。我感觉这是我们倾向于认为,大众的某种“主流”的观念是一种无可辩驳地正确,从而不会去思考其中正确性来源的几方观念的出发点,以及这些观念背后的逻辑。 具体来说在我们的生活中,常常会有一种压力,让我们按照主流的方式去行动。这种压力直接来自社会与文化的期望、他人的看法以及我们自身对所谓“成功”的刻板印象。我们害怕偏离主流,也害怕在便利主流后失败,从而被周围的人认为是典型异类或失败者的活生生的例子。因此,我们倾向于随着周围的人选择安全、确定的道路,即那些大家已经验证过的方法。顺从主流方式可以当然可以带来一些实实在在的成果,但这种方式并不一定最适合作为单独个体的某个人,你可能并未从自身的具体情况的细节出发来选择,而可能带有一定的盲目性。我们不断仰视其他人的成功,给自己制造一个个“神像”,时时刻刻去回忆去参拜,在谈论中夹杂这感叹、羡慕或者是丝丝的嫉妒。但我们强调要像这些“神像”看齐,要成为他们,但却始终忽视了自己的特点。我能不能成为另一种定义下的“神像”?这个是很多人都很少去思考的问题,很多时候大多是人可能认为这种思考无意义。 这个时候想象一下,如果别人向你寻求有关他们当下困难的决定或者说是某段时期的人生道路,你可能会毫无思考地说出这些“主流”方式,但当别人进一步问你为什么这些方式是正确的?是否可以列出实际的步骤并从自身出发去论证这些步骤的合理性?你可能第一时间就不知道如何回答了。 虽然说,当你跳出固有的“大众”思维去思考的时候,想着我可否在这个主干道的这个岔路口上走拐进另一条路。这可能会产生很多困惑,因为这对于你思维的惯性来说,何尝不是一次急刹车?也许你第一次考虑一种未曾设想的可能性。并且有可能突然发现原来这件事的逻辑是非常复杂的并陷入一种思维错乱中。在你尝试你认为是正确的新方法时,也有可能发现你其实是走了弯路。最后你可能会得出结论,主流方法也是有一定道理的,是基本上正确的。然而,只有意识到这些,有意识地去了解这些,在拥有可行性的基础上去试着尝试这些,你才能在不断尝试和试错中更清晰地把握事情的本质,最终正确认识“主流”方式的合理性以及它为什么会存在。这个时候你的脚或许真正开始走在实地上,这大概会逐渐在内心深处建立处一种内在的自信,不断引导你尝试其他的可能性,从而有可能找到最适合你个人特质和当下实际情况的道路。 所以,我个人认为不要因为害怕事情的复杂性而放弃寻找其他解决方法的可能性。这个时候要相信自己的现有的能力和基于大量有效思考而对事物判断产生的直觉,不要过分依赖他人的意见和观点,从而自己的一套思想理论和解决问题的宏观的体系。最重要的是,要始终坚持自己的价值观和目标,顺着这个内心的最终指引找到真正适合道路,才能有效避免在大量的思考和尝试汇总迷失方向。

2023年11月21日

自建图床服务并配置免费CDN全球加速 过程记录

个人有一些图片托管的需求,在很多场景下比如说在博客文章中插入图片,在社交网站上分享图片,抑或是个人开源项目中的图片等等,都需要在文档(特别是 MarkDown)中嵌入一个 URL 直链来指向图片。对于一些免费的图床服务商,个人感觉并不是很信任。数据安全是一方面,另一方面如果服务商跑路了,那么很多原先我创建的链接都会失效。据我所知,这样的例子比比皆是。 网上还有一些教程将 GitHub 当成免费图床来用。这样做弊端也很明显,GitHub 在国内的访问速度非常慢,图片加载很慢。另外,缺少很多图床该有的功能,比如说自动压缩,自动转换成 Webp,自动重命名等等。这样做也是不太可行的。下面就是一个我将 GitHub 用作图床的例子。每次我还需要将图片移动到代码仓库里,然后 commit,push,我个人感觉很麻烦。 还有一段时间我试过把 WordPress 的 Midea 库当作图床来使用,但是我发现图片链接中带有 wp-content/upload 这样的路径。然后链接中还会出现图片的名称。我感觉并不是很雅观,而且在隐私的保护中有一种说不好的感觉。就像是别人一看这个 URL 链接,就能获知一些图片以外的信息。下面就是用 WordPress 的 Media 库当图床例子。可以看到,产生的直链不是很美观,而且还能暴露一些信息。 所以,我这边产生了自建图床服务的想法。这种图床最好能自动转换 jpg、png 格式的图片为 webp。这样图片就能比较高质量、快速地在用户浏览器中加载出来。有关 WebP 的简要介绍如下: WebP is a raster graphics file format developed by Google intended as a replacement for JPEG, PNG, and GIF file formats. It supports both lossy and lossless compression,[8] as well as animation and alpha transparency. Google announced the WebP format in September 2010, and released the first stable version of its supporting library in April 2018. ...

2023年11月18日

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

最近,合规的要求逐渐渗透到技术层面。最近的一段时间,总有产品来找,说要实现这样那样的合规需求。或者是,做一些什么合规的调查问卷。我感觉,合规也主要就是说对于用户信息的存储、访问需要规范化,然后用户能够逐渐开始掌控自己的数据。然后还有一些人事上、组织架构上的变动对于技术层面造成的冲击,比如说一个业务被调度到其他部门去了,然后我们还在一直和这个业务共享数据库等资源。这个时候费用问题就突出了,就是说钱该算在那边。虽然是一个公司、一个事业群,但感觉可能是由于公司内部的核算机制问题,对于这些成本问题还是比较较真的,总会掰扯什么:他们用了我们的数据库,钱还在我们这边什么的。有时候感觉,这些都是属于内部消耗,并不是出于发展的眼光看待问题。另外,别的业务可能由于什么需求调整,需要修改一些公用的数据库结构或者接口,就比较麻烦了。因为这个时候,我们为他们花费工时来做这些事情,并不能得到任何肯定。但是,又会被那边的人催着,天天轰炸。自己夹在中间,感觉就比较难受。 然后,我上个周报提到的 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日

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

本周主要处理一个节前发现的风险项。某个服务,在使用 Redis 的时候并没有为键设置 TTL,而是寄希望于 redis 的淘汰策略。我看这个服务使用的 Redis 设置了 LRU 淘汰策略。这种策略看似比较完美,但是当某一较短时间段内,写入流量很大的时候,存在隐患。这个时候 Redis 会触发淘汰过程,并集中尽力于此,以求能腾出足够的空间。这意味着,Redis 不能很好地执行查询等正常操作了。这会造成业务层对 Redis 的读写延时均出现剧烈波动。到现在,我已经遇到过 2 次这样的问题了。而且不设置 TTL,Redis 一直处于 100%的使用率状态,我们从容量上并不能获知按照现在的业务量配置多少购买容量足够。业务低峰时期是否能够通过缩容降低成本也是不能确定的。所以,为 Redis 留出一个足够的可用空间是十分重要的。 所以,我需要改造这个服务,使之对每个写入的新键设置 TTL。按照 Redis 的逻辑,如果键在写入的时候已经存在,则也会被设置一个 TTL。为了业务稳定,我不求所有的键都设置 TTL。所以舍弃了在遍历 Redis 寻找未设置 TTL 的键的方案。其实我调研过这个方案,在遍历的时候,使用需要游标来做。设置的 TTL 长短也有讲究,为了防止同一时间大量键过期造成延时的大波动,设置 TTL 的时候在基础时长后加入了随机时长。假设基础时长为 T,那么加入随机时长后,TTL 的长度会处于 T ~ 2T 之间。这意味着,要求存留时间越长的键,过期的分布也越广。这能够确保业务延时更加平稳,防止数据库压力过大。

2023年6月21日

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

从一月底到二月初,都属于春节的范畴。期间,负责保障春节阶段的运行,需要随时待命处理线上问题。我一直处于一种担忧的状态,好在线上问题并没有主动找上门。整个春节期间维持整体不动是最好的。 这周我在评估一个重要的需求,所带来的影响。我认为,对于一个新的业务需求,特别是应用于一个复杂的业务系统,需要考虑多方面的影响。如果这个时候,对这个系统并不是特别熟悉,经验不多的话,最好选择做最小改动。这不是保守,而是将影响控制在你可以想象地到的地方。因为你不会知道在什么地方,某个违背直觉的机制在运行重要的业务逻辑。得出这样的结论,并不是出于我的想象。 这篇文章写于半年后,到我写这篇文章的时候,我已经遇到至少两次这样的事情了。当时我对某个服务做出了大刀阔斧的调整,在调整完的时候,一切正常。发布后,也看似正常。直到若干星期之后,我偶然发现了某个串联上下游的机制,它差点受到我的影响。在承接一个业务系统的时候,很大概率它是转接和很多手的,藏有很多你不知道的历史,所以框架、核心逻辑能不动就不动。 然后,本周彻底解决了针对一个安全加密服务加密接口不兼容中文的问题。主要的问题是,它将加密的原文直接作为 Redis 的 Key。当原文中存在中文,会发现虽然 Key 存储了,但是找不到。我的方案是在存储的时候,Key 的内容不要直接包含任何业务原文,先取哈希。这样既能够避免一些编码、兼容的问题,也可以大大提升安全性。

2023年6月21日

理解 Kubernetes 中的 CPU 资源分配机制

Kubernetes(k8s)是一种流行的容器编排平台,它允许开发人员在云环境中部署、管理和自动化容器化应用程序。在 Kubernetes 中,CPU 资源的分配是一个关键的问题,它直接影响着应用程序的性能和可靠性。本文将介绍 Kubernetes 中的 CPU 资源分配机制,包括 CPU 请求和限制、CPU Share 机制以及 CPU 调度器等相关概念,以帮助开发人员更好地控制容器的 CPU 分配,从而提高应用程序的性能和可靠性。 CPU 分配单位 在 Kubernetes 中,CPU 的分配单位是 millicpu(毫核),一个 CPU 资源等于 1000 毫核。例如,一个 Pod 请求 0.5 个 CPU 资源,可以表示为 500 毫核。它是基于 Linux 内核的 CPU 分配机制实现的。在 Linux 内核中,CPU 时间是以时间片的形式分配的,每个时间片通常为几毫秒。Kubernetes 利用这个特性,将 CPU 时间片分配单位转换为毫核。 CPU 资源分配机制 在 Kubernetes 中,CPU 资源的分配是通过两种方式来实现的:CPU 请求和限制。CPU 请求用于告诉 Kubernetes 调度器,该 Pod 至少需要多少 CPU 资源才能正常运行;CPU 限制用于告诉 Kubernetes,该 Pod 最多可以使用多少 CPU 资源。如果节点上可用的 CPU 资源不足以满足 Pod 的 CPU 请求,该 Pod 就无法调度到该节点上运行。如果节点上可用的 CPU 资源不足以满足 Pod 的 CPU 限制,该 Pod 仍然可以运行,但可能会受到 CPU 资源的限制,导致运行缓慢或者出现其他问题。 需要注意的是,CPU 请求和限制是 Kubernetes 中的两个独立的概念。通常情况下,应该根据应用程序的实际需求来设置 CPU 请求和限制。如果设置的 CPU 请求过低,可能会导致 Pod 无法正常运行或者运行缓慢;如果设置的 CPU 限制过高,可能会导致节点上的其他 Pod 的 CPU 资源不足,影响整个集群的性能和可用性。 ...

2023年6月20日

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

这一周主要是确保在春节前各类服务的稳定。近期发现某个服务经常在流量高峰时段报超时,我提醒转交服务负责人处理。但是几天过去,服务负责人依然无法说明原因。只好亲自处理这个问题,因为报警已经十分严重,部分节点超时率能够达到 20%。 在这段时间中,应该是由于临近假期,流量大幅度上涨,12 月底相比已经上涨了 100%。所以首先是怀疑服务的承载力不足,所以先进行了一次扩容。但是,扩容后并未解决此问题,告警频率和超时率基本未变化。这种情况下,对服务的源代码进行分析后发现,该服务的接口会首先调用一个下游服务,然后再异步地向数据库中插入数据。因为异步操作并不阻塞工作线程,所以首先应该怀疑是这个下游调用的问题(实际上,我在数据库那边纠结了很久)。 从终端登录进一个节点检查日志,发现所有下游调用走的居然是同一个 IP 和端口,并且走的是稻草人节点。所以我先将该服务做上云操作,排除稻草人转发损失造成的影响。上云完成后,刚切流量的时候发现某个地域(不是原先的地域)下有大量超时产生,做了扩容后也无解,十分疑惑,所以暂时切回来了。我非常奇怪,为什么上云之后,超时率却变得更高了。 从云上监控分析问题,发现在切流量后,下游服务的某个云上节点 CPU 占用非常高,而其他的节点却很低。开始怀疑是负载均衡的问题,所以又回去检查服务的源代码。果然,该服务从注册中心拿到下游服务可用节点列表后,只会调用列表中的第一项。所以某个地域的几乎所有流量都集中在下有服务的某一个节点上了。而原先通过稻草人转发的时候,稻草人调用云上节点的时候会做一次负载均衡。所以,最初的问题应该是流量大量上涨,该服务却没有负载均衡,所以导致某个稻草人过载,进而导致超时。稻草人是代理节点本身没有逻辑,所以能够处理的并发数更大,超时问题不是很明显。而一旦上云,流量会直接集中在某个业务节点上,大量流量一齐涌入,会瞬间导致该业务节点大批量超时。 有了上述的论断后,回去找稻草人的监控,发现某个稻草人确实已经过载了,CPU 占用居然达到了 95 以上。知道原因后,接下来继续上云,先最大限度消除超时告警,然后再改代码添加随机负载均衡算法。这样做是因为节前修改代码,流程上会比较麻烦。 所以首先,我加倍了下游服务的单个节点的核数,这大大增加处理能力。然后,做切流量的操作,一个下游节点的 CPU 占用数突然升高,最终一直保持在比其他节点高很多的比例上,这是意料之中的事情。当整个服务平稳下来后,上云就成功了,这个时候告警已经消除,超时率归零。接着,就是修改改服务的代码,添加负载均衡的机制,在各个流程审批通过后为该服务发布新的代码版本。 在我发布完新版本后,下游服务的各个节点的 CPU 占用就接近了,最终问题解决。

2023年2月14日