希望长大对我而言,是可以做更多想做的事,而不是被迫做更多不想做的事...... 首页 spring cloud服务间调用之feign 丁D 学无止境 2020-05-13 23:10 50898已阅读 spring cloud feign 摘要本文介绍使用feign进行服务之间的调用。 ### 概述 在微服务架构盛行的年代,我们将一个大型的系统,拆解成各个服务,要完成一个业务逻辑,就可能需要,调用不同服务。比如订单服务调用会员服务。当然我们可以使用JDK自带的URLConnection,或者Apache的Http Client来调用,但是最为优雅的使用feign。 Feign可以使我们调用远程服务跟调用本地方法一样,完全感知不到这是调用远程方法,更感知不到这个一个http请求。 Feign结合了Ribbon的负载均衡 Feign结合了Hystrix可以实现熔断,降级 Feign可以进行GZIP压缩。 ### Feign工作原理 程序启动的时候会自动FeignClients的注解的类,并注入spring ioc容器中,当定义的feign接口的方法被调用时,会使用JDK的动态代理,生成RequestTemplate,当生成代理时,会为每个接口方法建立一个RequestTemplate对象,这个对象封装了Http请求需要的信息,比如参数,请求方法等。 RequestTemplate生成request对象,将request交给client处理,这里的client默认是JDK原生的URLConnection、Apache的Http Client,当然可以是OKhttp,之后client会被封装成LoadBalanceClient,这个类结合ribbon提供负载均衡发起服务之间的调用。 ### Feign入门案例 1.引入依赖 ``` org.springframework.cloud spring-cloud-starter-openfeign ``` 2.启动类通过注解开启 ``` /** * 會員服務 * */ @SpringBootApplication(scanBasePackages = { "com.ding" }) @EnableDiscoveryClient @EnableFeignClients public class MemberApplication { public static void main(String[] args) { SpringApplication.run(MemberApplication.class, args); } } ``` 其中@EnableFeignClients注解表示当项目启动的时候,会自动扫描@FeignClient的类,进行处理 3.编写接口 ``` package com.ding.member.api; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import com.ding.core.Result; import com.ding.member.dto.MemberDto; import com.ding.member.hystrix.MemberQueryFeignHystrix; @FeignClient(value ="cloud-member",fallback=MemberQueryFeignHystrix.class) public interface MemberQueryFeignApi { @RequestMapping(value = "/memberQueryFeign/getMemberById",method=RequestMethod.GET) Result getMemberById(@RequestParam(value = "memberId")Long memberId); } ``` ### feign超时 https://www.136.la/jingpin/show-210022.html feign超时分为两种 1、ribbon超时 2、hystrix超时,高版本默认是关闭hystrix **ribbon超时** ![](/upload/微信图片_20200514100134.png) **hystrix超时** ![](/upload/微信图片_20200514100140.png) >注意:Hystrix和Ribbon的超时时间,较小的值生效,Hystrix超时时间要设置比Ribbon大,不然熔断失效。 ribbon 总超时时间(conn_time+read_time)(MaxAutoRetries + 1) (MaxAutoRetriesServer + 1) ### Feign默认clinet的替换 feign默认使用JDK原生的URLConnection发送Http请求,没有连接池。但是对每个地址保持一个长连接。我们可以使用Apache的HttpClient来替换,通过设置连接池,超时时间等来优化 **使用apache的httpclient来替换** **1、引入依赖** ![](/upload/微信图片_20200514101313.png) **2、修改配置文件** ![](/upload/微信图片_20200514102042.png) **使用okhttp来替换** **1、引入依赖** ![](/upload/1589422436833_A7F3F7C4-F803-4b2b-95C6-9C7487F1FA35.png) **2、修改配置文件** ![](/upload/1589422449043_9F2EDEF7-F541-4f2b-ADC7-7C2936C80956.png) ### 使用post和get传递实体参数 在实际开发中我们经常将多个参数封装成一个POJO,用于参数的接受 ,在spring mvc中get请求是可以直接绑定POJO的,但是在feign中get请求是不能这样弄的。。post请求可以 addUser(User user) 解决方案 1.将参数拆成一个个独立的属性,放在方法参数列表中 addUser(Integer age,String name) 2.将参数变成一个Map进行传递和接收 3.使用get请求传递@RequestBody,但是这个方式违反了restful规范 addUser(@RequestBody User user) 4.使用feign的拦截器 通过实现feign的RequestInterceptor的apply方法,统一拦截使用get方法并且使用body传递参数 ![](/upload/1589425569222_734F40A0-0B55-4284-A854-D0FB280D11A7.png) >注意 这里一定要注意,再定义各参数绑定时,@RequestParam、@RequestHeader等可以指定参数名称的主角,它们的value千万不能少。在Spring MVC程序中,这些注解会根据参数名来作为默认值,但是在Feign中绑定参数必须通过value属性来指明具体的参数名,不然会抛出==IllegalStateException==异常,value属性不能为空。addUser(@RequestParam String name)会报错要addUser(@RequestParam("name") String name)或者addUser(@RequestParam(value="name") String name) ### 第一次请求失败 当feign结合了Ribbon和Hystrix之后,可能会出现首次调用超时失败。原因:hystrix默认超时时间为1秒,如果这个时间之内没有响应,就会进入fallback方法,进行熔断。。而spring的懒加载机制,bean的加载和装配都要再使用的使用才执行,所以feign首次请求会比较慢,从而出现这个问题。 方案:延长熔断时间 ``` hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000 ``` ### 全局配置 全局配置的方法非常简单,我们可以直接使用ribbon.key=value的方式来设置ribbon的各项默认参数。如下: ``` #以下配置全局有效 ribbon.eureka.enabled=true #建立连接超时时间,原1000 ribbon.ConnectTimeout=60000 #请求处理的超时时间,5分钟 ribbon.ReadTimeout=60000 #所有操作都重试 ribbon.OkToRetryOnAllOperations=true #重试发生,更换节点数最大值 ribbon.MaxAutoRetriesNextServer=10 #单个节点重试最大值 ribbon.MaxAutoRetries=1 ``` 很多情况下,我们需要个性化设置,比如设置每个服务有不同的配置client.ribbon.key=value ``` #以下配置对服务cloud-member有效 cloud-member.ribbon.eureka.enabled=true #建立连接超时时间,原1000 cloud-member.ribbon.ConnectTimeout=60000 #请求处理的超时时间,5分钟 cloud-member.ribbon.ReadTimeout=60000 #所有操作都重试 cloud-member.ribbon.OkToRetryOnAllOperations=true #重试发生,更换节点数最大值 cloud-member.ribbon.MaxAutoRetriesNextServer=10 #单个节点重试最大值 cloud-member.ribbon.MaxAutoRetries=1 ``` ### 传递token 很多情况下,我们会发现当我们外部请求,经过网关--到达服务A(token可以取到)--到达服务B(token丢失)这样会导致我们token验证失败,我们可以使用拦截器来处理 ![](/upload/1589436692522_BBA247BA-6FD7-4421-BE61-5C1A3214E55A.png) ### 开启feign调用日志 我们可以为每个feign client开启日志。 可以在application.properties文件中使用logging.level.FeignClient的参数配置格式来开启指定Feign客户端的DEBUG日志 ``` @FeignClient(value ="cloud-member",fallback=MemberQueryFeignHystrix.class) public interface MemberQueryFeignApi { @RequestMapping(value = "/memberQueryFeign/getMemberById",method=RequestMethod.GET) Result getMemberById(@RequestParam(value = "memberId")Long memberId); } ``` 如上面 我们可以在配置文件 ``` logging.level.com.ding.member.api.MemberQueryFeignApi=DEBUG ``` 只是添加了如上配置,还无法实现对DEBUG日志的输出。这时由于Feign客户端默认对Logger.Level对象定义为NONE级别,该界别不会记录任何Feign调用过程中对信息,所以我们需要调整它对级别 ``` @EnableEurekaClient @SpringBootApplication @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } } ``` >对于Feign的Logger级别主要有下面4类,可根据实际需要进行调整使用。 - **NONE**:不记录任何信息。 - **BASIC**:仅记录请求方法、URL以及响应状态码和执行时间。 - **HEADERS**:出了记录BASIC级别的信息之外,还会记录请求和响应的头信息。 - **FULL**:记录所有请求与响应的细节,包括头信息、请求体、元数据等。 参考 《重新定义spring cloud》 [Spring Cloud Feign使用详解](https://www.jianshu.com/p/59295c91dde7 "Spring Cloud Feign使用详解") 很赞哦! (3) 上一篇:spring cloud网关之zuul 下一篇:spring cloud注册中心之Eureka 目录 点击排行 Elasticsearch6.3.2之x-pack redis哨兵 2019-07-09 22:05 Redis+Twemproxy+HAProxy+Keepalived 2019-07-12 17:20 GC优化策略和相关实践案例 2019-10-10 10:54 JVM垃圾回收器 2019-10-10 10:23 标签云 Java Spring MVC Mybatis Ansible Elasticsearch Redis Hive Docker Kubernetes RocketMQ Jenkins Nginx 友情链接 郑晓博客 佛布朗斯基 凉风有信 MarkHoo's Blog 冰洛博客 南实博客 Rui | 丁D Java研发工程师 生活可以用「没办法」三个字概括。但别人的没办法是「腿长,没办法」、「长得好看,没办法」、「有才华,没办法」。而你的没办法,是真的没办法。 请作者喝咖啡