0 序言
- 近期工作在搞壓力測試,我負責開發維護的、基於
sring-cloud-gateway
的大資料閘道器微服務,其底層是基於spring-webflux
-->reactor-netty
-->netty
。 - 在壓測過程中(200併發-->1000併發),發現大資料閘道器屢報ConnectException: finishConnect(..) failed: Connection refused錯誤。
Caused by: java.net.ConnectException: finishConnect(..) failed: Connection refused
at io.netty.channel.unix.Errors.newConnectException0(Errors.java:155) ~[netty-transport-native-unix-common-4.1.65.Final.jar!/:4.1.65.Final]
at io.netty.channel.unix.Errors.handleConnectErrno(Errors.java:128) ~[netty-transport-native-unix-common-4.1.65.Final.jar!/:4.1.65.Final]
at io.netty.channel.unix.Socket.finishConnect(Socket.java:278) ~[netty-transport-native-unix-common-4.1.65.Final.jar!/:4.1.65.Final]
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.doFinishConnect(AbstractEpollChannel.java:710) [netty-transport-native-epoll-4.1.65.Final-linux-x86_64.jar!/:4.1.65.Final]
關鍵行:
io.netty.channel.unix.Socket.finishConnect(Socket.java:278) ~[netty-transport-native-unix-common-4.1.65.Final.jar!/:4.1.65.Final]
- 經過長時間、大量的分析(Debug spring-cloud-gateway 原始碼、觀測服務所屬主機資源),傾向於本微服務所屬主機的
net.core.somaxconn
不足這種原因。
博主的做法:
128 --> 1024
- 環境資訊
linux os : Alpine Linux 3.17.3 - 一個社群開發的、面向安全應用的、基於Musl libc和busybox的輕量級Linux發行版
https://gitlab.alpinelinux.org/alpine/
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.17.3
PRETTY_NAME="Alpine Linux v3.17"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
spring-cloud-gateway:2.2.9.RELEASE
spring-cloud-starter:2.2.9.RELEASE
spring-cloud-alibaba:2.2.7.RELEASE
spring-cloud:Hoxton.SR12
spring-boot:2.3.12.RELEASE
spring:5.2.15.RELEASE
1 引數定義
net.core.somaxconn
- Linux 作業系統全域性引數,每個
TCP連線
監聽埠的佇列長度。
somaxconn
- INTEGER
- Limit of socket listen() backlog,在使用者空間中稱為 SOMAXCONN。
- 預設值為 4096。(在 linux-128.5 之前是 4)
- 另請參閱
tcp_max_syn_backlog
,瞭解TCP 套接字
的其他調整。
- 在
CentOS/RHEL
7.9 中,預設值為128
,對於高併發場景
都建議調大該值。
- 在RHEL 6和RHEL 7中,socket結構的
sk_max_ack_backlog
欄位被定義為無符號短整型,它將值限制為16位,最大值為65535。- 在 RHEL 8 中,socket結構的
sk_max_ack_backlog
欄位定義為 u32,它將值限制為 32 位,最大值為 2147483647。
2 檢視方法
2.1 Linux
- 方法1
cat /proc/sys/net/core/somaxconn
- 方法2
sysctl -a | grep somaxconn
3 不同應用的配置建議
3.1 Oracle WebLogic Server
- 客戶端數:設定
somaxconn
應至少設定為1024
,以 允許大量客戶端伺服器連線。
排隊的資料包數:將
netdev_max_backlog
設定為至少32768
,以最大程度地減少資料包丟失。
3.2 Apache Zookeeper
Configurable listen socket backlog for the client port - issues.apache.org/zookeeper
在 Linux 上,以下引數: net.core.somaxconn
需要大於“客戶端埠積壓工作”以上才能正確配置偵聽套接字積壓工作
3.3 TiDB
https://blog.csdn.net/weixin_43700866/articTiDBle/details/125667286
核心引數 - pingcap.com
- 核心引數
檢查各項核心引數的值:
…
`net.core.somaxconn: 32768`
3.4 Nginx
將 net.core.somaxconn
核心引數的值從其預設值 (128
) 增加到足以容納大量流量突發的值。在此示例中,它增加到 4096
sudo sysctl -w net.core.somaxconn=4096
...
3.5 Netty
推薦文獻
Caused by: io.netty.channel.ChannelException: Unable to create Channel from class class io.netty.channel.socket.nio.NioSocketChannel
...
最佳化建議
- 預設情況下,Netty 的
SO_BACKLOG
的值等於作業系統的somaxconn
的值
ChannelOption.SO_BACKLOG` = `io.netty.util.NetUtil#SOMAXCONN
- 在面對高併發場景下時,應當適當增大
BACKLOG
的值,使埠可以同時建立更多的TCP請求 - 當服務的QPS不高,且服務端程式響應請求的時間較長時,可以適當減小BACKLOG的值,避免大量連線佔據
系統記憶體資源
。 - 在設定
BACKLOG
引數時,還需要記得修改somaxconn
引數,若設定的BACKLOG
引數超過了系統somaxconn
引數的值時,則無法生效。
netty 原始碼
netty : 4.1.65
- 預設:
ChannelOption.SO_BACKLOG
=io.netty.util.NetUtil#SOMAXCONN
=/proc/sys/net/core/somaxconn
(Windows中預設值:200,Linux中預設值:128)
io.netty.util.NetUtil
SOMAXCONN
https://github.com/netty/netty/issues/4936
/proc/sys/net/core/somaxconn
io.netty.channel.epoll.EpollServerChannelConfig extends EpollChannelConfig implements ServerSocketChannelConfig
private volatile int backlog = NetUtil.SOMAXCONN;
D:/Program_Data/maven_repository/io/netty/netty-common/4.1.65.Final/netty-common-4.1.65.Final-sources.jar!/io/netty/util/NetUtil.java:134
io.netty.util.NetUtil#SOMAXCONN
static {
...
int somaxconn = PlatformDependent.isWindows() ? 200 : 128;
File file = new File("/proc/sys/net/core/somaxconn");
...
}
//io.netty.util.internal.logging.InternalLogger | 如何將 netty 日誌開關開啟?
如下是:
gateway-service
[Debug 原始碼分析] 以本地電腦(8 core cpu)在啟動後的1次請求呼叫為例(與 somaxcon 無關,僅是記錄;NioEventLoop 取決於 CPU 核數)
io.netty.bootstrap.Bootstrap#config
reactor.netty.resources.DefaultLoopResources#cacheNioServerLoops
D:/Program_Data/maven_repository/io/projectreactor/netty/reactor-netty/0.9.20.RELEASE/reactor-netty-0.9.20.RELEASE-sources.jar!/reactor/netty/resources/DefaultLoopResources.java:195
reactor.netty.resources.DefaultLoopResources#cacheNioServerLoops
watch reactor.netty.resources.DefaultLoopResources cacheNioServerLoops returnObj
io.netty.channel.nio.NioEventLoopGroup
io.netty.channel.nio.NioEventLoop
4 配置方法
4.1.臨時生效
sysctl -w net.core.somaxconn=10240
4.2.永久生效
echo "net.core.somaxconn = 10240" >>/etc/sysctl.conf
sysctl -p
X 參考文獻
- Linux net.core.somaxconn 引數詳解 - CSDN 【推薦】
- Netty-原始碼學習(1)-BACKLOG引數 - 掘金 【推薦】
- netty client 連線超時設定 - 部落格園 【不推薦/因無關】
/**
* *******************************************************************
* 如果不設定超時,連線會一直佔用本地執行緒,埠,連線客戶端一多,阻塞在那裡,會導致本地埠用盡及CPU壓力
*/
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
未設定超時:
30
io.netty.channel.ConnectTimeoutException: connection timed out: /192.168.1.1:8866
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe$1.run(AbstractNioChannel.java:206)
at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:123)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:354)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:353)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101)
at java.lang.Thread.run(Thread.java:745)
Process finished with exit code 0
設定後:
5
io.netty.channel.ConnectTimeoutException: connection timed out: /192.168.1.1:8866
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe$1.run(AbstractNioChannel.java:206)
at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:123)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:354)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:353)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101)
at java.lang.Thread.run(Thread.java:745)
Process finished with exit code 0