作为程序员,我们不能只管上线,不管线上!

作为一名程序员,我们不能只关注代码的实现和上线,而忽视了线上环境的运行和优化 。
近期遇到了两个线上服务的问题,一个后端应用和一个前端项目,它们存在一些 bug 和历史遗留问题 。为了不影响用户的使用体验,决定对它们进行一次优化 。
后端服务这个后端服务是年初的时候有同事离职了,交到了我这里,没接手的时候不知道,没想到接手后,到处都是问题,天天各种报警,基本上隔三差五就要重启 。
虽然一开始的时候知道这个服务不是很稳定,日常会有一些队列消息堆积,但是不在自己手上,不知道问题会这么多,动不动就堆积上亿条消息,天天慢 SQL 和高负载报警 。
平时工作日的时候收到报警不是很在意,顺手重启一下就算了,但是当每次周末或者出门在外的时候,收到报警心里还是蛮荒的 。
抱着做一个问题的终结者的想法,最后还是准备花时间把这个服务做一下手术,从根本上解决问题 。
效果先说一下效果,这个服务从优化过后,基本上除了迭代就再也没有需要重新启动过,更不存在隔三差五的重启,现在每天的报警量从之前的一天几百条变为 0,队列无任何堆积 。
优化过程优化的过程中最难的是发现问题,只要能精准的找到问题所在,解决起来还是很容易的 。
优化主要分两步:1. 解决慢 SQL;2. 解决堆积报警;
慢 SQL解决慢 SQL 的思路很简单,根据慢 SQL 日志,找到对应的慢 SQL 进行优化即可 。优化可以从两个方向来进行,一种是基于 SQL 本身来进行优化,另一种是可以通过缓存来解决 。这里需要根据具体的业务来选择,如果不是经常变动的数据,则可以通过增加缓存来解决,刚好我这里就可以满足 。
经过分析可以通过增加 redis 缓存来解决这个问题,所以通过引入的 Redis 解决了慢 SQL 问题 。
消息堆积队列消息堆积的处理方式无非也就是两种,减少数据量,加快处理速度 。
消息队列里面的消息因为是上游发过来的,没办法从发送方进行减少,不过分析了一下消息类型,发现有很多消息的类型是完全不需要关心的,所以第一步增加消息过滤,将无用的消息直接提交掉 。
另外之前遇到消息堆积的时候,观察到消费消息的 TPS 特别低,有时候只有个位数,完全不正常,而且每次重启过后 TPS 可以达到几千的级别,并且每次堆积的时候在日志层面都有一些“断开连接” 的错误 。
所以从日志层面分析,肯定是消费线程出了问题,导致消费能力下降从而堆积,从而问题就转变为为什么线程会出现异常 。
仔细查了下应用层面的监控,发现应用有频繁的 FullGC 发生,奇怪的是为什么频繁 FullGc 却没有触发报警呢?看了一眼简直要吐血,因为 FullGc 的报警开关被关了 。。。
至此基本上能知道问题的原因了,因为发生了 FullGc 导致 STW,然后消费线程挂了,导致消息堆积,重启后内存释放重新进行消费 。接下来的问题就转变为排查 FullGc 的原因了 。
排查 FullGc 的基本流程首先肯定是 dump 一下内存的 heap ,然后分析一下内存泄露的代码块 。通过 dump 下来的日志,发现在代码中使用 ThreadLocal,但是没有释放,从而导致频次的 FullGc 。问题到这基本上也解决了,修改了相关的地方,重新上线,稳定运行 。
至此没有堆积,没有报警,没有重启,爽歪歪!
总结敬畏线上,不要放过任何一个线上的异常和报警!
有时候问题的表象并不是真正的原因,我们需要精准的找到根源,解决问题最难的地方是找到问题!
别干随便关闭线上监控报警的事情!
另外之所以能快速的定位到问题所在也是因为系统有着很好的异常监控,可以监测到慢 SQL 和堆积报警,这也告诉我们平时的服务监控是很重要的 。
前端项目之前有个内部服务,在部署服务的时候,Nginx 配置了 http 和 https 两个 server,公司内部使用的时候一直都用的是 https,结果今天运营同事突然说访问不了了,通过观察发现是 http 协议访问不通 。
正常的逻辑是如果用户在地址栏直接输入 xxx.com 的时候默认是走的 http 协议 80 端口,在 nginx 层会转发到 https 的 443 端口,也就是会有一个重定向的过程 。
检查了一下 nginx 的配置文件,发现在 80 这个 server 里面没有配置 server_name,修改如下就好了 。


推荐阅读