本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Feign组件
Feign的核心组件
- Encoder和Decoder:Encoder编码器,如果调用接口的时候,传递的参数是个对象,Feign需要将对象进行encode序列化成JSON;Decoder解码器,处理收到的JSON,转换成对象。
- Logger:打印日志。
- Contract:Feign的@FeignClient注解和Spring Web Mvc支持的@PathVariable、@RequestMapping、@RequestParam等注解结合起来使用。Feign不支持Spring Web Mvc的注解,Contract组件负责解释Spring Web Mvc的注解,使Feign可以跟第三方的注解结合起来使用。
- Feign.Builder:FeignClient的一个实例构造器。
- FeignClient:Feign入口。FeignClient,里面包含了一系列的组件,比如说Encoder、Decoder、Logger、Contract等。
Spring Cloud环境下Feign的默认组件
Decoder:ResponseEntityDecoder
Encoder:SpringEncoder
Logger:Slf4jLogger
Contract:SpringMvcContract
Feign.Builder:HystrixFeign.Builder
FeignClient:LoadBalancerFeignClient。底层整合Ribbon
自定义Feign组件
package center.leon.eurekaconsumerribbonfeignapi.product.config;
import feign.Request;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author : Leon on XXM Mac
* @since : create in 2022/7/10 3:43 PM
*/
@Slf4j
@Configuration
public class ProductFacadeServiceFeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
String url = requestTemplate.url();
log.info("feign requestInterceptor log url : {}", url);
};
}
}
Feign的拦截器可以实现对Feign的请求进行拦截。实现RequestInterceptor,然后所有的请求发送之前都会被RequestInterceptor拦截。
Spring Cloud整合Feign自定义参数配置
普通参数配置
压缩配置
日志配置
普通参数配置
# feign service config
## feign eureka-provider-ribbon-feign-api-impl service config
feign.client.config.eureka-provider-ribbon-feign-api-impl.connect-timeout=5000
feign.client.config.eureka-provider-ribbon-feign-api-impl.read-timeout=5000
feign.client.config.eureka-provider-ribbon-feign-api-impl.logger-level=full
feign.client.config.eureka-provider-ribbon-feign-api-impl.decode404=false
## feign default service config
feign.client.config.default.connect-timeout=3000
feign.client.config.default.read-timeout=3000
feign.client.config.default.logger-level=headers
feign.client.config.default.decode404=true
压缩配置
# feign compression config
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
feign.compression.response.enabled=true
日志配置
logger有四种类型:NONE,BASIC,HEADERS, FULL
# feign log config
logging.level.center.leon.eurekaconsumerribbonfeignapi.product.server.ProductFacadeServiceFeign=debug
@Bean
public Logger.Level logger() {
return Logger.Level.FULL;
}
2022-07-10 16:36:09.595 DEBUG 18741 --- [nio-9090-exec-1] c.l.e.p.s.ProductFacadeServiceFeign : [ProductFacadeServiceFeign#listProduct] <--- HTTP/1.1 200 (218ms)
2022-07-10 16:36:09.595 DEBUG 18741 --- [nio-9090-exec-1] c.l.e.p.s.ProductFacadeServiceFeign : [ProductFacadeServiceFeign#listProduct] connection: keep-alive
2022-07-10 16:36:09.595 DEBUG 18741 --- [nio-9090-exec-1] c.l.e.p.s.ProductFacadeServiceFeign : [ProductFacadeServiceFeign#listProduct] content-type: application/json
2022-07-10 16:36:09.595 DEBUG 18741 --- [nio-9090-exec-1] c.l.e.p.s.ProductFacadeServiceFeign : [ProductFacadeServiceFeign#listProduct] date: Sun, 10 Jul 2022 08:36:09 GMT
2022-07-10 16:36:09.595 DEBUG 18741 --- [nio-9090-exec-1] c.l.e.p.s.ProductFacadeServiceFeign : [ProductFacadeServiceFeign#listProduct] keep-alive: timeout=60
2022-07-10 16:36:09.595 DEBUG 18741 --- [nio-9090-exec-1] c.l.e.p.s.ProductFacadeServiceFeign : [ProductFacadeServiceFeign#listProduct] transfer-encoding: chunked
2022-07-10 16:36:09.595 DEBUG 18741 --- [nio-9090-exec-1] c.l.e.p.s.ProductFacadeServiceFeign : [ProductFacadeServiceFeign#listProduct]
2022-07-10 16:36:09.597 DEBUG 18741 --- [nio-9090-exec-1] c.l.e.p.s.ProductFacadeServiceFeign : [ProductFacadeServiceFeign#listProduct] [{"id":1,"productName":"华为手机","productNum":300,"productPrice":4999.0},{"id":2,"productName":"小米手机","productNum":400,"productPrice":2999.0}]
2022-07-10 16:36:09.597 DEBUG 18741 --- [nio-9090-exec-1] c.l.e.p.s.ProductFacadeServiceFeign : [ProductFacadeServiceFeign#listProduct] <--- END HTTP (155-byte body)
Feign工作流程
Feign针对@FeignClient的注解的接口,通过动态代理生成动态代理实现类。根据Spring Web Mvc注解构造Http请求。
Feign整合了Ribbon基于Ribbon进行负载均衡。
服务集群结合Eureka来,Ribbon从Eureka获取对应的服务的server list,交给Ribbon进行负载均衡。
Feign核心机制
@EnableFeignClients注解
@FeignClient注解
Feign通过@EnableFeignClients注解@Import(FeignClientsRegistrar.class)去扫描所有打了@FeignClient注解的接口。
扫描到@FeignClient注解标注的接口之后,会针对接口生成Feign动态代理,以及解析和处理Spring Web Mvc注解,比如@RequestMapping,@PathVarialbe,@RequestParam,基于Spring Web Mvc的注解,生成对应的http请求。
扫描@FeignClient注解的接口
Application启动类的@EnableFeignClients注解,会触发FeignClientsRegistrar(ImportBeanDefinitionRegistrar)#registerBeanDefinitions(),扫描@EnableFeignClients注解标注类所在包及子包下面的@FeignClient注解的接口。
@FeignClient:
@FeignClient注解标注一个接口,这个接口会被创建为一个REST client(发送restful请求的客户端),然后将这个REST client注入其他的组件(比如OrderService),如果启用Ribbon,会采用负载均衡的方式,来进行http请求的发送,可以用@RibbonClient标注一个配置类,在配置类里可以自定义Ribbon的ILoadBalancer。
@RibbonClient的名称必须与@FeignClient的名称一样的。
package center.leon.eurekaconsumerribbonfeignapi.product.config;
import com.netflix.loadbalancer.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author : Leon on XXM Mac
* @since : create in 2022/7/11 9:51 AM
*/
@Configuration
public class ProductFacadeServiceRibbonConfig {
@Bean
public IRule iRule() {
return new RandomRule();
}
}
package center.leon.eurekaconsumerribbonfeignapi.product.server;
import center.leon.eurekaconsumerribbonfeignapi.product.config.ProductFacadeServiceFeignConfig;
import center.leon.eurekaconsumerribbonfeignapi.product.config.ProductFacadeServiceRibbonConfig;
import center.leon.eurekaproviderribbonfeignapi.product.service.ProductFacadeService;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.FeignClient;
/**
* @author : Leon on XXM Mac
* @since : create in 2022/7/10 1:45 PM
*/
@RibbonClient(value = "eureka-provider-ribbon-feign-api-impl", configuration =
ProductFacadeServiceRibbonConfig.class)
@FeignClient(value = "eureka-provider-ribbon-feign-api-impl", configuration =
ProductFacadeServiceFeignConfig.class)
public interface ProductFacadeServiceFeign extends ProductFacadeService {
}
使用@FeignClient注解,让Feign对这个接口创建一个对应的动态代理,这个动态代理就是REST client,发送rest请求的客户端
@FeignClient:
value & name:要调用的那个服务名称;
url:指定要请求的地址(`http://localhost:8080`);
decode404:用404替代抛出FeignException异常;
configurtation:指定一个配置类,可以在里面自定义Encoder、Decoder、Contract。
@EnableFeignClients:
扫描标注@FeignClient注解的接口。
basePackages:指定要扫描的包路径。
@Import(FeignClientsRegistrar.class):
负责扫描@FeignClient注解,并将@FeignClient标注的接口注册到BeanDefinitionRegistry中。
FeignClientRegistrar & ImportBeanDefinitionRegistrar:
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
注册默认的配置:这个是什么意思?一看就是解析相关的一些配置,注册到自己身体里去
注册FeignClients:扫描各包下面的@FeignClient注解,然后生成@FeignClient的动态代理。
Spring boot启动时,@Import机制调用FeignClientRegistrar.registerBeanDefinitions()方法,扫描@FeignClient注解标注的接口,生成对应的动态代理实例。