前文介绍了实现客户端负载均衡的Ribbon,但直接使用Ribbon的API来实现服务间的调用相对较为繁琐,服务间的调用可否像本地接口调用同样便捷、透明,更符合编程习惯呢?Feign就是用来干这事的。git
Feign是一个声明式的Web服务客户端,让服务之间的调用变得很是简单——定义带@FeignClient注解的接口,本地直接@Autowired 接口,经过调用接口的方法来实现远程服务的调用。github
支持的注解包括Feign注解与JAX-RS(Java API for RESTful Web Services)注解。web
每个Feign的客户端都包含一系列对应的组件,Spring Cloud经过FeignClientsConfiguration 为每个命名的Feign客户端建立一个组件集合,包括feign.Decoder,feign.Encoder,feign.Contract等。spring
Feign提供的默认bean实现及说明编程
Bean类型 | 默认实现类 | 说明 |
---|---|---|
Decoder | ResponseEntityDecoder | ResponseEntityDecoder封装了SpringDecoder,解码器,将服务的响应消息进行解码 |
Encoder | SpringEncoder | 编码器 |
Logger | Slf4jLogger | 日志框架 |
Contract | SpringMvcContract | 支持注解契约,使用SpringMvcContract能够对Spring MVC注解提供支持 |
Feign.Builder | HystrixFeign.Builder | 使用断路器来装饰Feign接口 |
Client | LoadBalancerFeignClient | 若是是ribbon则 LoadBalancerFeignClient, 若是是spring cloud LoadBalancer 则 FeignBlockingLoadBalancerClient,默认ribbon |
跟Ribbon相似,能够经过配置类来自定义Feign客户端,如微信
@FeignClient(name = "hello-service", configuration = CustomConfiguration.class) public interface StoreClient { //.. } public class CustomConfiguration { @Bean public Contract feignContract() { return new feign.Contract.Default(); } @Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new BasicAuthRequestInterceptor("user", "password"); } }
这样Feign客户端就包含了FeignClientsConfiguration 与CustomConfiguration 中定义的组件,而且后者会覆盖前者(即自定义配置的优先级高于默认配置)。 app
自定义配置类不须要加注解@Configuration,若是加了且被@ComponentScan扫描到,则将成为全部Feign客户端的默认配置负载均衡
一样Feign客户端也支持经过配置文件来配置框架
feign:
client:
config:
feignName:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: full
errorDecoder: com.example.SimpleErrorDecoder
retryer: com.example.SimpleRetryer
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
decode404: false
encoder: com.example.SimpleEncoder
decoder: com.example.SimpleDecoder
contract: com.example.SimpleContract
对于应用于全部Feign客户端的全局默认配置,也能够经过两种方式 spring-boot
经过@EnableFeignClients 的defaultConfiguration 属性指定默认配置类
在配置文件中经过名称为default的配置实现
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
优先级同Ribbon, 配置文件>自定义配置类>默认的FeignClientsConfiguration
本文案例演示基于前面搭建的springcloud-eureka 与 springcloud-eureka-client 两个示例项目。
新建springcloud-feign项目,pom.xml中加入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
spring-cloud-starter-openfeign 包含了spring-cloud-starter-netflix-ribbon 与 spring-cloud-starter-loadbalancer。
启动类加上@EnableFeignClients 注解
@SpringBootApplication @EnableFeignClients public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } }
定义Feign client(feign client支持继承)
@FeignClient("hello-service") public interface HelloClient extends BaseHelloClient{ @RequestMapping("hello/param") String hello(@SpringQueryMap QueryParam param); }
调用Feign client
@RestController public class FeignController { @Autowired private HelloClient helloClient; @RequestMapping("feign") public String feignTest(){ return "调用Hello-service返回:" + helloClient.hello(); } @RequestMapping("feign/param") public String feignTestParam(QueryParam param) { return "调用Hello-service返回:" + helloClient.hello(param); } }
依次启动三个项目,调用http://localhost:8083/feign 能正常返回调用hello-service的结果。
本示例项目还经过@SrpingQueryMap 注解实现了Feign对 pojo用于GET请求参数的支持。若是不加@SrpingQueryMap, 则pojo参数是没法经过Feign client传递的,可去掉注解自行验证下。
若是须要定制化产生的查询参数map,能够实现并注入一个自定义的 QueryMapEncoder bean
Feign client的日志可经过feign client接口的全路径名进行配置,如logging.level.project.user.UserClient: DEBUG,默认为NONE(即不打印日志)。全局设置
@Configuration public class FooConfiguration { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
可设置的level值
NONE:不记录日志 ,默认
BASIC:只记录请求方法,url,以及响应状态码与执行时间
HEADERS:包括BASIC与请求、响应头
FULL:包括请求与响应的headers,body,metadata
Feign默认使用Ribbon来作负载均衡,可经过配置spring.cloud.loadbalancer.ribbon.enabled=false 来使用spring cloud loadbalancer(目前Ribbon处于维护状态,近期内不作更新)
可经过配置feign.okhttp.enabled=true 或 feign.httpclient.enabled=true 来使用OkHttpClient 或ApacheHttpClient, 默认使用的是JDK 原生的URLConnection 发送HTTP请求,没有链接池
若是须要在RequestInterceptor 中使用ThreadLocal中的变量, 那么要么禁用Hystrix,要么设置hystrix的线程隔离策略为SEMAPHORE
feign:
hystrix:
enabled: false
# 或者
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
使用有Hystrix fallback的Feign时,会在ApplicationContext中存在多个同类型bean, 致使@Autowired 失效。为了解决这个问题,Spring cloud netflix 将全部feign实例标为@Primary,若是要关闭该特性, 可将@FeignClient的 primary属性置为false。
@FeignClient(name = "hello", primary = false) public interface HelloClient { // ... }
本文示例代码:https://github.com/ronwxy/springcloud-demos
认真生活,快乐分享
欢迎关注微信公众号:空山新雨的技术空间