目录
一、前言
二、openfeign介绍
2.1 openfeign介绍
2.2 openfeign优势
三、Spring Cloud Alibaba整合OpenFeign
3.1 前置准备
3.2 代码整合过程
3.2.1 添加feign依赖
3.2.2 添加feign接口类
3.2.3 调整调用的方法
3.2.4 核心配置文件
3.2.5 接口模拟测试
四、openfeign自定义配置使用
4.1 扩展日志配置 — 全局配置
4.1.1 添加一个日志配置类
4.1.2 配置文件调整日志级别
4.1.3 接口测试
4.2 扩展日志配置之局部配置
4.2.1 自定义日志配置类
4.2.2 在目标Feign接口指定日志配置类
4.2.3 配置文件方式实现局部日志扩展
五、openfeign契约配置
5.1 具体操作步骤
5.1.1 使用配置类的方式
5.1.2 修改Feign接口的方法调用
5.1.3 模拟测试
5.1.4 通过yml配置文件配置契约
六、openfeign超时配置
6.1 通过代码方式配置
6.2 通过配置文件方式配置
6.2.1 添加配置文件
6.2.2 修改被调用方接口
6.2.3 模拟测试调用
七、openfeign 自定义拦截器
7.1 拦截器概述
7.2 操作过程
7.2.1 自定义 FeignInterceptor
7.2.2 使自定义的拦截器生效
7.2.3 改造stock中的get接口
7.2.4 模拟测试
7.2.5 自定义拦截器小结
八、写在文末
一、前言
在springcloud微服务生态体系下,作为服务之间相互调用的重要组件openfeign,在其中承担着非常重要的作用,本篇以springcloud中提供的远程接口调用组件openfeign为例,来聊聊openfeign的详细使用。
二、什么是openfeign
2.1 openfeign介绍
- Feign是Netflix开发的,声明式、模板化的HTTP客户端;
- Feign可帮助开发者更加便捷、优雅地调用HTTP API;
- Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等;
2.2 openfeign优势
Feign可以做到使用 HTTP 请求远程服务时就像调用本地方法一样,开发者完全感知不
到这是远程调用,更感知不到这是个 HTTP 请求。具体来说:
- 它像 Dubbo 一样,consumer 直接调用接口方法调用 provider,而不需要通过常规的 Http Client 构造请求再解析返回数据;
- 它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发;
三、Spring Cloud Alibaba整合OpenFeign
以之前的工程案例为基座,在此基础上继续搭建新的模块,接下来,通过实际的操作,演示如何基于Spring Cloud Alibaba实现与OpenFeign的整合
3.1 前置准备
创建一个新的order模块,名为:openfign-order,工程目录如下
3.2 代码整合过程
3.2.1 添加feign依赖
添加如下依赖,主要是open-feign的依赖
org.springframework.boot
spring-boot-starter-web
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-openfeign
3.2.2 添加feign接口类
使用过dubbo的同学应该对此不陌生,使用服务端提供的接口时只需要注入就可以像调用本地方法一样使用了,feign的使用也是类似,只需在调用方定义一个接口类,里面添加被调用的接口中的完整的方法名,参数等,即保持与接口中的方法体参数一致即可;
比如在order模块调用stock模块时,stock的接口类提供了下面的方法
@GetMapping("/reduct")
public String reduct(){
System.out.println("扣减库存 : 8021");
return "扣减库存 : 8021";
}
那么在order模块中,自定义一个接口类,如下
@FeignClient(name = "stock-service",path = "/stock")
public interface StockFeignService {
@GetMapping("/reduct")
public String reduct();
}
参数说明:
- 该接口中的方法名,参数,请求类型等都与接口提供方接口保持一致;
- 接口上面用FeignClient注解修饰,name为服务提供方名称,path为接口根路径;
3.2.3 调整调用的方法
在之前的调用中,我们一直使用的是restTemplate的方式调用,如果使用了feign,调用看起来就变得简单了,做一下简单的改造即可;
@RestController
@RequestMapping("/order")
public class OrderController {
@Value("${service-url.nacos-user-service}")
private String serverURL;
@Autowired
private StockFeignService stockFeignService;
//localhost:8083/order/add
@GetMapping("/add")
public String add(){
System.out.println("下单成功");
String reduct = stockFeignService.reduct();
return "add order :" + reduct;
}
}
3.2.4 核心配置文件
这里调整一下端口即可
server:
port: 8040
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #服务注册中心地址
3.2.5 接口模拟测试
启动stock-service的两个工程,再启动order模块,启动完成后,浏览器进行接口调用
反复多次调用,默认情况下,呈现出的是轮询效果
四、openfeign自定义配置使用
Feign 提供了很多扩展机制,开发者可以使用这些机制更灵活的扩展系统功能。
4.1 扩展日志配置 — 全局配置
使用了Feign之后,有时开发过程中遇到Bug,比如接口调用失败、参数没收到等问题,或者想看看调用性能,这就需要配置Feign的日志了,以便让Feign把请求的完整信息输出来定位和分析问题。配置过程很简单,只需添加一个配置类, 定制Feign提供的Logger级别即可。
4.1.1 添加一个日志配置类
添加如下配置类,指定level的级别为FULL,表示输出完整的日志信息
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
也可以根据自身的需求配置其他的日志级别,从源码中可以看到内置了多种级别的日志
4.1.2 配置文件调整日志级别
默认情况下,服务工程以info级别输出,但Feign的日志级别为debug,两者不匹配,所以还需要额外做一下配置,在配置文件中补充下面的配置即可,即针对
logging:
level:
com.congge.feign: debug
4.1.3 接口测试
在order模块的Feign中添加一个新的接口进行调用
@GetMapping("/get")
public String get(@RequestParam("id") Integer id);
接口调用
//localhost:8040/order/get
@GetMapping("/get")
public String get(){
String stockInfo = stockFeignService.get(1);
return stockInfo;
}
浏览器执行调用:localhost:8040/order/get,说明调用成功
再看控制台,发现输出了详细的调用日志信息
4.2 扩展日志配置之局部配置
上面配置的这种方式是针对全局生效的,即Feign包路径下的所有类都生效,如果仅仅是针对某个具体的类生效,该怎么做呢?其实也很简单,只需要下面的两步;
4.2.1 自定义日志配置类
与上面的想必,去掉那个@Configuration注解即可
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
4.2.2 在目标Feign接口指定日志配置类
在@FeignClient注解中有一个configuration的熟悉,使用上面的配置类即可
@FeignClient(name = "stock-service",path = "/stock",configuration = FeignConfig.class)
public interface StockFeignService {
@GetMapping("/reduct")
public String reduct();
@GetMapping("/get")
public String get(@RequestParam("id") Integer id);
}
4.2.3 配置文件方式实现局部日志扩展
在5.2.1的基础上,也可以不用通过5.2.2的方式通过指定配置类添加,而是在配置文件中指定,只需要在配置文件中添加下面的配置即可;
feign:
client:
config:
stock-service:
loggerLevel: FULL
配置完成后再次调用一下,控制台仍然可以输出完整的调用日志信息
五、openfeign契约配置
Spring Cloud 在 Feign 的基础上做了扩展,使用 Spring MVC 的注解来完成Feign的功
能,以减少使用者的学习成本。
而原生的 Feign 是不支持 Spring MVC 注解的,如果你想在 Spring Cloud 中使用原生的
注解方式来定义客户端也是可以的,通过配置契约来改变这个配置,Spring Cloud 中默认的
是 SpringMvcContract。
Spring Cloud 1 早期版本就是用的原生Fegin. 随着netflix的停更替换成了Open feign
5.1 具体操作步骤
5.1.1 使用配置类的方式
在配置类中添加Contract 的bean
@SpringBootApplication
//@EnableDiscoveryClient
@EnableFeignClients
public class FeignOrderApp {
public static void main(String[] args) {
SpringApplication.run(FeignOrderApp.class, args);
}
@Bean
public Contract feignContract() {
return new Contract.Default();
}
}
5.1.2 修改Feign接口的方法调用
注意:修改契约配置后,OrderFeignService 不再支持springmvc的注解,需要使用Feign原
生的注解
接口中的调用改为如下(更多语法配置可以参阅相关资料)
@FeignClient(name = "stock-service",path = "/stock")
public interface StockFeignServiceV2 {
@RequestLine("GET /reduct")
public String reduct();
@RequestLine("GET /get")
public String get(@Param("id") Integer id);
}
5.1.3 模拟测试
再次启动order模块服务,然后在浏览器调用
5.1.4 通过yml配置文件配置契约
在5.1.1 基础上,将契约的配置配置到配置文件中也可以实现相同的效果,具体配置如下
feign:
client:
config:
stock-service:
loggerLevel: FULL
#配置契约
concontract: feign.Contract.Default
六、openfeign超时配置
客户端微服务通过Fiegn调用其他微服务接口时,可能因为某些原因导致接口响应超时达到默认的设置时间而调用失败,如果业务允许超时时长更长一点的话,就可以考虑手动配置openfeign的超时时间;
6.1 通过代码方式配置
在上述的配置类中添加下面的超时配置bean;
- 通过 Options 可以配置连接超时时间和读取超时时间;
- Options 的第一个参数是连接的超时时间(ms),默认值是 2s;
- Options 的第一个参数是连接的超时,第二个是请求处理的超时时间(ms),默认值是 5s;
@Bean
public Request.Options options() {
return new Request.Options(5000, 10000);
}
6.2 通过配置文件方式配置
6.2.1 添加配置文件
核心配置如下
feign:
client:
config:
stock-service:
loggerLevel: FULL
#配置契约
#concontract: feign.Contract.Default
connectTimeout: 5000
readTimeout: 3000
补充说明: Feign的底层用的是Ribbon,但超时时间以Feign配置为准
6.2.2 修改被调用方接口
为了模拟效果,将调用的stock-service中的接口加一个sleep时间
@GetMapping("/get")
public String get(@RequestParam("id") Integer id) throws InterruptedException {
Thread.sleep(4000);
return "查询到的库存商品ID : " + id;
}
6.2.3 模拟测试调用
启动两个模块的工程,浏览器调用接口:localhost:8040/order/get,将会看到下面的效果
同时观察控制台输出日志,可以看到输出了 超时的信息;
七、openfeign 自定义拦截器
7.1 拦截器概述
使用springmvc拦截器的同学对拦截器应该不陌生,拦截器可以在请求真正到达后端接口之前做一下预处理工作,比如非法请求拦截、参数校验过滤、全局token的认证、加解密、审计等等。在openfeign 中也提供了类似的可以自定义的拦截器,其存在的目的主要是针对服务调用端在调用服务接口时可以做的一些预处理工作。
接下来演示如何在openfeign 中自定义拦截器;
7.2 操作过程
7.2.1 自定义 FeignInterceptor
主要是实现RequestInterceptor 接口,重写里面的apply方法即可,比如在下面的apply方法中,给请求头中添加了两个参数,这样的话,服务提供方就可以解析到header中参数的值;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.UUID;
public class FeignInterceptor implements RequestInterceptor {
static final Logger logger = LoggerFactory.getLogger(FeignInterceptor.class);
@Override
public void apply(RequestTemplate requestTemplate) {
logger.info("enter FeignInterceptor ...");
String accessToken=UUID.randomUUID().toString();
requestTemplate.header("accessToken",accessToken);
requestTemplate.header("username","jerry");
}
}
7.2.2 使自定义的拦截器生效
可以通过配置全局bean的方式或者在yaml中配置都可以
配置bean的方式
@Bean
public FeignInterceptor feignInterceptor() {
return new FeignInterceptor();
}
配置文件方式
feign:
client:
config:
stock-service:
loggerLevel: FULL
connectTimeout: 5000
readTimeout: 3000
#自定义拦截器
requestInterceptors: com.congge.config.FeignInterceptor
7.2.3 改造stock中的get接口
这里将请求中的参数进行解析,并打印输出结果
@GetMapping("/get")
public String get(@RequestParam("id") Integer id) throws InterruptedException {
String accessToken = request.getHeader("accessToken");
System.out.println(accessToken);
String username = request.getHeader("username");
System.out.println(username);
//Thread.sleep(4000);
return "查询到的库存商品ID : " + id;
}
7.2.4 模拟测试
分别启动stock模块的工程和order模块的工程,然后浏览器调用:localhost:8040/order/get
接口可以正常返回结果;
观察stock服务控制台,header中的参数值也能正确的被解析出来;
7.2.5 自定义拦截器小结
自定义拦截器一种很有用的技巧,尤其是内部各个微服务之间进行调用的时候,为了确认对方的身份是否授信或者互信,可以通过这种方式传递一些可以加解密的参数进行身份确认。
八、写在文末
Feign在springcloud微服务生态体系中具有非常重要的作用,对于习惯使用dubbo服务调用方式的同学来说,系统在微服务架构演进过程中,可以采用同时兼容dubbo和feign的方式,而且只需要在现用的架构中做少量的适配和改造即可,这是一个很好的尝试,因此有必要深入了解Feign的技术。