文档章节

一次线上问题排查所引发的思考

crossoverJie
 crossoverJie
发布于 07/09 08:25
字数 1676
阅读 713
收藏 6
点赞 0
评论 8

前言

之前或多或少分享过一些内存模型对象创建之类的内容,其实大部分人看完都是懵懵懂懂,也不知道这些的实际意义。

直到有一天你会碰到线上奇奇怪怪的问题,如:

  • 线程执行一个任务迟迟没有返回,应用假死。
  • 接口响应缓慢,甚至请求超时。
  • CPU 高负载运行。

这类问题并不像一个空指针、数组越界这样明显好查,这时就需要刚才提到的内存模型、对象创建、线程等相关知识结合在一起来排查问题了。

正好这次借助之前的一次生产问题来聊聊如何排查和解决问题。

生产现象

首先看看问题的背景吧:

我这其实是一个定时任务,在固定的时间会开启 N 个线程并发的从 Redis 中获取数据进行运算。

业务逻辑非常简单,但应用一般涉及到多线程之后再简单的事情都要小心对待。

果不其然这次就出问题了。

现象:原本只需要执行几分钟的任务执行了几个小时都没退出。翻遍了所有的日志都没找到异常。

于是便开始定位问题之路。

定位问题

既然没办法直接从日志中发现异常,那就只能看看应用到底在干嘛了。

最常见的工具就是 JDK 自带的那一套。

这次我使用了 jstack 来查看线程的执行情况,它的作用其实就是 dump 当前的线程堆栈。

当然在 dump 之前是需要知道我应用的 pid 的,可以使用 jps -v 这样的方式列出所有的 Java 进程。

当然如果知道关键字的话直接使用 ps aux|grep java 也是可以的。

拿到 pid=1523 了之后就可以利用 jstack 1523 > 1523.log 这样的方式将 dump 文件输出到日志文件中。

如果应用简单不复杂,线程这些也比较少其实可以直接打开查看。

但复杂的应用导出来的日志文件也比较大还是建议用专业的分析工具。

我这里的日志比较少直接打开就可以了。

因为我清楚知道应用中开启的线程名称,所以直接根据线程名就可以在日志中找到相关的堆栈:

所以通常建议大家线程名字给的有意义,在排查问题时很有必要。

其实其他几个线程都和这里的堆栈类似,很明显的看出都是在做 Redis 连接。

于是我登录 Redis 查看了当前的连接数,发现已经非常高了。

这样 Redis 的响应自然也就变慢了。

接着利用 jps -v 列出了当前所以在跑的 Java 进程,果不其然有好几个应用都在查询 Redis,而且都是并发连接,问题自然就找到了。

解决办法

所以问题的主要原因是:大量的应用并发查询 Redis,导致 Redis 的性能降低。

既然找到了问题,那如何解决呢?

  • 减少同时查询 Redis 的应用,分开时段降低 Redis 的压力。
  • 将 Redis 复制几个集群,各个应用分开查询。但是这样会涉及到数据的同步等运维操作,或者由程序了进行同步也会增加复杂度。

目前我们选择的是第一个方案,效果很明显。

本地模拟

上文介绍的是线程相关问题,现在来分析下内存的问题。

以这个类为例:

https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/oom/heap/HeapOOM.java

public class HeapOOM {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>(10) ;
        while (true){
            list.add("1") ;
        }
    }
}

启动参数如下:

-Xms20m
-Xmx20m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users/xx/Documents

为了更快的突出内存问题将堆的最大内存固定在 20M,同时在 JVM 出现 OOM 的时候自动 dump 内存到 /Users/xx/Documents(不配路径则会生成在当前目录)。

执行之后果不其然出现了异常:

同时对应的内存 dump 文件也生成了。

内存分析

这时就需要相应的工具进行分析了,最常用的自然就是 MAT 了。

我试了一个在线工具也不错(文件大了就不适合了):

http://heaphero.io/index.jsp

上传刚才生成的内存文件之后:

因为是内存溢出,所以主要观察下大对象:

也有相应提示,这个很有可能就是内存溢出的对象,点进去之后:

看到这个堆栈其实就很明显了:

在向 ArrayList 中不停的写入数据时,会导致频繁的扩容也就是数组复制这些过程,最终达到 20M 的上限导致内存溢出了。

更多建议

上文说过,一旦使用了多线程,那就要格外小心。

以下是一些日常建议:

  • 尽量不要在线程中做大量耗时的网络操作,如查询数据库(可以的话在一开始就将数据从从 DB 中查出准备好)。
  • 尽可能的减少多线程竞争锁。可以将数据分段,各个线程分别读取。
  • 多利用 CAS+自旋 的方式更新数据,减少锁的使用。
  • 应用中加上 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp 参数,在内存溢出时至少可以拿到内存日志。
  • 线程池监控。如线程池大小、队列大小、最大线程数等数据,可提前做好预估。
  • JVM 监控,可以看到堆内存的涨幅趋势,GC 曲线等数据,也可以提前做好准备。

