no-image

jdbc socketTimeoutの設定について


以下は、中国の翻訳ツールで翻訳した中国の技術記事です

はじめに

この記事では、主にjdbcソケットのタイムアウト

Jdbcタイムアウトカテゴリ

主に以下のカテゴリがあります
トランザクションタイムアウト

複数の文を含むトランザクションの実行時間を設定する

  • 文のタイムアウト( 也相当于result set fetch timeout

文の実行タイムアウトを設定します。つまり、ドライバは文の完了を待機し、データのタイムアウトを受け取ります( 注意statement的timeout不是整个查询的timeout,只是statement执行完成并拉取fetchSize数据返回的超时,之后resultSet的next在必要的时候还会触发fetch数据,每次fetch的超时时间是单独算的,默认也是以statement设置的timeout为准

  • Jdbcソケットタイムアウト

jdbc入出力ソケットの読み取りおよび書き込み操作のタイムアウトを設定して、ネットワークの問題またはデータベースの問題によりドライバがブロックされないようにします。 ( 建议比statement timeout的时间长

  • OSソケットタイムアウト

これはオペレーティング・システム・レベルのソケット設定です( 如果jdbc socket timeout没有设置,而os级别的socket timeout有设置,则使用系统的socket timeout值 )。

つまり、以下の構成が上記の構成値よりも小さい場合は、最初にタイムアウトがトリガーされます。これは、上記の構成値「期限切れ」に相当します。

Jdbcソケットタイムアウト

この異なるデータjdbcドライバの実装は同じではありません

MySQL

jdbc:mysql://localhost:3306/ag_admin?useUnicode=true&characterEncoding=UTF8&connectTimeout=60000&socketTimeout=60000

urlパラメータをに渡す

Pg

jdbc:postgresql://localhost/test?user=fred&password=secret&&connectTimeout=60&socketTimeout=60

Pgもurlで渡されますが、その単位はmysqlとは異なります。mysqlはミリ秒で、pgは秒です

オラクル

oracle.jdbc.ReadTimeoutパラメータを使用してOracleを設定する必要があります。接続タイムアウト・パラメータはoracle.net.CONNECT_TIMEOUTです。

  • プロパティ設定を介して

            Class.forName("oracle.jdbc.driver.OracleDriver");
Properties props = new Properties() ;
props.put( "user" , "test_schema") ;
props.put( "password" , "pwd") ;
props.put( "oracle.net.CONNECT_TIMEOUT" , "10000000") ;
props.put( "oracle.jdbc.ReadTimeout" , "2000" ) ;
Connection conn = DriverManager.getConnection( "jdbc:oracle:thin:@10.0.1.9:1521:orcl" , props ) ;

  • 環境変数の設定

String readTimeout = "10000"; // ms
System.setProperty("oracle.jdbc.ReadTimeout", readTimeout);
Class.forName("oracle.jdbc.OracleDriver");
Connection conn = DriverManager.getConnection(jdbcUrl, user, pwd);

接続接続の前に環境変数を設定する必要があることに注意してください

  • TomcatのJDBCプール

一般的に、jdbc接続を直接使用するのではなく、接続プールを使用します。 tomcat jdbcプールはspringbootのデフォルトのデータベース接続プールであるため、tomcat jdbcプールに設定する方法は次のとおりです。

spring.datasource.tomcat.connectionProperties=oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000

ここでセミコロンは区切られていることに注意してください。単位はミリ秒で、状況に応じて接頭辞を設定できます( tomcat jdbc连接池的话,默认是spring.datasource.tomcat )。

    @Bean
@Qualifier("writeDataSource")
@ConfigurationProperties(prefix = "spring.datasource.write")
public DataSource writeDataSource() {
return DataSourceBuilder.create().build();
}

接頭辞spring.datasource.writeをカスタマイズしたと仮定すると、上記の構成は次のようになります。

spring.datasource.write.connectionProperties=oracle.net.CONNECT_TIMEOUT=10000;oracle.jdbc.ReadTimeout=60000

oracle.jdbc.ReadTimeout設定されていない場合、デフォルトのドライバは0

oracle.jdbc.ReadTimeout

ドライバは内部的にこの値をoracle.net.READ_TIMEOUT変数に設定します

  • oracle.net.nt.TcpNTAdapter

    @Override
public void setReadTimeoutIfRequired(final Properties properties) throws IOException, NetException {
String s = ((Hashtable)properties).get("oracle.net.READ_TIMEOUT");
if (s == null) {
s = "0";
}
this.setOption(3, s);
}
public void setOption(int var1, Object var2) throws IOException, NetException {
String var3;
switch(var1) {
case 0:
var3 = (String)var2;
this.socket.setTcpNoDelay(var3.equals("YES"));
break;
case 1:
var3 = (String)var2;
if(var3.equals("YES")) {
this.socket.setKeepAlive(true);
}
case 2:
default:
break;
case 3:
this.sockTimeout = Integer.parseInt((String)var2);
this.socket.setSoTimeout(this.sockTimeout);
}
}

最後のセットを見ることができるのは、soTimeoutソケットです

    @Test
public void testReadTimeout() throws SQLException {
Connection connection = dataSource.getConnection();
String sql = "select * from demo_table";
PreparedStatement pstmt;
try {
pstmt = (PreparedStatement)connection.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
int col = rs.getMetaData().getColumnCount();
System.out.println("============================");
while (rs.next()) {
for (int i = 1; i <= col; i++) {
System.out.print(rs.getObject(i));
}
System.out.println("");
}
System.out.println("============================");
} catch (SQLException e) {
e.printStackTrace();
} finally {
//close resources
}
}

タイムアウトエラー出力

//部分数据输出......
java.sql.SQLRecoverableException: IO 错误: Socket read timed out
at oracle.jdbc.driver.T4CPreparedStatement.fetch(T4CPreparedStatement.java:1128)
at oracle.jdbc.driver.OracleResultSetImpl.close_or_fetch_from_next(OracleResultSetImpl.java:373)
at oracle.jdbc.driver.OracleResultSetImpl.next(OracleResultSetImpl.java:277)
at com.example.demo.DemoApplicationTests.testReadTimeout(DemoApplicationTests.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: oracle.net.ns.NetException: Socket read timed out
at oracle.net.ns.Packet.receive(Packet.java:339)
at oracle.net.ns.DataPacket.receive(DataPacket.java:106)
at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:315)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:260)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:185)
at oracle.net.ns.NetInputStream.read(NetInputStream.java:102)
at oracle.jdbc.driver.T4CSocketInputStreamWrapper.readNextPacket(T4CSocketInputStreamWrapper.java:124)
at oracle.jdbc.driver.T4CSocketInputStreamWrapper.read(T4CSocketInputStreamWrapper.java:80)
at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1137)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:290)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:192)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:207)
at oracle.jdbc.driver.T4CPreparedStatement.fetch(T4CPreparedStatement.java:1119)
... 35 more

