背景&环境
与第三方方使用https接口传递部分订单数据, 我方为客户端, 对方提供数据接收服务
接口调用时偶尔会出现 Connection reset & Connection reset by peer: socket write error 错误, 也有数据下发成功的时候
环境: okhttp3 4.8.1
相关代码:
// 信任任意的https证书
private OkHttpClient strongOkHttpClient() throws IOException {
try {
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return builder
.connectTimeout(180, TimeUnit.SECONDS)
.readTimeout(180, TimeUnit.SECONDS)
.writeTimeout(180, TimeUnit.SECONDS)
.build();
} catch (Exception e) {
throw new IOException(e);
}
}
// http请求下发逻辑
public void send(String accNo, String vouchId) {
// 获取请求参数数据
String dbName = ufCoProperties.getAccountMap().get(accNo);
List<POInfo> poInfoList = poDao.listPO(accNo, dbName, vouchId);
poInfoList.forEach(item -> {
item.setSignature(tbOrderProperties.getAppKey());
});
log.info("采购订单接口下发数据: {}", JSON.toJSONString(poInfoList));
// 构建请求体
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.parse("application/json");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8(JSON.toJSONString(poInfoList));
}
};
log.info("请求URL: {}", tbOrderProperties.getUrl());
// 构建Http请求
Request request = new Request.Builder()
.url(tbOrderProperties.getUrl())
.header("Authorization", tbOrderProperties.getToken())
.post(requestBody)
.build();
String sendMessage;
// 发送Http请求
try {
OkHttpClient client = strongOkHttpClient();
// 在此处发起http请求时报错
Response response = client.newCall(request).execute();
String responseBody = response.body().string();
log.info("HTTP请求处理完成 - 响应数据: {}", responseBody);
Map<String,Object> result = JSON.parseObject(responseBody, Map.class);
boolean success = (boolean) result.get("success");
Object data = result.get("data");
if(success) {
sendMessage = "下发成功";
} else {
sendMessage = String.format("下发失败: %s", data);
}
} catch (IOException e) {
log.error("HTTP请求处理失败 - IO异常: {}", e.getMessage(), e);
sendMessage = "HTTP请求处理失败 - IO异常: " + e.getMessage();
}
// 回写单据处理结果
poDao.updatePOByCode(dbName, vouchId, sendMessage);
}
个人思考
参考网上部分关于Connection reset的相关回答,均表示是由服务器端关闭连接后,我方客户端服务仍通过接口读写数据导致的,那么按道理来说,服务器端应该是已经接收到请求了,然后在接收到请求之后因为种种原因关闭了连接导致客户端报错。
实际情况: 对接方告知我方并未收到接口请求
错误日志:
javax.net.ssl.SSLException: java.net.SocketException: Connection reset
at sun.security.ssl.Alert.createSSLException(Alert.java:127) ~[na:1.8.0_311]
at sun.security.ssl.TransportContext.fatal(TransportContext.java:370) ~[na:1.8.0_311]
at sun.security.ssl.TransportContext.fatal(TransportContext.java:313) ~[na:1.8.0_311]
at sun.security.ssl.TransportContext.fatal(TransportContext.java:308) ~[na:1.8.0_311]
at sun.security.ssl.SSLTransport.decode(SSLTransport.java:141) ~[na:1.8.0_311]
at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1290) ~[na:1.8.0_311]
at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1199) ~[na:1.8.0_311]
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:401) ~[na:1.8.0_311]
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:373) ~[na:1.8.0_311]
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:379) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201) ~[okhttp-4.9.1.jar!/:na]
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154) ~[okhttp-4.9.1.jar!/:na]
at com.demo.service.impl.POServiceImpl.send(POServiceImpl.java:122) ~[classes!/:0.0.1-SNAPSHOT]
at sun.reflect.GeneratedMethodAccessor82.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_311]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_311]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) [spring-aop-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) [spring-aop-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) [spring-aop-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115) [spring-aop-5.2.5.RELEASE.jar!/:5.2.5.RELEASE]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_311]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_311]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_311]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_311]
Suppressed: java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method) ~[na:1.8.0_311]
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111) ~[na:1.8.0_311]
at java.net.SocketOutputStream.write(SocketOutputStream.java:155) ~[na:1.8.0_311]
at sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:83) ~[na:1.8.0_311]
at sun.security.ssl.TransportContext.fatal(TransportContext.java:401) ~[na:1.8.0_311]
... 36 common frames omitted
求证目标
想请大家帮忙分析下,出现Connection reset报错时,服务器端是否必然会接收到请求?
如果服务端必然会接收到请求,那么就有可能是日志被抹掉了导致对方告知我方并未接收到接口请求。
如果服务端不一定会接收到请求(报错时), 那么我想知道什么样的原因会导致此问题?