Skip to content

故障: 事务内发消息

问题:

下游系统在你事务还没提交就消费了数据导致上下游业务处理不一致.

方案:

  1. 事务提交后发送消息(单数据源情况下: a.Spring的事务事件消息 b. Spring编程式事务提交后扩展点)
  2. 可靠消息发送

a.本地消息表,跟着本地事务一起写入数据库
b.发送MQ, 发送成功后删除该记录;内存重试, 仍发送不成功, 定时任务补偿发送.

多数据源场景下: 编程式事务和Spring Event的事务消息扩展?

《线程池相关故障梳理》

文章《案例分析 | 线程池相关故障梳理&总结》主要从故障视角和技术视角出发,分析并总结了线程池相关的问题及其解决方案,旨在帮助开发者避免类似故障。以下是对文章内容的总结:

故障视角

  • 数据库问题:热更新导致行锁竞争,大表DDL操作未使用Online DDL导致锁等待,索引选择不当导致全表扫描,以及深分页引发的慢SQL,均可能引起数据库连接池满,进而导致业务线程池满,服务不可用。
  • 调用量大:短时间内重试大量任务至单机导致线程池满,或压测未预热直接高并发导致线程池满。
  • 其他:未限制查询导致Full GC,虽不直接关联线程池满,但也反映了限制操作规模的重要性。

技术视角

  • 故障本质:通常由于远程调用IO慢、CPU密集型应用处理时间长或自定义线程池满导致的排队等待。
  • 连接池配置关键点
    • Dubbo线程池:做好接口隔离,合理设置timeout,consumer侧timeout应设得较小。
    • HTTP连接池:确保设置ConnectTimeout、SocketTimeout及ConnectionRequestTimeout,避免因DefaultMaxPerRoute配置不当导致阻塞。
    • 数据库连接池(Druid):配置ConnectTimeout、SocketTimeout,并设置TransactionTimeout以控制事务锁时间,合理设置Ibatis的timeout及MaxWait时间。
    • 自定义线程池:避免过长的队列长度,对future.get应设定超时时间。
    • Redis连接池:配置MaxWait、ConnectTimeout、SocketTimeout等参数。

经验总结与建议

  • Fast-Fail理念:设置合理的超时时间以快速失败,避免资源浪费。
  • 保护措施:实施多维度流控和背压机制,包括数据库自动限流、不同维度的流控策略,以及消息队列客户端的背压实现。
  • 谨慎重试:避免无节制的自动重试导致雪崩,采用令牌桶控制重试速率,并分散定时任务执行时间以减轻峰值压力。

总之,文章通过具体案例展示了线程池故障的常见原因与解决思路,强调了合理配置连接池参数、实施流控策略以及正确处理重试逻辑对于保障系统稳定的重要性。

Redis 事务遇上 @Transactional 有大坑

文章《Redis 事务遇上 @Transactional 有大坑!记一次 Redis 生产事故!》详细记录了一次在生产环境中遭遇的问题及排查过程。以下是文章内容的总结:

问题现象: 每天早晨客服人员尝试在后台创建客服事件时,操作频繁失败,但重启相关微服务后问题即刻得到解决,次日早晨又重复出现问题。

初步分析: 问题与生成客服事件ID时使用的Redis递增操作有关,此操作在特定时间点(早晨)会返回null,导致后续逻辑错误和事件保存失败。

排查过程:

推测一(连接未释放):考虑到夜间可能有大量Job执行,初步怀疑是Redis连接未释放导致。但其他Redis操作正常,故排除此推测。
推测二(Spring事务影响):注意到递增操作在带有@Transactional注解的方法中,推测Spring事务可能影响Redis操作。通过实验发现,即使在事务中,Redis递增操作也不应返回null,此推测不成立。
推测三(Redis事务冲突):进一步调查发现,有同事新加入的Redis事务处理代码在凌晨执行,该代码开启了Redis事务。通过实验确认,当Redis事务支持开启时,若在带有@Transactional注解的方法中执行Redis操作,递增命令会返回null,尽管实际Redis中的计数仍会递增。这是因为Redis命令被放入事务队列中,直到事务提交才执行,导致即时操作看起来“失效”。

问题根源: RedisTemplate在开启了事务支持后,即使在Spring的事务管理之外,任何Redis操作都会被视为事务的一部分。在带有@Transactional注解的方法内执行Redis操作时,由于事务未提交,操作结果(本例中的递增结果)不会立即返回,而是返回null,直至事务结束才真正执行。

解决方案:

方案一:事务处理完后关闭Redis事务支持,但此方法存在局限性,如果在事务处理期间仍有其他事务操作,问题依旧。 方案二:创建两个独立的StringRedisTemplate实例,一个用于事务操作,另一个用于非事务的常规操作,确保二者互不影响。 结论:文章通过深入排查,揭示了Redis事务与Spring事务管理器相互作用时的潜在问题,提出了有效的解决方案,并强调了在设计涉及Redis事务的操作时需考虑其与应用框架事务管理的兼容性问题。

系统没有实现优雅关闭,Spring Event会有异常

解决方案:

1.弃用 SpringEvent。

2.允许 Spring Event 出现异常,但上层调用捕获异常,上报 MQ 重试。

3.彻底服务优雅关闭。Rpc、Http、MQ 入口在进程关闭前,先禁用流量。

  1. 实现持久化异步消息 升级版 @Async,让异步任务无懈可击

Released under the MIT License.