Feign 远程调用
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
1. Feign替代RestTemplate
1.1 Feign的介绍
-
RestTemplate存在的问题我们之前使用的
RestTemplate远程调用存在着代码可读性差,编程体验不统一,参数复杂URL难以维护。如果再工作中遇见了类似百度搜索的URL将难以维护https://www.baidu.com/s?wd=feign&rsv_spt=1&rsv_iqid=0xe4369c8d00722920&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_dl=tb&rsv_sug3=11&rsv_sug1=7&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&inputT=3165&rsv_sug4=3281当我们面对这么长的URL的时候,当然是希望能够少些甚至不写,最低的要求就是不要再service层看到这种东西。所以我们就使用了Feign。 -
Feign的介绍Feign是一个声明式的http客户端,官方地址:github.com/OpenFeign/f…其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
Feign我理解是常用于微服务相互调用的。
1.2 SpringCloud使用Feign
-
添加依赖
<!-- Feign客户端依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> -
引导类开启功能
在引导类上添加
@EnableFeignClients注解@SpringBootApplication @EnableFeignClients public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } ... } -
编写
Feign客户端为同一个提供者的服务创建一个接口,里面就替代了所有需要用
RestTemplate远程调用的url@FeignClient("userservice") public interface UserClient { @GetMapping("/user/{id}") User queryOrderByUserId(@PathVariable Long id); }这个客户端主要是基于
SpringMVC的注解来声明远程调用的信息,比如:- 服务名称:
userservice - 请求方式:
GET - 请求路径:
/user/{id} - 请求参数:
Long id - 返回值类型:
User
这样,
Feign就可以帮助我们发送http请求,无需自己使用RestTemplate来发送了。 - 服务名称:
-
在对应
service层中使用首先将
Feign客户端自动装配,再调用其中的方法。@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private UserClient userClient; public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // 2. 利用 Feign 发送 http 请求到对应的 微服务中 User user = userClient.queryOrderById(orderId); // 3. 封装 User 到 Order 中。 order.setUser(user); // 4.返回 return order; } }
2. 自定义配置
2.1 Feign 自定义配置
Feign可以支持很多的自定义配置,如下表所示:
| 类型 | 作用 | 说明 |
|---|---|---|
| feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
| feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
| feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
| feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
| feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。
2.2 配置文件方式
-
全局配置
feign: client: config: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置 default: # 日志级别 loggerLevel: FULL -
单个服务配置
feign: client: config: # 服务名称 userservice: # 日志级别 loggerLevel: FULL
而日志的级别分为四种:
- NONE:不记录任何日志信息,这是默认值。
- BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
- HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
2.3 Java代码方式
先创建Feign日志级别配置类
public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.HEADERS;
}
...
}
-
全局配置
在引导类开启
Feign并传入日志级别配置类@EnableFeignClients(defaultConfiguration = 日志级别配置类.class)@SpringBootApplication @EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class) public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } ... } -
单个服务配置
将配置文件放到对应的
Feign客户端的@FeignClient(value = "服务名称", configuration = 日志级别配置类.class)@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration.class) public interface UserClient { ... }
2.4 配置优先级
- 当我们在一个项目在
Feign对相同的服务有着不同的配置,配置文件方式 >Java代码方式 - 同一个
Java方式下有不同的配置,根据先后顺序后面的配置覆盖前面的配置
3. Feign使用优化
Feign作为一个网关,连接使用TCP/IP协议发起http请求,依赖于其它的框架。如果频繁发送请求就会有多次的“三次握手四次挥手”。其底层客户端实现包括:
-
URLConnection:默认实现,不支持连接池 -
Apache HttpClient:支持连接池 -
OKHttp:支持连接池
为了减少频繁请求的情况下资源的消耗,所以在优化方面可以着力于客户端底层使用连接池代替默认的URLConnection。下面我们就用Apache HttpClient来演示。
3.1 Apache HttpClient使用
-
引入依赖
<!--httpClient的依赖 --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency> -
配置
feign: httpclient: # 开启 httpClient enabled: true # 连接池最大连接数 max-connections: 200 # 单个服务最大占用连接数 max-connections-per-route: 50
4. 最佳实践
4.1 统一父接口标准
我们可以发现,Feign的客户端和提供者的controller层是非常相似的
所以为了让代码简化,我们可以将Feign客户端和提供者controller层提作为接口放在一个微服务中,通过引入微服务将对应的接口导入。
4.2 抽取Feign独立模块
将FeiginClient抽取成独立的模块,并且将接口有关的pojo、默认的Feign配置都放到这个模块中,提供给所有消费者使用。
-
创建
Feign-api模块 -
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> -
将对应的
FeignClient和接口相关的pojo以及默认的配置都放在项目中 -
在使用
Feign的项目引导类上,开启扫描Feign-api模块对应的位置- 我们可以将
Feign-api模块内某个包下的所有FeignClient都扫描进去。通过@EnableFeignClients(clients = basePackages = "包路径")完成。 - 我们可以将
Feign-api模块下指定的FeignClient字节码扫描。通过@EnableFeignClients(clients = {clients.class, clients.class})
- 我们可以将
-
启动服务,就成功了。