一、Feign远程调用
Feign [feɪn]
先来看我们以前利用RestTemplate
发起远程调用的代码:
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/bdd1ca32a69bc8b2.webp)
存在下面的问题:
- 代码可读性差,编码体验不统一
- 参数复杂URL,难以维护
Feign是一个声明式的http
客户端,官方地址:https://github.com/OpenFeign/feign
其作用就是帮助我们优雅的实现http
请求的发送,解决上面提到的问题。
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/a1be7872e8d4934f.webp?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
Feign is a Java to Http client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign’s first goal was reducing the complexity of binding Denominator uniformly to Http APIs regardless of ReSTfulness.
翻译:Feign是受Retrofit,JAXRS-2.0和WebSocket启发的Java到Http客户端绑定程序。 Feign的第一个目标是减少与ReSTfulness无关的将Denominator统一绑定到Http API的复杂性。
Feign是一个声明式的伪HTTP客户端,它使得写HTTP客户端变得更简单。使用Feign,只需要创建一个接口并注解。
1.1、Feign替代RestTemplate
Feign的使用步骤如下:
1.1.1、引入依赖
我们在order-service服务的pom文件中引入feign的依赖:
<dependency><groupId>org.springframework.cloudgroupId><artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/ea91d84a82557da5.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
1.1.2、添加注解
在order-service的启动类添加注解开启Feign的功能&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/6789f68dabde0aed.png?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
1.1.3、编写Feign的客户端
在order-service中新建一个接口&#xff0c;内容如下&#xff1a;
package com.xbmu.order.client;import com.xbmu.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;&#64;FeignClient("userservice")
public interface UserClient {&#64;GetMapping("/user/{id}")User findById(&#64;PathVariable("id") Long id);
}
这个客户端主要是基于SpringMVC的注解来声明远程调用的信息&#xff0c;比如&#xff1a;
- 服务名称&#xff1a;userservice
- 请求方式&#xff1a;GET
- 请求路径&#xff1a;
/user/{id}
- 请求参数&#xff1a;Long id
- 返回值类型&#xff1a;User
这样&#xff0c;Feign就可以帮助我们发送http请求&#xff0c;无需自己使用RestTemplate
来发送了。
1.1.4、测试
修改order-service中的OrderService
类中的queryOrderById
方法&#xff0c;使用Feign客户端代替RestTemplate
&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/086aec93f5e1e9b2.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
是不是看起来优雅多了。
多请求几次http://localhost:8080/order/101
&#xff0c;发现UserApplication&#xff08;8082&#xff09;与UserApplication&#xff08;8081&#xff09;两个服务&#xff0c;都处理了请求。
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/e62700fe09f8933e.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
由下图可见&#xff0c;Feign组件中已经引入了Ribbon实现负载均衡。
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/0ef126b5295c089b.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
1.1.5、总结
使用Feign的步骤:
- 引入依赖
- 添加
&#64;EnableFeignClients
注解 - 编写FeignClient接口
- 使用FeignClient中定义的方法代替
RestTemplate
1.2、自定义配置
Feign可以支持很多的自定义配置&#xff0c;如下表所示&#xff1a;
类型 | 作用 | 说明 |
---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别&#xff1a;NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析&#xff0c;例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码&#xff0c;便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制&#xff0c;默认是没有&#xff0c;不过会使用Ribbon的重试 |
一般情况下&#xff0c;默认值就能满足我们使用&#xff0c;如果要自定义时&#xff0c;只需要创建自定义的&#64;Bean覆盖默认Bean即可。
下面以日志为例来演示如何自定义配置。
1.2.1、配置文件方式
基于配置文件修改Feign的日志级别可以针对单个服务&#xff1a;
feign:client:config: userservice: loggerLevel: FULL
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/0a0ce631ec450943.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
也可以针对所有服务&#xff1a;
feign:client:config:default: loggerLevel: FULL
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/1e3db12dd78db092.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
测试&#xff0c;查看日志信息&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/ddcc574beb16294e.jpeg?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
日志的级别分为四种&#xff1a;
- NONE&#xff1a;不记录任何日志信息&#xff0c;这是默认值。
- BASIC&#xff1a;仅记录请求的方法&#xff0c;URL以及响应状态码和执行时间
- HEADERS&#xff1a;在BASIC的基础上&#xff0c;额外记录了请求和响应的头信息
- FULL&#xff1a;记录所有请求和响应的明细&#xff0c;包括头信息、请求体、元数据。
1.2.2、Java代码方式
也可以基于Java代码来修改日志级别&#xff0c;先声明一个类&#xff0c;然后声明一个Logger.Level
的对象&#xff1a;
package com.xbmu.order.config;import feign.Logger;
import org.springframework.context.annotation.Bean;public class DefaultFeignConfiguration {&#64;Beanpublic Logger.Level feignLogLevel(){return Logger.Level.BASIC; }
}
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/ff61bfdd3c0af92e.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
如果要全局生效&#xff0c;将其放到启动类的&#64;EnableFeignClients
这个注解中&#xff1a;
&#64;EnableFeignClients(defaultConfiguration &#61; DefaultFeignConfiguration.class)
![在这里插入图片描述](https://img.php1.cn/3cd4a/1e618/bdf/129913486c37ddf6.jpeg?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
如果要局部生效&#xff0c;则把它放到对应的&#64;FeignClient
这个注解中&#xff1a;
&#64;FeignClient(value &#61; "userservice",configuration &#61; DefaultFeignConfiguration.class)
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/7494af3c1cda418d.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
1.3、Feign使用优化
Feign底层发起http请求&#xff0c;依赖于其它的框架。其底层客户端实现包括&#xff1a;
URLConnection
&#xff1a;默认实现&#xff0c;不支持连接池Apache HttpClient
&#xff1a;支持连接池OKHttp
&#xff1a;支持连接池
因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection
。
这里我们用Apache的HttpClient来演示。
1.3.1、引入依赖
在order-service的pom文件中引入Apache的HttpClient依赖&#xff1a;
<dependency><groupId>io.github.openfeigngroupId><artifactId>feign-httpclientartifactId>
dependency>
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/8170a21e8dddfd22.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
1.3.2、配置连接池
在order-service的application.yml
中添加配置&#xff1a;
feign:client:config:default: loggerLevel: BASIC httpclient:enabled: true max-connections: 200 max-connections-per-route: 50
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/617c1173853af4b6.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
接下来&#xff0c;在FeignClientFactoryBean
中的loadBalance
方法中打断点&#xff1a;
Debug方式启动order-service服务&#xff0c;可以看到这里的client&#xff0c;底层就是Apache HttpClient
&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/c72d78d7317a9e8e.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
1.3.3、总结
Feign的优化&#xff1a;
- 日志级别尽量用
BASIC
- 使用HttpClient或OkHttp代替URLConnection
引入feign-httpclient依赖
配置文件开启httpclient功能&#xff0c;设置连接池 参数
1.4、最佳实践
所谓最佳实践&#xff0c;就是使用过程中总结的经验&#xff0c;最好的一种使用方式。
仔细观察可以发现&#xff0c;Feign的客户端于服务提供者的Controller代码非常相似&#xff1a;
Feign客户端&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/6789f68dabde0aed.png?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
UserController&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/bff2716168d1ed7b.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
有没有一种办法简化这种重复的代码编写呢&#xff1f;
1.4.1、继承方式
一样的代码可以通过继承来共享&#xff1a;
- 定义一个API接口&#xff0c;利用定义方法&#xff0c;并基于SpringMVC注解做声明。
- Feign客户端和Controller都继承该接口。
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/bcafc120671304eb.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
优点&#xff1a;
缺点&#xff1a;
1.4.2、抽取方式
将Feign的Client抽取为独立模块&#xff0c;并且把接口有关的POJO、默认的Feign配置都放到这个模块中&#xff0c;提供给所有消费者使用。
例如&#xff0c;将UserClient、User、Feign的默认配置都抽取到一个feign-api包中&#xff0c;所有微服务引入该依赖包&#xff0c;即可直接使用。
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/0a0ce631ec450943.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
1.4.3、实现基于抽取的最佳实践
1.4.3.1、抽取
首先创建一个module&#xff0c;命名为feign-api&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/e3aa5425383ba10d.png?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
项目结构&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/43a754c811e7ec5c.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
在feign-api中&#xff0c;引入feign的start依赖
<dependency><groupId>org.springframework.cloudgroupId><artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/2d903861d5ad779c.png?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
然后&#xff0c;order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/8170a21e8dddfd22.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
1.4.3.2、在order-service中使用feign-api
首先&#xff0c;删除order-service中的UserClient、User、DefaultFeignConfiguration等类或接口。
在order-service的pom文件中引入feign-api的依赖&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/443b30bb45e66690.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
1.4.3.3、重启测试
重启后&#xff0c;发现服务报错了&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/e88efe5b0a13a7fa.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
这是因为UserClient现在在com.xbmu.feign.client
包下&#xff0c;
而order-service的&#64;EnableFeignClients注解是在com.xbmu.order
包下&#xff0c;不在同一个包&#xff0c;无法扫描到UserClient。
1.4.3.4、解决扫描包问题
- 方式一&#xff1a;
指定feign应用扫描的包&#xff1a;
&#64;EnableFeignClients(basePackages &#61; "com.xbmu.feign.client")
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/bff2716168d1ed7b.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
- 方式二&#xff1a;
指定需要加载的Client接口&#xff1a;
&#64;EnableFeignClients(clients &#61; {UserClient.class})
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/b428d8f746fb8d47.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)
重启&#xff0c;成功&#xff1a;
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/8343fdbffb0056b5.webp?x-oss-process&#61;image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiK5ZaE6Iul5rC0,size_20,color_FFFFFF,t_70,g_se,x_16)