Feign远程调用,核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVA Bean,返回给调用者。
1 Feign基础使用
1.1 导入依赖
在消费者工程service-consumer中导入Feign依赖信息:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1.2 客户端接口实现
在消费者工程service-consumer中实现Feign客户端接口:
package com.giser.consumer.feign;import com.giser.consumer.pojo.User;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value="user-service")public interface UserFeignClient {@GetMapping("/user/{id}")User queryById(@PathVariable("id") Long id);}
- 编写FeignClient,作为一个接口,Feign会通过动态代理生成实现类;
- 通过@FeignClient标识当前接口为Feign客户端,同时通过value属性指定服务名称;
- 接口中定义的方法可以使用SpringMVC中的注解,实现方法映射,Feign会根据注解拼接访问地址,并请求服务,将响应结果返回。
1.3 编写控制器
package com.giser.consumer.controller;import com.giser.consumer.feign.UserFeignClient;import com.giser.consumer.pojo.User;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("cf")public class ConsumerFeignController {@Autowiredprivate UserFeignClient userFeignClient;@GetMapping("/{id}")public User queryById(@PathVariable Long id){return userFeignClient.queryById(id);}}
1.4 开启Feign
package com.giser.consumer;import org.springframework.boot.SpringApplication;import org.springframework.cloud.client.SpringCloudApplication;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.openfeign.EnableFeignClients;import org.springframework.context.annotation.Bean;import org.springframework.web.client.RestTemplate;@SpringCloudApplication@EnableFeignClients // 开启Feignpublic class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}}
1.5 服务测试
启动EurekaServer、UserService、ConsumerService服务,访问http://localhost:8081/cf/1进行测试。
2 负载均衡
在Feign中已经集成了Ribbon依赖和自动配置。
Feign内置的Ribbon默认了请求超时时长为1000,在服务消费工程service-consumer中,可以通过以下配置修改:
ribbon:ConnectTimeout: 1000 # 连接超时时长ReadTimeout: 2000 # 数据通信超时时长MaxAutoRetries: 0 # 当前服务器的重试次数MaxAutoRetriesNextServer: 0 # 重试多少次服务OkToRetryOnAllOperations: false # 是否对所有的请求方式都重试
Ribbon内部有重试机制,如果不希望重试,则可以添加ribbon.OkToRetryOnAllOperations=false配置。
3 支持Hystrix
Feign默认也集成了Hystrix,不过默认时关闭的,如需开启可以通过以下配置:
feign:hystrix:enabled: true
此时对于Feign中的fallback需要通过类来实现:
- 定义fallback处理类
package com.giser.consumer.fallback;import com.giser.consumer.feign.UserClient;import com.giser.consumer.pojo.User;import org.springframework.stereotype.Component;@Componentpublic class UserClientFallback implements UserClient {@Overridepublic User queryById(Long id) {User user = new User();user.setId(id);user.setUsername("用户异常");return user;}}
- 在FeignClient中配置Fallback类
package com.giser.consumer.feign;import com.giser.consumer.fallback.UserClientFallback;import com.giser.consumer.pojo.User;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value="user-service", fallback = UserClientFallback.class)public interface UserClient {@GetMapping("/user/{id}")User queryById(@PathVariable("id") Long id);}
- 启动测试
4 请求压缩
SpringCloud Feign支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。
- 开启请求与响应的压缩功能
feign:compression:request:enabled: true # 开启请求压缩response:enabled: true
- 设置请求的数据类型及触发压缩的大小下限
feign:compression:request:enabled: true # 开启请求压缩mime-types: text/html,application/xml,application/json # 设置压缩的数据类型min-request-size: 2048 # 设置触发压缩的大小下限
5 日志级别
由于@FeignClient 注解修改的客户端在被代理时,都会创建一个新的Fegin.Logger实例,因此通过logging.level.xx=debug 来设置日志级别对Fegin客户端而言不会产生效果,需要额外指定这个日志的级别。
在service-consumer中的yml中配置日志级别:
logging:level:com.giser: debug
在service-consumer中编写FeignLogConfig类,定义日志级别:
package com.giser.consumer.config;import feign.Logger;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class FeignLogConfig {@BeanLogger.Level feignLoggerLevel(){return Logger.Level.FULL;}}
Feign支持四种Level级别,包括:
-
NONE:不记录任何日志信息,这是默认值。
-
BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
-
HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
-
FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
在@FeignClient注解中指定配置类:
package com.giser.consumer.feign;import com.giser.consumer.config.FeignLogConfig;import com.giser.consumer.fallback.UserClientFallback;import com.giser.consumer.pojo.User;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value="user-service", fallback = UserClientFallback.class, configuration = FeignLogConfig.class)public interface UserClient {@GetMapping("/user/{id}")User queryById(@PathVariable("id") Long id);}
重启项目,进行服务访问,可以看到类似如下日志信息:
[UserClient#queryById] ---> GET http://user-service/user/3 HTTP/1.1[UserClient#queryById] Accept-Encoding: deflate[UserClient#queryById] Accept-Encoding: gzip[UserClient#queryById] ---> END HTTP (0-byte body)[UserClient#queryById] <--- HTTP/1.1 200 (888ms)[UserClient#queryById] content-type: application/json;charset=UTF-8[UserClient#queryById] date: Sat, 16 Oct 2021 05:51:53 GMT[UserClient#queryById] transfer-encoding: chunked[UserClient#queryById] [UserClient#queryById] {"id":3,"username":"wangwu","password":"666"}[UserClient#queryById] <--- END HTTP (45-byte body)