关于HttpsURLConnection自动重试导致的请求重复

来源:互联网 发布:c并发编程实战 中文版 编辑:程序博客网 时间:2024/05/21 06:40

[问题描述] 

        在业务中与第三方对接时,在11秒log显示开始发起请求,24秒接受到请求结果,显示“请求序列号重复,请求失败”。查询log确认这段程序只触发一次,对方给出的日志显示第一次请求11秒接收,14秒给出返回结果,23秒收到同一序列号的请求,给出请求失败的response。

[代码段] 

        

/**   * 向指定URL发送POST方法的请求   *   * @return URL所代表远程资源的响应   * @throws Exception   */  public static String sendPost(String tr1XML, String postUrl, String authString, int timeOutSecond, KeyManager[] keyManagers) throws Exception {    log.info("发送tr1报文:{}", tr1XML);    ParseXMLUtil parseUtil = new ParseXMLUtil();    OutputStream out = null;    String reqData = null;    try {      HttpsURLConnection conn = (HttpsURLConnection) createConnection(postUrl, authString, timeOutSecond, keyManagers);      // 获取URLConnection对象对应的输出流      out = conn.getOutputStream();      //发送请求参数      out.write(tr1XML.getBytes("utf-8"));      //flush 输出流的缓冲      out.flush();      //得到服务端返回      InputStream is = conn.getInputStream();      if (is != null && !"".equals(is)) {        ByteArrayOutputStream bos = new ByteArrayOutputStream();        byte[] receiveBuffer = new byte[2048];        int readBytesSize = is.read(receiveBuffer);//读取数据长度,InputStream要读取的数据长度一定要小于等于缓冲区中的字节数        while (readBytesSize != -1) {          bos.write(receiveBuffer, 0, readBytesSize);          readBytesSize = is.read(receiveBuffer);        }        reqData = new String(bos.toByteArray(), "UTF-8");      }      log.info("返回tr2报文:{}", reqData);    } catch (Exception e) {      throw YqgException.wrap(e);    } finally {      if (out != null) {        out.close();      }    }    return reqData;  }


[资料查询]

        在网上查询 HttpsURLConnection 相关资料,得知在  conn.getInputStream()   才开始真正发起请求,猜测可能是由于网络超时等原因丢包,发起了两次请求,但是这个过程是发生在底层机制中而不是业务层,所以没有被log记录下。然后同事查到貌似HttpsURLConnection确实有这样的机制(本人英语渣渣,只能附上url

相关资料:

  • HttpURLConnection超时处理
  • Android (Java) HttpURLConnection silent retry on 'read' timeout - Stack Overflow

[解决]

    没法解决。
    虽然在第二篇文章中有解决办法,但是第一篇中也提到应该在JDk1.5就得到了修复,我们的生产环境是1.8,而且不稳定复现,目前为止就出现了一笔。

    所以在同一个同事的建议下,使用okHttp替代HttpsURLConnection。

[修改后的代码段]

........//static段先注册KeyManagerprivate void prepareHttpClient() throws Exception {    SSLContext sslContext = SSLContext.getInstance("SSL");    TrustManager[] trustManagers = {new MyX509TrustManager()};    sslContext.init(keyManagers, trustManagers, null);    SSLSocketFactory factory = sslContext.getSocketFactory();    client.setSslSocketFactory(factory);    client.setConnectTimeout(HttpClient.TIMEOUT_SECOND_60, TimeUnit.SECONDS);    client.setReadTimeout(HttpClient.TIMEOUT_SECOND_60, TimeUnit.SECONDS);  }private String sendPost(String tr1XML, String postUrl, String authString) throws Exception {    log.info("发送tr1报文:{}", tr1XML);    String reqData = null;    try {      String auth = "Basic " + Base64Binrary.encodeBase64Binrary(authString.getBytes());      Response response = HttpClientCommand.syncPostXmlWithAuthorizationOrThrow(client, postUrl, auth, tr1XML);      reqData = response.body().string();      log.info("返回tr2报文:{}", reqData);    } catch (Exception e) {      throw YqgException.wrap(e);    }    return reqData;  }--------------------public static Response syncPostXmlWithAuthorizationOrThrow(OkHttpClient client, String url, String authorization, String request) throws IOException {    MediaType mediaType = MediaType.parse("application/xml");    Request httpReq = new Request.Builder()        .url(url)        .post(RequestBody.create(mediaType, request))        .header("Authorization", authorization)        .build();    Response response = doExecute(client, httpReq);    LOGGER.info("syncPostRaw: url: {}, response: {}, mediaType: {}", url, response, mediaType);    return response;  }


阅读全文
0 0
原创粉丝点击