微服务调用组件-Open Feign
Feign和OpenFeign
Feign是Netflix开发的声明式、模板化的HTTP客户端,可以方便快捷的调用Http请求;Spring Cloud OpenFeign对Feign进行了增强,支持了MVC的注解,通过动态代理的方式产生实现类,还整合了ribbon和nacos,实现了负载均衡。
Java中传统的调用远程接口的方式有HttpClient、Okhttp、HttpURLConnection、RestTemplate、WebClient,而OpenFeign使用HTTP请求远程服务时,就像调用本地接口一样方便,无需关注远程的交互细节。
Spring Cloud Alibaba整合OpenFeign
1. 快速使用
1.1 调用端依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1.2 调用端启动注解
调用端启动类添加@EnableFeignClients注解
1.3 调用端编写调用接口
如果远程端的接口如下:
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/a/{id}")
public String a(@PathVariable("id") Integer id) {
return "Hello";
}
}
则本地端创建类似远程端的本地接口:
package com.xc.order.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author wyp
*/
@FeignClient(value = "A-SERVICE",path = "/test")
public interface AFeignService {
@RequestMapping("/a/{id}")
//@PathVariable("id")里的值不能省略
String a(@PathVariable("id") Integer id);
}
其中@FeignClient注解的value属性代表调用的远程服务名,path属性为由所有方法级映射使用的路径前缀。
1.4 调用该接口
调用AFeignService类中的接口可实现远程调用。
@Autowired
private AFeignService aFeignService;
@RequestMapping("/abc")
public String abc() {
return aFeignService.a(1);
}
1.5 注意
当存在多个类的@FeignClient注解指定的value值相同时,启动会报“已经定义了具有该名称的 bean”错误,The bean '服务名.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled.
解决方式:配置文件中加入spring.main.allow-bean-definition-overriding=true
允许 bean 定义覆盖。
消费者和提供者需在同一个命名空间内,否则会出现com.netflix.client.ClientException: Load balancer does not have available server for client:XX
错误。
2. OpenFeign日志级别配置
2.1 定义指定日志级别的配置类(全局配置)
package com.xc.a.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author wyp
*/
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
其中Level枚举类有四种值:
- NONE: 没有记录。
- BASIC: 仅记录请求方法和 URL 以及响应状态代码和执行时间。
- HEADERS: 记录基本信息以及请求和响应标头。
- FULL: 记录请求和响应的标头、正文和元数据。
2.2 局部配置
当配置加了@Configuration注解之后,会作用于全局,所有的服务都会生效,当不加该注解后,可通过@FeignClient注解指定哪个服务,该注解configuration属性指定哪个配置类。
去掉上面配置类代码中的@Configuration注解,并在要调用的服务上做如下配置:
@FeignClient(value = "FEIGN-B",path = "/b",configuration = FeignConfig.class)
public interface FeignBService {
@RequestMapping("/add")
String add();
}
当然也可以通过配置文件的方式代替配置类,如下:
feign:
client:
config:
# 服务名
FEIGN-B:
# 日志级别
loggerLevel: FULL
这样,只会在调用服务名为FEIGN-B的时候输出日志。当然由于spring boot默认的日志级别为info,所以还需要将yml中做一下配置,将该服务类包的路径重新定义日志级别:
logging:
level:
com.xc.a.feign: debug
下面内容的局部和全局配置的方式都可以按照这一小节来配置,就不多赘述了。
3. Feign契约配置
原生Feign不支持MVC的注解,当Feign升级为OpenFeign后,里面的注解将不可用,这时可做契约配置,就不需要替换里面所有的原生Feign注解了。
3.1 通过配置类的方式
@Bean
public Contract feignContract() {
return new Contract.Default();
}
3.2 通过配置文件的方式
feign:
client:
config:
#对应微服务
A-SERVICE:
loggerLevel: FULL
# 指定Feign原生注解契约配置
contract: feign.Contract.Default
4. OpenFeign超时时间
4.1 配置类的方式设置超时时间
Request.Options(long connectTimeout, TimeUnit connectTimeoutUnit, long readTimeout, TimeUnit readTimeoutUnit, boolean followRedirects)方法,connectTimeout为连接超时时间(默认为2秒),readTimeout为请求处理超时时间(即连接成功后响应时间,默认为5秒),TimeUnit 分别为其设置单位。
package com.xc.a.config;
import feign.Request;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
/**
* @author wyp
*/
@Configuration
public class FeignConfig {
@Bean
public Request.Options options() {
return new Request.Options(5, TimeUnit.SECONDS,3000,TimeUnit.MILLISECONDS,true);
}
}
4.2 配置文件的方式设置超时时间
feign:
client:
config:
# 针对的服务名
FEIGN-B:
# 连接超时时间
connectimeout: 5000
# 请求处理超时时间
readTimeout: 3000
5. OpenFeign自定义拦截器
客户端–①–>消费端–②–>提供端
springMVC的拦截器作用在①处,而feign的拦截器是作用在②处。
5.1 先自定义拦截器
通过实现RequestInterceptor类自定义拦截器
package com.xc.a.config;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.UUID;
/**
* @author wyp
*/
public class MyFeignInterceptor implements RequestInterceptor {
Logger logger = LoggerFactory.getLogger(this.getClass());
public void apply(RequestTemplate template) {
String accessToken = UUID.randomUUID().toString();
template.header("Authorization",accessToken);
logger.info("feign拦截器--在这里做一些操作");
}
}
5.2 配置类的方式
用Bean的方式注入MyFeignInterceptor自定义拦截器。
package com.xc.a.config;
import org.springframework.context.annotation.Bean;
/**
* @author wyp
*/
public class FeignConfig {
/**
* 自定义拦截器
*/
@Bean
public MyFeignInterceptor myFeignInterceptor() {
return new MyFeignInterceptor();
}
}
5.3 配置文件的方式
feign:
client:
config:
# 针对的服务名
FEIGN-B:
# 配置拦截器 自定义拦截器类路径
requestInterceptors[0]:
com.xc.a.config.MyFeignInterceptor