undertow服务线程过多问题排查

一、背景

线上应用系统1300+多个线程,而且线程不会销毁,达到了1300个线程的UMP告警阈值;

容器情况:
JRE版本:1.8.0_20
CPU个数:4
web服务器:undertow 2.0.16.Final

二、分析

1、下载dump日志

jmap -dump:live,format=b,file=heap.hprof 进程号

2、 使用jvisualvm进行分析

image.png
image.png

分析发现1300多个线程大致分为如下几类:

  1. 守护线程400+ 个
  2. XNIO-1 I/O 线程96个,处于Running状态
  3. XNIO task 线程768,处于waiting状态
  4. 其他线程:几十个

通过查阅资料可知XNIO开头的都是undertow线程(undertow是基于xnio框架实现的)

XNIO-1 I/O 线程:是服务中的非阻塞线程,线程数默认等于CUP核数(其实是与2做比较取最大值)
XNIO task 线程:是阻塞线程,默认情况下该类线程对应的线程池的核心线程数和最大线程数是一致的,都是XNIO-1 I/O线程数的8倍;由于这些线程全是核心线程,所以这些线程都不会被销毁;

问题

我们的应用服务在部署的时候,容器一般都是4核或者8核,那为什么会创建这么多的96个非阻塞线程呢?

答案

应用服务使用的是docker容器创建的linux环境,在这个环境下,如果使用Java SE 8u131 (JRE 1.8.0_131-b11)以下的版本,通过Runtime.getRuntime().availableProcessors(),获取到的cpu核数是宿主机的核数,undertow就是这样获取的;

而我们的容器中JDK版本为1.8.0_20,就是上述版本之下,这样就能解释为什么默认情况下会创建XNIO-1 I/O 线程96个,XNIO task768线程个了;

附Jdk8版本号:https://www.java.com/zh-CN/download/help/release_changes.html

三、解决

方案1

在yml文件中指定undertow的线程数:

server.undertow.io-threads=4
server.undertow.worker-threads=40

方案2

使用Java SE 8u131 (JRE 1.8.0_131-b11)以上的版本,或者用JDK11;

四、思考

其实如果比较熟悉undertow,知道undertow中的线程的命名、以及线程数的关系的话,通过查看服务日志基本就能定位到问题
undertow配置的核心参数如下:

# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程,XNIO建议设置默认值即可
server.undertow.io-threads=4

# 阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线程
# 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8,一般情况下每个C大约10个线程即可
server.undertow.worker-threads=32

# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分,不要设置太大,以免影响其他应用,合适即可
server.undertow.buffers-per-region=1024

# 是否分配的直接内存(NIO直接分配的堆外内存)
server.undertos-per-region
server.undertow.buffer-size=1024

# 每个区分配的buffer数量 , 所以pool的大小是buffer-size * bufferw.direct-buffers=true

参考:https://undertow.io/undertow-docs/undertow-docs-2.1.0/index.html

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是使用Undertow创建WebSocket服务的简要步骤: 1. 添加Undertow依赖项:在您的Maven或Gradle项目中,添加Undertow服务器和WebSocket依赖项。 2. 创建WebSocket端点:定义一个类来实现WebSocket协议和事件处理程序,例如: ``` @ServerEndpoint("/example") public class ExampleWebSocketEndpoint { @OnOpen public void onOpen(Session session) { System.out.println("WebSocket opened: " + session.getId()); } @OnMessage public void onMessage(Session session, String message) { System.out.println("WebSocket message received: " + message); } @OnClose public void onClose(Session session) { System.out.println("WebSocket closed: " + session.getId()); } @OnError public void onError(Session session, Throwable throwable) { System.out.println("WebSocket error: " + throwable.getMessage()); } } ``` 在这个示例中,`@ServerEndpoint`注释指定了WebSocket端点的URL,即`/example`。`@OnOpen`、`@OnMessage`、`@OnClose`和`@OnError`注释分别指定了WebSocket事件的处理方法。 3. 配置Undertow服务器:创建一个Undertow服务器实例,并将WebSocket端点添加到部署描述符中,例如: ``` Undertow server = Undertow.builder() .addHttpListener(8080, "localhost") .setHandler(path() .addPrefixPath("/websocket", websocket(new WebSocketDeploymentInfo() .addEndpoint(ExampleWebSocketEndpoint.class)))) .build(); server.start(); ``` 在这个示例中,使用`Undertow.builder()`创建了一个Undertow服务器实例,并使用`addHttpListener()`方法指定了服务器监听的端口和主机名。`setHandler()`方法定义了一个处理程序,它使用`path()`方法创建了一个路径处理程序,并使用`addPrefixPath()`方法将WebSocket端点的URL路径`/websocket`与WebSocket处理程序关联。然后,使用`websocket()`方法将WebSocketDeploymentInfo对象添加到处理程序中,该对象包含了WebSocket端点的配置信息,例如端点的类和URL路径。 4. 测试WebSocket服务:使用任何WebSocket客户端(例如JavaScript、Python或Java)连接到WebSocket服务并发送消息,例如: ``` const websocket = new WebSocket("ws://localhost:8080/websocket/example"); websocket.onopen = () => { console.log("WebSocket opened"); websocket.send("Hello, WebSocket!"); }; websocket.onmessage = (event) => { console.log("WebSocket message received: " + event.data); }; websocket.onclose = () => { console.log("WebSocket closed"); }; websocket.onerror = (event) => { console.log("WebSocket error: " + event.message); }; ``` 在这个示例中,使用JavaScript创建了一个WebSocket实例,并在连接到WebSocket服务后发送了一条消息。在收到消息后,WebSocket实例将其打印到控制台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值