wechatpay-apiv3 / wechatpay-java

微信支付 APIv3 的官方 Java Library
Apache License 2.0
923 stars 206 forks source link

启用双域名容灾后,调用订单查询接口异常 #180

Closed ningnao closed 1 year ago

ningnao commented 1 year ago

错误描述

按照文档中的说明,启用双域名容灾后,请求Native支付的订单查询接口,OkHttp抛出异常,但仍能正确返回查询结果 异常信息如下

WARN  c.w.p.j.c.h.o.OkHttpMultiDomainInterceptor - [intercept,34] - Retrying request due to connectivity failure: unexpected end of stream on https://api.mch.weixin.qq.com/...
java.io.IOException: unexpected end of stream on https://api.mch.weixin.qq.com/...
    at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.java:236)
    at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.java:115)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:94)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:43)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:94)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:88)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
    at com.wechat.pay.java.core.http.okhttp.OkHttpMultiDomainInterceptor.intercept(OkHttpMultiDomainInterceptor.java:32)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:142)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:117)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:229)
    at okhttp3.RealCall.execute(RealCall.java:81)
    at com.wechat.pay.java.core.http.okhttp.OkHttpClientAdapter.innerExecute(OkHttpClientAdapter.java:49)
    at com.wechat.pay.java.core.http.AbstractHttpClient.execute(AbstractHttpClient.java:44)
    at com.wechat.pay.java.service.payments.nativepay.NativePayService.queryOrderByOutTradeNo(NativePayService.java:238)
...
Caused by: java.io.EOFException: \n not found: limit=0 content=…
    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:240)
    at okhttp3.internal.http1.Http1ExchangeCodec.readHeaderLine(Http1ExchangeCodec.java:242)
    at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.java:213)

重现bug的步骤

1.按照文档说明,启用双域名容灾 2.创建NativePayService,调用queryOrderByOutTradeNo()方法查询订单状态 3.OkHttp抛出异常,但订单状态能正确返回

预期行为

无异常

导致错误的代码片段

HttpClient httpClient =
                    new DefaultHttpClientBuilder()
                            .config(WechatPayRunner.config)
                            .disableRetryOnConnectionFailure()
                            .enableRetryMultiDomain()
                            .build();
            // 构建service
            NativePayService service = new NativePayService.Builder().httpClient(httpClient).build();

            QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();

            ...

            Transaction transaction = service.queryOrderByOutTradeNo(request); // 此处异常

操作系统

Windows 11 25387.1

Java 版本

1.8

wechatpay-java 版本

v0.2.8

其他信息

No response

xy-peng commented 1 year ago

重试前会打印日志,会把捕获的 IOException 输出,然后执行重试。

@ningnao 你实际遇到的异常是?我看你写到“订单状态能正确返回”,是真的有异常吗?

ningnao commented 1 year ago

重试前会打印日志,会把捕获的 IOException 输出,然后执行重试。

@ningnao 你实际遇到的异常是?我看你写到“订单状态能正确返回”,是真的有异常吗?

重试后确实获取到了数据,但似乎第一次请求查询订单状态时必定会出现重试,这是预期的效果吗

xy-peng commented 1 year ago

我怀疑是 HTTP 长链接已经断了,但是 OkHttp 并不知道,所以第一次就会重试。之前不出现异常,是因为 OkHttp 的 retryOnConnectionFailure 帮你重试了。现在主动关闭了,但在重试时会输出日志,你可能感觉到了这一点。

你做一个实验,不开启双域名,但是 disableRetryOnConnectionFailure,看看是否会直接失败。这样能推断出是否跟双域名重试有关系。

我怀疑跟你的使用模式和网络情况有关系,看看以下两个问题:

  1. 从下单到查询订单,之间的时间间隔是多长?
  2. 你是直接访问的微信支付服务器,还是使用了代理或者 cloudbase 之类的服务?
ningnao commented 1 year ago

试了一下,使用disableRetryOnConnectionFailure,不开启双域名,第一次请求直接失败

你提到的两个问题: 1.或许是跟我的使用方式用关系,我是在一段时间后未收到回调才尝试查询订单,间隔大约20S 2.是直接访问的微信支付服务

xy-peng commented 1 year ago

或许是跟我的使用方式用关系,我是在一段时间后未收到回调才尝试查询订单,间隔大约20S

微信支付的 Connection Keep-Alive 大概是8s,20s 服务器端一定断连接了。

你可以试试自定义 ConnectionPool 的 keepAliveDuration,在微信支付服务器断掉之前先清理空闲连接,还会不会失败。

OkHttpClient client = new OkHttpClient.Builder()
        .retryOnConnectionFailure(false)
        .connectionPool(new ConnectionPool(5, 5, TimeUnit.SECONDS))
        .build();

HttpClient httpClient =
    new DefaultHttpClientBuilder()
        .config(config)
        .okHttpClient(okHttpClient)
        .build();

不过,最终还是要根据生产环境的实际访问情况,选择合适的配置。以上仅供实验。

ningnao commented 1 year ago

你可以试试自定义 ConnectionPool 的 keepAliveDuration,在微信支付服务器断掉之前先清理空闲连接,还会不会失败。

经测试,自定义OkHttpClient,指定keepAliveDuration后不再失败了。

感觉可以在文档中补充说明一下这一点...