如何配合RestTemplate优雅的记录请求以及返回的信息

6 篇文章 0 订阅

现如今比较火的微服务架构,SpringCloud顺势成为了主流框架,当然SpringCloud并不是一个框架,而是一个框架的集合。不管是否为微服务,难免会有程序之间的调用,当然zipkin可以帮助收集时间数据,解决在微服务架构下的延迟问题,如何详细记录请求以及返回的信息变得比较重要。

RestTemplate 对rest复杂请求封装简单的调用方法,默认JDK facilities。当然你也可以 通过setRequestFactory属性切换到不同的HTTP源,比如Apache HttpComponents、Netty和OkHttp 具体可以看ClientHttpRequestFactory 实现。当然本质上做是封装的匣子,使用者方便很多。

以上均是本章节的废话,转入正题如何优雅的记录请求以及返回信息。当然考虑一个问题得有切入点,知道@LoadBalanced注解的当然很容易理解

Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.

其实他也就是个Interceptor 默认LoadBalancerInterceptor详见LoadBalancerAutoConfiguration类,它可以做到地址的‘偷梁换柱’,记录请求信息也就没有那么困难。新加一个Interceptor就可以解决,当然这不怎么太完美,众所周知http输入输出流只能读取一次的问题,当然spring比较完美的是做了处理。详看 BufferingClientHttpRequestFactory#createRequest 方法BufferingClientHttpRequestWrapper#executeInternal,提供对输入/输出流的缓冲。ok前提了解工作已备齐,撸码开始。
LogClientHttpRequestInterceptor.java


/**
* restTemplate log interceptor
*
* @author liweigao
* @date 2019/7/9 下午2:06
*/
@Slf4j(topic = "outgoing")
public class LogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

   @Override
   public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

       StopWatch stopWatch = new StopWatch();
       stopWatch.start();
       ClientHttpResponse response = execution.execute(request, body);

       stopWatch.stop();
       StringBuilder resBody = new StringBuilder();
       try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(),
               Charset.forName("UTF-8")))) {
           String line = bufferedReader.readLine();
           while (line != null) {
               resBody.append(line);
               line = bufferedReader.readLine();
           }
       }
        //当然图片、文件一类的就可以省了,打出日志没啥用处,此处的业务逻辑随意撸了,比如header头信息类似于  Accept 、Accept-Encoding 、Accept-Language、Connection 等等
       if (request.getHeaders().getContentType() != null && request.getHeaders().getContentType().includes(MediaType.MULTIPART_FORM_DATA)) {
           body = new byte[]{};
       }

       log.info(JSON.toJSONString(RestLog.builder().costTime(stopWatch.getLastTaskTimeMillis()).headers(request.getHeaders()).method(request.getMethodValue())
               .reqUrl(request.getURI().toString()).reqBody(new String(body, Charset.forName("UTF-8"))).resBody(resBody.toString()).resStatus(response.getRawStatusCode()).build()));
       return response;
   }

   @Data
   @Builder
   @SuppressWarnings("rawtypes")
   private static class RestLog {
       private String reqUrl;
       private String method;
       private HttpHeaders headers;
       private String reqBody;
       private String resBody;
       private long costTime;
       private int resStatus;
   }
}

@Bean
   public RestTemplate restTemplate(ClientHttpRequestFactory httpRequestFactory) {

       RestTemplate restTemplate = new RestTemplate();
       /**
        * StringHttpMessageConverter 默认使用ISO-8859-1编码,此处修改为UTF-8
        */
       List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
       Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
       while (iterator.hasNext()) {
           HttpMessageConverter<?> converter = iterator.next();
           if (converter instanceof StringHttpMessageConverter) {
               ((StringHttpMessageConverter) converter).setDefaultCharset(Charset.forName("UTF-8"));
           }
       }

       //Interceptors 添加写的 Interceptors
       restTemplate.setInterceptors(Lists.newArrayList(
               new LogClientHttpRequestInterceptor()));
       //BufferingClientHttpRequestFactory  此处替换为BufferingClientHttpRequestFactory
       restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(httpRequestFactory));
       return restTemplate;
   }
   

完成搞定,当然还可以扩展
扩展1 上面指定了@Slf4j(topic = "outgoing") 可以配合logback 输出到指定的文件,收集到es中做数据监控用,毕竟是符合规则的json格式,当然接口间调用媒体类型都是application/json格式,response对象也可以通过判断contentType进行格式化输出标准的json格式,出去tostring带来的转移符。
扩展2 应用于Interceptors 我们还可以添加相应的请求头信息,方便日志追踪,附赠

ClientHttpRequestInterceptor.java

/**
* requestId interceptor
*
* @author liweigao
* @date 2019/7/9 下午2:52
*/
public class RequestIdInterceptor implements ClientHttpRequestInterceptor {

   private static final String REQUEST_ID = "X-Request-Id";

   @Override
   public ClientHttpResponse intercept(HttpRequest request, byte[] body,
                                       ClientHttpRequestExecution execution) throws IOException {
       if (CollectionUtils.isEmpty(request.getHeaders().get(REQUEST_ID))) {
           request.getHeaders().set(REQUEST_ID, UUID.randomUUID().toString());
       }

       return execution.execute(request, body);
   }

}

初始化restTemplate对象时restTemplate.setInterceptors(Lists.newArrayList(new RequestIdInterceptor(), new LogClientHttpRequestInterceptor())); 即可。
那么问题来了,很多个Interceptors 执行的顺序怎么保证,如果你能想到这个问题,那么你真是个机灵鬼。话不多说,详看InterceptingHttpAccessor.java#setInterceptors

当然spring也有自己的详看HttpHeaderInterceptor 当然还是自己撸一把比较爽吧。

/**
  * Set the request interceptors that this accessor should use.
  * <p>The interceptors will get sorted according to their order
  * once the {@link ClientHttpRequestFactory} will be built.
  * @see #getRequestFactory()
  * @see AnnotationAwareOrderComparator
  */
 public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
 	// Take getInterceptors() List as-is when passed in here
 	if (this.interceptors != interceptors) {
 		this.interceptors.clear();
 		this.interceptors.addAll(interceptors);
 		AnnotationAwareOrderComparator.sort(this.interceptors);
 	}
 }
好吧,只能说spring是真鸡儿机智。
如果本片对你有帮助,点不点赞没关系,方正不费事,评论不评论也没关系,方正也不费事。大神留步欢迎留下脚印,欢迎指导教育,谢谢~
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据引用和引用,RestTemplate是一个用于发送HTTP请求的类库。它封装了复杂请求的调用方法,并默认使用JDK的HTTP库进行请求。可以通过设置setRequestFactory属性来切换不同的HTTP源,比如Apache HttpComponents、Netty和OkHttp。因此,通过RestTemplate可以实现对Elasticsearch(ES)进行请求。 然而,引用中没有提到具体的返回状态码409。根据HTTP规范,状态码409表示冲突。这可能意味着在请求中发生了冲突,例如在更新文档时发生了版本冲突。要解决这个问题,可以尝试使用ES的乐观并发控制机制来确保更新的操作不会与其他操作冲突。具体的解决方法可以根据具体的业务需求和ES的版本来确定。 总结来说,如果使用RestTemplate请求ES返回409状态码,表示在请求中发生了冲突。可以考虑使用ES的乐观并发控制机制来解决该问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [需要用户名、密码认证的 RestTemplate 请求](https://blog.csdn.net/u011731053/article/details/112261934)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [如何配合RestTemplate优雅记录请求以及返回信息](https://blog.csdn.net/SoberChina/article/details/95215974)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值