精选文章 Feign整合Ribbon和Hystrix源码解析

Feign整合Ribbon和Hystrix源码解析

作者:javaxuexilu 时间: 2019-11-05 08:08:00
javaxuexilu 2019-11-05 08:08:00

我们直接来看这个类的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 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 loadBalance(Feign.Builder builder, FeignContext context,	
                            HardCodedTarget 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 target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,	
                        Target.HardCodedTarget 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 targetWithFallback(String feignClientName, FeignContext context,Target.HardCodedTarget 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 hystrixCommand = new HystrixCommand(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的负载均衡了,具体内容可以参考之前的文章

往期好文

Feign整合Ribbon和Hystrix源码解析1

好文章就该:收藏、转发、在看!!!

勿删,copyright占位
分享文章到微博
分享文章到朋友圈

上一篇:僵尸进程可以被“杀死”吗?

下一篇:Ubuntu16.04安装CUDA9.0+CUDNN

CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。