zoukankan      html  css  js  c++  java
  • Feign整合Ribbon和Hystrix源码解析

    在上篇文章Feign自动装配中,我们提到了Feign的自动装配的原理,以及Feign整合Ribbon和Hystrix的核心在类FeignClientFactoryBean中,那么本篇文章就来揭开这个类的神秘面纱

    首先,我们看到这个类实现了FactoryBean这个接口,这个接口的主要作用就是利用getObject()来创建一些实例化过程比较复杂的bean,更多关于这个接口的内容可以参考这篇文章:Spring扩展点之FactoryBean接口

    我们直接来看这个类的getObject方法:

    	public Object getObject() throws Exception {
    		FeignContext context = applicationContext.getBean(FeignContext.class);
    		Feign.Builder builder = feign(context);
    
    		if (!StringUtils.hasText(this.url)) {
    			String url;
    			if (!this.name.startsWith("http")) {
    				url = "http://" + this.name;
    			}
    			else {
    				url = this.name;
    			}
    			url += cleanPath();
    			return loadBalance(builder, context, new HardCodedTarget<>(this.type,
    					this.name, url));
    		}
    		if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
    			this.url = "http://" + this.url;
    		}
    		String url = this.url + cleanPath();
    		Client client = getOptional(context, Client.class);
    		if (client != null) {
    			if (client instanceof LoadBalancerFeignClient) {
    				// not lod balancing because we have a url,
    				// but ribbon is on the classpath, so unwrap
    				client = ((LoadBalancerFeignClient)client).getDelegate();
    			}
    			builder.client(client);
    		}
    		Targeter targeter = get(context, Targeter.class);
    		return targeter.target(this, builder, context, new HardCodedTarget<>(
    				this.type, this.name, url));
    	}
    
    1. 获取bean:FeignContext,这个bean上篇文章已经说过了。里面包含了各个Feign客户端的配置
    2. 构建Feign.Builder

    设置编解码器

    	protected Feign.Builder feign(FeignContext context) {
    		FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    		Logger logger = loggerFactory.create(this.type);
    
    		Feign.Builder builder = get(context, Feign.Builder.class)
    				.logger(logger)
    				.encoder(get(context, Encoder.class))
    				.decoder(get(context, Decoder.class))
    				.contract(get(context, Contract.class));
    		configureFeign(context, builder);
    
    		return builder;
    	}
    

    设置日志、重试策略、错误code解析、超时时间、拦截器

    	protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
    		Logger.Level level = getOptional(context, Logger.Level.class);
    		if (level != null) {
    			builder.logLevel(level);
    		}
    		Retryer retryer = getOptional(context, Retryer.class);
    		if (retryer != null) {
    			builder.retryer(retryer);
    		}
    		ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
    		if (errorDecoder != null) {
    			builder.errorDecoder(errorDecoder);
    		}
    		Request.Options options = getOptional(context, Request.Options.class);
    		if (options != null) {
    			builder.options(options);
    		}
    		Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
    				this.name, RequestInterceptor.class);
    		if (requestInterceptors != null) {
    			builder.requestInterceptors(requestInterceptors.values());
    		}
    
    		if (decode404) {
    			builder.decode404();
    		}
    	}
    
    1. 判断Feign是否指定url属性,正常情况下是没有指定url的,所以会添加一个http://前缀
    4. 获取代理
    protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
                                HardCodedTarget<T> target) {
    
        Client client = getOptional(context, Client.class);
        if (client != null) {
            builder.client(client);
            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-netflix-ribbon?");
    }
    

    首先获取Client的实现类,这个实现类是LoadBalancerFeignClient,这个类里融合了Ribbon的相关内容。然后将Client包装到Feign.Builder中,接着获取Targeter,这里我们存在Hystrix环境,所以Targeter的实现类为HystrixTargeter

    	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
    						Target.HardCodedTarget<T> target) {
    		if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
    			return feign.target(target);
    		}
    		feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
    		SetterFactory setterFactory = getOptional(factory.getName(), context,
    			SetterFactory.class);
    		if (setterFactory != null) {
    			builder.setterFactory(setterFactory);
    		}
    		Class<?> fallback = factory.getFallback();
    		if (fallback != void.class) {
    			return targetWithFallback(factory.getName(), context, target, builder, fallback);
    		}
    		Class<?> fallbackFactory = factory.getFallbackFactory();
    		if (fallbackFactory != void.class) {
    			return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
    		}
    
    		return feign.target(target);
    	}
    

    接着以Feign客户端设置了fallback为例

    	private <T> T targetWithFallback(String feignClientName, FeignContext context,Target.HardCodedTarget<T> target,HystrixFeign.Builder builder, Class<?> fallback) {
    		T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
    		return builder.target(target, fallbackInstance);
    	}
    

    接着就是这个代理的创建,现在这个代理中包含了Ribbon和Hystrix。而这个代理类的实现类是HystrixInvocationHandler

      public Object invoke(final Object proxy, final Method method, final Object[] args)
          throws Throwable {
        // early exit if the invoked method is from java.lang.Object
        // code is the same as ReflectiveFeign.FeignInvocationHandler
        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();
        }
    
        HystrixCommand<Object> hystrixCommand = new HystrixCommand<Object>(setterMethodMap.get(method)) {
          @Override
          protected Object run() throws Exception {
            try {
              return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
            } catch (Exception e) {
              throw e;
            } catch (Throwable t) {
              throw (Error) t;
            }
          }
    
          @Override
          protected Object getFallback() {
            if (fallbackFactory == null) {
              return super.getFallback();
            }
            try {
              Object fallback = fallbackFactory.create(getExecutionException());
              Object result = fallbackMethodMap.get(method).invoke(fallback, args);
              if (isReturnsHystrixCommand(method)) {
                return ((HystrixCommand) result).execute();
              } else if (isReturnsObservable(method)) {
                // Create a cold Observable
                return ((Observable) result).toBlocking().first();
              } else if (isReturnsSingle(method)) {
                // Create a cold Observable as a Single
                return ((Single) result).toObservable().toBlocking().first();
              } else if (isReturnsCompletable(method)) {
                ((Completable) result).await();
                return null;
              } else {
                return result;
              }
            } catch (IllegalAccessException e) {
              // shouldn't happen as method is public due to being an interface
              throw new AssertionError(e);
            } catch (InvocationTargetException e) {
              // Exceptions on fallback are tossed by Hystrix
              throw new AssertionError(e.getCause());
            }
          }
        };
    
        if (isReturnsHystrixCommand(method)) {
          return hystrixCommand;
        } else if (isReturnsObservable(method)) {
          // Create a cold Observable
          return hystrixCommand.toObservable();
        } else if (isReturnsSingle(method)) {
          // Create a cold Observable as a Single
          return hystrixCommand.toObservable().toSingle();
        } else if (isReturnsCompletable(method)) {
          return hystrixCommand.toObservable().toCompletable();
        }
        return hystrixCommand.execute();
      }
    

    这里就利用到了Hystrix的知识,更多关于Hystrix的内容可以参考之前的文章

    接着深入invoke方法

    public Object invoke(Object[] argv) throws Throwable {
        RequestTemplate template = buildTemplateFromArgs.create(argv);
        Retryer retryer = this.retryer.clone();
        while (true) {
            try {
                return executeAndDecode(template);
            } catch (RetryableException e) {
                retryer.continueOrPropagate(e);
                if (logLevel != Logger.Level.NONE) {
                    logger.logRetry(metadata.configKey(), logLevel);
                }
                continue;
            }
        }
    }
    

    这里构建了请求信息和重试策略,具体请求内容在下面:

    Object executeAndDecode(RequestTemplate template) 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 10
          response.toBuilder().request(request).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);
    
        boolean shouldClose = true;
        try {
          if (logLevel != Logger.Level.NONE) {
            response =
                logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
            // ensure the request is set. TODO: remove in Feign 10
            response.toBuilder().request(request).build();
          }
          if (Response.class == metadata.returnType()) {
            if (response.body() == null) {
              return response;
            }
            if (response.body().length() == null ||
                    response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
              shouldClose = false;
              return response;
            }
            // Ensure the response body is disconnected
            byte[] bodyData = Util.toByteArray(response.body().asInputStream());
            return response.toBuilder().body(bodyData).build();
          }
          if (response.status() >= 200 && response.status() < 300) {
            if (void.class == metadata.returnType()) {
              return null;
            } else {
              return decode(response);
            }
          } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
            return decode(response);
          } else {
            throw errorDecoder.decode(metadata.configKey(), response);
          }
        } catch (IOException e) {
          if (logLevel != Logger.Level.NONE) {
            logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
          }
          throw errorReading(request, response, e);
        } finally {
          if (shouldClose) {
            ensureClosed(response.body());
          }
        }
      }
    
    

    再往下深入就是Ribbon的负载均衡了,具体内容可以参考之前的文章

    原文地址

  • 相关阅读:
    基于Servlet+JSP+JavaBean开发
    jsp&servlet报红线javax.servlet.jsp.XXXX cannot be resolved to a type类似错误解决办法
    java为什么要定义接口等相关解释
    SpringMVC的注解方式
    MyBatis映射
    hibernate、JDBC 实现 oracle ID 的自动增加 功能
    SSH框架的JSP网站添加数据为什么没反应
    又考完一科 又过完一年
    一个招标书文件的需求分析
    《需求分析》读后感之二
  • 原文地址:https://www.cnblogs.com/zhixiang-org-cn/p/11832875.html
Copyright © 2011-2022 走看看