最初はデータ出力がありますが、 close_or_fetch_from_nextの次のものになると、タイムアウト( close_or_fetch_from_next )を報告します。このタイムアウトは、result.nextメソッドが新しいバッチのデータを引き出すタイミングを指定します( 当一个fetchSize的数据消费完之后,接下来的next会触发新一批数据的fetchタイムアウト時に戻ると、データベースからデータが返されません。

OracleのjdbcのデフォルトのfetchSizeは10です。つまり、指定した時間を超えてデータを受け取らない場合は、フェッチごとにタイムアウト例外がスローされます。

要約

Jdbc socketTimeout値の設定は、異なるデータベースJDBCドライバ設定が同じではない、特に異なる接続プールの使用は、設定が同じではない可能性があります非常に注意する必要があります。 それ以外の場合、ネットワークまたはデータベースの例外が発生した場合、サービススレッドは常にjava.net.SocketInputStream.socketRead0でブロックされます。

  • クエリデータが大きすぎる場合、スレッドが保持するデータリストを解放することはできません。これはメモリリークに相当し、最終的にOOMが発生します。
  • 要求データベースの操作が大規模でブロックされていると、サーバーが使用するワーカースレッドが少なくなり、サービスが利用できなくなります。nginx 504 Gateway Timeout

Doc

  • oracle.jdbc.ReadTimeout
  • JDBCタイムアウト設定の詳細な理解
  • SpringでデータアクセスがJDBCに基づいている場合のタイムアウトを制御する方法
  • BugFix-HttpURLConnection

関連記事