总结

线上问题定位需要综合技能,所以是需要一些基础技能。如线程、内存模型、Linux 等。

当然这些问题没有实操过都是纸上谈兵;如果第一次碰到线上问题,不要慌张,反而应该庆幸解决之后你又会习得一项技能。

号外

最近在总结一些 Java 相关的知识点,感兴趣的朋友可以一起维护。

地址: https://github.com/crossoverJie/Java-Interview

欢迎关注公众号一起交流:

© 著作权归作者所有

共有 人打赏支持
crossoverJie
粉丝 403
博文 51
码字总数 87769
作品 0
江北
后端工程师
加载中

评论(8)

y
yishuai18
感觉没找出根本的问题
crossoverJie
crossoverJie

引用来自“Gillian_Male”的评论

这分析的是个啥?根据一个Runnable的线程栈看出来了啥?
还不明显嘛?都在做网络 IO。
crossoverJie
crossoverJie

引用来自“young7”的评论

洋洋洒洒写了半天也没看到分析出啥,最重要的并发数多少没有说,然后突然就得出了结论说
“所以问题的主要原因是:大量的应用并发查询 Redis,导致 Redis 的性能降低。”

根据网上的测试结果,最高redis插入吞吐量是 4.9W/S,单线程客户端最高吞吐 1.7W/S,博主那点并发量恐怕远未达到redis的极限,因此我觉得博主连问题的原因都还没有找到。
别什么都网上的测试结果,具体情况具体分析。

至少在得出这个结论之前将应用连接数降低确实是解决了问题。
crossoverJie
crossoverJie

引用来自“young7”的评论

洋洋洒洒写了半天也没看到分析出啥,最重要的并发数多少没有说,然后突然就得出了结论说
“所以问题的主要原因是:大量的应用并发查询 Redis,导致 Redis 的性能降低。”

根据网上的测试结果,最高redis插入吞吐量是 4.9W/S,单线程客户端最高吞吐 1.7W/S,博主那点并发量恐怕远未达到redis的极限,因此我觉得博主连问题的原因都还没有找到。
这个和网络 IO 也是有关系啊,所有的请求都发自一台机器,IO 也撑不住的。
young7
young7
洋洋洒洒写了半天也没看到分析出啥,最重要的并发数多少没有说,然后突然就得出了结论说
“所以问题的主要原因是:大量的应用并发查询 Redis,导致 Redis 的性能降低。”

根据网上的测试结果,最高redis插入吞吐量是 4.9W/S,单线程客户端最高吞吐 1.7W/S,博主那点并发量恐怕远未达到redis的极限,因此我觉得博主连问题的原因都还没有找到。
Gillian_Male
Gillian_Male
这分析的是个啥?根据一个Runnable的线程栈看出来了啥?
crossoverJie
crossoverJie

引用来自“每周精粹”的评论

没看出来什么问题。。。 Redis 连接数 单台达到 6000 这样 性能才会 急剧下降;
这个和网络 IO 也是有关系啊,所有的请求都发自一台机器,IO 也撑不住的。
每周精粹
每周精粹
没看出来什么问题。。。 Redis 连接数 单台达到 6000 这样 性能才会 急剧下降;
上周热点回顾(7.9-7.15)

