java偏运维_运维视角分析Java网络异常

本文从一起MySQL服务器异常导致的Java网络问题出发,探讨了常见的`java.net.SocketException`异常,包括连接超时、连接拒绝、连接重置等,并详细解释了这些异常的原因。此外,还深入介绍了JDBC的三层超时机制:Transaction Timeout、Statement Timeout和Socket Timeout,以及它们在数据库连接管理中的重要性,特别是如何设置和避免死连接。
摘要由CSDN通过智能技术生成

近日我大NOS一台极其重要的MySQL数据库服务器触发内核bug(背啊~),导致数据库异常,大量连接卡住,直到切换备库前端tomcat才恢复(还有部分没有恢复,这个稍后再表)。从线上db异常到切换这一段时间,tomcat报了各种错误,可谓百花齐放,其中大多是网络相关的,我们就从这些错误入手,以运维的视角审视java程序网络异常。

超时是解决网络异常很重要的方法,文章的后半部分,我们会详细的介绍JDBC的几种超时。

一、几种常见的java.net.SocketException异常

1)

#######################

##############################################

Caused by: com.netease.backend.db.common.exceptions.DDBConnFullException: Get resource from pool 'ddb_nos_new:nos_common' time out

at com.netease.backend.db.exec.conn.ConnResManager.getConnRes(ConnResManager.java:133)

#####################################################################

注解:这个错是从连接池获取数据库连接超时,严格来讲这不是个网络异常。但是

time out 却是我们经常遇到的一类异常,其中很大一部分是跨网络的服务导致的。它表示我们请求的模块或者服务长时间不给响应,超过了等待时间。看到这个异常你基本可以确定两点,一是自己依赖的服务不能正常处理请求了;二是自己(打这条日志的程序)目前还是正常的,线程还可以正常退出,没有因为依赖的服务而无限的等待。

2)

#####################################################################

java.net.ConnectException: Connection refused

at java.net.PlainSocketImpl.socketConnect(Native Method)

at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)

at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)

at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)

at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)

at java.net.Socket.connect(Socket.java:529)

#####################################################################注解:错误是服务端拒绝请求。该异常发生在客户端进行 new Socket(ip, port)操作时,该异常发生的原因是找不到ip地址或者找不到指定的端口进行监听。如果不是配置有问题,则代表服务端已经不再监听端口。

3)

#####################################################################

java.net.SocketException: Connection reset

at java.net.SocketInputStream.read(SocketInputStream.java:168)

at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:113)

at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:160)

at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:188)

#####################################################################

注解:Connection reset产生的原因是如果服务端的Socket端退出,但退出时并未关闭该连接(或主动关闭或者因为异常退出而引起的关闭),客户端如果再从连接中读数据则抛出该异常。该异常在客户端和服务器端均有可能发生。

与该异常相近的一个错误是

(Connect reset by peer),它发生在写数据的情况:如果一端的Socket被关闭,另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer),如果再继续写数据则抛出(java.net.SocketException: Broken pipe)。

下面再介绍几种这次事故没有遇到,但是也很常见的

SocketException

4)

#####################################################################

java.net.BindException:Address already in use: JVM_Bind。

#####################################################################

注解:该错误发生在服务监听端口的时候,它表示端口已经被占用,应该使用netstat检查下。

5)

#####################################################################

java.net.SocketException: Socket is closed

#####################################################################

注解:该异常在客户端和服务器均可能发生。异常的原因是己方主动关闭了连接后(调用了Socket的close方法)再对网络连接进行读写操作。

二、jdbc超时详解

前文说到有部分的tomcat在数据库切换后没有自动回复,我们的数据库一般是有镜像的,但是有时数据库切换到了镜像,应用服务器并没有自动恢复,需要重启才能正常服务。究其原因,是因为应用没有设置好超时,很多idle的tcp连接不能自动释放,需要等待操作系统释放(默认2小时),从而导致大量线程hung住。下面我们详细介绍下应用与数据库之间的超时设置:

2c91190a27928d9563358f0a0c385fb4.png

这张图可以比较好的说明JDBC的超时层级,一般而言,数据库超时分为三个层级:(1)Transaction Timeout;(2)Statement Timeout;(3)socket timeout。这三个层次由高到低,从业务层深入到网络层。低级别的超时有效力要大于高层级的,例如

socket timeout设置的很短的话,Transaction Timeout设置的再大也不会起作用。

1) Transaction Timeout:

transaction timeout一般存在于框架(Spring, EJB)或应用级。简单地说,transaction timeout就是“statement Timeout * N(需要执行的statement数量) + @(垃圾回收等其他时间)”。transaction timeout用来限制执行statement的总时长。

2)

Statement Timeout

statement timeout用来限制statement的执行时长,timeout的值通过调用JDBC的java.sql.Statement.setQueryTimeout(int timeout) API进行设置。不过现在开发者已经很少直接在代码中设置,而多是通过框架来进行设置。

0f33fe4300d25a8ef36442515d3cb9fe.png

MySQL JDBC timeout执行流程

1. 通过调用Connection的createStatement()方法创建statement

2. 调用Statement的executeQuery()方法

3. statement通过自身connection将query发送给MySQL数据库

4. statement创建一个新的timeout-execution线程用于超时处理

5. 5.1版本后改为每个connection分配一个timeout-execution线程

6. 向timeout-execution线程进行注册

7. 达到超时时间

8. TimerThread调用JtdsStatement实例中的TsdCore.cancel()方法

9. timeout-execution线程创建一个和statement配置相同的connection

10. 使用新创建的connection向超时query发送cancel query(KILL QUERY “connectionId”)

89fa2e3f2368130bf19082cf5e970ee6.png

Oracle JDBC Statement的QueryTimeout处理过程

1. 通过调用Connection的createStatement()方法创建statement

2. 调用Statement的executeQuery()方法

3. statement通过自身connection将query发送给Oracle数据库

4. statement在OracleTimeoutPollingThread(每个classloader一个)上进行注册

5. 达到超时时间

6. OracleTimeoutPollingThread调用OracleStatement的cancel()方法

7. 通过connection向正在执行的query发送cancel消息

(3)

socket timeout

JDBC的socket timeout在数据库被突然停掉或是发生网络错误(由于设备故障等原因)时十分重要。由于TCP/IP的结构原因,socket没有办法探测到网络错误,因此应用也无法主动发现数据库连接断开。如果没有设置socket timeout的话,应用在数据库返回结果前会无期限地等下去(linux 默认的超时是2小时),这种连接被称为dead connection。

为了避免dead connections,socket必须要有超时配置。socket timeout可以通过JDBC设置,socket timeout能够避免应用在发生网络错误时产生无休止等待的情况,缩短服务失效的时间。

下面是不同驱动的socket timeout配置方式

1e655e9f84b1789de7959a7b80924d35.png

本文来自网易实践者社区,经作者潘威授权发布。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值