首页 > 其他分享 >微服务RPC框架-Feign

微服务RPC框架-Feign

时间:2023-03-10 11:45:47浏览次数:26  
标签:feign 调用 请求 框架 default Feign RPC class

一个成熟的微服务集群,内部调用必然依赖一个好的RPC框架,比如:基于http协议的feign,基于私有tcp协议的dubbo。本文内容介绍feign。

一、What?
如果不使用rpc框架,那么调用服务需要走http的话,配置请求head、body,然后才能发起请求。获得响应体后,还需解析等操作,十分繁琐。

Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了http调用流程。

二、How?
feign底层基于http协议,适应绝大部分内外部API调用的应用场景,并且SpringCloud对feign已经有了比较好的封装。使用上可以依赖于SpringCloud封装过的feign:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1
2
3
4
Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的
persistence connection。建议替换为Apache HttpClient,作为底层的http client包,从而获取连接池、超时时间等与性能息息相关的控制能力:

<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
1
2
3
4
在配置文件中启用ApacheHttpClient:

feign.httpclient.enabled=true
1
FeignClient参数:

public @interface FeignClient {
@AliasFor("name")
String value() default "";

/** @deprecated */
@Deprecated
String serviceId() default "";

String contextId() default "";

// 指定FeignClient的名称
@AliasFor("value")
String name() default "";

String qualifier() default "";

// 全路径地址或hostname,http或https可选
String url() default "";
// 当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException
boolean decode404() default false;
// Feign配置类,可以自定义Feign的LogLevel
Class<?>[] configuration() default {};
// 容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑
Class<?> fallback() default void.class;
// 工厂类,用于生成fallback类实例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
Class<?> fallbackFactory() default void.class;
// 定义当前FeignClient的统一前缀,类似于controller类上的requestMapping
String path() default "";

boolean primary() default true;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
三、Why?
启动时,程序会进行包扫描,扫描所有包下所有@FeignClient注解的类,并将这些类注入到spring的IOC容器中。
当定义的Feign中的接口被调用时,通过JDK的动态代理来生成RequestTemplate。RequestTemplate中包含请求的所有信息,如请求参数,请求URL等。
RequestTemplate声场Request,然后将Request交给client处理,client默认是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
最后client封装成LoadBaLanceClient,结合ribbon负载均衡地发起调用。
使用Feign涉及两个注解:@EnableFeignClients,用来开启Feign;@FeignClient,标记要用Feign来拦截的请求接口。

 

1、启用
启动配置上检查是否有@EnableFeignClients注解,如果有该注解,则开启包扫描,扫描被@FeignClient注解的接口。扫描出该注解后,
通过beanDefinition注入到IOC容器中,方便后续被调用使用。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};
}
1
2
3
4
5
6
7
8
9
10
11
@EnableFeignClients 是关于注解扫描的配置,使用了@Import(FeignClientsRegistrar.class)。在spring context处理过程中,这个Import会在解析Configuration的时候当做提供了其他的bean definition的扩展,Spring通过调用其registerBeanDefinitions方法来获取其提供的bean definition。

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}

}
1
2
3
4
5
6
7
8
9
FeignClientsRegistrar里重写了spring里ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法。也就是在启动时,处理了EnableFeignClients注解后,registry里面会多出一些关于Feign的BeanDefinition。

2、发起请求
ReflectiveFeign内部使用了jdk的动态代理为目标接口生成了一个动态代理类,这里会生成一个InvocationHandler统一的方法拦截器,同时为接口的每个方法生成一个SynchronousMethodHandler拦截器,并解析方法上的元数据,生成一个http请求模板RequestTemplate。

public class ReflectiveFeign extends Feign {

@Override
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);

for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Feign内置了一个重试器,当HTTP请求出现IO异常时,Feign会有一个最大尝试次数发送请求:

