六、Spring Cloud Alibaba项目,Feign
一、Feign
什么是Feign?
Feign是Netflix开发的声明式、模板化的HTTP客户端,实现接口的调用。
Spring Cloud openfeign 对 Feign 进行了增强,使其支持 SpringMVC 注解,另外还整合了 Ribbon 和 Eureka,从而使得 Feign 的使用更加方便。
1.1 优势
Feign 可以做到使用 HTTP 请求远程服务时就像调用本地方法一样的体验。它像Dubbo一样,consumer 直接调用接口方法调用 provider,而不需要通过常规的 HttpClient 构造请求再解析返回数据。它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。
1.2 Feign的设计架构
1.3 Ribbon & Feign 对比
Ribbon + RestTemplate进行微服务调用
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } //调用方式 String url = "http://service-order/order/findOrderByUserId/"+id; R result = restTemplate.getForObject(url,R.class);
Feign进行微服务调用
@FeignClient(value = "service-order",path = "/order") public interface OrderFeignService { @RequestMapping("/findOrderByUserId/{userId}") public R findOrderByUserId(@PathVariable("userId") Integer userId); } @Autowired OrderFeignService orderFeignService; //feign调用 R result = orderFeignService.findOrderByUserId(id);
1.4 Feign 单独使用
引入依赖
<dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-core</artifactId> <version>8.18.0</version> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-jackson</artifactId> <version>8.18.0</version> </dependency>
编写接口
public interface RemoteService { @Headers({"Content-Type: application/json","Accept: application/json"}) @RequestLine("GET /order/findOrderByUserId/{userId}") public R findOrderByUserId(@Param("userId") Integer userId);
}
调用
public static void main(String[] args) { //基于json // encoder指定对象编码方式,decoder指定对象解码方式 RemoteService service = Feign.builder() .encoder(new JacksonEncoder()) .decoder(new JacksonDecoder()) .options(new Request.Options(1000, 3500)) .retryer(new Retryer.Default(5000, 5000, 3)) .target(RemoteService.class, "http://localhost:8020/"); System.out.println(service.findOrderByUserId(1)); }
二、Spring Cloud Alibaba 快速整合feign
2.1 service-user服务的pom文件添加依赖
<!-- openfeign 远程调用 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2.2 service-user服务的启动类添加注解
@SpringBootApplication @EnableFeignClients //开启feign public class ServiceUserApplication { public static void main(String[] args) { SpringApplication.run(ServiceUserApplication.class, args); } }
2.3 service-user服务中添加feign接口
提示:实际开发中,将服务的接口定义单独抽出来,作为公共的依赖,以方便使用。
/** * 订单服务接口 * value:微服务名称 * 对于着微服务的controller接口 */ @FeignClient(value = "service-order", path = "/order") public interface OrderFeignService { @RequestMapping("/add") public String add(); @RequestMapping("/cancel") public String cancel(); }
2.4 修改service-user服务的UserController
@RestController @RequestMapping("/user") public class UserController { @Value("${server.port}") private String port; @Autowired private RestTemplate restTemplate; @Autowired OrderFeignService orderFeignService; @RequestMapping("/addOrder") public String addOrder() { String url = "http://service-order/order/add"; String apiReqResult = orderFeignService.add(); return "用户服务-新增订单成功:" + apiReqResult; } @RequestMapping("/cancelOrder") public String cancelOrder() { String url = "http://service-order/order/cancel"; String apiReqResult = orderFeignService.cancel(); return "用户服务-取消订单成功:" + apiReqResult; } }
三、Spring Cloud Feign 的自定义配置及使用
3.1 日志配置
有时候我们遇到 Bug,比如接口调用失败、参数没收到等问题,或者想看看调用性能,就需要配置 Feign 的日志了,以此让 Feign 把请求信息输出来。
(1)定义一个配置类,指定日志级别
四种日志级别:
- NONE【性能最佳,适用于生产】:不记录任何日志(默认值)。
- BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态代码以及执行时间。
- HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
- FULL【比较适用于开发及测试环境定位问题】:记录请求和响应的header、body和元数据。
// 注意: 此处配置@Configuration注解就会全局生效,如果想指定对应微服务生效,就不能配置 @Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
(2)局部配置,让调用的微服务生效,在 @FeignClient 注解中指定使用的配置类
注意不能与(1)同时使用。
@FeignClient(value = "service-order",path = "/order",configuration = FeignConfig.class) public interface OrderFeignService { }
(3)在yml配置文件中执行 Client 的日志级别才能正常输出日志,格式是"logging.level.feign接口包路径=debug"
feign.client.config.xxx.loggerLevel=FULL
xxx为调用的微服务名称
3.2 契约配置
(1)修改契约配置,支持Feign原生的注解。
方式一:修改FeignConfig
public class FeignConfig { /** * 修改契约配置,支持Feign原生的注解 * @return */ @Bean public Contract feignContract() { return new Contract.Default(); } }
方式二:修改yml文件
# 设置为默认的契约(还原成原生注解) feign.client.config.xxx.contract=feign.Contract.Default
xxx为调用的微服务名称
(2)OrderFeignService 中配置使用 Feign 原生的注解
注意:修改契约配置后,OrderFeignService 不再支持springmvc的注解,需要使用Feign原生的注解
@FeignClient(value = "service-order",path = "/order") public interface OrderFeignService { @RequestLine("GET /findOrderByUserId/{userId}") public R findOrderByUserId(@Param("userId") Integer userId); }
3.3 通过拦截器实现认证
通常我们调用的接口都是有权限控制的,很多时候可能认证的值是通过参数去传递的,还有就是通过请求头去传递认证信息,比如 Basic 认证方式、 接口鉴权
Feign 中我们可以直接配置 Basic 认证
@Configuration // 全局配置 public class FeignConfig { @Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new BasicAuthRequestInterceptor("fox", "123456"); } }
@Configuration // 全局配置 public class FeignConfig { /** * 开启Basic认证 */ @Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new BasicAuthRequestInterceptor("fox","123456"); } /** * 自定义拦截器 */ @Bean public FeignAuthRequestInterceptor feignAuthRequestInterceptor(){ return new FeignAuthRequestInterceptor(); }
}
使用场景
- 统一添加 header 信息;
- 对 body 中的信息做修改或替换;
自定义拦截器实现认证逻辑
public class FeignAuthRequestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { // 业务逻辑 String access_token = UUID.randomUUID().toString(); template.header("Authorization",access_token); } }
全局配置:
@Configuration public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } /** * 自定义拦截器 * @return */ @Bean public FeignAuthRequestInterceptor feignAuthRequestInterceptor(){ return new FeignAuthRequestInterceptor(); } }
局部配置
feign.client.config.service-order.requestInterceptors[0]=
com.shiwn.serviceuser.Interceptor.FeignAuthRequestInterceptor
3.4 超时时间配置
通过 Options 可以配置连接超时时间和读取超时时间,Options 的第一个参数是连接的超时时间(ms),默认值是 2s;第二个是请求处理的超时时间(ms),默认值是 5s。
全局配置
@Configuration public class FeignConfig { @Bean public Request.Options options() { return new Request.Options(5000, 10000); } }
局部配置
#修改某个服务的默认超时时间 # 配置 类别调用商品服务openfeign默认超时时间 默认连接超时和等待超时时间都是1s # 配置指定服务连接超时 #feign.client.config.service-order.connectTimeout=5000 # 配置指定服务等待超时 #feign.client.config.service-order.readTimeout=5000 #修改OpenFeign默认调用所有服务的默认超时时间 # 配置openfeign默认超时时间 默认时间 单位毫秒 feign.client.config.default.connectTimeout=5000 feign.client.config.default.readTimeout=5000
3.5 客户端组件配置
<!-- Apache HttpClient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.7</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>10.1.0</version> </dependency>
feign.httpclient.enabled=true
(2)配置 OkHttp
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
#feign 关闭 httpclient feign.httpclient.enabled=false #feign 使用 okhttp feign.okhttp.enabled=true
3.6 GZIP 压缩配置
# 开启配置 feign.compression.request.enabled=true feign.compression.response.enabled=true # 配置压缩类型 feign.compression.request.mime-types=text/xml,application/xml,application/json # 最小压缩值 feign.compression.request.min-request-size=2048
3.7 编码器解码器配置
public interface Encoder { void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException; } public interface Decoder { Object decode(Response response, Type type) throws IOException, DecodeException, FeignException; }
@Bean public Decoder decoder() { return new JacksonDecoder(); } @Bean public Encoder encoder() { return new JacksonEncoder(); }
yml配置方式:
# 对应的微服务"service-order"配置编解码器 feign.client.config.service-order.encoder=feign.jackson.JacksonEncoder feign.client.config.service-order.decoder=feign.jackson.JacksonEncoder