目录
1、Feign-简介
Feign是Neflix开发的声明式、模块化的HTTP客户端,集成了Ribbon
、RestTemplate
实现了负载均衡的执行Http调用,Feign可以帮助我们更加便捷、优雅的调用HTTP API。
Spring Cloud OpenFeign是对Feign的增强
,使其支持Spring MVC 注解,另外还整合了Ribbon和Nacos,从而是的Feign使用更加方便,有了feign我们就不用使用resttemplate远程调用了(调用Controller层)。
Feign 和 OpenFeign区别
1. 维护者和状态:
- Netflix Feign:Netflix公司最初开发了Feign,但后来决定停止其维护。
- OpenFeign:由于Netflix不再维护Feign,Feign由Spring Cloud社区接管并更名为OpenFeign。OpenFeign是Feign的后续版本,由社区维护,并且项目已经迁移到新的仓库。
2. 依赖和集成:
- Netflix Feign:在Netflix维护期间,Feign是一个独立的组件,主要用于Spring Cloud中的服务调用。
- OpenFeign:OpenFeign是基于Netflix Feign的改进版本,并集成了Spring MVC的注解等,使其更方便地与Spring Boot项目集成。Spring Cloud官方推荐使用OpenFeign,并通过spring-cloud-starter-openfeign依赖来提供支持。
2、spring-cloud快速整合OpenFeign
1)在调用服务中添加依赖
<!--添加openfeign依赖,依赖于sprin-cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2)配置需要调用的接口
/*
* name 指定调用rest接口对应服务名
* path 指定调用接口所在Controller的@RequestMapping对应路径,没有则不填
*/
@FeignClient(name = "stock-service", path = "/stock")
public interface StockFeignService {
// 声明需要调用的接口方法名
@PostMapping("/reduct")
String add(@RequestParam("name") String name);
}
3)启动类加上注解
@EnableFeignClients
4)直接调用
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private StockFeignService stockFeignService;
@GetMapping("/getOrder")
public String getOrder(){
stockFeignService.add("10");
return "Hello world";
}
}
注:使用feign需要在参数前面加上@RequestParam和@PathVariable注解并指定参数,不然获取不到参数。
3、Feign日志
1)feign日志级别:
- NONE(默认):不记录任何日志,性能最佳,适用于生产环境;
- BASIC:仅记录请求方法、URL、响应状态代码以及执行时间,适用于生产环境追踪问题;
- HEADERS:在BASIC级别的基础上,记录请求和响应的header;
- FULL:记录请求和响应的header、body和元数据,适用于开发测试定位问题。
2):配置feign日志:
package com.swp.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 全局配置: 使用@Configuration会全局配置到所有服务提供方(被调用方)
// 局部配置: 如果只针对某个服务,就不要使用@Configuration,在@FeignClient(name = "stock-service", path = "/stock", configuration = FeignClient.class)中加入configuration = FeignClient.class
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLogerLevel(){
return Logger.Level.FULL;
}
}
yaml配置日志级别
#spring 默认日志级别是info,feign的debug日志不会输出,所以需要配置输出日志级别
logging:
level:
com.swp.feign: debug #只输出feign目录下的日志
日志输出:
4、Feign契约配置
若以前使用的feign原生注解,在不想改变原生注解的情况下,可以使用Feign契约配置
feign:
client:
config:
contract: feign.Contract.Default
5、Feign配置超时时间
全局配置:
@Configuration
public class FeignConfig(){
@Bean
public Request.Options options(){
return new Request.Options(5000,10000);
}
}
yaml配置
feign:
client:
config:
mall-order: #对应微服务
# 连接超时时间。默认2s
connectTimeout: 5000
# 请求处理超时时间,默认5s
readTimeout: 10000
6、Feign拦截器
在服务消费者调用服务提供者时触发。
Feign拦截器配置类
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CustomFeignInterceptor implements RequestInterceptor {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header("token", "123");
requestTemplate.query("name", "999");
// requestTemplate.uri("/111"); 拦截所有路径,并修改为/{xx}
logger.info("feign拦截器");
}
}
注入配置类
// 注入自定义feign拦截器
@Bean
public CustomFeignInterceptor customFeignInterceptor(){
return new CustomFeignInterceptor();
}
}
7、Feign断路器
#开启hysrris
feign:
hystrix:
enabled: true
定义SysMgrHystrix ,实现FallbackFactory的create方法来提供服务降级实现类对象的创建,
ServiceSysMgrClient是自己的feign接口
@Component
public class SysMgrHystrix implements FallbackFactory<ServiceSysMgrClient> {
@Override
public ServiceSysMgrClient create(Throwable throwable) {
return new ServiceSysMgrClient() {
// 下面是feign接口
@Override
public Result<List<OrgNode>> getOrgChildren(Integer parentOrgId) {
return "被回滚了";
}
};
}
}
然后在Feign接口上面加上 fallbackFactory = SysMgrHystrix.class
8、Feign 相关问题
8.1 Feign 如何实现负载均衡
Feign内部集成了Ribbon
,用于处理服务之间的负载均衡。Ribbon是一个可插拔的客户端负载均衡器,它可以在运行时动态地从“服务中心”(如Eureka、Consul等)获取服务实例列表,并根据配置的负载均衡策略选择一个服务实例进行调用。
8.2 为什么Feign第一次调用耗时较长?
这通常是由于Ribbon的懒加载机制
。当第一次通过Feign客户端发起调用时,Ribbon需要执行一系列操作,包括从服务注册中心(如Eureka、Consul等)获取服务实例列表、建立连接池等,这些操作会导致首次调用的延迟。
如何解决这个问题?
1.采用饥饿加载
ribbon:
eager-load:
enabled: true
clients: service-1,service-2 # 列出所有需要预加载的服务
请注意,clients属性应该是一个逗号分隔的服务名列表。但是,不是所有的服务注册中心都支持Ribbon的eager-load功能,因此这可能不适用于所有情况。
2.应用启动时预热Feign客户端
如果eager-load不起作用,或者您想要更细粒度的控制,您可以在应用启动时编写一些代码来预热Feign客户端。这可以通过在Spring Boot的@PostConstruct注解的方法中执行一个无关紧要的Feign调用来实现。
例如:
@Component
public class FeignClientInitializer {
private final MyFeignClient myFeignClient;
@Autowired
public FeignClientInitializer(MyFeignClient myFeignClient) {
this.myFeignClient = myFeignClient;
}
@PostConstruct
public void init() {
// 执行一个无关紧要的调用来预热Feign客户端
try {
myFeignClient.someInnocuousMethod(); // 假设这个方法不会造成实际影响
} catch (Exception e) {
// 记录或忽略异常,因为这只是预热调用
}
}
}
8.3 Feign怎么实现认证传递?
因为请求到服务是通过 HandlerInterceptor
传递认证信息(如OAuth令牌、JWT令牌或其他认证令牌),但是通过openfeign调用时在HandlerInterceptor
是没有认证信息的。
在Feign中传递认证信息的常见做法是通过实现RequestInterceptor
接口来定义一个拦截器。拦截器允许你在请求发送之前对请求进行修改,比如添加请求头。以下是使用Feign和RequestInterceptor来传递认证信息的步骤:
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignClientConfig {
@Autowired
private AuthService authService; // 假设AuthService是一个Spring管理的Bean
@Bean
public RequestInterceptor requestInterceptor() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
// 从AuthService获取JWT令牌并添加到请求头中
String authHeader = "Bearer " + authService.getJwtToken();
template.header("Authorization", authHeader);
}
};
}
}