排查记录: Java所有接口卡死

2024-11-07 14:01:56 3

线上接口, 每天凌晨三四点后开始卡死, 重启后恢复

调用后等待两分钟, 然后504

两分钟正好是nginx反代的超时时间


排查过程

日志

把nginx和应用日志都找出来看了下, 没发现什么问题

应用机器状态

  1. 看了下cpu占用率/内存, 没问题
  2. 磁盘占用也没问题

应用状态

  1. 通过jps查找到java进程
  2. 然后jstack 进程id > result.txt
  3. 发现http-nio线程绝大部分阻塞在java.io.BufferedOutputStream.write(BufferedOutputStream.java:94), 都是在等待锁
  4. 查看代码, 发现controller有aop, 接口处理完后, 会发送相关记录到mq
  5. 再用arthasthread -b命令找出阻塞其他线程的线程
"http-nio-8080-exec-10" Id=142 BLOCKED on java.io.BufferedOutputStream@4b194cd3 owned by "http-nio-8080-exec-9" Id=141
    at java.io.BufferedOutputStream.write(BufferedOutputStream.java:94)
    -  blocked on java.io.BufferedOutputStream@4b194cd3
    at java.io.DataOutputStream.writeByte(DataOutputStream.java:153)
    at com.rabbitmq.client.impl.Frame.writeTo(Frame.java:185)
    at com.rabbitmq.client.impl.SocketFrameHandler.writeFrame(SocketFrameHandler.java:171)
    -  locked java.io.DataOutputStream@19e9fe54 <---- but blocks 199 other threads!
    at com.rabbitmq.client.impl.AMQConnection.writeFrame(AMQConnection.java:542)
    at com.rabbitmq.client.impl.AMQCommand.transmit(AMQCommand.java:104)
    -  locked com.rabbitmq.client.impl.CommandAssembler@35ace671
    at com.rabbitmq.client.impl.AMQChannel.quiescingTransmit(AMQChannel.java:337)
    -  locked java.lang.Object@14917eea
    at com.rabbitmq.client.impl.AMQChannel.quiescingTransmit(AMQChannel.java:319)
    -  locked java.lang.Object@14917eea
    at com.rabbitmq.client.impl.AMQChannel.quiescingRpc(AMQChannel.java:254)
    -  locked java.lang.Object@14917eea
    at com.rabbitmq.client.impl.AMQChannel.rpc(AMQChannel.java:245)
    -  locked java.lang.Object@14917eea
    at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:222)
    at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:117)
    at com.rabbitmq.client.impl.ChannelN.open(ChannelN.java:132)
    at com.rabbitmq.client.impl.ChannelManager.createChannel(ChannelManager.java:176)
    at com.rabbitmq.client.impl.AMQConnection.createChannel(AMQConnection.java:533)
    at org.springframework.amqp.rabbit.connection.SimpleConnection.createChannel(SimpleConnection.java:55)
    at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$ChannelCachingConnectionProxy.createBareChannel(CachingConnectionFactory.java:1154)
    ...
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:615)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:818)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1623)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    -  locked org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@52e0d8d8
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

    Number of locked synchronizers = 1
    - java.util.concurrent.ThreadPoolExecutor$Worker@cb644e
  1. http-nio-8080-exec-9这个虽然拿到了锁, 但貌似一直写不进数据, 所以导致阻塞
  2. 然后查看rabbitmq的状态. 登录上对应mq服务的控制台, 发现节点磁盘已经满了, 控制台也有这么个警告The disk free space alarm for this node has gone off. It will block incoming network traffic untilthe amount of free space exceeds the limit. 大概意思就是节点的磁盘不够了, 后续所有传入的数据都会阻塞,直到磁盘有足够空间
  3. 然后清理磁盘, 服务恢复



tips

rabbitmq发送消息是要拿到锁才能写数据, 因为channel共享了同一个tcp连接

com.rabbitmq.client.impl.SocketFrameHandler.writeFrame

@Override
public void writeFrame(Frame frame) throws IOException {
    synchronized (_outputStream) {
        frame.writeTo(_outputStream);
    }
}


Java操作Redis的常见误区

不能使用 keys * 命令不能在set中存放大量数据
2021-01-11

Java 单例模式

双重校验懒汉式单例public class Singleton { //1. 避免jvm指令重排 由于JVM具有指令重排的特性,执行顺序有可能变成 1-3-2。指令重排在单线程下不会出现问题,但是在多线程下会导致一个线程获得一个未初始化的实例。例如:线程T1执行了1和3,此时T2调用 ge
2021-01-06

Java 工厂模式

public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }
2021-01-02

Java 模板模式

