我正在使用Java11
和httpclient 4.5.11
向API发出请求,到目前为止一切正常。但是一周多以前,向https://api.moip.com.br
的付款开始出现间歇性错误(Connection reset
)。
我知道他们需要TLSv1.2,并且Java11支持它,而且错误是间歇性的(某些东西起作用,有时却不起作用)。我联系了他们,他们说他们进行了代码库的迁移,但是他们处理请求的配置是相同的,对此我表示怀疑,但是无论如何,考虑到他们处理多个组织的付款,我认为存在一些问题。我可以避免这些错误的方法。附带说明,我可以毫无问题地向PayPal,Stripe,Google Recaptcha和其他服务发出请求。
我如何创建连接:
try (CloseableHttpClient httpClient = createHttpClientBuilder().build()) {
HttpUriRequest httpMethod = createHttpUriRequest(request);
HttpResponse httpResponse = httpClient.execute(httpMethod);
SimpleHttpClientResponse response = createResponse(httpResponse);
return response;
} catch (Exception e) {
throw new HTTPException(request, e);
}
以下是我创建连接管理器,客户端构建器和请求本身的方法:
private static HttpClientBuilder createHttpClientBuilder() throws KeyManagementException, NoSuchAlgorithmException {
CookieStore httpCookieStore = new BasicCookieStore();
RequestConfig defaultRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
HttpClientBuilder builder = HttpClients.custom()
.setConnectionManager(connectionManager)
.setConnectionManagerShared(true)
.setDefaultCookieStore(httpCookieStore)
.setDefaultRequestConfig(defaultRequestConfig);
return builder;
}
private static PoolingHttpClientConnectionManager createConnectionManager() {
try {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
SSLContext.getDefault(),
new String[] {"TLSv1.2"},
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", socketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(20);
return cm;
} catch (NoSuchAlgorithmException | RuntimeException e) {
LogUtils.error(e);
return null;
}
}
private static HttpUriRequest createHttpUriRequest(SimpleHttpClientRequest request) throws ParseException, IOException, URISyntaxException {
HttpUriRequest httpUriRequest;
if (request.getMethod().equals(HTTPMethod.POST)) {
HttpPost httpPost = new HttpPost(request.getUrl());
HttpEntity entity = createEntity(request);
httpPost.setEntity(entity);
httpUriRequest = httpPost;
} else if (request.getMethod().equals(HTTPMethod.HEAD)) {
httpUriRequest = new HttpHead(getUrlWithParameters(request));
} else if (request.getMethod().equals(HTTPMethod.DELETE)) {
httpUriRequest = new HttpDelete(getUrlWithParameters(request));
} else {
httpUriRequest = new HttpGet(getUrlWithParameters(request));
}
Map<String, String> headers = request.getHeaders();
if (headers != null) {
for (String key : headers.keySet()) {
String value = headers.get(key);
httpUriRequest.setHeader(key, value);
}
}
return httpUriRequest;
}
SimpleHttpClientRequest
只是我的一类,它定义发出请求时要使用的uri,标头和其他数据,但是请求本身由apache HttpClient完成。
就像我说的那样,如果我多次尝试发出相同的请求,则错误是间歇性的。
我定义了-Djavax.net.debug=all
以了解可能的情况,日志如下所示:
Obs:我删除了ERROR [stderr] (default task-2) 0000: D2 A3 F4 C9 87 BF 23 89 65 F7 50 B6 90 8C 9D 8B ......#.e.P.....
之类的行,因为在这种情况下我不认为它们会添加任何有用的信息
当我收到错误消息时:
10:03:16,752 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23
10:03:16.750 GMT-03:00|SSLSocketInputRecord.java:249|READ: TLSv1.1 application_data, length = 2160
10:03:16,756 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:16.755 GMT-03:00|SSLCipher.java:1329|Padded plaintext after DECRYPTION (
10:03:16,775 ERROR [stderr] (default task-2) )
10:03:16,793 ERROR [stderr] (default task-2) javax.net.ssl|WARNING|DC|default task-2|2020-06-23 10:03:16.792 GMT-03:00|SSLSocketImpl.java:1280|handling exception (
10:03:16,793 ERROR [stderr] (default task-2) "throwable" : {
10:03:16,793 ERROR [stderr] (default task-2) java.net.SocketTimeoutException: Read timed out
10:03:16,793 ERROR [stderr] (default task-2) at java.base/java.net.SocketInputStream.socketRead0(Native Method)
10:03:16,794 ERROR [stderr] (default task-2) at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:115)
10:03:16,794 ERROR [stderr] (default task-2) at java.base/java.net.SocketInputStream.read(SocketInputStream.java:168)
10:03:16,802 ERROR [stderr] (default task-2) at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
...
10:03:16,812 ERROR [stderr] (default task-2) at java.base/java.lang.Thread.run(Thread.java:834)}
10:03:16,812 ERROR [stderr] (default task-2)
10:03:16,813 ERROR [stderr] (default task-2) )
10:03:16,820 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:16.813 GMT-03:00|SSLSocketOutputRecord.java:309|WRITE: TLS12 application_data, length = 329
10:03:16,821 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:16.821 GMT-03:00|SSLCipher.java:1743|Plaintext before ENCRYPTION (
10:03:16,827 ERROR [stderr] (default task-2) )
10:03:16,828 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:16.827 GMT-03:00|SSLSocketOutputRecord.java:323|Raw write (
10:03:16,829 ERROR [stderr] (default task-2) )
10:03:16,990 ERROR [stderr] (default task-2) javax.net.ssl|WARNING|DC|default task-2|2020-06-23 10:03:16.989 GMT-03:00|SSLSocketImpl.java:1280|handling exception (
10:03:16,990 ERROR [stderr] (default task-2) "throwable" : {
10:03:16,991 ERROR [stderr] (default task-2) java.net.SocketException: Connection reset
10:03:16,991 ERROR [stderr] (default task-2) at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
10:03:16,991 ERROR [stderr] (default task-2) at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
...
10:03:17,022 ERROR [stderr] (default task-2) at java.base/java.lang.Thread.run(Thread.java:834)}
10:03:17,022 ERROR [stderr] (default task-2)
10:03:17,022 ERROR [stderr] (default task-2) )
10:03:17,025 ERROR [stderr] (default task-2) javax.net.ssl|ERROR|DC|default task-2|2020-06-23 10:03:17.024 GMT-03:00|TransportContext.java:318|Fatal (UNEXPECTED_MESSAGE): Connection reset (
10:03:17,025 ERROR [stderr] (default task-2) "throwable" : {
10:03:17,025 ERROR [stderr] (default task-2) java.net.SocketException: Connection reset
10:03:17,026 ERROR [stderr] (default task-2) at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
10:03:17,026 ERROR [stderr] (default task-2) at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
10:03:17,026 ERROR [stderr] (default task-2) at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:448)
...
10:03:17,052 ERROR [stderr] (default task-2) at java.base/java.lang.Thread.run(Thread.java:834)}
10:03:17,052 ERROR [stderr] (default task-2)
10:03:17,052 ERROR [stderr] (default task-2) )
10:03:17,053 ERROR [stderr] (default task-2) javax.net.ssl|ALL|DC|default task-2|2020-06-23 10:03:17.053 GMT-03:00|SSLSessionImpl.java:784|Invalidated session: Session(1592917276199|TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
10:03:17,055 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:17.054 GMT-03:00|SSLSocketOutputRecord.java:71|WRITE: TLS12 alert(unexpected_message), length = 10
10:03:17,056 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:17.055 GMT-03:00|SSLCipher.java:1743|Plaintext before ENCRYPTION (
10:03:17,056 ERROR [stderr] (default task-2) )
10:03:17,059 ERROR [stderr] (default task-2) javax.net.ssl|WARNING|DC|default task-2|2020-06-23 10:03:17.059 GMT-03:00|TransportContext.java:360|Fatal: failed to send fatal alert UNEXPECTED_MESSAGE (
10:03:17,060 ERROR [stderr] (default task-2) "throwable" : {
10:03:17,060 ERROR [stderr] (default task-2) java.net.SocketException: Broken pipe (Write failed)
10:03:17,060 ERROR [stderr] (default task-2) at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
10:03:17,061 ERROR [stderr] (default task-2) at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
...
10:03:17,076 ERROR [stderr] (default task-2) at java.base/java.lang.Thread.run(Thread.java:834)}
10:03:17,076 ERROR [stderr] (default task-2)
10:03:17,076 ERROR [stderr] (default task-2) )
10:03:17,077 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:17.077 GMT-03:00|SSLSocketImpl.java:1353|close the underlying socket
10:03:17,077 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:17.077 GMT-03:00|SSLSocketImpl.java:1372|close the SSL connection (initiative)
10:03:17,078 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:17.078 GMT-03:00|SSLSocketImpl.java:663|close outbound of SSLSocket
10:03:17,078 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:03:17.078 GMT-03:00|SSLSocketImpl.java:629|close inbound of SSLSocket
10:03:17,079 ERROR [stderr] (default task-2) javax.net.ssl|WARNING|DC|default task-2|2020-06-23 10:03:17.079 GMT-03:00|TransportContext.java:284|Closed transport, general or untracked problem
10:03:17,105 WARN [my.company.util.LogUtils] (default task-2)
at my.deployment//my.company.util.ExceptionUtils.wrap(ExceptionUtils.java:26)
at my.deployment//my.company.util.PagamentoUtil.consultarRespostaHttpPagamento(PagamentoUtil.java:618)
at my.deployment//my.company.controller.admin.verification.VerifyPaymentApiController.doGet(VerifyPaymentApiController.java:61)
... 58 more
Caused by: my.company.exceptions.HTTPException: javax.net.ssl.SSLException: Connection reset - 'GET': 'https://api.moip.com.br/v2/orders/ORD-KC1CSM2SVPMX'
at my.deployment//my.company.util.HttpClientUtils.send(HttpClientUtils.java:63)
at my.deployment//my.company.util.URIUtils.getResponseObjectFromRequest(URIUtils.java:315)
at my.deployment//my.company.util.PagamentoUtil.accessMoipV2(PagamentoUtil.java:2060)
at my.deployment//my.company.util.PagamentoUtil.lambda$consultarRespostaHttpPagamento$0(PagamentoUtil.java:597)
at my.deployment//my.company.util.PagamentoUtil.getResponseResultFromApi(PagamentoUtil.java:806)
at my.deployment//my.company.util.PagamentoUtil.consultarRespostaHttpPagamento(PagamentoUtil.java:597)
... 59 more
Caused by: javax.net.ssl.SSLException: Connection reset
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:127)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:326)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:269)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
at java.base/sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1306)
at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:832)
at my.deployment//org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)
at my.deployment//org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153)
at my.deployment//org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:280)
at my.deployment//org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)
at my.deployment//org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
at my.deployment//org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
at my.deployment//org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
at my.deployment//org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157)
at my.deployment//org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
at my.deployment//org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at my.deployment//org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
at my.deployment//org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
at my.deployment//org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at my.deployment//org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at my.deployment//org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at my.deployment//org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at my.deployment//org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at my.deployment//my.company.util.HttpClientUtils.send(HttpClientUtils.java:59)
... 64 more
Suppressed: java.net.SocketException: Broken pipe (Write failed)
at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:81)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:357)
... 86 more
Caused by: java.net.SocketException: Connection reset
at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:448)
at java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:68)
at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1096)
at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:816)
... 82 more
发生错误时,它不会到达成功记录的请求部分:
10:01:13,234 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:01:13.233 GMT-03:00|TrustStoreManager.java:161|Inaccessible trust store: /usr/lib/jvm/java-11-openjdk-11.0.7.10-4.el7_8.x86_64/lib/security/jssecacerts
10:01:13,234 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:01:13.234 GMT-03:00|TrustStoreManager.java:112|trustStore is: /usr/lib/jvm/java-11-openjdk-11.0.7.10-4.el7_8.x86_64/lib/security/cacerts
10:01:13,234 ERROR [stderr] (default task-2) trustStore type is: pkcs12
10:01:13,234 ERROR [stderr] (default task-2) trustStore provider is:
10:01:13,235 ERROR [stderr] (default task-2) the last modified time is: Thu Jun 11 13:31:42 GMT-03:00 2020
10:01:13,553 ERROR [stderr] (default task-2) javax.net.ssl|DEBUG|DC|default task-2|2020-06-23 10:01:13.551 GMT-03:00|X509TrustManagerImpl.java:79|adding as trusted certificates (
10:01:13,554 ERROR [stderr] (default task-2) "certificate" : {
10:01:13,554 ERROR [stderr] (default task-2) "version" : "v3",
我看到了SocketTimeoutException
(即使在请求开始后仅200毫秒就发生这种情况,我也很努力),试图在连接管理器,客户端构建器以及请求中增加套接字超时,但是它没有用。我还尝试删除并使其保持连接状态,以使连接不被重用,但是它也不起作用。我还在SO中查看了其他类似问题,但都无济于事。以下是我尝试的更改:
在连接管理器中:
SocketConfig socketConfig = SocketConfig.custom()
.setSoKeepAlive(false)
.setSoTimeout(600000)
.setSoReuseAddress(false)
.build();
cm.setDefaultSocketConfig(socketConfig);
在客户端中:
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(15000)
.setConnectTimeout(15000)
.setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
builder = builder.setDefaultRequestConfig(requestConfig);
builder = builder.setConnectionReuseStrategy((response, context) -> {
LogUtils.info("**** connectionReuse strategy returning false");
return false;
});
builder = builder.setKeepAliveStrategy((response, context) -> {
LogUtils.info("**** keepAlive strategy returning -1");
return -1l;
});
在请求中:
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(15000)
.setConnectTimeout(15000)
.setCookieSpec(CookieSpecs.IGNORE_COOKIES).build();
httpUriRequest.setConfig(requestConfig);
我一起尝试了上述方法,也没有分别尝试。
我意识到的一件事情是,在本地进行测试(可能是巧合,艰难)后,在启动Java应用程序后我从未收到异常,并且当一个请求有效时,以下请求可以工作1分钟左右,但即使我定义为不重用连接,该连接以相同的方式继续进行,出现间歇性错误,并且在一个请求成功的情况下工作一段时间(尽管该配置并未禁用连接的重用,但是我认为它已禁用,因为我看到了日志connectionReuse strategy returning false
),所以我最终没有选择。
现在,我将它们包含在while
循环中,该循环最多执行4次(出现错误时),以使对该特定端点的请求无缝地对用户起作用,但这是一种非常hack的方法,我希望解决此问题。
我希望有人可以帮助我。