最新消息:www.yxkong.com也可以访问

我遇到的生产事故

技术总结 yxkong 108浏览 0评论

事故一,jvm相关

  1. 大对象(运营商数据;数据库查询未加查询条件,结果集过大;第三方接口或异步消息);来回转化

  2. 死循环;

  3. 推文并发量大

  4. String.intern() 导致oom

  5. static字符串拼接 导致oom

  6. 堆内存过小

  7. ConcGCThreads 线程过多,stw,导致性能上不去

  8. jvm配置问题,堆、对象晋升(进入老年代过快或过慢)

  9. 额度服务因stw停顿过长导致程序性能不稳定

  10. 堆外内存过大;

堆外内存,人脸ocr服务,照片上传 排查方法:

jmap -heap 查看进程jvm的使用情况
jmap -histo pid 统计对象的数量
jmap -dump:live dump堆对象
jstack 快速连续几次输出(建议shellfor循环,sleep 1秒)
jstat -gcutil pid 2
工具:show-busy-java-threads
阿里的阿尔萨斯
查询是否有慢sql
select * from information_schema.`PROCESSLIST` t where t.INFO is not null ORDER BY t.TIME desc;
针对慢查询sql生成kill语句;
select CONCAT('kill -9 ',t.ID,';') from information_schema.`PROCESSLIST` t where t.INFO is not null ORDER BY t.TIME desc;
统计tcp的数量
netstat -na|grep ESTABLISHED|wc -l

pstree -a|wc -l 统计当前进程总数
ulimit -a 查看用户支持线程数

事故二:并发多线程问题

  1. 内存中额度扣减或增加,并行时,最终不一致;

  2. log4j 1多线程堵塞;

  3. 多线程静态锁synchronized;

  4. 消息消费执行顺序不一致 解决:

  • 确保执行顺序 -> 设置msgid(rocketMq)或partitionKey (kafka)

  • 多线程时,同一个用户确保在一个线程里

  • 将额度扣减和增加改为 sql语句执行,利用mysql本身的机制处理

事故三:并发幂等问题

  1. 额度重复恢复 --> 推入消息队列,做幂等处理

  2. 用户重复下单 --> 临时表,redis分布式锁

  3. 本地有序queue 以上方案都需要确保停机数据不丢失

事故四:缓存雪崩

  1. 同一时间大批量临时额度到期导致

  2. redis故障 解决:

  • 大批量数据刷入缓存,失效时间+一定范围的随机数,错峰

  • 针对热点数据或共用数据,定时批量刷缓存

  • 针对数据的实时性要求不高的话,可做本地缓存

事故五:redis相关事故

  1. 代码生成器自带开启事务,导致redis链接未及时释放,无连接可用;

  2. 缓存共用,某些后台项目使用keys *

  3. 大对象存储,长时间占用cpu资源和带宽导致redis其他操作大量超时;

  4. redis缓存不设置有效期;解决:

  5. 代码生成器默认无事务,并制定规范;

  6. 自建redis 屏蔽keys操作

  7. 制定规范禁止大于100kb的数据存储,可以统一缓存工具类限制value大小

事故六:磁盘IO导致线程阻塞,cpu彪高

特征:

  1. cpu占用过高,gc频繁;

  2. 发现log4j的线程占用过高;

  3. 日志文件过大(几十g),同时日志输出延迟;禁止System.out.println,禁止console输出,日志切割

  4. log4j1.* 并发死锁

事故七:无可用线程可创建

1,appuser启动的项目,默认只有1024,通过ulimit -a查看 

2,一台ecs部署多个tomcat,请求量稍微大些的时候会出现该问题 

3, open文件的个数;

ps:注意下文件句柄数

 cat /etc/security/limits.d/90-nproc.conf
appuser soft nproc 10240
appuser hard nproc 10240

事故八:高并发情况下短连接释放过慢,大量链接处于time_wait

操作系统默认 240 秒后,才会关闭处于 time_wait 状态的连接

推荐几篇博文,从根上了解下

Linux上TCP的几个内核参数调优

从Linux源码看TIME_WAIT状态的持续时间

从Linux源码看Socket(TCP)的accept

从linux源码看socket(tcp)的timeout

vim/etc/sysctl.conf
#编辑文件,加入以下内容:
net.ipv4.tcp_syncookies =1#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse =1#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle =1#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
,NAT环境必须为0
net.ipv4.tcp_fin_timeout=30#修改系統默认的 TIMEOUT 时间。
#然后执行以下命令让参数生效
/sbin/sysctl -p

事故九:数据库死锁问题

  1. 常见给慢sql添加索引或修改表结构,表锁导致慢sql,以及无法insert

  2. 复杂查询带来的死锁;

事故十:域名劫持

前端h5,引导到联通的广告 更换https

事故十一:宽带资源耗尽

前端访问过慢,10mb带宽升级到100mb(app升级包未用cdn加速,直接在挂在项目的域名下) 外网100mb的宽带

  1. 静态资源cdn;

  2. 开启tomcat/ng压缩;

  3. 增加带宽

事故十二:静态锁导致的并发问题

额度重构,分表后批量生成主键id,静态方法加锁,多线程调用,导致死锁

  1. 临时去掉静态方法

  2. 使用雪花算法

事故十三:tomcat配置问题

  1. maxPostSize="0" 之前0为不限制,升级一次tomcat版本到7.0.70,0的含义变为了post过来的数据量为0,导致生产服务不可用;

  2. 多个tomcat部署到一台实例线程数不够用,无法创建本地线程

  3. tomcat 线程少,acceptCout大,

tomcat优化:

  1. nio ;

  2. 线程池数量& acceptCount ;

  3. gzip压缩

  4. catalina.out ,日志切割(最好的办法就是不往catalina里输出日志)

