springCloud(三续)
来源:互联网 发布:sql分页查询语句join 编辑:程序博客网 时间:2024/06/09 16:36
接着上一篇,继续学习springCloud
上回晚上太困了,写了一半,学习到了FallBackFactory,用途就是当feignclient出现任何错误时的fallback,并且可以给出错误信息。
看一下具体的接口只有一个泛型方法:
T create(Throwable cause);
返回一个继承该@feignclient注解的接口的实例。cause即错误信息。可以根据错误信息做不同的逻辑处理。
/** * callback * * @author 刘研 * @create 2017-08-06 22:15 **/@Componentpublic 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方法创建实例,
@Overridepublic 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 unwrapclient = ((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中的
@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()); } } }
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中,没有弄明白为什么,下一次学习下这里的
- springCloud(三续)
- springcloud(三)
- springcloud常见问题(三)
- SpringCloud-Eureka(三)常见问题
- SpringCloud(三)Ribbon负载均衡
- SpringCloud
- springcloud
- SpringCloud
- springcloud
- springcloud
- springcloud
- springcloud(三):服务提供与调用
- springcloud学习(三)之Ribbon
- springcloud(三):服务提供与调用
- 【SpringCloud】(三):客户端发现方式 Eureka
- SpringCloud(三):服务发现组件Eureka
- springcloud(三):服务提供与调用
- SpringCloud 笔记 (三)---- Eureka通信过程
- 大数据学习笔记:编写Mapreduce代码并运行
- php增删改
- C#版SQLite的备份和还原
- Android JNI对象与Java对象的绑定
- mac os 下安装好Mysql后无法连接(强行修改密码)
- springCloud(三续)
- 饿了么移动APP的架构演进
- HDU 2852 KiKi's K-Number (树状数组+二分)
- 分享些电子书,有需要的自行下载
- 基础概念之内存对齐
- 寻找无序数组的中位数
- 变量,包
- Unity3D之iTween动画插件应用
- Java短信实现