Feign介绍
Feign是一个声明式的web service客户端,它使得编写web service客户端更为容易。创建接口,为接口添加注解,即可使用Feign。Feign可以使用Feign注解或者JAX-RS注解,还支持热插拔的编码器和解码器。Spring Cloud为Feign添加了Spring MVC的注解支持,并整合了Ribbon和Eureka来为使用Feign时提供负载均衡。核心就是通过一系列的封装和处理(动态代理:将这些本地Proxy代理实例,注入到Spring IOC容器中。当远程接口的方法被调用,由Proxy代理实例去完成真正的远程访问,并且返回结果),将以JAVA注解的方式定义的远程调用API接口,最终转换成HTTP的请求形式,然后将HTTP的请求的响应结果,解码成JAVABEAN,返回给调用者。核心流程图如下:
Feign工作原理
- SpringBoot启动类添加@EnableFeignClients注解,Spring容器会扫描标记了@FeignClient注解的接口,并生成此接口的代理对象去执行处理调用;
- @FeignClient(value = "XC_SERVICE_MANAGE_CMS")即指定了cms的服务名称,Feign会从注册中心获取cms服务列表,并通过负载均衡算法进行服务调用;
- 在接口方法中如果拼接有参数,FeignClient的方法中需要使用注解@PathVariable("XXX") 或 @RequestParam("XXX"),指定调用的url,Feign将根据url进行远程调用;
Feign核心组件及特性
feign核心组件:
(1)Client.Default类:默认的feign.Client 客户端实现类,内部使用HttpURLConnnection 完成URL请求处理。虽然在JKD1.8中HttpURLConnnection 的底层使用了非常简单的HTTP连接池技术,但是,其HTTP连接的复用能力,实际是还是非常弱的,所以接口的性能当然也很低;
(2)ApacheHttpClient 类:内部使用 Apache httpclient 开源组件完成URL请求处理的feign.Client 客户端实现类。
- 从开发角度而言,Apache HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性,它不仅使客户端发送Http请求变得容易,而且也方便开发人员测试接口。既提高了开发的效率,也方便提高代码的健壮性;
- 从性能的角度而言,Apache HttpClient带有连接池的功能,具备优秀的HTTP连接的复用能力。关于带有连接池Apache HttpClient的性能提升倍数;
- ApacheHttpClient 类处于 feign-httpclient 的专门jar包中,如果使用,还需要通过Maven依赖或者其他的方式,倒入配套版本的专门jar包;
(3)OkHttpClient类:内部使用 OkHttp3 开源组件完成URL请求处理的feign.Client 客户端实现类。OkHttp3 开源组件由Square公司开发,用于替代HttpUrlConnection和Apache HttpClient。由于OkHttp3较好的支持 SPDY协议(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验),从Android4.4开始,google已经开始将Android源码中的 HttpURLConnection 请求类使用OkHttp进行了替换。也就是说,对于Android 移动端APP开发来说,OkHttp3 组件,是基础的开发组件之一。
(4)LoadBalancerFeignClient 类:内部使用 Ribben 负载均衡技术完成URL请求处理的feign.Client 客户端实现类。LoadBalancerFeignClient 在原理上,简单的使用了delegate包装代理模式,Ribben负载均衡组件计算出合适的服务端server之后,由内部包装 delegate 代理客户端完成到服务端server的HTTP请求,所封装的 delegate 客户端代理实例的类型,可以是 Client.Default 默认客户端,也可以是 ApacheHttpClient 客户端类或OkHttpClient 高性能客户端类,还可以其他的定制的feign.Client 客户端实现类型。
feign特性:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解;
- 支持可插拔的HTTP编码器和解码器;
- 支持Hystrix和它的Fallback;
- 支持Ribbon的负载均衡;
- 支持HTTP请求和响应的压缩;
Feign使用
1. pom引入启动类添加注解@EnableFeignClients
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-httpclient -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>9.5.1</version>
</dependency>
2. client详情
// client实体
@FeignClient(
name = "TestClient",
url = "http://localhost:8080",
fallbackFactory = TestApiFailBack.class,
configuration = TestApiClientConfiguration.class,
decode404 = true
)
@RequestMapping("/test")
public interface HiqApiClient {
/**
* 查询用户信息
*
* @param dto userID
* @return 用户信息
*/
@PostMapping("/user/info/get")
Result<UserVO> getUserInfo(@RequestBody UserDTO dto);
}
// 配置类
@Slf4j
public class TestApiClientConfiguration implements RequestInterceptor {
@Value("${tokenKey}")
private String apiKey;
@Override
public void apply(RequestTemplate template) {
String param = template.toString();
log.info("调用Test " + template.url() + " 入参:{}", param.replace(param.split("\\{")[0], ""));
template.header("Content-Type", "application/json;charset=utf-8");
template.header("X-Gaia-Api-Key", apiKey);
}
}
// 回调
@Slf4j
@Component
public class TestApiFailBackimplements FallbackFactory<TestClient> {
@Override
public TestClient create(Throwable throwable) {
return new TestClient() {
@Override
public Result<UserVO> getStudentStatus(UserDTO dto) {
return fail(dto, throwable);
}
};
}
public <T> Result<T> fail(Object param, Throwable throwable) {
String methodPath = ClassUtil.getMethodPath(TestClient.class);
log.error("调用TEST {} 错误:{} param:{}", methodPath, throwable.getMessage(), JSON.toJSONString(param));
return HIqResult.error("请联...");
}
}
3. 服务调用
@Slf4j
@Service
public class RpcService {
@Autowired
TestClient testClient;
/**
* 获取客户信息
*/
public List<UserInfoResult > getUserInfo(String userId, String userName) {
UserDTO param = new UserDTO ()
.setId(userId)
.setName(userName);
Result<List<UserVO>> hIqResult = testClient.getUserInfo(param);
if (hIqResult.success()) {
return hIqResult.getData().stream().map(RpcService ::build).collect(Collectors.toList());
}
return new ArrayList<>();
}
static UserInfoResult build(UserVO info) {
return new UserInfoResult ()
.setUserId(info.getId())
.setUserMobile(info.getMobilephone())
.setUserName(info.getName());
}
}
Feign源码的github地址:https://github.com/OpenFeign/feign
如有披露或问题欢迎留言或者入群探讨