日志切割
vim /etc/logrotate.d/auth
/app/auth/apache-tomcat-8.5.28/logs/catalina.out {
copytruncate
dateext
daily
rotate 10
missingok
notifempty
compress
delaycompress
}

tomcat 配置

独立tomcat配置4c8g建议配置
<Executor name="tomcatThreadPool" namePrefix="quota-exec-" maxThreads="1000" minSpareThreads="200"/>
<!-- namePrefix为项目名-->
<Connector executor="tomcatThreadPool" port="9000"
protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="1000"
redirectPort="8443"
URIEncoding="UTF-8"
maxPostSize="0" # 慎用此参数,在低版本上,该参数不限制postsize7.0.70上,则表示post0
maxThreads="1000"
minSpareThreads="200"
acceptCount="1000"
compression="on"
compressionMinSize="2048"
noCompressionUserAgents="gozilla,traviata"
CompressableMimeType="text/html,text/xml,text/plain,text/css,text/javascript,text/json,application/x-javascript,application/javascript,application/json,image/png,image/jpeg"
/>



server:
port: 7424 # tomcat端口号
tomcat:
uri-encoding: UTF-8 # tomcat编码
accept-count: 1000 # 等待队列,默认100
max-connections: 2000 # nio默认10000,链接数达到max-connections的时候会继续接收accept-count
max-threads: 1000 # 最大线程数,nio4c8g建议800
min-spare-threads: 100
max-http-post-size: -1 # 有的版本是0,最好根据实际业务设置个大小
connection-timeout:
1000
compression:
enabled: true
servlet:
path: /
session:
timeout:
600

事故十四 数据库

  1. 多个业务库共用一个实例,部分业务统计到只数据库cpu彪高;->核心业务库独立;

  2. 慢sql,无索引,或者索引类型不对;

  3. 修改库表结构时,锁死;

  4. 复杂嵌套sql;

  5. 对查询条件的字段进行函数处理,导致全表扫描;

  6. 主键非自增,导致数据全表扫描;

事故十五:redis的key已过期,但还能获取

用户请求量,在一段时间内,只能请求2w次,用了一个固定key缓存,到了第二天还有效 当redis的cpu过高的时候,定期删除就不一定启动作用;定期删除& 惰性删除 定期删除: 定期从redis中获取数据,检查并删除,cpu过高时就无法执行,有很大概率能拿到未删除的key 惰性删除:每次get的时候主动去校验,会影响redis的性能,量不大可用

事故十六 同一个用户启动多个dubbo项目;

  1. dubbo的缓存文件是固定名称和路径的,同一个用户启动dubbo会导致.dubbo文件被覆盖

  2. 改变缓存文件的名称和路径

事故十七 定时任务中创建线程池;

  1. 重复创建线程池;

  2. 导致资源打满,cpu彪高,服务假死;

事故十八,默认超时时间 60秒

导致系统群雪崩; 

  1. 业务板块拆分并独立域名;

  2. 架构有向无环;

  3. 对非关键业务熔断,制定统一的业务超时标准;

事故十九 线程池打满

服务调用使用httpclient的线程池模式, 当某个接口慢的时候,导致链接堵塞,直接把所有的线程都耗费掉了,影响了其他的系统调用;

线程池有个配置max-connections-per-route,按接口来,最多能占用多少链接,一般设置为1/2或者1/3

事故二十 消息积压

  1. 后端消费堵塞(因为内外部系统宕机)

  2. 新进来的用户处理不了(风控,授信);

  3. 慢sql+异常重试;

解决

  • 修复系统问题

  • 水平扩展,修改partition数量增加消费节点(大部分能解决,得注意下游的处理能力,要不然会压垮);

  • 直接将挤压的消息读取到一个新的topic,先不影响新用户,再监听新topic(如果后端用线程池不要共用)

  • 死信队列

事故二十一 threadlocal事故

当某个用户进入系统时,会将用户信息缓存到threadlocal中一份,开发没有在finally中移除 测试和生产tomcat配置的是线程池,最小线程都是200 当用户使用独立的线程的时候,都没有问题,当线程被重复使用时,就铁定出问题;

时间工具类,未从threadlocal中移除,导致时间不一致

定时任务单节点故障

单节点定时任务故障:

  1. 系统宕机 --> 健康检查

  2. 系统正常运行,但定时任务停止 --> 分布式定时、多节点执行,利用分布式锁,只能同时有一台执行(ps 锁一定要设置有效期)

线程池事故

  1. 使用Executors.newFixedThreadPool 创建线程池,队列是无界队列(Integer.MAX_VALUE),导致oom;

  2. 使用Executors.newCachedThreadPool 创建线程池,corepoolsize 为Integer.MAX_VALUE

  3. 核心业务和非核心业务共用线程池,非核心业务堵塞,导致核心业务无法执行(还有spring的@Async注解,类似);

  4. 定时任务里创建线程池,导致资源不够用,任务处理不及时,导致cpu彪高;

  5. disruptor 配置

事务里调用接口

接口超时导致事务回滚,导致数据库链接数过多;引起服务排队,获取不到资源:

代码生成器不能生成带事务的代码

其他非技术性事故

  1. slb白名单 checklist

  2. 数据库,redis白名单 checklist

  3. kafka 和ecs不在一个域里,网络不通 checklist

  4. sql未执行就上线 checklist

  5. 测试不到位 单元测试规范

  6. 拆表后dts同步,dba未及时将同步去掉,数据迁移规范化操作;

  7. 链接和超时时间用默认 checklist;

  8. topic未创建, checklist;

转载请注明:我要编程 » 我遇到的生产事故

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址