1 Feign简介
- RestTemplate实现远程调用
Feign之前常利用RestTemplate发起远程调用
String url = "http://userservice/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
RestTemplate存在下面的问题
- 代码可读性差,编程体验不统一
- 参数复杂,URL难以维护
- Feign概述
- Feign是一个声明式的伪http客户端,官方地址:https://github.com/OpenFeign/feign
其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
- Feign是Spring Cloud提供的一个声明式的Http客户端,它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可
- Nacos注册中心很好的兼容了Feign
- Feign默认集成了Ribbon,所以在Nacos下使用Fegin默认就实现了负载均衡的效果
- Feign与Dubbo的异同
https://www.cnblogs.com/ying-z/p/14781757.html
https://www.jianshu.com/p/45c4ebcfea82
2 使用Feign
Fegin的使用步骤如下
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
添加注解
在服务启动类添加注解@EnableFeignClients开启Feign的功能
- 编写Feign客户端
新建一个接口,封装所有对其他服务的远程调用
如在order-service服务中新建如下接口(调用user-service服务的接口)
package cn.itcast.order.client;
import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
这个接口使用@FeignClient注解加上SpringMVC的注解来声明被远程调用的服务信息,如:
- 被调用服务名称:userservice
- 请求方式:GET
- 请求路径:/user/{id}
- 请求参数:Long id
- 返回值类型:User
这样,Feign就可以帮助我们发送http请求实现远程调用,无需自己使用RestTemplate来发送http请求了。
3 Feign自定义配置
- Feign可以支持很多的自定义配置,如下表所示 | 类型 | 作用 | 说明 | | —- | —- | —- | | feign.Logger.Level | 修改日志级别 | Feign包含四种不同的日志级别:NONE、BASIC、HEADERS、FULL,默认为NONE(性能较好) | | feign.codec.Decoder | 响应结果的解析器 | 解析器用于对http远程调用的结果做解析,例如解析json字符串为Java对象 | | feign.codec.Encoder | 请求参数的编码器 | 编码器用于将请求参数编码,便于发送http请求 | | feign.Contract | 支持的注解格式 | 默认是SpringMVC的注解,一般不需要改变 | | feign.Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过默认会使用Ribbon的重试机制 |
- 一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。
下面以日志为例来演示如何自定义配置
4 使用Http连接池
Feign并不是一个Http客户端实现,其底层依赖其他Http客户端,如下
- URLConnection:Feign默认选项,不支持连接池
- Apache HttpClient:支持连接池
- OKHttp:支持连接池
Http客户端的选择
由于默认的Http客户端URLConnection并不支持连接池,因此通常需要手动为Feign配置一个Http客户端
- 配置Feign使用Apache HttpClient
feign:
httpclient:
enabled: true # 开启feign对Apache HttpClient的支持
max-connections: 200 # 连接池最大连接数
max-connections-per-route: 50 # 每个路径的最大连接数
5 企业级Feign使用方案
可以发现Feign客户端中的方法和服务提供者的Controller方法非常相似,如下
Feign客户端
@FeignClient("userservice") public interface UserServiceClient { @GetMapping("/user/{id}") User findById(@PathVariable("id") Long id); }
服务提供者userservice的Controller
@RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public User queryById(@PathVariable("id") Long id) { return userService.queryById(id); } }
1 继承方式
一样的代码可以通过继承来共享
- 定义一个API接口并基于SpringMVC注解做声明。
- Feign客户端和Controller都集成该接口
- 优点
- 简单
- 实现了代码共享
- 缺点
- 服务提供方、服务消费方紧耦合,Spring官方不建议该方式
- 参数列表中的注解并不会继承,因此服务提供者的Controller中必须再次声明方法、参数列表、注解
2 抽取方式
- 抽取前,调用同一服务的每个服务都会写重复Client,如下
- 解决方案
将所有服务中的FeignClient抽取到一个单独的feign-api模块中,这样需要使用Feign远程调用的服务只需要依赖feign-api模块即可
- 优点
- 实现了代码共享,但不会造成服务提供方与消费方的耦合
- 缺点
- 随着远程调用的增多,feign-api模块可能包含越来越多的方法,服务调用者可能只需要远程调用几个方法,却依赖了很多没用的内容
3 抽取方式实现
- 服务提供者抽取api包
- 一般来说,抽取的api包由服务提供者来定义(api中定义接口,service中实现接口)。
- 服务提供者只需要将 向其他服务暴露的Controller方法 集中到api模块中,同时在api模块中定义需要用到的对象即可
- 服务消费者依赖api包
- 对于服务消费者来说,需要用到哪个服务就依赖哪个服务定义的api模块即可
- 这里需要注意的是,由于服务消费者需要用到api模块中Client定义的Bean,并且Client所在包并不在启动类所在包内,因此我们需要在启动类的@EnableFeignClients注解中指定basePackages属性,该属性指定扫描@FeignClient的包,从而为这些Client创建Bean