1. 客户端注解问题
spring cloud openFeign 的客户端 ,即调用方,在启动类中一定要加上 @EnableFeignClients 这个注解,service接口上的@service注解和@FeignClient("微服务名") 都写在一起,如果不加,会报@service 没初始化这个service接口。
2. openFeign的http连接优化选择
openFeign 默认用的是jdk的httpUrlConnection,没有连接池,http连接没复用,性能不太好,我们可以集成 httpClient 或者 okHttp,二选一,不用指定版本号,他会自动根据你的spring cloud 的openfeign 解析出来。
io.github.openfeign
feign-okhttp
或者
io.github.openfeign
feign-httpclient
3.关于记录feign调用日志,可以配置如下:
@Configuration
public class FeignLogConfig {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.FULL;
}
}
4. 关于微服务传参
公共bean
@Data
public class Trans implements Serializable {
private Integer id;
private Date date;
private LocalDateTime ldt;
private String name;
private Double money;
private InnerTrans innerTrans;
}
@Data
public class InnerTrans implements Serializable {
private String code;
private Integer age;
}
调用方
@Service
@FeignClient("provider")
public interface TestService {
@GetMapping("/test/getWithInt")
Integer getWithInt(@RequestParam("i") Integer i);
@GetMapping("/test/getWithDouble")
Double getWithDouble(@RequestParam("d") Double d);
@GetMapping("/test/getWithString")
String getWithString(@RequestParam("str") String s);
@GetMapping("/test/getWithPvInt/{id}")
Integer getWithPvInt(@PathVariable("id") Integer id);
@GetMapping("/test/getWithPvStr/{code}")
String getWithPvStr(@PathVariable("code") String code);
@GetMapping("/test/getWithMap")
//Map getWithMap(@RequestParam("m") Map m);
Map getWithMap(@SpringQueryMap Map m);
//复杂类型接收失败(bean 嵌 bean)
@GetMapping("/test/getWithBean")
Trans getWithBean(@SpringQueryMap Trans t);
@GetMapping("/test/getWithSimpleBean")
InnerTrans getWithSimpleBean(@SpringQueryMap InnerTrans t);
//-------------------------------**- get end -**-------------------------------------
//--------------------------------- post start -------------------------------------
@PostMapping("/test/postWithInt")
Integer postWithInt(@RequestParam("i") Integer i);
@PostMapping("/test/postWithDouble")
Double postWithDouble(@RequestParam("d") Double d);
@PostMapping("/test/postWithString")
String postWithString(@RequestParam("s") String s);
@PostMapping("/test/postWithMap")
Map postWithMap(Map m);
@PostMapping("/test/postWithList")
List postWithList(List l);
@PostMapping("/test/postWithBean")
Trans postWithBean(Trans t);
@PostMapping("/test/postWithSimpleBean")
InnerTrans postWithSimpleBean(InnerTrans t);
}
被调用方:
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/getWithInt")
public Integer getWithInt(@RequestParam Integer i) {
return i + 10;
}
@GetMapping("/getWithDouble")
public Double getWithDouble(@RequestParam Double d) {
return d + 10;
}
@GetMapping("/getWithString")
public String getWithString(@RequestParam String str) {
return "provider-a test getWithString : " + str + " return";
}
@GetMapping("/getWithPvInt/{id}")
public String getWithPvInt(@PathVariable(name = "id") Integer id) {
return "provider-a test getWithPvInt : " + id + " return";
}
@GetMapping("/getWithPvStr/{code}")
public String getWithPvStr(@PathVariable(name = "code") String code) {
return "provider-a test getWithPvStr : " + code + " return";
}
@GetMapping("/getWithMap")
public Map getWithMap(@RequestParam Map m) {
System.out.println(m);
return m;
}
@GetMapping("/getWithBean")
public Trans getWithBean(Trans t) {
System.out.println(t.toString());
t.setName("provider-a test getBean return");
return t;
}
@GetMapping("/getWithSimpleBean")
public InnerTrans getWithSimpleBean(InnerTrans t) {
System.out.println(t.toString());
t.setCode("provider-a test getBean return");
return t;
}
@GetMapping("/getString")
public String getString() {
return "provider-a test getString";
}
@GetMapping("/getBean")
public Trans getBean() {
Trans trans = new Trans();
trans.setDate(new Date());
trans.setLdt(LocalDateTime.now().minusYears(1L));
trans.setId(99);
trans.setMoney(99.99);
trans.setName("provider-a test getBean");
InnerTrans innerTrans = new InnerTrans();
innerTrans.setAge(88);
innerTrans.setCode("inner");
trans.setInnerTrans(innerTrans);
return trans;
}
//---------------- post -----------------
@PostMapping("/postWithInt")
public Integer postWithInt(@RequestParam Integer i){
return i + 20;
}
@PostMapping("/postWithDouble")
public Double postWithDouble(@RequestParam Double d){
return d + 4;
}
@PostMapping("/postWithString")
public String postWithString(@RequestParam String s){
return "test postWithString : provider-a receive " + s + " and return";
}
@PostMapping("/postWithMap")
public Map postWithMap(@RequestBody Map m){
System.out.println(m);
return m;
}
@PostMapping("/postWithList")
public List postWithList(@RequestBody List l){
System.out.println(Arrays.toString(l.toArray()));
return l;
}
@PostMapping("/postWithBean")
public Trans postWithBean(@RequestBody Trans t){
System.out.println(t.toString());
return t;
}
@PostMapping("/postWithSimpleBean")
public InnerTrans postWithSimpleBean(@RequestBody InnerTrans t){
System.out.println(t.toString());
return t;
}
经过测试,得到以下结论:
对于GET
参数是基本的类型(integer、string这些),调用方 必须加上@RequestParam,而且还要在@RequestParam那加上被 调用方 端参数的别名,如@RequestParam("xxx") ,@PathVariable 依然是如此,否则应用也报错启动不了,而 被调用方 则可加可不加,为保持一致,还是加上好点。
参数是map,调用方 可以使用@RequestParam 或 @SpringQueryMap,被调用方则用 @RequestParam,必须用,如springmvc那样,不加的话直接访问 被调用方 也是报错的。
参数是bean,复合的bean会有问题(即bean里面有别的自定义bean参数),简单类型参数的bean就没问题。因为参数都拼接在后面,如
http://provider/test/getWithBean?date=2021-10-22 23:14:04&money=99.99&name=consumer-a test getWithBean&innerTrans=InnerTrans(code=inner, age=88, favor=null, time=2019-10-22T23:14:04.828, date=Fri Oct 22 23:14:04 CST 2021)&id=99&ldt=2020-10-22 23:14:04,会报错。
错误如下:
2021-10-22 11:51:19.131 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean] ---> GET http://provider/test/getWithBean?date=Fri%20Oct%2022%2011%3A51%3A19%20CST%202021&money=99.99&name=consumer-a%20test%20getWithBean&innerTrans=InnerTrans%28code%3Dinner%2C%20age%3D88%29&id=99&ldt=2020-10-22T11%3A51%3A19.128 HTTP/1.1
2021-10-22 11:51:19.131 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean] ---> END HTTP (0-byte body)
2021-10-22 11:51:19.142 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean] <--- HTTP/1.1 400 (10ms)
2021-10-22 11:51:19.142 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean] connection: close
2021-10-22 11:51:19.142 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean] content-type: application/json
2021-10-22 11:51:19.142 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean] date: Fri, 22 Oct 2021 03:51:19 GMT
2021-10-22 11:51:19.142 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean] transfer-encoding: chunked
2021-10-22 11:51:19.143 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean]
2021-10-22 11:51:19.143 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean] {"timestamp":"2021-10-22 11:51:19","status":400,"error":"Bad Request","message":"","path":"/test/getWithBean"}
2021-10-22 11:51:19.143 DEBUG 11640 --- [nio-8000-exec-8] com.orion.service.TestService : [TestService#getWithBean] <--- END HTTP (110-byte body)
2021-10-22 11:51:19.145 ERROR 11640 --- [nio-8000-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.FeignException$BadRequest: [400] during [GET] to [http://provider/test/getWithBean?date=Fri%20Oct%2022%2011%3A51%3A19%20CST%202021&money=99.99&name=consumer-a%20test%20getWithBean&innerTrans=InnerTrans%28code%3Dinner%2C%20age%3D88%29&id=99&ldt=2020-10-22T11%3A51%3A19.128] [TestService#getWithBean(Trans)]: [{"timestamp":"2021-10-22 11:51:19","status":400,"error":"Bad Request","message":"","path":"/test/getWithBean"}]] with root cause
feign.FeignException$BadRequest: [400] during [GET] to [http://provider/test/getWithBean?date=Fri%20Oct%2022%2011%3A51%3A19%20CST%202021&money=99.99&name=consumer-a%20test%20getWithBean&innerTrans=InnerTrans%28code%3Dinner%2C%20age%3D88%29&id=99&ldt=2020-10-22T11%3A51%3A19.128] [TestService#getWithBean(Trans)]: [{"timestamp":"2021-10-22 11:51:19","status":400,"error":"Bad Request","message":"","path":"/test/getWithBean"}]
at feign.FeignException.clientErrorStatus(FeignException.java:195) ~[feign-core-10.10.1.jar:na]
at feign.FeignException.errorStatus(FeignException.java:177) ~[feign-core-10.10.1.jar:na]
at feign.FeignException.errorStatus(FeignException.java:169) ~[feign-core-10.10.1.jar:na]
at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:92) ~[feign-core-10.10.1.jar:na]
at feign.AsyncResponseHandler.handleResponse(AsyncResponseHandler.java:96) ~[feign-core-10.10.1.jar:na]
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:138) ~[feign-core-10.10.1.jar:na]
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:89) ~[feign-core-10.10.1.jar:na]
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100) ~[feign-core-10.10.1.jar:na]
at com.sun.proxy.$Proxy111.getWithBean(Unknown Source) ~[na:na]
at com.orion.controller.TestController.getWithBean(TestController.java:69) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_191]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_191]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_191]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_191]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.37.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.37.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93) ~[spring-boot-actuator-2.3.3.RELEASE.jar:2.3.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589) [tomcat-embed-core-9.0.37.jar:9.0.37]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.37.jar:9.0.37]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_191]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.37.jar:9.0.37]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191]
值得注意的是,多参数不建议用map,可读性差,如果少的话,可以用分开用多个参数来传,各个参数用 @RequestParam("xxx")注解就行,如果太多的话,建议封装成一个bean,使用@SpringQueryMap注解。
使用get方式发送,就是在url后面通过 & 把所有参数拼接起来一起传输,这个可以打开日志来观察即可。前提得配置,结论3 处。
对于post
参数是基本的类型(integer、string这些),调用方 必须加上@RequestParam,而且还要在@RequestParam那加上被 调用方 端参数的别名,如@RequestParam("xxx") ,@PathVariable 依然是如此,否则应用也报错启动不了,而 被调用方 则可加可不加,为保持一致,还是加上好点。
参数是map,list,bean嵌bean,简单参数bean,调用方 不需要加注解,被调用方则必须用 @RequestBody来接收,如果是list的话,必须指定list的泛型类型,不然报错。
注意:
get方法已经说了是在url后面拼接的,所以如果get的方式传@SpringQueryMap 的bean参数里面,如果有LocalDatetime、Date这类的属性,它们默认是用toString方法,所以Date是话使用cst这种时区,LocalDatetime有T这种时区在里面,不符合我们日常使用的yyyy-MM-dd HH:mm:ss 这种格式,必须自定义自己的QueryMapEncoder,其实现QueryMapEncoder接口,并配置上; 自定义feignBuilder会导致hystrix失效。
Post是不会有影响的
@Configuration
public class CustomFeignConfig {
@Bean
public Feign.Builder feignBuilder() {
return Feign.builder()
.queryMapEncoder(new QueryMapImproveEncoder())
.retryer(Retryer.NEVER_RETRY);
}
}
public class QueryMapImproveEncoder implements QueryMapEncoder {
private final Map, ObjectParamMetadata> classToMetadata =
new HashMap<>();
private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern(JacksonConfig.PATTERN);
@Override
public Map encode(Object object) throws EncodeException {
try {
ObjectParamMetadata metadata = getMetadata(object.getClass());
Map propertyNameToValue = new HashMap<>(16);
for (PropertyDescriptor pd : metadata.objectProperties) {
Method method = pd.getReadMethod();
Object value = method.invoke(object);
if (value != null && value != object) {
Param alias = method.getAnnotation(Param.class);
String name = alias != null ? alias.value() : pd.getName();
String handleClazzName = method.getReturnType().getName();
switch (handleClazzName){
case "java.time.LocalDateTime" :
propertyNameToValue.put(name, dtf.format((LocalDateTime) value));
break;
case "java.util.Date" :
LocalDateTime ldt = LocalDateTime.ofInstant(((Date)value).toInstant(), ZoneId.systemDefault());
propertyNameToValue.put(name, dtf.format(ldt));
break;
default:
propertyNameToValue.put(name, value);
break;
}
}
}
return propertyNameToValue;
} catch (IllegalAccessException | IntrospectionException | InvocationTargetException e) {
throw new EncodeException("Failure encoding object into query map", e);
}
}
private ObjectParamMetadata getMetadata(Class> objectType) throws IntrospectionException {
ObjectParamMetadata metadata = classToMetadata.get(objectType);
if (metadata == null) {
metadata = ObjectParamMetadata.parseObjectType(objectType);
classToMetadata.put(objectType, metadata);
}
return metadata;
}
private static class ObjectParamMetadata {
private final List objectProperties;
private ObjectParamMetadata(List objectProperties) {
this.objectProperties = Collections.unmodifiableList(objectProperties);
}
private static ObjectParamMetadata parseObjectType(Class> type)
throws IntrospectionException {
List properties = new ArrayList<>();
for (PropertyDescriptor pd : Introspector.getBeanInfo(type).getPropertyDescriptors()) {
boolean isGetterMethod = pd.getReadMethod() != null && !"class".equals(pd.getName());
if (isGetterMethod) {
properties.add(pd);
}
}
return new ObjectParamMetadata(properties);
}
}
}
参考:
SpringCloud中利用FeignClient发送请求时参数的传递和返回值
feign请求返回值反序列LocalDateTime异常记录
openfeign get请求参数dto包含LocalDateTime的处理