public abstract class Template { public final void templ() { System.out.println("开始"); code(); System.out.println("结束"); } public abstract v
2021-01-02

Java 装饰者模式

接口Printerpublic interface Printer { void start(); void print(); void stop(); } 实现类 HPPrinter public class HPPrinter implements Printer {
2021-01-02
Java Proxy.newProxyInstance动态代理

Java Proxy.newProxyInstance动态代理

定义接口public interface Student { void buy(); String talk(); } 实现类public class Lisi implements Student { @Override public void buy() { System.ou
2021-01-02

Java 解析windows 快捷方式 lnk文件

转载自 https://www.oschina.net/code/snippet_12_274import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; public c
2020-12-10

Spring Boot项目中使用RedisTemplate.delete() 删除指定key失败的解决办法

https://blog.csdn.net/hello_world_qwp/article/details/85763286上面这篇博客扯一大堆, 居然还分析源码实际只是自定义了key的序列化方式导致最终操作redis的时候序列化的key与预期的key不一致而已, 自然就删不掉redis中的数据了
2020-11-26

jpa方法参数必须加上@Param

void deleteByKl(String kl);线上可能报错原因可能是编译时没有加-parameters这个参数, 编译后丢失了参数名称, 使得反射拿不到对应参数需要加上注解void deleteByKl(@Param("kl") String kl);同理public ResultVO de
2020-09-28

日志规范

日志中要打印参数错误示例 @GetMapping("/share_coupon") public ActionResult shareCoupon(Long couponSn) { //validate code try { retur
2020-09-17

Java中的Date和时区转换

1.Date中保存的是什么在java中,只要我们执行 Date date = new Date(); 就可以得到当前时间。如:Date date = new Date(); System.out.println(date); 输出结果是: Thu Aug 24 10:15:29 CST 2017 也
2020-03-28
基于自定义注解手写权限控制

基于自定义注解手写权限控制

方法一: AOP 方法二: 拦截器项目结构项目依赖<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-w
2020-03-28

如何在Java中替换多个if语句

1. 概述选择结构是任何编程语言的重要组成部分。但是我们编写了大量嵌套的if语句,这使得我们的代码更加复杂和难以维护。 在本教程中,我们将介绍替换嵌套if语句的各种方法。 让我们探索如何简化代码的不同选项。2. 案例研究我们经常遇到涉及很多条件的业务逻辑,并且每个都需要不同的处理。为了演示,我们以C
2020-03-28
Java中方法的参数传递机制

Java中方法的参数传递机制

来看一段代码 public class Man { private String name; private Integer age; public String getName() { return name; } publi
2020-03-28

freemarker 时间显示不正常 设置时区

项目在本地开发的时候显示正常,部署上服务器就一直差8个小时,最后发现freemarker官方文档有这样的说明time_zone:时区的名称来显示并格式化时间。 默认情况下,使用JVM的时区。 也可以是 Java 时区 API 接受的值,或者 "JVM default" (从 FreeMarker 2
2020-03-28
IDEA 2019.1 xml 不高亮

IDEA 2019.1 xml 不高亮

前几天更新了idea后,发现xml里的代码都没有了高亮,变得跟记事本一个德性了打开setting ,搜索 File Types,找到xml项, 查看下方的匹配格式,果然没有xml,(idea真是厉害)点击右方的+,输入*.xml,点击ok,解决问题
2020-03-28

npm install 淘宝镜像

npm install --registry=https://registry.npm.taobao.org
2020-03-28
Java中方法的参数传递机制

Java中方法的参数传递机制

来看一段代码 public class Man { private String name; private Integer age; public String getName() { return name; } publi
2020-03-28
基于自定义注解手写权限控制

基于自定义注解手写权限控制

方法一: AOP 方法二: 拦截器项目结构项目依赖<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-w
2020-03-28

Docker 部署 详细全过程 附代码

Docker 部署本站 全过程环境:CentOS7.61. 安装Docker其他版本CentOS可以参考这个https://help.aliyun.com/document_detail/187598.html查看本机内核版本,内核版本需高于 3.10uname -r 确保 yum 包最新yum u
2020-03-28

SpringBoot 启动普通java工程

引入依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.0.9</version> </dependency>
2020-03-28

Vue.js DOM操作

<template> <input type="button" @click="reply($event)" value="回复"> </template> export default { methods: { replyFun(e) {
2020-03-29
CentOS7编译调试OpenJDK12

CentOS7编译调试OpenJDK12

1. 下载源码https://hg.openjdk.java.net/jdk/jdk12点击左侧的browse,再点击zip,就可以下载zip格式的源码压缩包。unzip xxx.zip 解压文件2. 安装jdkyum install java-11-openjdk-devel -y3. 运行con
2020-04-23
编写自己的Spring Boot Starter

编写自己的Spring Boot Starter

1.新建一个maven项目命名规则统一是xxx-spring-boot-starter完整pom.xml<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"
2020-06-29