asp装修公司网站源码,网站建设360,在线qq登录无需下载,外卖网站建设的策划方案openfeign是通过FeignClientFactoryBean生成动态代理对象的方式实现http客户端无感调用#xff0c;可以做到像定义接口一样写http客户端调用代码。 配置Feign接口后#xff0c;我们通常会在SpringBoot项目启动类上标记EnableFeignClients#xff0c;这个是生成动态代理对象的…openfeign是通过FeignClientFactoryBean生成动态代理对象的方式实现http客户端无感调用可以做到像定义接口一样写http客户端调用代码。 配置Feign接口后我们通常会在SpringBoot项目启动类上标记EnableFeignClients这个是生成动态代理对象的入口。
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.TYPE)
Documented
Import(FeignClientsRegistrar.class)
public interface EnableFeignClients {/*** basePackages 配置的别称*/String[] value() default {};/*** 扫描包路径下有FeignClient注解的接口*/String[] basePackages() default {};/*** 扫描类对应包下有FeignClient注解的接口*/Class?[] basePackageClasses() default {};/*** 默认配置*/Class?[] defaultConfiguration() default {};/*** 直接指定标记了FeignClient的类*/Class?[] clients() default {};}EnableFeignClients里面的配置主要是为了找到FeignClient接口正常情况下可指定basePackages路径或者直接不加会直接扫描启动类及其对应包下面的所有类去找到所有标记了FiegnClient的接口。 这里最关键的是Import(FeignClientsRegistrar.class)引入了FeignClientsRegistrar类。 首先看下FeignClientsRegistrar实现的接口ImportBeanDefinitionRegistrarSpring IOC在创建bean实例的时候 会调ImportBeanDefinitionRegistrar.registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)方法这个方法里面是注册默认配置和注册FeignClient的逻辑。
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {...Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 注册默认配置registerDefaultConfiguration(metadata, registry);// 注册FeignClientregisterFeignClients(metadata, registry);}
}继续看registerFeignClients(metadata, registry)方法前面主要是通过EnableFeignClients注解配置找到所有的Feign接口生成BeanDefinition最后遍历根据BeanDefinition一个个注册FeignClient
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// 通过EnableFeignClients的配置找到对应的Feign接口并生成对应的BeanDefinitionLinkedHashSetBeanDefinition candidateComponents new LinkedHashSet();MapString, Object attrs metadata.getAnnotationAttributes(EnableFeignClients.class.getName());final Class?[] clients attrs null ? null : (Class?[]) attrs.get(clients);if (clients null || clients.length 0) {ClassPathScanningCandidateComponentProvider scanner getScanner();scanner.setResourceLoader(this.resourceLoader);scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));SetString 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 interfaceAnnotatedBeanDefinition beanDefinition (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata beanDefinition.getMetadata();Assert.isTrue(annotationMetadata.isInterface(), FeignClient can only be specified on an interface);MapString, Object attributes annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());String name getClientName(attributes);// 注册客户端配置registerClientConfiguration(registry, name, attributes.get(configuration));// 注册FeignClientregisterFeignClient(registry, annotationMetadata, attributes);}}
}点进去registerFeignClient(registry, annotationMetadata, attributes)这个方法才是真正的注册逻辑。 可以看到方法里面主要是创建了一个FeignClientFactoryBean工厂bean最终通过getObject()生成bean实例。
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,MapString, Object attributes) {String className annotationMetadata.getClassName();Class clazz ClassUtils.resolveClassName(className, null);ConfigurableBeanFactory beanFactory registry instanceof ConfigurableBeanFactory? (ConfigurableBeanFactory) registry : null;String contextId getContextId(beanFactory, attributes);String name getName(attributes);FeignClientFactoryBean factoryBean new FeignClientFactoryBean();factoryBean.setBeanFactory(beanFactory);factoryBean.setName(name);factoryBean.setContextId(contextId);factoryBean.setType(clazz);factoryBean.setRefreshableClient(isClientRefreshEnabled());BeanDefinitionBuilder definition BeanDefinitionBuilder.genericBeanDefinition(clazz, () - {factoryBean.setUrl(getUrl(beanFactory, attributes));factoryBean.setPath(getPath(beanFactory, attributes));factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get(decode404))));Object fallback attributes.get(fallback);if (fallback ! null) {factoryBean.setFallback(fallback instanceof Class ? (Class?) fallback: ClassUtils.resolveClassName(fallback.toString(), null));}Object fallbackFactory attributes.get(fallbackFactory);if (fallbackFactory ! null) {factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class?) fallbackFactory: ClassUtils.resolveClassName(fallbackFactory.toString(), null));}// 生成bean实例return factoryBean.getObject();});definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);definition.setLazyInit(true);validate(attributes);AbstractBeanDefinition beanDefinition definition.getBeanDefinition();beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);beanDefinition.setAttribute(feignClientsRegistrarFactoryBean, factoryBean);// has a default, wont be nullboolean primary (Boolean) attributes.get(primary);beanDefinition.setPrimary(primary);String[] qualifiers getQualifiers(attributes);if (ObjectUtils.isEmpty(qualifiers)) {qualifiers new String[] { contextId FeignClient };}BeanDefinitionHolder holder new BeanDefinitionHolder(beanDefinition, className, qualifiers);BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);registerOptionsBeanDefinition(registry, contextId);
}进入FeignClientFactoryBean的getObject方法里面又继续调了getTarget方法。这里有两种情况一种是指定了url则会通过给的url封装请求 如果没有则从注册中心获取服务地址这里继续看loadBalance(builder, context, new HardCodedTarget(type, name, url));
public Object getObject() {return getTarget();
}T T getTarget() {FeignContext context beanFactory ! null ? beanFactory.getBean(FeignContext.class): applicationContext.getBean(FeignContext.class);Feign.Builder builder feign(context);// 如果没有指定具体的url则通过负载均衡从注册中心获取对应的服务地址信息if (!StringUtils.hasText(url)) {if (LOG.isInfoEnabled()) {LOG.info(For name URL not provided. Will try picking an instance via load-balancing.);}if (!name.startsWith(http)) {url http:// name;}else {url name;}url cleanPath();return (T) loadBalance(builder, context, new HardCodedTarget(type, name, url));}...
}继续loadBalance方法解析这里主要是从FeignContext获取到Client实例和Targeter实例debug的话这两个实例分别对应的类是 Client - TraceRetryableFeignBlockingLoadBalancerClient Targeter - DefaultTargeter
这两个类都很关键
TraceRetryableFeignBlockingLoadBalancerClient是后续调用feign接口时通过RetryableFeignBlockingLoadBalancerClient获取对应的负载均衡服务实例DefaultTargeter则是生成动态代理对象
protected T T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTargetT target) {Client client getOptional(context, Client.class);if (client ! null) {builder.client(client);applyBuildCustomizers(context, builder);Targeter targeter get(context, Targeter.class);// 点进去看是如何生成代理对象return targeter.target(this, builder, context, target);}throw new IllegalStateException(No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?);
}继续看动态代理对象生成的逻辑DefaultTargeter的target方法里面主要调用了feign.target(target)
class DefaultTargeter implements Targeter {Overridepublic T T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,Target.HardCodedTargetT target) {return feign.target(target);}}feign.target先调用了build方法然后再调newInstance方法。先看builde方法的逻辑核心是创建了SynchronousMethodHandler.Factory对象并且返回了ReflectiveFeign对象。 这个工厂对象主要是用来创建SynchronousMethodHandler的而SynchronousMethodHandler就是feign接口每个方法都会封装一个对应的MethodHandler里面包含了封装请求发送请求解析响应的所有流程。
public T T target(TargetT target) {return build().newInstance(target);
}public Feign build() {super.enrich();SynchronousMethodHandler.Factory synchronousMethodHandlerFactory new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,responseInterceptor, logger, logLevel, dismiss404, closeAfterDecode,propagationPolicy, forceDecoding);ParseHandlersByName handlersByName new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,errorDecoder, synchronousMethodHandlerFactory);return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}继续看ReflectiveFeign.newInstance(Target target)方法通过SpringMvcContract解析了feign接口相关的元信息 SynchronousMethodHandler.Factory创建了这个类所有方法的MethodHandler并最终通过InvocationHandlerFactory创建ReflectiveFeign.FeignInvocationHandler最终返回动态代理对象。
public T T newInstance(TargetT target) {// 通过SpringMvcContract解析了feign接口相关的元信息创建MethodHandlerMapString, MethodHandler nameToHandler targetToHandlersByName.apply(target);MapMethod, MethodHandler methodToHandler new LinkedHashMapMethod, MethodHandler();ListDefaultMethodHandler defaultMethodHandlers new LinkedListDefaultMethodHandler();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接口通过FeignInvocationHandler生成了动态代理对象里面包含了feign接口所有方法的MapMethod, MethodHandler dispatch。 后续调用feign接口只需要直接注入使用即可。动态代理对象直接对应方法时最终会调用InvocationHandler的invoke方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (equals.equals(method.getName())) {try {Object otherHandler args.length 0 args[0] ! null ? Proxy.getInvocationHandler(args[0]) : null;return equals(otherHandler);} catch (IllegalArgumentException e) {return false;}} else if (hashCode.equals(method.getName())) {return hashCode();} else if (toString.equals(method.getName())) {return toString();}// 通过方法获取到对应的SynchronousMethodHandler执行return dispatch.get(method).invoke(args);
}在调用feign接口方法时最终会执行SynchronousMethodHandler的invoke方法。
封装请求执行请求解码如果抛RetryableException异常如果不配置Retryer实例则默认是不重试
public Object invoke(Object[] argv) throws Throwable {RequestTemplate template buildTemplateFromArgs.create(argv);Options options findOptions(argv);Retryer retryer this.retryer.clone();while (true) {try {// 执行请求并且对响应结果进行解码return executeAndDecode(template, options);} 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;}}
}executeAndDecode方法执行请求并且处理响应结果
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {Request request targetRequest(template);if (logLevel ! Logger.Level.NONE) {logger.logRequest(metadata.configKey(), logLevel, request);}Response response;long start System.nanoTime();try {// 发送请求response client.execute(request, options);// ensure the request is set. TODO: remove in Feign 12response response.toBuilder().request(request).requestTemplate(template).build();} catch (IOException e) {if (logLevel ! Logger.Level.NONE) {logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));}throw errorExecuting(request, e);}long elapsedTime TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);if (decoder ! null) {return responseInterceptor.aroundDecode(new InvocationContext(decoder, metadata.returnType(), response));}CompletableFutureObject resultFuture new CompletableFuture();// 处理响应结果asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,metadata.returnType(), elapsedTime);try {if (!resultFuture.isDone())throw new IllegalStateException(Response handling not done);return resultFuture.join();} catch (CompletionException e) {Throwable cause e.getCause();if (cause ! null)throw cause;throw e;}
}RetryableFeignBlockingLoadBalancerClient做了三件事
获取负载均衡服务实例信息构建完整请求发送请求并得到响应结果
public Response execute(Request request, Request.Options options) throws IOException {final URI originalUri URI.create(request.url());String serviceId originalUri.getHost();Assert.state(serviceId ! null, Request URI does not contain a valid hostname: originalUri);final LoadBalancedRetryPolicy retryPolicy loadBalancedRetryFactory.createRetryPolicy(serviceId,loadBalancerClient);RetryTemplate retryTemplate buildRetryTemplate(serviceId, request, retryPolicy);return retryTemplate.execute(context - {Request feignRequest null;ServiceInstance retrievedServiceInstance null;SetLoadBalancerLifecycle supportedLifecycleProcessors LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors(loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),RetryableRequestContext.class, ResponseData.class, ServiceInstance.class);String hint getHint(serviceId);DefaultRequestRetryableRequestContext lbRequest new DefaultRequest(new RetryableRequestContext(null, buildRequestData(request), hint));// On retries the policy will choose the server and set it in the context// and extract the server and update the request being madeif (context instanceof LoadBalancedRetryContext) {LoadBalancedRetryContext lbContext (LoadBalancedRetryContext) context;ServiceInstance serviceInstance lbContext.getServiceInstance();if (serviceInstance null) {if (LOG.isDebugEnabled()) {LOG.debug(Service instance retrieved from LoadBalancedRetryContext: was null. Reattempting service instance selection);}ServiceInstance previousServiceInstance lbContext.getPreviousServiceInstance();lbRequest.getContext().setPreviousServiceInstance(previousServiceInstance);supportedLifecycleProcessors.forEach(lifecycle - lifecycle.onStart(lbRequest));// 负载均衡client获取到服务实例信息retrievedServiceInstance loadBalancerClient.choose(serviceId, lbRequest);if (LOG.isDebugEnabled()) {LOG.debug(String.format(Selected service instance: %s, retrievedServiceInstance));}lbContext.setServiceInstance(retrievedServiceInstance);}if (retrievedServiceInstance null) {if (LOG.isWarnEnabled()) {LOG.warn(Service instance was not resolved, executing the original request);}org.springframework.cloud.client.loadbalancer.ResponseServiceInstance lbResponse new DefaultResponse(retrievedServiceInstance);supportedLifecycleProcessors.forEach(lifecycle - lifecycle.onComplete(new CompletionContextResponseData, ServiceInstance, RetryableRequestContext(CompletionContext.Status.DISCARD, lbRequest, lbResponse)));feignRequest request;}else {if (LOG.isDebugEnabled()) {LOG.debug(String.format(Using service instance from LoadBalancedRetryContext: %s,retrievedServiceInstance));}// 生成带IP和端口的完整服务地址并构建feign请求String reconstructedUrl loadBalancerClient.reconstructURI(retrievedServiceInstance, originalUri).toString();feignRequest buildRequest(request, reconstructedUrl);}}org.springframework.cloud.client.loadbalancer.ResponseServiceInstance lbResponse new DefaultResponse(retrievedServiceInstance);LoadBalancerProperties loadBalancerProperties loadBalancerClientFactory.getProperties(serviceId);// 执行请求得到结果Response response LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(delegate, options,feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors, retrievedServiceInstance ! null,loadBalancerProperties.isUseRawStatusCodeInResponseData());int responseStatus response.status();if (retryPolicy ! null retryPolicy.retryableStatusCode(responseStatus)) {if (LOG.isDebugEnabled()) {LOG.debug(String.format(Retrying on status code: %d, responseStatus));}byte[] byteArray response.body() null ? new byte[] {}: StreamUtils.copyToByteArray(response.body().asInputStream());response.close();throw new LoadBalancerResponseStatusCodeException(serviceId, response, byteArray,URI.create(request.url()));}return response;}, new LoadBalancedRecoveryCallbackResponse, Response() {Overrideprotected Response createResponse(Response response, URI uri) {return response;}});
}具体的发送请求流程可以详细debug看下所有细节列出来可能会显得太冗长。过程涉及了很多工厂模式和委托模式自己debug以下会更加清楚一些细节。比如发送http的客户端默认是HttpsURLConnection发送请求LoadBalancerClientConfiguration默认会创建RoundRobinLoadBalancer轮询负载均衡策略可以看下spring-cloud-openfeign-core下的META-INF\spring.factories自动装备类进一步结合项目实际情况还可以自定义EncoderDecoderErrorDecoder等。