springCloud(三续)

接着上一篇,继续学习springCloud

上回晚上太困了,写了一半,学习到了FallBackFactory,用途就是当feignclient出现任何错误时的fallback,并且可以给出错误信息。

看一下具体的接口只有一个泛型方法:

 T create(Throwable cause);

返回一个继承该@feignclient注解的接口的实例。cause即错误信息。可以根据错误信息做不同的逻辑处理。

/**
 * callback
 *
 * @author 刘研
 * @create 2017-08-06 22:15
 **/
@Component
public class MyCallBackFactory implements FallbackFactory<CallFeignService> {

  @Override
  public CallFeignService create(Throwable cause) {
    System.out.println(cause.getCause());
    return name -> "come into fallback factory";
  }
}

这里谢了一个简单的实现,启动工程,可以看到浏览器内容

我们把启动模式改为debug,可以看到在启动时,springcloud会自动调用一次fallbackfactory,测试一下我们的类是否正确。

在创建FeignClientFactoryBean时,spring默认调用bean的getObject方法创建实例,


@Override
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));
}

由于我们没有配置url,所以会调用loadBalance方法,如果设置了url,则不会进行负载平衡,直接调用被代理类的Targeter对象的target方法。

注意:我们跟下去会发现loadBalance最终也会进入target方法,并且是同一个Targeter对象,即HystrixTargeter对象。

在target方法中,由于我们设置了fallbackFactory属性,所以会调用targetWithFallbackFactory,该方法就会为我们自动设置一次异常抛出,用以检验我们的fallbackFactory是否正确。

/* We take a sample fallback from the fallback factory to check if it returns a fallback
that is compatible with the annotated feign interface. */

源代码中如下注释。

这里重点看一下方法参数中的HardCodedTarget对象,该对象是Target接口的默认实现类,Target类就是我们FeignClient实际上真正去做http请求的实现类,该接口有只有四个方法,Class<T> type()即接口所代理的类,String name()所调用的服务名,String url()所调用的服务url。

最主要的就是public Request apply(RequestTemplate input)方法。

看一下文档的描述:

/**
* Targets a template to this target, adding the {@link #url() base url} and any target-specific
* headers or query parameters. <br> <br> For example: <br>
* <pre>
* public Request apply(RequestTemplate input) {
*     input.insert(0, url());
*     input.replaceHeader("X-Auth", currentToken);
*     return input.asRequest();
* }
* </pre>
* <br> <br><br><b>relationship to JAXRS 2.0</b><br> <br> This call is similar to {@code
* javax.ws.rs.client.WebTarget.request()}, except that we expect transient, but necessary
* decoration to be applied on invocation.
*/

这就是我们最终真正去做请求的地方。跟下代码,从HystrixInvocationHandler开始,该类中有Map<Method,MethodHandler>变量,该map中key为被代理的@FeignClient接口的方法,value为上面所说的HardCodeTarget对象。执行的是通过dispatch找到要执行的代理类,执行相应的方法:

 return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);


SynchronousMethodHandler中的

invoke方法如下:


@Override
  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) {
        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());
      }
    }
  }


看到方法第一行就是实际上执行的我们前面说的hardCodeTaget的apply方法:

Request targetRequest(RequestTemplate template) {
    for (RequestInterceptor interceptor : requestInterceptors) {
      interceptor.apply(template);
    }
    return target.apply(new RequestTemplate(template));
  }

知道了最终的调用关系,

再从头继续分析,invoke方法中先根据参数创建了一个requestTemplate对象,调用的就是ReflectiveFeign对象的create方法,首先根据MethodMetadata对象创建基本的template对象,即参数由占位符代替,然后再替换占位符为具体参数,具体不再表述,看源码很清晰。

Retryer即我们设置的重试。

在executeAndDecode中先执行hardCodeTarget的apply方法,获取request对象,包含url,header,method,body,charset等基础的http请求的request对象。

然后就是真正的用request对象执行http请求,拿到结果后,对结果进行处理,返回结果。


以上就是整个feignclient的调用过程,其中还有一点,就是在哪里的代码是因为异常情况而调用的fallback。

在fallbackFactory中设置断点后,只有启动时进入,在实际调用controller时,却不会进入fallbackFactory中,没有弄明白为什么,下一次学习下这里的












  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值