热点随笔: · 上海程序员 落户攻略(小坦克) · 欠的债,这一次都还给你们(阿豪聊干货) · 男程序猿和女程序猿的网恋(一)(56899◎か) · 给正在努力的您几条建议(附开源代码)(陈珙...

博客园团队
07/16
0
0
CPU 100% 异常排查实践与总结

CPU 100% 异常排查实践与总结 leejun2005的个人页面2018-01-041 阅读 java 1、问题背景 昨天下午突然收到运维邮件报警,显示数据平台服务器cpu利用率达到了98.94%,而且最近一段时间一直持续...

leejun_2005的个人页面
01/04
0
0
在线 Debug 平台--simple debug

1、面向问题 1.1 测试环境或正式环境下接口监控 当我们项目发布后,往往碰到由测试或用户触发的各种BUG,由不同的情况而产生不同的结果。然而人力有时穷有时弱,再资深的工程师也不能保证所有...

Coody
2017/02/07
1K
0
一次线上问题排查:java.lang.OutOfmemoryError: PermGen Space

背景 用基于Spring Boot(环境是java7)的系统做了一个报表导出的功能,第一次部署的时候用的默认JVM参数,但是测试人员在点击了一次导出之后,其他功能就疑似挂掉了,过了一会后才能正常使用...

rickiyeat
2017/10/25
0
0
In Practice:一个由CountDownLatch引发的Bug

最近恢复Venus的工作,恰逢十一假期尾声,突发一个Bug,让人匪夷所依。我们今天快速倒序追踪,层层回顾。 1. 背景 Venus交易中需要并行开数十至百个线程分别快速计算,程序中使用CountDownLa...

erixhao
2017/04/01
0
0
Dubbo性能优化

Dubbo性能优化 背景 dubbo作为一款分布式服务框架,除了提供远程调用的细节封装,还提供了基本的服务治理功能,能够粗略地监控系统性能。 上图展示的是dubbo执行流程的原理图,在客户端和服务...

jonathan_loda
2016/08/29
841
0
2018-06-22_编程项目阶段小总结

个人理解的项目重点: 主要是处理答题竞赛活动的房间状态,用户身份,以及前端的请求。 5个主要过程,等待,开始答题发布题目和确认答案,请求复活,中途退出,以及最后显示奖励; 结合redis的队列,...

洛根森
06/22
0
0
如何高效排查系统故障?一分钱引发的系统设计“踩坑”案例

摘要:阿里妹导读:阿里巴巴的电商业务十分复杂,一方面是市场多样化,业务多样化,另外是消费者,商家的影响面非常广,任何一个小故障都可能引发一些社会问题,所以阿里对产品的质量,对服务...

阿里云云栖社区
2017/11/28
0
0
CPU100%问题快速定位思路

在我日常运维工作中,无论自己 或同事、朋友总会问我,服务器CPU使用率100%,卡死了,这样的话,那今天咱们就一起模拟故障,进行细致的分析,首先介绍下,CPU出现问题的几种原因: 一、CPU 1...

邱月涛
06/04
0
0
JDBC驱动自身问题引发的FullGC

公众号HelloJava刊出一篇《MySQL Statement cancellation timer 故障排查分享》,作者的某服务的线上机器报 502(502是 nginx 做后端健康检查时不能连接到 server 时抛出的提示),他用 jsta...

旁观者-郑昀
2016/03/29
261
1

没有更多内容

加载失败,请刷新页面

加载更多

下一页

about git flow

  昨天元芳做了git分支管理规范的分享,为了拓展大家关于git分支的认知,这里我特意再分享这两个关于git flow的链接,大家可以看一下。 Git 工作流程 Git分支管理策略   git flow本质上是...

qwfys
今天
2
0
Linux系统日志文件

/var/log/messages linux系统总日志 /etc/logrotate.conf 日志切割配置文件 参考https://my.oschina.net/u/2000675/blog/908189 dmesg命令 dmesg’命令显示linux内核的环形缓冲区信息,我们可...

chencheng-linux
今天
1
0
MacOS下给树莓派安装Raspbian系统

下载镜像 前往 树莓派官网 下载镜像。 点击 最新版Raspbian 下载最新版镜像。 下载后请,通过 访达 双击解压,或通过 unzip 命令解压。 检查下载的文件 ls -lh -rw-r--r-- 1 dingdayu s...

dingdayu
今天
1
0
spring boot使用通用mapper(tk.mapper) ,id自增和回显等问题

最近项目使用到tk.mapper设置id自增,数据库是mysql。在使用通用mapper主键生成过程中有一些问题,在总结一下。 1、UUID生成方式-字符串主键 在主键上增加注解 @Id @GeneratedValue...

北岩
今天
2
0
告警系统邮件引擎、运行告警系统

告警系统邮件引擎 cd mail vim mail.py #!/usr/bin/env python#-*- coding: UTF-8 -*-import os,sysreload(sys)sys.setdefaultencoding('utf8')import getoptimport smtplibfr......

Zhouliang6
今天
1
0
Java工具类—随机数

Java中常用的生成随机数有Math.random()方法及java.util.Random类.但他们生成的随机数都是伪随机的. Math.radom()方法 在jdk1.8的Math类中可以看到,Math.random()方法实际上就是调用Random类...

PrivateO2
今天
3
0
关于java内存模型、并发编程的好文

Java并发编程:volatile关键字解析    volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在...

DannyCoder
昨天
1
0
dubbo @Reference retries 重试次数 一个坑

在代码一中设置 成retries=0,也就是调用超时不用重试,结果DEBUG的时候总是重试,不是0吗,0就不用重试啊。为什么还是调用了多次呢? 结果在网上看到 这篇文章才明白 https://www.cnblogs....

奋斗的小牛
昨天
2
0
数据结构与算法3

要抓紧喽~~~~~~~放羊的孩纸回来喽 LowArray类和LowArrayApp类 程序将一个普通的Java数组封装在LowArray类中。类中的数组隐藏了起来,它是私有的,所以只有类自己的方法才能访问他。 LowArray...

沉迷于编程的小菜菜
昨天
1
0
spring boot应用测试框架介绍

一、spring boot应用测试存在的问题 官方提供的测试框架spring-boot-test-starter,虽然提供了很多功能(junit、spring test、assertj、hamcrest、mockito、jsonassert、jsonpath),但是在数...

yangjianzhou
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部