事故一,jvm相关
-
大对象(运营商数据;数据库查询未加查询条件,结果集过大;第三方接口或异步消息);来回转化
-
死循环;
-
推文并发量大
-
String.intern() 导致oom
-
static字符串拼接 导致oom
-
堆内存过小
-
ConcGCThreads 线程过多,stw,导致性能上不去
-
jvm配置问题,堆、对象晋升(进入老年代过快或过慢)
-
额度服务因stw停顿过长导致程序性能不稳定
-
堆外内存过大;
堆外内存,人脸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 查看用户支持线程数`
事故二:并发多线程问题
-
内存中额度扣减或增加,并行时,最终不一致;
-
log4j 1多线程堵塞;
-
多线程静态锁synchronized;
-
消息消费执行顺序不一致 解决:
-
确保执行顺序 -> 设置msgid(rocketMq)或partitionKey (kafka)
-
多线程时,同一个用户确保在一个线程里
-
将额度扣减和增加改为 sql语句执行,利用mysql本身的机制处理
事故三:并发幂等问题
-
额度重复恢复 --> 推入消息队列,做幂等处理
-
用户重复下单 --> 临时表,redis分布式锁
-
本地有序queue 以上方案都需要确保停机数据不丢失
事故四:缓存雪崩
-
同一时间大批量临时额度到期导致
-
redis故障 解决:
-
大批量数据刷入缓存,失效时间+一定范围的随机数,错峰
-
针对热点数据或共用数据,定时批量刷缓存
-
针对数据的实时性要求不高的话,可做本地缓存
事故五:redis相关事故
-
代码生成器自带开启事务,导致redis链接未及时释放,无连接可用;
-
缓存共用,某些后台项目使用keys *
-
大对象存储,长时间占用cpu资源和带宽导致redis其他操作大量超时;
-
redis缓存不设置有效期;解决:
-
代码生成器默认无事务,并制定规范;
-
自建redis 屏蔽keys操作
-
制定规范禁止大于100kb的数据存储,可以统一缓存工具类限制value大小
事故六:磁盘IO导致线程阻塞,cpu彪高
特征:
-
cpu占用过高,gc频繁;
-
发现log4j的线程占用过高;
-
日志文件过大(几十g),同时日志输出延迟;禁止System.out.println,禁止console输出,日志切割
-
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 状态的连接
推荐几篇博文,从根上了解下
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
事故九:数据库死锁问题
-
常见给慢sql添加索引或修改表结构,表锁导致慢sql,以及无法insert
-
复杂查询带来的死锁;
事故十:域名劫持
前端h5,引导到联通的广告 更换https
事故十一:宽带资源耗尽
前端访问过慢,10mb带宽升级到100mb(app升级包未用cdn加速,直接在挂在项目的域名下) 外网100mb的宽带
-
静态资源cdn;
-
开启tomcat/ng压缩;
-
增加带宽
事故十二:静态锁导致的并发问题
额度重构,分表后批量生成主键id,静态方法加锁,多线程调用,导致死锁
-
临时去掉静态方法
-
使用雪花算法
事故十三:tomcat配置问题
-
maxPostSize=“0” 之前0为不限制,升级一次tomcat版本到7.0.70,0的含义变为了post过来的数据量为0,导致生产服务不可用;
-
多个tomcat部署到一台实例线程数不够用,无法创建本地线程
-
tomcat 线程少,acceptCout大,
tomcat优化:
-
nio ;
-
线程池数量& acceptCount ;
-
gzip压缩
-
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" # 慎用此参数,在低版本上,该参数不限制postsize,7.0.70上,则表示post为0`
`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 # 最大线程数,nio,4c8g建议800`
`min-spare-threads: 100`
`max-http-post-size: -1 # 有的版本是0,最好根据实际业务设置个大小`
`connection-timeout:`
`1000`
`compression:`
`enabled: true`
`servlet:`
`path: /`
`session:`
`timeout:`
`600`
事故十四 数据库
-
多个业务库共用一个实例,部分业务统计到只数据库cpu彪高;->核心业务库独立;
-
慢sql,无索引,或者索引类型不对;
-
修改库表结构时,锁死;
-
复杂嵌套sql;
-
对查询条件的字段进行函数处理,导致全表扫描;
-
主键非自增,导致数据全表扫描;
事故十五:redis的key已过期,但还能获取
用户请求量,在一段时间内,只能请求2w次,用了一个固定key缓存,到了第二天还有效 当redis的cpu过高的时候,定期删除就不一定启动作用;定期删除& 惰性删除 定期删除: 定期从redis中获取数据,检查并删除,cpu过高时就无法执行,有很大概率能拿到未删除的key 惰性删除:每次get的时候主动去校验,会影响redis的性能,量不大可用
事故十六 同一个用户启动多个dubbo项目;
-
dubbo的缓存文件是固定名称和路径的,同一个用户启动dubbo会导致.dubbo文件被覆盖
-
改变缓存文件的名称和路径
事故十七 定时任务中创建线程池;
-
重复创建线程池;
-
导致资源打满,cpu彪高,服务假死;
事故十八,默认超时时间 60秒
导致系统群雪崩;
-
业务板块拆分并独立域名;
-
架构有向无环;
-
对非关键业务熔断,制定统一的业务超时标准;
事故十九 线程池打满
服务调用使用httpclient的线程池模式, 当某个接口慢的时候,导致链接堵塞,直接把所有的线程都耗费掉了,影响了其他的系统调用;
线程池有个配置max-connections-per-route,按接口来,最多能占用多少链接,一般设置为1/2或者1/3
事故二十 消息积压
-
后端消费堵塞(因为内外部系统宕机)
-
新进来的用户处理不了(风控,授信);
-
慢sql+异常重试;
解决
-
修复系统问题
-
水平扩展,修改partition数量增加消费节点(大部分能解决,得注意下游的处理能力,要不然会压垮);
-
直接将挤压的消息读取到一个新的topic,先不影响新用户,再监听新topic(如果后端用线程池不要共用)
-
死信队列
事故二十一 threadlocal事故
当某个用户进入系统时,会将用户信息缓存到threadlocal中一份,开发没有在finally中移除 测试和生产tomcat配置的是线程池,最小线程都是200 当用户使用独立的线程的时候,都没有问题,当线程被重复使用时,就铁定出问题;
时间工具类,未从threadlocal中移除,导致时间不一致
定时任务单节点故障
单节点定时任务故障:
-
系统宕机 --> 健康检查
-
系统正常运行,但定时任务停止 --> 分布式定时、多节点执行,利用分布式锁,只能同时有一台执行(ps 锁一定要设置有效期)
线程池事故
-
使用Executors.newFixedThreadPool 创建线程池,队列是无界队列(Integer.MAX_VALUE),导致oom;
-
使用Executors.newCachedThreadPool 创建线程池,corepoolsize 为Integer.MAX_VALUE
-
核心业务和非核心业务共用线程池,非核心业务堵塞,导致核心业务无法执行(还有spring的@Async注解,类似);
-
定时任务里创建线程池,导致资源不够用,任务处理不及时,导致cpu彪高;
-
disruptor 配置
事务里调用接口
接口超时导致事务回滚,导致数据库链接数过多;引起服务排队,获取不到资源:
代码生成器不能生成带事务的代码
其他非技术性事故
-
slb白名单 checklist
-
数据库,redis白名单 checklist
-
kafka 和ecs不在一个域里,网络不通 checklist
-
sql未执行就上线 checklist
-
测试不到位 单元测试规范
-
拆表后dts同步,dba未及时将同步去掉,数据迁移规范化操作;
-
链接和超时时间用默认 checklist;
-
topic未创建, checklist;
想了解更多,请关注公众号哦。