SpringBoot(二十):SpringBoot+Feign:实现Feign日志写入一行几种方案
@Configuration(proxyBeanMethods = false) @AutoConfigureAfter({HttpClientFeignConfiguration.class}) @AutoConfigureBefore({FeignClientsConfiguration.class}) public class FeignConfig { /** * +使用Apache Httpclient实现FeignClient客户端 * @param httpClient #HttpClientFeignConfiguration.customHttpClient() * @return ApacheHttpClient实例 */ @Bean @Primary public Client feignClient(HttpClient httpClient) { return new ApacheHttpClient(httpClient); } /** * +用Spring定义的日志系统代理feign日志系统 * @return 代理日志系统 */ @Bean @Primary public Logger logger() { return new FeignLog(this.getClass()); } /** * Feign的代理log * */ final class FeignLog extends Logger { private Log log; public FeignLog(Class<?> clazz) { log = LogFactory.getLog(clazz); } @Override protected void log(String configKey, String format, Object... args) { if (log.isInfoEnabled()) { log.info(String.format(methodTag(configKey) + format, args)); } } } }
# hystrix相关 feign: okhttp: enabled: false httpclient: #Apache的HTTP Client替换Feign原始的http client enabled: true max-connections: 20480 max-connections-per-route: 512 time-to-live: 60 time-to-live-unit: SECONDS connection-timeout: 60000 user-agent: 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0' client: # 配置文件优先级高 default-to-properties: true config: default: logger-level: FULL #BASIC connect-timeout: 60000 read-timeout: 60000 decode404: true hystrix: enabled: true compression: request: #请求和响应GZIP压缩支持 enabled: true mime-types: application/json,application/xml,text/xml min-request-size: 2048 response: enabled: true useGzipDecoder: true hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 15000 # 熔断关闭 timeout: enabled: false threadpool: default: coreSize: 40 maximumSize: 100 maxQueueSize: 100
INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] ---> POST http://api.xxx.com/api/v1/point/remove HTTP/1.1] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] Accept: application/json] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] Accept-Encoding: gzip] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] Accept-Encoding: deflate] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] authorization: hmac id="xxx", algorithm="hm-a1", headers="date source", signature="xxtR8="] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] Content-Length: 20] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] Content-Type: application/json] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] date: Mon, 15 Mar 2021 13:57:38 GMT] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] tenantid: xxxx] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] x-date: Mon, 15 Mar 2021 13:57:38 GMT] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] ] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] {"point_id":"5646"}] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] ---> END HTTP (20-byte body)] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] <--- HTTP/1.1 500 Internal Server Error (83ms)] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] connection: keep-alive] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] content-type: text/html; charset=utf-8] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] date: Mon, 15 Mar 2021 13:57:38 GMT] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] server: nginx] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] transfer-encoding: chunked] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] x-powered-by: PHP/7.0.33] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] ] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removePoint] <!DOCTYPE html> <html> <head> ... </html> ] INFO c.d.d.f.cfg.FeignConfig - [[MyFeignClient#removeTarget] <--- END HTTP (36033-byte body)]
1)定义MyClient类
自定义MyClient类
public class MyClient extends Client.Default { protected static Log logger = LogFactory.getLog("FEIGN.LOG"); public MyClient(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) { super(sslContextFactory, hostnameVerifier); } @Override public Response execute(Request request, Request.Options options) throws IOException { FeignLog feignLog = new FeignLog(); feignLog.setRequestUrl(request.url()); feignLog.setRequestMethod(request.httpMethod().name()); feignLog.setRequestHeader(new HashMap<>()); if (request.headers() != null && !request.headers().isEmpty()) { for (Map.Entry<String, Collection<String>> ob : request.headers().entrySet()) { for (String val : ob.getValue()) { feignLog.getRequestHeader().put(ob.getKey(), val); } } } if (request.body() != null && request.body().length > 0) { feignLog.setRequestBody(new String(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 { feignLog.setCosTime(costTime + "ms"); if (response != null) { feignLog.setStatus(response.status()); } if (response != null) { feignLog.setResponseHeader(new HashMap<>()); if (response.headers() != null && !response.headers().isEmpty()) { for (Map.Entry<String, Collection<String>> ob : response.headers().entrySet()) { for (String val : ob.getValue()) { feignLog.getResponseHeader().put(ob.getKey(), val); } } } if (request.body() != null && request.body().length > 0) { feignLog.setResponseBody(new String(response.body())); } } if (exception != null) { feignLog.setException(exception.getMessage()); } //logger.info(feignLog); } Response ret = response.getResponse().toBuilder().body(response.getBody(), response.getResponse().body().length()).build(); List<String> list = new ArrayList<>(); list.add(feignLog.toString()); ret.headers().getOrDefault("my-client-log", list); response.close(); return ret; } @Data static final class FeignLog { private String requestUrl;// 请求地址 private String requestMethod; // 请求发送方式:GET/POST/DELETE/PUT/HEADER/... private Map<String, String> requestHeader;// 请求header信息 private String requestBody;// 请求body private Integer status;// 请求返回状态 private String exception;// 请求返回异常信息 private Map<String, String> responseHeader;// 响应header信息 private String responseBody;// 响应body private String cosTime;// 请求到接收到响应数据耗费时长单位ms @Override public String toString() { return "request url='" + requestUrl + "\'\r\n" + "request method='" + requestMethod + "\'\r\n" + "request header=" + requestHeader + "\'\r\n" + "request body='" + requestBody + "\'\r\n" + "status=" + status + "\'\r\n" + "response header='" + responseHeader + "\'\r\n" + "response body='" + responseBody + "\'\r\n" + "cost Time='" + cosTime + "\'\r\n" + "exception='" + exception + "\'\r\n"; } } 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(); } } }
在覆盖feign.Client.Deafult#exceute(...),记录日志包含:
private String requestUrl;// 请求地址 private String requestMethod; // 请求发送方式:GET/POST/DELETE/PUT/HEADER/... private Map<String, String> requestHeader;// 请求header信息 private String requestBody;// 请求body private Integer status;// 请求返回状态 private String exception;// 请求返回异常信息 private Map<String, String> responseHeader;// 响应header信息 private String responseBody;// 响应body private String cosTime;// 请求到接收到响应数据耗费时长单位ms
@Configuration(proxyBeanMethods = false) @AutoConfigureAfter({HttpClientFeignConfiguration.class}) @AutoConfigureBefore({FeignClientsConfiguration.class}) public class FeignConfig { /** * +使用Apache Httpclient实现FeignClient客户端 * @param httpClient #HttpClientFeignConfiguration.customHttpClient() * @return ApacheHttpClient实例 */ // @Bean // @Primary // public Client feignClient(HttpClient httpClient) { // return new ApacheHttpClient(httpClient); // } // 默认不注入,如果yml配置里有 com.beyonds.phoenix.integration.domain.feign.client.MyClient 才注入 @Bean @ConditionalOnProperty("com.dx.domain.feign.client.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); } }
1)只能注入一个自定义Client类,上边feignClient也是自定义Client,因此这里注释掉了该注解;
2)注入自定义MyClient加上了@ConditionalOnProperty("com.dx.domain.feign.client.MyClient")修饰,因此需要在yml中配置了com.dx.domain.feign.client.MyClient:FULL 或者BASIC/DEBUG等才能生效,否定义MyClient bean不生效。
# hystrix相关 feign: okhttp: enabled: false httpclient: #Apache的HTTP Client替换Feign原始的http client enabled: true max-connections: 20480 max-connections-per-route: 512 time-to-live: 60 time-to-live-unit: SECONDS connection-timeout: 60000 user-agent: 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0' client: # 配置文件优先级高 default-to-properties: true config: default: logger-level: BASIC connect-timeout: 60000 read-timeout: 60000 decode404: true hystrix: enabled: true compression: request: #请求和响应GZIP压缩支持 enabled: true mime-types: application/json,application/xml,text/xml min-request-size: 2048 response: enabled: true useGzipDecoder: true com: dx: domain: feign: client: MyClient: FULL
2021-03-15 14:33:37,107 [hystrix-myFeignClient-1-14175][TraceId:] INFO FEIGN.LOG - [ request url='http://api.xxx.com/api/v1/point/remove' request method='POST' request header={authorization=hmac id="xx", algorithm="hx-a1", headers="date source", signature="xxAWzmpSM=", date=Mon, 15 Mar 2021 06:33:36 GMT, Accept=application/json, tenantid=xxxx, User-Agent=Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0, Accept-Encoding=deflate, Content-Length=20, x-date=Mon, 15 Mar 2021 06:33:36 GMT, Content-Type=application/json}' request body='{"point_id":"5646"}' status=500' response header='{date=Mon, 15 Mar 2021 06:33:37 GMT, server=nginx, transfer-encoding=chunked, x-powered-by=PHP/7.0.33, connection=keep-alive, content-type=text/html; charset=utf-8}' response body='<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>System Error</title> <meta name="robots" content="noindex,nofollow" /> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> ... </body> </html> ' cost Time='61ms' exception='null' ]
1)MyFeingClient#removePoint方法;
2)缺少了MyFeingClient#removePoint方法参数信息。
@Aspect @Component public class FeignAspect { protected static Log logger = LogFactory.getLog("FEIGN.LOG"); // 这个也行 @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) { logger.warn(dto.toString()); } else { logger.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; } }
2)配置FeignConfig和yml
都和“默认情况下Feign日志”章节配置一致
3)日志格式:
[FeignAspect.Dto( clas=com.dx.domain.feign.MyFeignClient|com.sun.proxy.$Proxy179, method=removeTarget, para=["http://api.xxx.com/","xx","Mon, 15 Mar 2021 06:40:06 GMT","Mon, 15 Mar 2021 06:40:06 GMT","hmac id=\"AKIxxxw8\", algorithm=\"h-a1\", headers=\"date source\", signature=\"xxxY=\"",{"point_id":"5646"}], result={"status":5000,"message":"Api调用失败","data":false,"meta":null}, costTime=348, remark=null, exp=null )]
缺少完整的请求地址;
缺少feign原始日志中的请求响应信息。
日志写入一行#重写feign.Logger
1)自定义FeignLogger extends feign.Logger
/** * Springboot的代理log * author: * date: 2021年3月15日 * time: 下午10:07:57 * */ final static class FeignLogger extends feign.Logger { private final Log logger; private final static ThreadLocal<MyLoggerModel> myLoggerModelThreadLocal = new ThreadLocal(); private final Boolean loggerable; public FeignLogger(Class<?> clazz) { this.logger = LogFactory.getLog(clazz); this.loggerable = logger.isInfoEnabled(); } @Override protected void logRequest(String configKey, Level logLevel, Request request) { final MyLoggerModel myLoggerModel = new MyLoggerModel(); if (request.requestTemplate() != null && request.requestTemplate().feignTarget() != null && request.requestTemplate().feignTarget().type() != null) { myLoggerModel.setFeignClass(request.requestTemplate().feignTarget().type().getName()); } else { myLoggerModel.setFeignClass(null); } myLoggerModel.setFeignMethod(configKey); if (request.requestTemplate() != null && request.requestTemplate().methodMetadata() != null && request.requestTemplate().methodMetadata().returnType() != null) { myLoggerModel.setFeignMethodReturnType(request.requestTemplate().methodMetadata().returnType().getTypeName()); } else { myLoggerModel.setFeignMethodReturnType(null); } myLoggerModel.setRequestHttpMethod(request.httpMethod() == null ? null : request.httpMethod().name()); myLoggerModel.setRequestUrl(request.url()); myLoggerModel.setRequestCharset(request.charset() == null ? null : request.charset().name()); myLoggerModel.setRequestHeader(request.headers()); myLoggerModel.setRequestBody(request.body() == null ? null : new String(request.body())); final LinkedHashMap<String, String> feignMethodParameters = new LinkedHashMap<>(); final HashMap<String, String> urlRequestKeyValueMap = new HashMap<>(); if (myLoggerModel.getRequestUrl() != null && myLoggerModel.getRequestUrl().split("\\?").length > 1) { final String[] urlSplitArr = myLoggerModel.getRequestUrl().split("\\?"); final String[] paramKeyValueArr = urlSplitArr[1].split("&"); for (String keyValue : paramKeyValueArr) { final String[] keyValueArr = keyValue.split("="); urlRequestKeyValueMap.put(keyValueArr[0], keyValueArr.length > 1 ? keyValueArr[1] : null); } } if (request.requestTemplate() != null && request.requestTemplate().methodMetadata() != null && request.requestTemplate().methodMetadata().indexToName() != null && request.requestTemplate().methodMetadata().indexToName().size() > 0) { for (Map.Entry<Integer, Collection<String>> entry : request.requestTemplate().methodMetadata().indexToName().entrySet()) { final String paramName = entry.getValue().toArray()[0].toString(); final Collection<String> paramValues = myLoggerModel.getRequestHeader().getOrDefault(paramName, null); String paramValue = null; if (paramValues != null && paramValues.size() > 0) { paramValue = paramValues.toArray()[0].toString(); } if (paramValue == null && urlRequestKeyValueMap.containsKey(paramName)) { paramValue = urlRequestKeyValueMap.get(paramName); } feignMethodParameters.put(paramName, paramValue); } } myLoggerModel.setFeignMethodParameters(feignMethodParameters); myLoggerModelThreadLocal.set(myLoggerModel); } @Override protected Response logAndRebufferResponse( String configKey, Level logLevel, Response response, long elapsedTime) throws IOException { final MyLoggerModel myLoggerModel = myLoggerModelThreadLocal.get(); myLoggerModelThreadLocal.remove(); myLoggerModel.setResponseStatus(response.status()); myLoggerModel.setResponseReason(response.reason()); myLoggerModel.setResponseHeader(response.headers()); myLoggerModel.setElapsedTime(elapsedTime); if (response.body() != null && !(response.status() == 204 || response.status() == 205)) { byte[] bodyData = Util.toByteArray(response.body().asInputStream()); if (bodyData.length > 0) { String responseBody = feign.Util.decodeOrDefault(bodyData, feign.Util.UTF_8, "Binary data"); myLoggerModel.setResponseBody(responseBody.replaceAll("\\s*|\t|\r|\n", "")); } if (this.loggerable) { this.logger.info(myLoggerModel); } return response.toBuilder().body(bodyData).build(); } if (this.loggerable) { this.logger.info(myLoggerModel); } return response; } protected IOException logIOException(String configKey, Level logLevel, IOException ioe, long elapsedTime) { final MyLoggerModel myLoggerModel = myLoggerModelThreadLocal.get(); myLoggerModelThreadLocal.remove(); myLoggerModel.setElapsedTime(elapsedTime); myLoggerModel.setIoException(ioe); if (this.loggerable) { this.logger.warn(myLoggerModel); } return ioe; } @Override protected void log(String configKey, String format, Object... args) { if (this.loggerable) { this.logger.debug(String.format(methodTag(configKey) + format, args)); } } } @Data final static class MyLoggerModel { private String feignClass;// requestTemplate#feignTarget#type#name=com.dx.domain.feign.MyFeignClient private String feignMethod; // MyFeignClient#removeTarget(URI,String,String,String,String,RemoveTargetRequest) private Map<String, String> feignMethodParameters; private String feignMethodReturnType;//request#requestTemplate#methodMetadata#returnType=com...ReturnResult<Boolean> private String requestHttpMethod;// post private String requestUrl;// http://api.xx.com/api/v1/point/remove private String requestCharset; private Map<String, Collection<String>> requestHeader; private String requestBody; private Integer responseStatus; // 500 private String responseReason; // Internal Server Error private Map<String, Collection<String>> responseHeader; private String responseBody; // private Long elapsedTime; private IOException ioException; @Override public String toString() { return "feign Class=" + feignClass + "\r\n" + "feign Method=" + feignMethod + "\r\n" + "feign Method Parameters=" + feignMethodParameters + "\r\n" + "feign Method ReturnType=" + feignMethodReturnType + "\r\n" + "request HttpMethod='" + requestHttpMethod + "\r\n" + "request Url=" + requestUrl + "\r\n" + "request Charset=" + requestCharset + "\r\n" + "request Header=" + requestHeader + "\r\n" + "request Body=" + requestBody + "\r\n" + "response Status=" + responseStatus + "\r\n" + "response Reason='" + responseReason + "\r\n" + "response Header=" + responseHeader + "\r\n" + "response Body=" + responseBody + "\r\n" + "elapsed Time=" + elapsedTime + "\r\n" + "io Exception=" + ioException + "\r\n"; } }
@Configuration(proxyBeanMethods = false) @AutoConfigureAfter({HttpClientFeignConfiguration.class}) @AutoConfigureBefore({FeignClientsConfiguration.class}) public class FeignConfig { /** * +使用Apache Httpclient实现FeignClient客户端 * @param httpClient #HttpClientFeignConfiguration.customHttpClient() * @return ApacheHttpClient实例 */ @Bean @Primary public Client feignClient(HttpClient httpClient) { return new ApacheHttpClient(httpClient); } /** * +用Spring定义的日志系统代理feign日志系统 * @return 代理日志系统 */ @Bean @Primary public Logger logger() { return new FeignLogger(this.getClass()); } ... }
3)配置yml
都和“默认情况下Feign日志”章节配置一致
4)日志格式
2021-03-16 22:00:34,744 [hystrix-machineIntegrationFeignClient-1-28356][TraceId:] INFO com.dx.cfg.FeignConfig - [ feign Class=com.dx.feign.MyClient feign Method=MyFeignClient#createOrUpdateTarget(URI,String,String,String,String,CreateOrUpdateTargetRequest) feign Method Parameters={tenantid=xx, date=Tue, 16 Mar 2021 14:00:34 GMT, x-date=Tue, 16 Mar 2021 14:00:34 GMT, authorization=hmac id="xx8", algorithm="h-a1", headers="x-date tenantId", signature="fgxxKSec="} feign Method ReturnType=com.dx...ReturnResult<java.lang.String> request HttpMethod='POST request Url=http://api.xxx.com/api/v1/point/deploy request Charset=UTF-8 request Header={Accept=[application/json], Accept-Encoding=[gzip, deflate], authorization=[hmac id="xx8", algorithm="-a1", headers="x-date tenantId", signature="fgxxSec="], Content-Length=[107], Content-Type=[application/json], date=[Tue, 16 Mar 2021 14:00:34 GMT], tenantid=[2020051800043], User-Agent=[Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0], x-date=[Tue, 16 Mar 2021 14:00:34 GMT]} request Body={"bname":null,"cy":4,"floor":3,"name":"2","serial_no":"de","site_id":"1"} response Status=200 response Reason='OK response Header={access-control-allow-credentials=[true], access-control-allow-headers=[x-token,x-uid,x-token-check,x-requested-with,content-type,Host], access-control-allow-methods=[*], access-control-allow-origin=[*], connection=[keep-alive], content-type=[application/json; charset=utf-8], date=[Tue, 16 Mar 2021 14:00:34 GMT], server=[nginx], transfer-encoding=[chunked], x-powered-by=[PHP/7.0.33]} response Body={"data":"17","status":200,"message":"success"} elapsed Time=392 io Exception=null ]
缺少调用feign后代码接口返回值,这块目前也不在需求范围内,暂时不需要实现。
参考:
基础才是编程人员应该深入研究的问题,比如:
1)List/Set/Map内部组成原理|区别
2)mysql索引存储结构&如何调优/b-tree特点、计算复杂度及影响复杂度的因素。。。
3)JVM运行组成与原理及调优
4)Java类加载器运行原理
5)Java中GC过程原理|使用的回收算法原理
6)Redis中hash一致性实现及与hash其他区别
7)Java多线程、线程池开发、管理Lock与Synchroined区别
8)Spring IOC/AOP 原理;加载过程的。。。
【+加关注】。