系统设计
转转运营系统之商品标签平台
高并发下秒杀系统的设计
- 压力分摊。利用
分桶策略压力分摊,往往受到很多人的青睐。具体而言,面对单品库存,巧妙地拆分为多组,大大缓解抢购高峰的压力。但这背后藏着诸多棘手难题,如何精准地将库存均匀分配至各桶,杜绝分配不均;怎样有效处理拆分产生的库存碎片,避免少卖;分桶间的调度规则如何制定,确保协同高效;还有,业务量起伏不定,又该如何实现分桶动态扩容以灵活适配。 - Redis + MQ。
- Inventory Hint。AliSQL 提供 Inventory Hint,帮助您快速提交/回滚事务,配合 Returning 和 Statement Queue,能有效提高业务吞吐能力。
- 压力分摊 + Redis + MQ。
Redis+MQ 的具体逻辑:
- Redis 部分,通过 Lua 脚本进行数据处理。这里还进行了流水的记录。
- MQ 部分,使用 RocketMQ 的事务机制。首先先向 RocketMQ 中发送 half 消息,然后检查本地事务执行情况(Redis 执行是否成功) 来判断是 commit 或者 rollback。同时配置 RocketMQ 反查机制,避免收不到本地事务执行结果,反查机制则是查询 Redis 中有没有流水的记录。后续就是 MySQL 消费消息了。
- 记录流水的原因:为了保证下单和库存的一致性,我们可以用定时对账机制来核对库存流水和订单表中数据是否一致。当然也有其他一致性保证方案,比如
Seata、TCC等,可以根据具体的业务场景选择。
转转数仓评估体系实践
亿级高性能通知系统实践
消息通知系统设计:
系统设计
请求发送:RPC 无需考虑消息丢失;MQ 异步解耦。
消息发送请求:
消息的幂等性处理:消息量大的情况下 + 重复消息只在短时间内发生,采用 Redis 判断。
问题服务动态发现器:异常服务的动态发现,是借助于 sentinel 的 API 在各自节点 JVM 内实现的,针对设置的 时间窗口 内 请求的总次数 和 失败的总次数 进行统计。统计后,采用类似“熔断”的机制来处理服务的可访问情况。
sentinel 滑动窗口的实现原理(环形数组):假设统计 1s 请求量,窗口为 2 个,那么每个窗口对应的 id 就是 0、1,相应的时间范围就是 0m-500ms,500ms-1000ms。如果当前时间是 700ms,那么对应的窗口 id=(700/500)%2=0,对应的 windowStart=700-(700%500)=200,对应的起始就是 id 为 0 的窗口;如果当前时间是 1200ms,对应的窗口 id=(1200/500)%2=0;对应的 windowStart=1200-(1200%500)=1000 大于 id=0 的起始时间,重置 id 为 0 的窗口起始值,id=0 的位置不变。
动态调整的线程池:自定义一个无界的阻塞队列,核心线程数和最大线程数相等,而且用的是默认的丢弃策略。在添加任务的时候,判断队列中的任务数目是否达到了 config 的最大值,通过调整 config 值,动态调整队列长度。如果无法添加,可以配置后续处理逻辑,如 MQ 落库 + 重试任务,来避免触发拒绝策略。
重试消息发送:重试的消息机制可以利用分布式定时任务调度框架,一般为了提高重试效率,会采用分片广播这种方式,自己做好消息重复发送的控制,我们也可以利用调度线程池来实现。
ES 与 MySQL 数据同步:由于发送消息的数据量,后台在进行数据查询时主要是通过 ES 进行查询处理的,这就涉及到数据库数据与 ES 数据一致性的问题。ES 更新完成后修改数据库状态为更新完成状态,若此时通知记录表还有更新,就会将同步状态初始化,若修改数据库为init先于同步完成后的更新就会出现数据不一致的问题,所以每次同步时携带上数据库中的 update_time,大于等于db中的 update_time 才会更新完成(其实 update_time 就是一个 版本号)。
ES 按月滚动建立索引,每月新建立的索引,标签都是 hot,新增的数据都会放入 hot 节点上进行存储,到了第二月,通过定时任务将上月索引的 tag 修改为 cold,ES 集群就会自动将数据迁移到标签为 cold 节点上。
稳定性保障
- 流量突增:两层降级。当流量缓慢增大时,线程池满了后,利用 MQ 进行流量削峰,后续定时任务处理。当流量陡增,sentinel 判断后,直接改为 MQ 落库,后续延迟消费。
- 资源隔离:耗时长的请求和耗时短的请求放在不同线程池中处理。
- 第三方服务:限流、降级、熔断,避免影响别人服务。
- 中间件的容错:比如需要考虑到 MQ 扩容时,会 MQ 宕机数秒的容错。
- 监控 + 多机房部署 + 弹性扩缩容。
将查询数据性能提升100倍的计数系统实践
链接:计数系统设计 TODO
商品准时达,购物不抓瞎,快来学习转转履约时效新姿势
京东商品预计在 xxx 时候送达。在转转 APP 的商品列表页、商品详情页、确认下单页、商品筛选项等等都会有履约时效的身影,由此可见,日常获取商品的预计送达时间的 QPS 很高,大促时足以破万。
送达时间的万级 QPS 支撑:
将这个链路图进行细分,如:下单 → 仓储出货 → 物流揽收 → 物流派送。不同阶段的时间更新时间不同,有的可能要经常更新(存 Redis),有的可能不需要经常更新(本地缓存)。
处理逻辑包括:用户地址转换、商品寻仓储、仓库信息填充、匹配揽收时间、获取预计送达时间。前几项的优化如下,最后一个通过“发件省市 ID - 收件省市 ID - 揽收时间:预计送达时间” 来存储 Redis,进行快速匹配。
| 查询描述 | 优化方案 | 说明 |
|---|---|---|
| 用户地址转换 | 本地缓存 | 城市名称和 code 的映射关系发放入本地缓存 |
| 商品寻仓 | Redis | 请求一次放入 Redis |
| 仓库信息填充 | 本地缓存 | 仓库信息为低频变化数据,数据量少,可以放入本地缓存 |
| 匹配揽收时间 | Redis | 配置修改是次日生效,每天定时刷入 Redis |
| 次日达、隔日达以及三日达的可行性判断: |
- 在商品标记上每个商品所在的仓库站点。
- 提供到全国任意地址支持次日达的仓站点的能力。
优化存储结构:
- 存储结构由原来的 key-list 转变为 key-zset,把所有仓的所有批次(最晚支付时间)聚合成一个 zset,最晚支付时间作为 score,仓库信息作为 value 生成倒排索引。
- 原本的存储结构是针对某个收件区的次日达,每个仓下有哪些最晚支付时间支持;现在的存储结构是针对某个区的次日达,每个时间下有哪些仓支持。
转转客服IM系统:高效沟通背后的技术挑战和解决方案
客服 IM 系统:从客户端到服务端,再从服务端到另一个客户端,任何一个环节的故障都可能导致消息延迟、丢失、乱序或重复,从而影响用户体验。
实时性问题:长短轮询 → Websocket。技术选型考虑点:浏览器支持情况、服务端负载、客户端延迟、客户端消耗、实现复杂度。
可靠性问题:
- 利用 TCP 的 ACK 机制,通过 ACK + 等待 ACK 消息列表。
- 服务器,维护全量数据 + 当消息发出时同时发送一个延迟 mq,延迟消息被消费时对应的消息仍在等待 ACK 列表中,则表示消息未能在规定时间内被确认,需要进行重试发送。
- 客户端重新刷新 or Websocket 重连时,重新拉取数据。
- 避免重复消费:发送方使用发送人 id 和消息发送时间戳作为唯一的 ACK 标识,服务端基于雪花算法生成消息 ID,将 ACK 与 ID 建立映射,再将消息 ID 发送给用户、客服。
心跳机制:人工客服有闲,如果长期没人说话,那么就需要断开连接。客户端定时发送心跳,服务端接受心跳后,开启心跳监控的定时任务。定时扫描,如果超过一定时间,进行一定的处理,如标记离线状态 or 彻底断开连接。
消息协议:首先,消息类型有心跳 message、用户/客服 message、系统 message。其次,客户 ID、用户 ID、消息内容。消息内容:消息格式(文本、图片、视频、优惠券等)、消息文本。
转转回收持久层的架构演进
项目背景:优惠券发放门槛低,随着业务发展激增,单表难以 hold;再加上查询逻辑越来越复杂,需要优化。
- 分库分表的技术选型
- 优化服务时,若长期方案开发慢,先实现一个临时改进方案
- ElasticSearch 数据写入到能查到存在延迟,与 Redis 结合来满足时效性。
数据增多的解决办法 —— 分库分表,技术选型如下:
- 基于 JDBC 进行代理:该方案不需要运维等人员的介入,技术内部即可进行开发优化。
- 基于数据库进行代理:该方案需要 DBA 或者运维的介入,维护起来不方便。
- TiDB 数据库:支持无限的水平扩展,具备强一致性和高可用性,编码层面的使用跟 MYSQL 无异。
最终选择了基于 JDBC 进行代理,因为这种方案可以纯内部进行消化,不需要外部部门介入,对于开发成本、时间周期来讲都是比较容易弹性调整的,后续有改造也不需要外部介入。
数据多并不一定要分库分表,参考“技术选型”中,有个业务场景中选择 TiDB。
关联:MySQL 分库分表、高性能-数据库:读写分离+分库分表。
查询越来越复杂
- 例子:小明查询 iPhone13 非全新机、价格满 1000 元可用、以旧换新场景下、邮寄售卖可用的优惠券。
- 当前的方案:把配置表全部拿出来机型进行内存过滤,拿着满足条件的配置 id 去绑定关系表里进行查找。
- 临时改进方案:将优惠券放到内存中进行缓存,内存过滤减少不必要的额外查询,降低 gc 频率。同时,借鉴了中间件同步缓存数据的方案,一方面实时广播推送保证时效性,另一方面定时去拉数据来进行兜底处理。
- 长期方案:ElasticSearch 来支持查询,但是 ES 数据写入到查询存在一定的延迟,而场景是需要实时的场景。解决方案是,通过 Redis+ElasticSearch 联动查询来保证时效性,在写入成功之后将配置 id 同步保存到 Redis 的 zset 结构中,设置个 10s 的过期时间。
其他性能优化手段:
- 查询只返回需要的字段信息。
- 定义索引的时候使用合适的字段。
- 限制数据总量,根据实际场景做数据归档。
- 减少索引范围,强制根据 uid 进行分片路由。
转转流量录制与回放的原理及实践
其他业务场景
支付对账系统:支付使用方(转转)和支付提供方(第三方支付)相互确认交易、资金的正确性,保证双方的交易、资金一致正确。
其他
Spring声明式事务源码详解
接口从4秒到200毫秒-小小的日志竟能引发如此问题
通过阿里的 Arthas 工具的 trace 命令可以分析方法的耗时,发现是 org.slf4j.Logger:info() 耗时长。
Debug 过程:日志同步打印 → 多线程争抢资源 → 探究异步打印日志的原理 → 测试代码进行打断点。
火焰图应该更好一些,直接分析出每个函数的处理时间。
异步日志打印的底层采用的是 Disruptor 框架,采用环形数组来存储日志,生产者消费者模式。
org.apache.logging.log4j.spi.AbstractLogger #getLocation ,用来获取内容匹配日志输出格式中的 %C、%F、%l、%L、%M。最终发现是底层执行 C++代码慢的缘故。
结论为:
- 避免打印过多无用日志,将测试过程中需要观察的日志输出为为 DEBUG 级别。
- 生产环境打印日志的级别设置为 INFO。
- 如果不需要关注日志打印的位置信息,可以将日志输出格式中的
%C、%F、%l、%L、%M去掉。
SpringBoot的脚本引擎初始化也会导致OOM?
搜广推
技术选型
Geo技术助力,让风险定位更精准
GEO 工具的选型逻辑供参考。关连 GeoHash 编码
业务背景:下单地址可能和黑中介相邻,那么这个订单可能有风险,订单机器可能拿不回来。
MySQL。优点 1:兼容性高,通用性强。优点 2:数据化长期持久化。缺点:大数据量下复杂查询性能差,百万级经纬度的地址空间计算。
Redis。优点 1:内存存储,查询延时低,GEO 内部数据存储 SortedSet,支持高效范围查询和排序。优点 2:支持集群模式,适合分布式场景。缺点 1:内存容量有限,不适合长期存储海量数据。缺点 2:不支持高级地理计算(如面积、地理围栏计算)
ElasticSearch。优点 1:分布式架构,适合海量数据和高并发场景。内部倒排索引和分片机制,优化查询性能和保证容错。优点 2:内置地址空间数据类型,支持复杂地址查询(地理围栏、距离排序、多边形查询等)。缺点 1:需维护 ES 集群,开发学习成本高。缺点 2:内存和磁盘占用高,不适合小规模场景。
结合需求的场景,首先保证性能,次要无须重量级的框架,开发成本低,同时还要能够满足地理计算的基本要求。最终选择 Redis。
Redis 支持(1)添加与查询经纬度、(2)查询两个地址的距离、(3)指定范围半径内的地址。
突破数据存储瓶颈!转转业财系统亿级数据存储优化实践
业财系统接收了上游各个业务系统(例如:订单、oms、支付、售后等系统)的数据,并将其转换为财务数据。
这里使用 TiDB。没有使用分库分表的原因在于,业财系统数据多样性+复杂性,难以定义出一个业务主键。没有使用冷热库的原因在于,现阶段还会更改和查询历史数据,时间口径不统一,边界比较模糊;后期可能还会有其他类型数据,数据格式难以统一。
MySQL
”慢SQL”治理的几点思考
“服务稳定性治理”:通过错误日志、慢 SQL、接口性能等各项指标优化,进一步提升系统稳定性和可靠性。
索引不发挥作用。因为优化器可能认为查找成本过大,规避的方式是,区分度小的字段避免建立索引。
内存碎片同样需要关注。当内存碎片过大时,如果出现库表的统计信息未及时更新,也会因为优化器评估的结果与实际差距太大,从而影响实际执行效果。
通过 show table status like ‘xxx’ 可以查看数据库的内存碎片。
DELETE 逻辑会使得内存碎片增加:
- 删除标记。InnoDB 删除时,只将对应行标识为“已删除”,物理空间并不释放,为 MVCC 和回滚保留可能性。
- 页内空洞。删除数据较多/频繁删除,会导致页内大量空洞,页实际利用率下降(如 90% 到 50%),读取相同数据量需要访问更多的数据页,IO 增加。
- 页合并的局限性。InnoDB 仅合并相邻空闲页,若页分散则无法合并。
强制 MySQL 引擎清理内存碎片,是可以在线处理的吗,还是停止服务的?如果前者,可以流量小的时候,合并下?
前缀索引的坑。使用了唯一索引,导致两个不同的字符串,只是前缀相同就触发重复冲突。
长事务问题。当长事务导致 Undo Log 膨胀时,容易使得扫描效率降低。同时 Buffer Pool 中缓存页因旧版本数据过多,其缓存命中率也会下降。我们可以通过 SHOW ENGINE INNODB STATUS 中 History list length 值是否飙升,加以判断。
聊聊Druid连接池的内部原理及推荐配置
Java 数据库连接池 Druid 源码分析
揭秘 MyBatis-Plus 批量插入的终极优化技巧
性能瓶颈:
- 逐条插入效率低:传统的逐条插入模式效率欠佳,每次插入数据时都要与数据库进行交互,从而产生较高的网络开销以及数据库解析成本。
- 外键关系处理复杂:题目与选项之间存在外键关联,这就需要在插入数据后获取主键 ID,无疑增加了操作的复杂程度。
- 批量操作性能有限:使用默认的 saveBatch 方法,其性能提升并不显著,难以满足高并发、大数据量的实际需求。
优化方法:
- 将 rewriteBatchedStatements 配置为 true:以此启用 JDBC 驱动的批处理重写功能,可显著提高批量插入的性能表现。
- 预先生成 ID:采用 zzidc 等方式预先生成主键 ID,有效解决外键关系问题,进而支持批量插入操作。
- 使用异步方法进行多线程批量插入:运用异步方法来进行多线程批量插入,确保线程安全与事务独立,避免出现资源竞争的情况。
- 调整数据库连接池和线程池参数:对数据库连接池和线程池的参数进行调整,以满足多线程并发操作的实际需求。
- 监控异步任务和数据库性能:对异步任务和数据库性能进行实时监控,以便能够及时发现并解决性能瓶颈问题。
rewriteBatchedStatements=true,启用批处理重写功能后,驱动能够将多条同类型的 SQL 语句进行合并,进而发送给数据库执行。网络交互降低,引擎解析次数降低,执行效率提升,总内存消耗降低。配置方式:jdbc:mysql://localhost:3306/db_name?rewriteBatchedStatements=true
多线程并发插入的实现。在 MyBatis 中,SqlSession 在默认情况下并非线程安全的。若在多线程环境下共享同一个 SqlSession,极有可能导致数据错误或引发异常。利用 Spring 的 @Async 注解,实现异步方法调用,每个异步方法都有自己的事务和 SqlSession。具体实现见原文。
评论提到:异步注解加声明式事务并不能保证所有批次同步回滚。
MySQL怎么出现幻读啦!
例子 1:
- 开启事务 1
- 在事务 1 中查询用户信息 - 3 条
- 开启事务 2
- 在事务 2 中插入一条新数据
- 提交事务 2
- 在事务 1 中将 ID 为 1 的数据的用户姓名修改为 Iversen
- 在事务 1 中再次查询用户信息 - 3 条
- 提交事务 1
例子 2:
- 开启事务 1
- 在事务 1 中查询用户信息 - 3 条
- 开启事务 2
- 在事务 2 中插入一条新数据
- 提交事务 2
- 在事务 1 中将所有用户的邮箱信息的后缀更换为@gmail.com - 4 条
- 在事务 1 中再次查询用户信息 - 4 条
- 提交事务 1
为什么例子 2 中再次查询的时候,查到了 4 条数据,出现了幻读?
- UPDATE 执行全表更新 → 使用当前读 → 会看到事务 2 插入的新行
- 这个 UPDATE 操作将新行也纳入了事务的修改范围
- 后续查询(即使是普通 SELECT)现在能看到 4 条记录,因为这个 UPDATE 操作”提升”了事务的可见性
MySQL 的”半一致读”机制 —— 在 REPEATABLE READ 隔离级别下,MySQL 有一个特殊优化:
- 当 UPDATE 语句可能修改多行时,会先使用”半一致读”检查哪些行需要更新
- 这个过程会看到其他事务已提交的最新数据
- 一旦 UPDATE 操作修改了某行,该行就会对当前事务完全可见
解决办法:
- 采用串行化的隔离级别(不建议);
- 开发时注意考虑这种产生幻读的场景,尽量通过调整代码逻辑规避幻读问题的发生(建议);
- 若不能通过调整代码逻辑规避,可以考虑采用当前读
select xxx for update的方式避免(建议);
间隙锁只会在当前读操作(如 SELECT FOR UPDATE、UPDATE、DELETE 等)时,在 REPEATABLE READ 或 SERIALIZABLE 隔离级别下使用。
关联:行锁、快照读和当前读、MVCC、MVCC在InnoDB中的实现。
其他
ToB复杂业务状态的可复用的解决方案
解决状态多、状态流转复杂的场景 —— 状态模式 or 状态机
设计模式 - 状态模式:但是如果状态太多,流转逻辑过多,会分散在各个类中,难以直观查看。
有限状态机:状态机由三部分组成,状态(State) 、事件(Event) 和动作(Action)组成。其中事件也称为转移条件。
在状态较多的情况下,更适合查表法来处理。查表法,就是将整个的状态机等价为“图”,将状态之间的流转等价为“边”,通过二维数组记录“边”的形式来存储状态的流转。
通过查表法来实现,一个好处就是可以将状态流转规则在配置文件 or DB 等位置中统一维护,可以看到全部流转规则,简化状态管理。
转转对于状态机的处理逻辑是,初始化是从 DB 中读规则,并转换为 Map<String, List> fsmNodeMap,key 是事件+当前状态,value 是要执行的 FsmNode 类。
public class FsmNode {
private Integer opType;
private Integer role;
private Integer sourceStatus;
private Integer targetStatus;
private NodeType nodeType;
private FsmAction action;
private HashSet<JobConfig> jobConfigs;
private HashSet<TransactionConfig> transactionConfigs;
}除了基础的数据结构、规则保存到 DB 外,还需注意工程上实现:
- 通过定时任务轮训数据表,重试执行失败的 Action。
- 无状态设计,每次状态流转无需生成状态机实例,只要根据当前状态、事件到 Map 里找到需要执行的 Action 及 job 来执行就好。
- 支持每次状态转换的时候发送事务消息(结合数据库事务实现)
阿里 Cola 状态机。Cola 状态机是一个轻量级的开源框架,相比 Spring Statemachine 和 squirrel-foundation,它更加简单、轻量且性能极高。1)简单、轻量,仅支持状态流转的状态机,不支持嵌套、并行等高级玩法。 2)无状态设计。可以使用一个状态机实例来响应所有的请求。
Cola 状态机本质上是状态模式 + 两个 Map。
第一个 Map,Map<S, State<S, E, C>> stateMap,key 为当前状态,value 为 State 对象。
State 对象类似状态模式,内部存储了状态转移 transitions map,即第二个 Map。
第二个 Map,HashMap<E, Transition<S, E, C>> transitions,在 State 对象内部,key 是 Event 事件,value 是 Transition 对象,Transition 内有要执行的 Action、初始状态、目标状态、驱动事件(Event)等。
Spring Statemachine。Spring 官方提供的一个状态机框架,支持状态的嵌套(substate)、状态的并行(parallel, fork, join)、子状态机等高级特性。1)简单易用。2)状态机结构层次化,有助于简化状态控制的开发过程。3)功能完备。与 Cola 状态机类似,也包括状态(State)、事件(Event)、转换(Transition)、动作(Action)等要素。并提供了更丰富的状态类型配置,如 choice、join、fork、history 等。
适用场景:适用于需要将复杂逻辑拆分为较小可管理任务、状态嵌套或需要循环遍历 if-else 结构并进行异常处理的场景。
小白也能看得懂!日志审计插件从入门到实战
好用的工具:
-
git-commit-id-maven-plugin是一个 Maven 插件,在 Maven 构建过程中,插件会生成一个名为git-commit-id.properties的文件。这个文件通常包含有关当前构建的 Git 提交哈希、分支名称、提交时间等信息。 -
hibernate-validator框架,然后利用@Validated配套的校验注解,自定义校验规则,避免配置错误。 -
spring-boot-configuration-processor插件,该插件在打包的时候,会生成target/classes/META-INF/ spring-configuration-metadata.json文件,该文件被 IDEA 读取到后,在用户配置属性的时候,会有自动提示的效果。 -
如果想让用户 import 插件包后,能看到源码,最简单的方法就是让用户利用 IDEA 的反编译功能,反编译出代码,但是会丢失很多的注释信息,因此我们可以使用
maven-source-plugin插件。
转转日志审计插件,设计的组件有:
| 组件 | 作用 |
|---|---|
| LogPluginProperties | 配置的实体类,配置其他组件需要的属性,如 MQ 的连接信息 |
| PluginLogDto | 日志信息实体,如日志标题、服务器地址、服务器名字、URL、参数、方法等 |
| IlogPersistenceService | 日志持久化服务,可以直接打印控制台 or 存储 ES 等等 |
| ICreatedByService | 租户服务,获得当前请求的用户标识 |
| IDataStreamService | 日志数据流服务,将日志推送到 MQ 中 |
| ThreadPoolTaskExecutor | 线程池,专用于传输日志数据,与“业务”隔离,避免影响性能 |
| Server | 用于获取当前“宿主”服务的状态信息,例如 IP,环境信息等 |
| LogRelayTask | 封装的日志任务,提交给线程池执行 |
| 具体更详细的再看一下上面的连接了解。 |
Prometheus在B端门店回收系统中的应用
普罗米修斯,上报类型:
Counter。递增的整数型指标,用于表示累计数量。只能增加,不能减少,一般用于统计请求次数、错误次数等场景。Gauge。是一个可变的数值指标,用于表示某个瞬时值。可变大变小,一般用于表示当前连接数、温度等持续变化指标。Histogram。可以将事件按照不同的桶进行分组,用来观察指标在各个不同区间范围的分布情况,一般用于分析请求延迟,操作响应时间。Summary。与 Histogram 类似,区别在于 Histogram 存储的是数据,Summary 直接存储的就是百分位,本质上并没有什么区别,通过 Histogram 也可以计算出百分位。
在转转中一些应用:
- 监控回收订单的数量、创建失败次数;
- 接口响应情况、接口异常原因(异常类型的 Gauge)
- 不同监控指标,报警力度不同
- RPC 的 QPS 和耗时情况
- 基础设施的监控:Java 程序情况(GC 情况)、数据库(事务耗时、回滚次数、事务提交次数)、容器的内存/磁盘等。
当我重构时,我在想些什么
转转游戏MQ重构:思考与心得之旅
重构影响范围大/重要服务时,参考一下上线计划 + 测试计划。
系统重构上线计划:
- 第一期:主要针对非核心业务 MQ 逻辑进行迁移重构。非核心业务灰度上线,控制影响范围,迅速验证架构的可行性与稳定性。
- 第二期:核心业务相关 MQ 迁移重构。灰度上线,关注对核心业务的影响。完成此步基本完成全部业务逻辑迁移。
- 第三期:对结构进行微调,主要是对相关功能进一步拆解、重构,使功能内部更为内聚,降低耦合,令整个系统最终达成设计之初的预期效果。
测试计划:
- 黑盒测试,校验新老流程处理后的数据是否一致。
- 白盒测试。测试每一行代码的覆盖率,并观察新老流程数据是否一致。
- 调用接口前数据对比。在调用更新接口之处打印日志,对比新老流程调用更新接口的传参是否一致。
针对大规模服务日志敏感信息的长效治理实践
脱敏工具类、JSON 脱敏、APT 自动脱敏。弃用方案:日志中正则匹配,存在误判率。