@RestControllerpublicclassTestController{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(TestController.class);@GetMapping("test/client")publicObjecttestClient(HttpServletRequestrequest){logger.info("coming in {}",request.getRemoteAddr());HashMap<Object,Object>map=newHashMap<>();map.put("error_code",500);map.put("msg","inner server error");try{// 线程睡眠10s,模拟超时TimeUnit.SECONDS.sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}returnmap;}}
@RestControllerpublicclassHttpClientController{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(HttpClientController.class);privatestaticfinalintTIMEOUT=3000;privatestaticfinalintRETRY_COUNT=3;@GetMapping("test")publicObjecttest(){Stringurl="http://127.0.0.1:8080/test/client";CloseableHttpClienthttpClient=HttpClientUtils.createHttpClient(TIMEOUT,newMyHttpRequestRetryHandler(RETRY_COUNT));HttpGethttpGet=newHttpGet(url);logger.info("开始请求: {}",url);try{CloseableHttpResponsehttpResponse=httpClient.execute(httpGet);intstatusCode=httpResponse.getStatusLine().getStatusCode();StringhttpResult=EntityUtils.toString(httpResponse.getEntity());if(statusCode==HttpStatus.SC_OK){logger.info("请求结果:{}",httpResult);returnhttpResult;}}catch(IOExceptione){logger.error("接口请求异常",e);}return"接口异常返回";}privatestaticclassMyHttpRequestRetryHandlerimplementsHttpRequestRetryHandler{privatefinalintretryCount;publicMyHttpRequestRetryHandler(intretryCount){this.retryCount=retryCount;}@OverridepublicbooleanretryRequest(IOExceptionexception,intexecutionCount,HttpContexthttpContext){if(executionCount>this.retryCount){logger.warn("重试次数已达上限:{}",this.retryCount);returnfalse;}// Unknown hostif(exceptioninstanceofUnknownHostException){returnfalse;}// SSL handshake exceptionif(exceptioninstanceofSSLException){returnfalse;}if(exceptioninstanceofInterruptedIOException||exceptioninstanceofNoHttpResponseException||exceptioninstanceofSocketException){logger.warn("开始请求重试");returntrue;}HttpClientContextclientContext=HttpClientContext.adapt(httpContext);HttpRequestrequest=clientContext.getRequest();// Retry if the request is considered idempotentreturn!(requestinstanceofHttpEntityEnclosingRequest);}}}
@OverridepublicbooleanretryRequest(finalIOExceptionexception,finalintexecutionCount,finalHttpContextcontext){Args.notNull(exception,"Exception parameter");Args.notNull(context,"HTTP context");// 超过了最大重试次数之后就不在重试if(executionCount>this.retryCount){returnfalse;}// 发生的异常是否是在不重试处理的异常集合中if(this.nonRetriableClasses.contains(exception.getClass())){returnfalse;}for(finalClass<?extendsIOException>rejectException:this.nonRetriableClasses){if(rejectException.isInstance(exception)){returnfalse;}}finalHttpClientContextclientContext=HttpClientContext.adapt(context);finalHttpRequestrequest=clientContext.getRequest();if(requestIsAborted(request)){returnfalse;}// 是否考虑幂等处理if(handleAsIdempotent(request)){// Retry if the request is considered idempotentreturntrue;}// 请求未完全发送 || 是否开启重试if(!clientContext.isRequestSent()||this.requestSentRetryEnabled){// Retry if the request has not been sent fully or// if it's OK to retry methods that have been sentreturntrue;}// 其他情况都不重试returnfalse;}/**
* @since 4.2
*/protectedbooleanhandleAsIdempotent(finalHttpRequestrequest){return!(requestinstanceofHttpEntityEnclosingRequest);}
@Contract(threading=ThreadingBehavior.IMMUTABLE)publicclassDefaultServiceUnavailableRetryStrategyimplementsServiceUnavailableRetryStrategy{/**
* Maximum number of allowed retries if the server responds with a HTTP code
* in our retry code list. Default value is 1.
*/privatefinalintmaxRetries;/**
* Retry interval between subsequent requests, in milliseconds. Default
* value is 1 second.
*/privatefinallongretryInterval;publicDefaultServiceUnavailableRetryStrategy(finalintmaxRetries,finalintretryInterval){super();Args.positive(maxRetries,"Max retries");Args.positive(retryInterval,"Retry interval");this.maxRetries=maxRetries;this.retryInterval=retryInterval;}publicDefaultServiceUnavailableRetryStrategy(){this(1,1000);}@OverridepublicbooleanretryRequest(finalHttpResponseresponse,finalintexecutionCount,finalHttpContextcontext){// 重试次数小于等于最大重试次数returnexecutionCount<=maxRetries&&// Http Status等于503response.getStatusLine().getStatusCode()==HttpStatus.SC_SERVICE_UNAVAILABLE;}@OverridepubliclonggetRetryInterval(){returnretryInterval;}}
publicclassMyServiceUnavailableRetryStrategyimplementsServiceUnavailableRetryStrategy{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(MyServiceUnavailableRetryStrategy.class);privatefinalintmaxRetries;privatefinallongretryInterval;publicMyServiceUnavailableRetryStrategy(finalintmaxRetries,finallongretryInterval){this.maxRetries=maxRetries;this.retryInterval=retryInterval;}publicMyServiceUnavailableRetryStrategy(){this(3,1000);}@OverridepublicbooleanretryRequest(HttpResponseresponse,intexecutionCount,HttpContextcontext){logger.info("Come in MyServiceUnavailableRetryStrategy");if(executionCount>maxRetries){logger.warn("RetryStrategy 重试次数已达:{}",maxRetries);returnfalse;}if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){try{StringresultResp=EntityUtils.toString(response.getEntity());JsonObjectjsonObject=JsonParser.parseString(resultResp).getAsJsonObject();// 假设第三方请求返回业务状态码使用error_code字段intcode=jsonObject.get("error_code").getAsInt();if(code==5000){logger.info("开始第{}重试",executionCount);returntrue;}}catch(IOExceptione){logger.error("读取结果异常");}}// Http Status 503 也重试returnresponse.getStatusLine().getStatusCode()==HttpStatus.SC_SERVICE_UNAVAILABLE;}@OverridepubliclonggetRetryInterval(){returnthis.retryInterval;}}
INFO 10604 --- [nio-8880-exec-4] c.m.b.m.controller.HttpClientController : 开始请求: http://127.0.0.1:8080/test/client
INFO 10604 --- [nio-8880-exec-4] .b.m.h.MyServiceUnavailableRetryStrategy : Come in MyServiceUnavailableRetryStrategy
INFO 10604 --- [nio-8880-exec-4] .b.m.h.MyServiceUnavailableRetryStrategy : 开始第1重试
INFO 10604 --- [nio-8880-exec-4] .b.m.h.MyServiceUnavailableRetryStrategy : Come in MyServiceUnavailableRetryStrategy
INFO 10604 --- [nio-8880-exec-4] .b.m.h.MyServiceUnavailableRetryStrategy : 开始第2重试
INFO 10604 --- [nio-8880-exec-4] .b.m.h.MyServiceUnavailableRetryStrategy : Come in MyServiceUnavailableRetryStrategy
INFO 10604 --- [nio-8880-exec-4] .b.m.h.MyServiceUnavailableRetryStrategy : 开始第3重试
INFO 10604 --- [nio-8880-exec-4] .b.m.h.MyServiceUnavailableRetryStrategy : Come in MyServiceUnavailableRetryStrategy
WARN 10604 --- [nio-8880-exec-4] .b.m.h.MyServiceUnavailableRetryStrategy : RetryStrategy 重试次数已达:3
INFO 10604 --- [nio-8880-exec-4] c.m.b.m.controller.HttpClientController : 请求结果:{"msg":"业务繁忙请重试","error_code":5000}