OpenFeign简介
Feign已经不再更新,进入维护状态。SpringCloud版本 OpenFeign作为后起之秀接替他。
Feign是声明性Web服务客户端。 它使编写Web服务客户端更加容易。 要使用Feign,请创建一个接口并对其进行注释。 它具有可插入的注释支持,包括Feign注释和JAX-RS注释。 Feign还支持可插拔编码器和解码器。 Spring Cloud添加了对Spring MVC注释的支持,并支持使用Spring Web中默认使用的相同HttpMessageConverters。 Spring Cloud集成了Ribbon和Eureka以及Spring Cloud LoadBalancer,以在使用Feign时提供负载平衡的http客户端。
https://github.com/OpenFeign/feign
https://cloud.spring.io/spring-cloud-openfeign/2.2.x/reference/html/
Ribbon和Feign的区别
Ribbon
是一个基于 HTTP 和 TCP 客户端 的负载均衡的工具。主启动类上使用@RibbonClient
开启
它可以 在客户端 配置 RibbonServerList(服务端列表),使用 HttpClient 或 RestTemplate 模拟http请求,步骤相当繁琐。
Ribbon 可以用来做客户端负载均衡,调用注册中心的服务
Ribbon的使用需要代码里手动调用目标服务,请参考官方示例:https://github.com/Netflix/ribbon
Feign
Feign 是在 Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。
采用接口的方式, 只需要创建一个接口,然后在上面添加注解即可 ,就能完成对服务方的接口绑定。只需要将需要调用的其他服务的方法定义成抽象方法即可, 不需要自己构建http请求。
然后就像是调用自身工程的方法调用,而感觉不到是调用远程方法,使得编写 客户端变得非常容易。Feign自动封装底层Http请求。
Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端
Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。
Feign支持的注解和用法请参考官方文档:https://github.com/OpenFeign/feign
Feign本身不支持Spring MVC的注解,它有一套自己的注解
OpenFeign
OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。
OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,
并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

实践案例
- 主启动类开启
@EnableFeignClients
- 写服务接口,对应提供方的Controller,接口上
@FeignClient(服务名)
和@Component
实例化。
- 把这个service接口注入到业务中即可使用。(这个Service会有springcloud动态代理生成实现并实例化)
Server端:
pom新增open-feign支持:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>com.sam.cloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies>
|
主启动类
1 2 3 4 5 6 7 8 9 10 11 12 13
| @SpringBootApplication @EnableFeignClients public class CustomerOpenFeign8084 {
public static void main(String[] args) { SpringApplication.run(CustomerOpenFeign8084.class,args); } }
|
Service接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@Component @FeignClient("CLOUD-PAYMENT-SERVICE") public interface PaymengFeignService {
@PostMapping(value="/payment/add") public CommonResult<Payment> addPayment(@RequestBody Payment payment);
@GetMapping(value = "/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") long id); }
|

提供方
controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @Controller @Slf4j @RequestMapping("/payment") public class PaymentController { @Resource private PaymentService paymentService;
@Value("${server.port}") private String serverPort;
@Resource private DiscoveryClient discoveryClient;
@PostMapping(value="/add") @ResponseBody public CommonResult<Payment> addPayment(@RequestBody Payment payment){ int result = paymentService.add(payment); log.info("---------插入:"+result); if(result > 0){ return CommonResult.success("插入payment成功!serverPort="+serverPort); }else{ return CommonResult.businessFail("插入payment失败"); } }
@GetMapping(value = "/get/{id}") @ResponseBody public CommonResult<Payment> getPaymentById(@PathVariable("id") long id){ Payment payment = paymentService.getById(id); if(payment != null){ return CommonResult.success("ok,serverPort="+serverPort, payment); }else{ return CommonResult.businessFail("查询失败!"); } }
}
|
feign常见配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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底层是使用的ribbon,调用方的application.yml
中配置ribbon的超时时间:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
feign: client: config: default: connectTimeout: 5000 readTimeout: 5000 loggerLevel: basic
|
Feign日志控制
yml对应包打开日志
1 2 3
| logging: level: com.sam.springcloud.service.PaymengFeignService: debug
|
feign配置日志级别,使用上面的yml配置文件控制,或者下面人肉注入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import feign.Logger; @Configuration public class FeignConfig {
@Bean public Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } }
|
Feign更多
Encoder、Decoder、和Hystyix结合,消息压缩,见
https://cloud.spring.io/spring-cloud-openfeign/2.2.x/reference/html/#spring-cloud-feign-hystrix