final class SynchronousMethodHandler implements MethodHandler {

@Override
public Object invoke(Object[] argv) throws Throwable {
// 根据输入参数,构造Http请求
RequestTemplate template = buildTemplateFromArgs.create(argv);
// 克隆出一份重试器
Retryer retryer = this.retryer.clone();
// 尝试最大次数,如果中间有结果,直接返回
while (true) {
try {
return executeAndDecode(template);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Feign真正发送HTTP请求是委托给feign.Client来做的:

public interface Client {
Response execute(Request request, Options options) throws IOException;
class Default implements Client {
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}
}
}
1
2
3
4
5
6
7
8
9
10
默认底层通过JDK的java.net.HttpURLConnection实现了feign.Client接口类。在每次发送请求的时候,都会创建新的HttpURLConnection链接,这样的话默认情况下Feign的性能很差,一般扩展该接口,比如使用Apache的HttpClient或者OkHttp3等基于连接池的高性能Http客户端。

注意:SynchronousMethodHandler并不是直接完成远程URL的请求,而是通过负载均衡机制,定位到合适的远程server服务器,然后再完成真正的远程URL请求。即:SynchronousMethodHandler实例的client成员,其实际不是feign.Client.Default类型,而是LoadBalancerFeignClient客户端负载均衡类型。

3、性能分析
Feign框架比较小巧,在处理请求转换和消息解析的过程中,基本上没什么时间消耗。真正影响性能的,是处理Http请求的环节。可以从这个方面着手分析系统的性能提升点。


————————————————
版权声明:本文为CSDN博主「yaofengdoit」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fengqifengluoyao/article/details/106447512

标签:feign,调用,请求,框架,default,Feign,RPC,class
From: https://www.cnblogs.com/gaoyanbing/p/17202830.html

相关文章

  • 日志框架之日志门面SLF4J的使用
    (日志框架之日志门面SLF4J的使用)SLF4J概述SLF4J(SimpleLoggingFacadeforJava)是一种Java编程语言的日志门面(loggingfacade)。它提供了一种将应用程序代码与特定......
  • 通过Feign在服务之间传递header请求头信息
    微服务等情况下,通过feign调用接口,很多情况下需要在fegin所在的服务中心获取header等等的信息,默认情况下是不带有的。所以,最好是自行处理,转发所有,以下以header为例@Slf4jpubl......
  • 基于OpenHarmony/HarmonyOS操作系统的ArkUI框架——Harmony原生开发
    一.基于OpenHarmony/HarmonyOS操作系统的ArkUI框架——Harmony原生开发开发需要的IDE:HUAWEIDevEcoStudio1.1什么是ArkUI框架?ArkUI是一套构建分布式应用界面的声明......
  • Celery框架从入门到精通
    目录Celery介绍、安装、基本使用一、Celery服务1、celery架构2、celery快速使用二、Celer包结构1、创建clery包结构2、Celery执行异步任务、延迟任务、定时任务三、Django......
  • JUC(七)分支合并框架
    JUC分支合并框架简介Fork/Join可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务的结果合并称为最终的计算结果。Fork:负责将任务拆分Join:合并拆分任务For......
  • 一Spring框架基础--2设计模式
    一Spring框架基础--2设计模式1.3spring用到的设计模式1.3.1责任链模式有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象......
  • 一Spring框架基础--3动态代理
    一Spring框架基础--3动态代理1.4代理模式1.4.1Java代码执行流程1.4.1.1class文件Java编译器编译好Java文件后,产生.class文件在磁盘,该文件是二进制文件,内容只有jvm......
  • IdentityServer4: 集成 AspNetCore Identity 框架
    IdentityServer4集成identity框架新增依赖包在IdentityServer4项目中新增集成AspNetCoreIdentity所需的依赖包:<PackageReferenceInclude="IdentityServer......
  • SpringBoot框架
    SpringBoot框架https://blog.csdn.net/friggly/article/details/123888590?spm=1001.2101.3001.6650.8&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7......
  • 02、大数据安全分析技术框架与关键技术
    转载公众号《微言晓意》,仅用于个人学习大数据分析通过对安全告警、系统日志以及网络流量等海量多源异构数据进行采集、存储与分析,打破原有网络安全烟囱式防护模式,将所有安......