feign动态调用服务,feign 调用有规律的调用成功和失败

  feign动态调用服务,feign 调用有规律的调用成功和失败

  

目录

1、项目里定义FeignClient接口2、单个FeignClient接口开启日志3、所有FeignClient接口开启日志3.1、修改FeignConfiguration3.2,还是修改应用程序。阳明海运股份有限公司配置3.3、好的了,此时项目里4、重写FeignClient输出日志5、使用方面切面输出日志项目里使用了假装进行远程调用,有时为了问题排查,需要开启请求和响应日志

 

  下面简介一下如何开启Feign日志:

  注:本文基于

  弹簧-启动-启动-父母2。3 .4 .发布spring-cloud-starter-open feign 2。2 .3 .释放;排放;发布

  

1、项目里定义FeignClient接口

包com。举例。演示。假装;导入org。spring框架。云。打开假装。假装客户;导入org。spring框架。网络。绑定。注释。获取映射;@FeignClient(name=deom ,url=https://www.baidu.com )公共接口FeignDemo { @ get mapping(/)String test();}

 

  

2、单个FeignClient接口开启日志

在应用程序。阳明海运股份有限公司里指定假装接口日志级别为调试,类型为完整:

 

  注:com。举例。演示。假装。假装演示就是上面定义的FeignClient接口

  日志:级别: com。举例。演示。假装。feigndemo :调试#下面的配置,也可以写代码代替# @Bean#公共记录器Level level() {返回记录器。水平。满;}假装:客户端:配置:默认:记录器级别:完全正常了,重启项目,调用FeignDemo.test()方法后,会输出如下日志:

  2020-10-13 11:46:24.161调试20824 - [nio-8080-exec-4]com。举例。演示。FeignDemo :[FeignDemo #测试]-获取https://www.baidu.com调试20824-[nio-8080-执行

  .demo.feign.FeignDemo : [FeignDemo#test]2020-10-13 11:46:24.257 DEBUG 20824 --- [nio-8080-exec-4] com.example.demo.feign.FeignDemo : [FeignDemo#test] <!DOCTYPE html><!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>'); </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>

  

 

  

3、所有FeignClient接口 开启日志

上面的方法,只能开启单个FeignClient接口,如果项目里有10个接口,那么要在yml里配置10项,而且以后添加新的FeignClient,还要记得去修改yml配置,太麻烦。

 

  所以,下面是开启所有FeignClient接口日志的配置:

  

 

  

3.1、修改FeignConfiguration

自定义feign.Logger,如下:

 

  

package com.example.demo.cacheDemo;import feign.slf4j.Slf4jLogger;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class FeignConfiguration { @Bean public feign.Logger logger() { return new Slf4jLogger(); }}

 

  

3.2、还是修改 application.yml 配置

logging: level: # 删除具体的FeignClient接口配置,只保留这一个就好了 feign.Logger: debug# 也可以写代码代替# @Bean# public Logger.Level level() { return Logger.Level.FULL; }feign: client: config: default: loggerLevel: full

 

  

3.3、OK了,此时项目里

不管新增多少个 FeignClient,都会输出日志。

 

  

 

  

4、重写FeignClient输出日志

根据上面输出的日志,可以看到是多条INFO日志,在并发时,很有可能会互相干扰,而且格式也无法调整。

 

  我们知道,Feign默认情况下,是使用 feign.Client.Default 发起http请求;

  我们可以重写Client,并注入Bean来替换掉 feign.Client.Default,从而实现日志记录,当然也可以做其它任意事情了,比如添加Header。下面是注入Bean的代码:

  

// 默认不注入,如果yml配置里有 logging.level.beinet.cn.demostudy.MyClient 才注入@Bean@ConditionalOnProperty("logging.level.beinet.cn.demostudy.MyClient")MyClient getClient() throws NoSuchAlgorithmException, KeyManagementException { // 忽略SSL校验 SSLContext ctx = SSLContext.getInstance("SSL"); X509TrustManager tm = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[]{tm}, null); return new MyClient(ctx.getSocketFactory(), (hostname, sslSession) -> true);}

下面是重写的Client完整代码:

 

  

package beinet.cn.demostudy;import feign.Client;import feign.Request;import feign.Response;import lombok.extern.slf4j.Slf4j;import org.springframework.util.StreamUtils;import javax.net.ssl.HostnameVerifier;import javax.net.ssl.SSLSocketFactory;import java.io.*;import java.util.Collection;import java.util.Map;@Slf4jpublic class MyClient extends Client.Default { public MyClient(SSLSocketFactory socketFactory, HostnameVerifier hostnameVerifier) { super(socketFactory, hostnameVerifier); } @Override public Response execute(Request request, Request.Options options) throws IOException { StringBuilder sb = new StringBuilder("[log started]rn"); sb.append(request.httpMethod()).append(" ").append(request.url()).append("rn"); CombineHeaders(sb, request.headers()); // 请求Header CombineBody(sb, request.body()); long costTime = -1; Exception exception = null; BufferingFeignClientResponse response = null; long begin = System.currentTimeMillis(); try { response = new BufferingFeignClientResponse(super.execute(request, options)); costTime = (System.currentTimeMillis() - begin); } catch (Exception exp) { costTime = (System.currentTimeMillis() - begin); exception = exp; throw exp; } finally { sb.append("rnResponse cost time(ms): ").append(String.valueOf(costTime)); if (response != null) sb.append(" status: ").append(response.status()); sb.append("rn"); if (response != null) { CombineHeaders(sb, response.headers()); // 响应Header sb.append("Body:rn").append(response.body()).append("rn"); } if (exception != null) { sb.append("Exception:rn ").append(exception.getMessage()).append("rn"); } sb.append("rn[log ended]"); log.debug(sb.toString()); } Response ret = response.getResponse().toBuilder() .body(response.getBody(), response.getResponse().body().length()).build(); response.close(); return ret; } private static void CombineHeaders(StringBuilder sb, Map<String, Collection<String>> headers) { if (headers != null && !headers.isEmpty()) { sb.append("Headers:rn"); for (Map.Entry<String, Collection<String>> ob : headers.entrySet()) { for (String val : ob.getValue()) { sb.append(" ").append(ob.getKey()).append(": ").append(val).append("rn"); } } } } private static void CombineBody(StringBuilder sb, byte[] body) { if (body == null body.length <= 0) return; sb.append("Body:rn").append(new String(body)).append("rn"); } static final class BufferingFeignClientResponse implements Closeable { private Response response; private byte[] body; private BufferingFeignClientResponse(Response response) { this.response = response; } private Response getResponse() { return this.response; } private int status() { return this.response.status(); } private Map<String, Collection<String>> headers() { return this.response.headers(); } private String body() throws IOException { StringBuilder sb = new StringBuilder(); try (InputStreamReader reader = new InputStreamReader(getBody())) { char[] tmp = new char[1024]; int len; while ((len = reader.read(tmp, 0, tmp.length)) != -1) { sb.append(new String(tmp, 0, len)); } } return sb.toString(); } private InputStream getBody() throws IOException { if (this.body == null) { this.body = StreamUtils.copyToByteArray(this.response.body().asInputStream()); } return new ByteArrayInputStream(this.body); } @Override public void close() { this.response.close(); } }}

输出日志示例:

 

  

2020-10-15 16:48:26.081 DEBUG 15664 --- [ main] beinet.cn.demostudy.MyClient : [log started]POST https://www.baidu.com?flg=3Headers: Content-Length: 14 Content-Type: text/plain;charset=UTF-8Body:abcde我是ddd

 

  Response cost time(ms): 207 status: 200Headers: content-length: 2443 content-type: text/html date: Thu, 15 Oct 2020 08:48:27 GMT server: bfeBody:<!DOCTYPE html>百度的html</html>

  

 

  

5、使用Aspect切面输出日志

这个不推荐,因为它无法打印出具体的url、header等数据,大家可以参考:

 

  不需要yml配置,直接在项目里添加如下代码即可:

  

package com.example.demo.feign;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.Data;import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;@Aspect@Component@Slf4jpublic class FeignAspect { // 这个也行 @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)") // 参考 https://github.com/spring-cloud/spring-cloud-openfeign/issues/322 @Pointcut("@within(org.springframework.cloud.openfeign.FeignClient)") public void feignClientPointcut() { } @Around("feignClientPointcut()") public Object feignAround(ProceedingJoinPoint joinPoint) throws Throwable { return logAround(joinPoint); } private static ObjectMapper mapper = new ObjectMapper(); private Object logAround(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); Object result = null; Exception exception = null; try { result = point.proceed(); } catch (Exception exp) { exception = exp; } long time = System.currentTimeMillis() - beginTime; saveLog(point, result, exception, time); if (exception != null) { throw exception; } return result; } private static void saveLog(ProceedingJoinPoint joinPoint, Object result, Exception exception, long time) { Dto dto = new Dto(); dto.setCostTime(time); try { if (exception != null) { dto.setExp(exception.toString()); } if (result != null) { dto.setResult(serial(result)); } MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //请求的 类名、方法名 String className = joinPoint.getTarget().getClass().getName(); String signName = signature.getDeclaringTypeName(); if (!signName.equalsIgnoreCase(className)) signName += "" + className; dto.setClas(signName); String methodName = signature.getName(); dto.setMethod(methodName); //请求的参数 Object[] args = joinPoint.getArgs(); if (args != null && args.length > 0) { dto.setPara(serial(args)); } } catch (Exception e) { dto.setExp(e.toString()); } if (exception != null) { log.warn(dto.toString()); } else { log.info(dto.toString()); } } private static String serial(Object obj) { try { return mapper.writeValueAsString(obj); } catch (Exception ex) { return obj.toString(); } } @Data private static class Dto { /** * 调用类名 */ private String clas; /** * 调用方法名 */ private String method; /** * 调用的参数 */ private String para; /** * 方法返回结果 */ private String result; /** * 执行时长,毫秒 */ private long costTime; /** * 备注 */ private String remark; /** * 出现的异常 */ private String exp; }}

OK,输出的日志如下:

 

  

2020-10-13 14:24:48.321 INFO 21304 --- [nio-8080-exec-3] com.example.demo.feign.FeignAspect : FeignAspect.Dto(clas=com.example.demo.feign.FeignDemocom.sun.proxy.$Proxy72, method=test, para=null, result="<!DOCTYPE html>rn<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');rn </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>

 

  

注:Feign官方文档:

 

  https://cloud.spring.io/spring-cloud-openfeign/reference/html/

  以上为个人经验,希望能给大家一个参考,也希望大家多多支持盛行IT。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: