原因
Spring Cloud 作为微服务框架,工作当中必用。起因也很简略,Spring cloud 依靠Springboot,背靠Spring Framework,又有Eureka 和 Alibaba 两个大厂的反对,大多数的互联网公司会优先选择以此作为后端开发框架。在服务调用方面,OpenFeign 和 Dubbo 都是非常优良的组件,OpenFeign 由 Spring cloud 推出,其应用非常风行。
简述
Spring Cloud OpenFeign 是在feign的根底上,与Spring Cloud集成,做出了很多的扭转,比方SpringMvcContract,融入服务发现、负载平衡等等。对罕用技术进行原理剖析,晋升技术积攒的同时,还能理解OpenFeign的瓶颈,为后续的调优工作做筹备。
注释
OpenFeign 应用Http协定调用服务接口,提供简洁的RPC调用,应用大量的注解就能够达到靠近本地办法调用的成果。
应用
引入pom 依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
性能开启
@EnableFeignClients({"com.lake.feign.uaa"})@SpringBootApplicationpublic class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); }}
定义feign接口
@FeignClient(value = "uaa", fallback = UaaEchoFallback.class)public interface UaaEchoFeign { @GetMapping(value = "/api/feign/uaa/echo", consumes = MediaType.APPLICATION_JSON_VALUE) ResultFeignBean<String> echo(@RequestParam(value = "echo") String echo);}
- 其余服务须要对提供对应的Http接口。
- 服务当中引入UaaEchoFeign接口,调用本地办法就能够实现RPC调用。
OpenFeign的应用十分的不便,相比于单体利用开发,仅仅是多应用了一些注解。然而背地的技术原理值得剖析一番,如此便捷的技术背地的技术也很值得大家学习。
原理
OpenFeign 的大体工作流程如下:
- 开启OepnFeign,扫描Feign接口,注册到SpringIoc。
- Bean创立时生成代理类。
- RPC调用时,执行封装Http申请。
- 应用服务发现获取能够拜访的服务实例。
- 应用负载平衡筛选服务实例地址。
- 发动调用。
- 对调用后果进行解析解决。
Feign性能开启
@EnableFeignClients 注解开启了Feign性能的尾声。
- 正文阐明此注解会扫描@FeignClient注解的类。
- 应用@EnableFeignClients注解时能够指定须要扫描的package 或者 class。
- 指定自定义的配置类。
- 指定@FeignClient标注的类,不须要再去执行扫描。
- @Import(FeignClientsRegistrar.class),FeignClientsRegistrar 拿到开启的接力棒。
@EnableFeignClients 注解在classpath中,会在Springboot启动时,扫描到此注解并解析其中的@Import注解导入的类。
FeignClientsRegistrar 实现ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions()。
- 加载@EnableFeignClients中指定的配置类
- 扫描@FeignClient注解类,或者注册@EnableFeignClients中指定的FeignClient类
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // 加载@EnableFeignClients中指定的配置类 registerDefaultConfiguration(metadata, registry); // 扫描@FeignClient注解类 或 注册@EnableFeignClients中指定的FeignClient类 registerFeignClients(metadata, registry); }
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>(); Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName()); AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class); // 获取@EnableFeignClients中指定的FeignClient类 final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) { // 扫描@FeignClient注解类 ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class)); Set<String> basePackages = getBasePackages(metadata); for (String basePackage : basePackages) { candidateComponents.addAll(scanner.findCandidateComponents(basePackage)); } } else { for (Class<?> clazz : clients) { candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz)); } } for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface"); Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes(FeignClient.class.getCanonicalName()); String name = getClientName(attributes); // 注册FeignClient注解中指定的配置类 registerClientConfiguration(registry, name, attributes.get("configuration")); // 注册FeignClient类,beanDefinition中指定应用 FeignClientFactoryBean 类 registerFeignClient(registry, annotationMetadata, attributes); } } }
Feign性能的开启仅通过一个FeignClientsRegistrar,扫描FeignClient类,解析FeignClient注解中指定的属性,封装到 BeanDefinition,作为一个FeignClientFactoryBean,注册到IOC当中。
将接力棒传到 FeignClientFactoryBean 中,Bean创立时执行FactoryBean的getObject获取Feign接口代理类。
生成Feign代理
FeignClientFactoryBean.getObject() 会生成Feign接口代理类。
spring cloud openFeign starter中的 spring.factories中,EnableAutoConfiguration接口的实现类 FeignAutoConfiguration,会注册一个 FeignContext Bean,FeignContext 继承 NamedContextFactory,NamedContextFactory 实现 ApplicationContextAware,利用执行Aware回调时会设置NamedContextFactory的 parent,创立FeignContext 是就会将以后利用设置为父context。
@FeignClient注解中的value值是服务名,利用会为每个服务创立一个FeignContext,Feign 配置时反对服务级别的配置,比方为用户服务设置一个超时工夫之类的。
Feign代理类创立时,从FeignContext中获取Feign.Builder Encoder Decoder Contract Client Targetar... 单例Bean,而后依据全局配置或者服务级别的Feign配置对Feign.Builder进行定制。
client 和 targetar 有多个实现,LoadBalancerFeignClient 具备负载平衡性能,DefaultTargeter feign的默认实现。Hystrix 和 seata 引入的同时也会引入新的实现。
Feign.Builder.build() 实现对Feign对象ReflectiveFeign的创立。
public Feign build() { // ... 省略代码 SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); }
ReflectiveFeign.newInstance() 创立Feign接口的代理类。
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>(); // feign 接口类中的办法转换为 DefaultMethodHandler 或者 SynchronousMethodHandler 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))); } } // 创立代理对象 ReflectiveFeign.FeignInvocationHandler 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; }
Feign代理对象 ReflectiveFeign.FeignInvocationHandler 生成到此已实现,其中应用到 父子Context 、 builder 结构、jdk反射、 FactoryBean 技术。过程还是比拟精彩的。
RPC调用过程
ReflectiveFeign.FeignInvocationHandler中的invoke() 就是RPC的工作过程。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // ... 省略代码 // 办法名和 DefaultMethodHandler 的映射。 return dispatch.get(method).invoke(args); }
将接力棒交给 SynchronousMethodHandler.invoke(),feign接口办法到Http申请的转化。
public Object invoke(Object[] argv) throws Throwable { // template 构建 RequestTemplate template = buildTemplateFromArgs.create(argv); Options options = findOptions(argv); Retryer retryer = this.retryer.clone(); while (true) { try { return executeAndDecode(template, options); } catch (RetryableException e) { continue; } } }
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable { // 筹备申请 Request request = targetRequest(template); Response response; long start = System.nanoTime(); try { // 执行申请 埋个彩蛋 response = client.execute(request, options); // ensure the request is set. TODO: remove in Feign 12 // 申请响应 response = response.toBuilder() .request(request) .requestTemplate(template) .build(); } catch (IOException e) { } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); if (decoder != null) // Decoder 可自定义 return decoder.decode(response, metadata.returnType()); CompletableFuture<Object> resultFuture = new CompletableFuture<>(); asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response, metadata.returnType(), elapsedTime); try { return resultFuture.join(); } catch (CompletionException e) { } }
Rpc调用过程逻辑还是比较简单的。
OpenFeign 是一个提供开发效率的框架,也对SpringCloud提供的性能进行交融,也是对微服务框架剖析的第一站,后续会基于此文进行扩大,比方 Hystrix ribbon sluth...