项目踩坑

MQ 踩坑

坑一、消费者宕机,消息积压百万

挖坑: 实际项目中有发生过消费者挂掉,导致mq积压了上百万的消息。

填坑: 首先向公司申请了一批机器,构建了一个多倍于原有队列的mq,并且为他们设定了各自的消费者,然后临时修改原有的宕机消费者代码,使其接收到消息后不再消费,而是直接发到新建的mq中,然后重启恢复挂掉的消费者,让新建的mq的消费者去快速消费。消费完毕后,撤掉新建的mq,恢复原有代码,填坑完毕。

坑二、未按照预期顺序消费

挖坑: 实际项目中有两台消费者,它们消费同一个mq,但是mq中只有一个队列,发生过本来对同一个数据的两个操作消息a、消息b,分别被两台消费者各消费了一个,但是呢,恰好本来应在消息a之后的消息b却被先消费掉了,导致数据库中产生的数据与预期的不一致。

填坑: 首先在mq中多加了一个队列,使每个队列对应一个消费者,然后在代码中实现了将同类型的(如同一个 id )业务数据hash到同一个队列中,这样保证了每个队列中的相同消息顺序被同一个消费者消费,填坑完毕。

坑三、消息重复消费

挖坑: 实际项目中发生过消费者消费了消息,但是还没有给mq发送ack确认的时候,宕机了,导致mq以为该消息没有被消费,从而没有从mq中移除,消费者重启后,又一次消费了该消息。

填坑:
1、乐观锁,表中添加版本字段,修改数据时,判断version值是否变化,变化的就直接丢弃消息。
2、去重表,可以使用业务中有唯一属性的字段,也可以自己生成一个全局唯一id,每消费一个消息,将其唯一属性存入redis,下次消费消息时,先查redis里面是否有此数据,有的话就直接丢弃消息。

Redis 踩坑

坑一、rdb 数据恢复,总是恢复失败

挖坑: 实际项目中,redis 同时开启了aof 和 rdb 持久化。由于操作失误,导致aof文件丢失,利用rdb文件,做数据恢复时,rdb 有数据,恢复出来的结果却没有数据。上述情况,是由于redis在启动时,如果开启了aof,会优先使用aof去恢复数据,如果没有aof文件,会自动创建一个空的文件,redis 根据空的aof文件,恢复了数据,并且同步到了有数据的rdb,导致恢复数据总是是失败。

填坑: 当 aof 文件损坏或丢失时,应该先停止redis,关闭aof,拷贝rdb备份,重启redis,确认恢复了数据,直接通过命令行config set appendonly yes 热修改redis配置,临时打开 aof, 此时,就会将内存中的数据写入一个aof文件,再次停止redis,手动修改配置文件aof为打开,重启redis。

JVM 踩坑

坑一、堆内存 OOM,java heap space

挖坑: 在项目中,我们对于一些数据的查询必须是带过滤条件的,比如集团id和机构id,而这些属性通过拦截器都封装在线程变量里,当某位同学做业务查询时,开启了异步线程,导致了从线程变量里获取到的数据为空,过滤条件未生效,一次查询加载了差不多几十万的数据出来,直接导致 OOM

填坑: 通过MAT工具分析了dump文件,找到了占用内存最多的那个线程,发现该线程的一个方法创建了一万多的对象,通过本地实践验证,发现是过滤条件失效的问题引发,修改之后,恢复正常。

顺便说一下,栈内存溢出,一般是方法递归调用产生,metaspace 内存溢出,一般是无限制的动态生成类产生。

坑二、服务周期性卡顿

实际项目运行高峰期出现过,每隔十几分钟出现服务卡顿几秒的现象。

线上jvm配置:
4核8G, 分配给jvm 4g,新生代1.5G(1.2G:150m:150m),老年代1.5G,永久代500m,栈1m(一般几百个线程,也就是几百兆)

我们的医疗系统,目前是有几十家医院在使用,高峰期的qps在1000左右,目前是部署了两台机器在运行,基本上也就是每台机器高峰期qps也就500左右,我所在的供应链模块,基本上一个对象平均在500字节,按照一个请求平均创建20个对象,差不多每秒产生的对象占用的堆内存在500* 500* 20=10m, 一分钟 60*10=600m, 4 分钟 就会触发ygc,由于部分接口比较耗时,观察发现会有180兆的对象没有被回收,此时,servior区只有150兆,已经放不下这180m的对象了,就会直接进入老年代,这样的话,差不多16分钟就会发生fullgc,这样对性能是有很大影响的。

优化过程:
如果直接增大新生代的大小为2G(1.6G:200m:200m),老年代减为1G,这样的确可以保证servior能够放下每次ygc没回收的180m对象,但是,由于jvm垃圾回收的动态年龄规划,只要超过servior的一半内存,还是会把部分对象直接移入老年代,在某个时刻还是会触发fullgc,最终我们决定把新生代的比例改为6,即(1.2G:400m:400m),这样每次ygc剩下的180m对象刚好不够400m的一半,不足于触发动态年龄规划,也就不会有对象进入老年代。我们调优的原则就是把gc都发生在年轻代,尽量不发生或少发生fullgc。

0%