eureka 巧妙的EurekaHttpClient工厂设计与请求发送过程

1.背景

在研究从eureka server拉取服务注册信息列表时,源码阅读到拉取全量列表时,需要调用EurekaHttpClient进行。

方法:com.netflix.discovery.DiscoveryClient#getAndStoreFullRegistry

疑惑代码:eurekaTransport.queryClient.getApplications(remoteRegionsRef.get()) 这里的queryClient返回的竟然不是JerseyApplicationClient而是SessionedEurekaHttpClient

但是通过代码调试还是走到了JerseyApplicationClient

那么如何从SessionedEurekaHttpClient一步步到JerseyApplicationClient的呢?


以下是EurekaHttpClient的子类,在从SessionedEurekaHttpClientJerseyApplicationClient的过程中,我们到底会用到哪些子类,下面仔细梳理一下。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3aSaosSG-1621563674462)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/55b2fb16-dc8c-4b1e-a366-0d9baa753549/Untitled.png)]


2.EurekaHttpClient工厂的疯狂套娃

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FUol703t-1621563674470)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fabd8c17-c481-4044-86c2-a8792ab6c7f2/03_EurekaHttpClient.png)]

上图是我整理的从eureka server拉取注册列表的EurekaHttpClient的创建过程,在上面我们可以看到每个EurekaHttpClient对象都对应一个工厂,而每个工厂都会引用其他工厂来形成套娃。


在上图中,transportClientFactory引用了metricsFactorymetricsFactory引用了jerseyFactory工厂。

transportClientFactory 、metricsFactory 和jerseyFactory 的关系:

  • jerseyFactory 用于创建JerseyApplicationClient,JerseyApplicationClient主要是用于发送请求的
  • metricsFactory 用于创建MetricsCollectingEurekaHttpClient,这个类也是继承自EurekaHttpClientDecoratorMetricsCollectingEurekaHttpClient的主要作用是收集和统计JerseyApplicationClient发送请求和响应的行为信息。
  • transportClientFactory是对metricsFactory 进行了一层封装,这个类只是重写了shutdown方法,进行metricsFactoryjerseyFactoryshutdown处理。

剩下的三个工厂是一个链式处理:

  1. eurekaTransport.queryClientFactory工厂(工厂1)负责创建SessionedEurekaHttpClientSessionedEurekaHttpClient 主要负责超时重建RetryEurekaHttpClient
  2. SessionedEurekaHttpClient 持有RetryEurekaHttpClient的工厂(工厂2)类,在执行完自己的业务后,就将行为传递给RetryEurekaHttpClient处理
  3. RetryEurekaHttpClient 负责请求重试和多服务器节点失败轮询。RetryEurekaHttpClient持有RedirectingEurekaHttpClient工厂(工厂3)类,RetryEurekaHttpClient在执行完自己的业务后,会将行为传递给RetryEurekaHttpClient处理
  4. RedirectingEurekaHttpClient持有transportClientFactory 工厂对象,RedirectingEurekaHttpClient主要用于控制重定向次数,超过限次的重定向会进行失败处理。RedirectingEurekaHttpClient在处理完自己的业务后,会将行为交给transportClientFactory 创建的EurekaHttpClient对象处理,这时候便与上面的三个工厂类进行关联了。

其实可以看到其实是5个EurekaHttpClient的链式处理,只不过是通过TransportClientFactory来进行串联的。

  1. SessionedEurekaHttpClient 负责RetryEurekaHttpClient的超时重建
  2. RetryEurekaHttpClient 负责请求失败重试
  3. RedirectingEurekaHttpClient 负责死循环重定向或者超次数重定向断开处理
  4. MetricsCollectingEurekaHttpClient 负责进行面板信息统计处理
  5. JerseyApplicationClient 负责发送http请求到eureka server

3.源码定位于剖析

eurekaTransport.queryClient.getApplications(String[] regions) 这个方法是用来拉取注册表的,而EurekaHttpClient对象是存储在eurekaTransport.queryClient中,它的创建是在com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask方法内完成的

在这里插入图片描述

newQueryClient 实际上是一个SessionedEurekaHttpClient

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YQYXf0o6-1621563674478)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fbe5b30d-90de-4339-9094-5f3853a76bf8/Untitled.png)]

从上面看到SessionedEurekaHttpClient的构造函数中,创建了两个工厂,分别是RetryEurekaHttpClient工厂和RedirectingEurekaHttpClient工厂。

具体流程:

1.SeesionedEurekaHttpClient是持有RetryableEurekaHttpClient对象工厂

2.RetryableEurekaHttpClient对象持有RedirectingEurekaHttpClient对象工厂

3.RedirectingEurekaHttpClient持有eurekaTransport.transportClientFactory 对象


下图是eurekaTransport.transportClientFactory 的工厂套娃:
在这里插入图片描述

那么说到底SeesionedEurekaHttpClient可以通过自己持有的工厂对象进行层层调用,最终调用的还是JerseyApplicaitonClient的方法。


4.SessionedEurekaHttpClient如何调用的getApplications(String[] region)方法?

这里使用的是装饰者模式+模板方法 + 访问者。

核心类:com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator

@Override
public EurekaHttpResponse<Applications> getApplications(final String... regions) {
    // excute方法为模板方法,抽象类EurekaHttpClientDecorator中定义,在子类中实现
		// 主要是为了EurekaHttpClient在执行具体方法之前,做一些特殊处理(会话重连,重试,重定向等等)
		// 这里还有一个亮点是执行器的存在,这里可以向RequestExecutor的execute方法中传入
		// EurekaHttpClientDecorator的子类,那么就能形成链式调用
		return execute(new RequestExecutor<Applications>() {
        @Override
        public EurekaHttpResponse<Applications> execute(EurekaHttpClient delegate) {
            return delegate.getApplications(regions);
        }

        @Override
        public RequestType getRequestType() {
            return RequestType.GetApplications;
        }
    });
}

protected abstract <R> EurekaHttpResponse<R> execute(RequestExecutor<R> requestExecutor);

在SeesionedEurekaHttpClient类中,execute方法做了什么?

SessionedEurekaHttpClient类其实并没有做执行,只是做了EurekaHttpClient的会话管理,而且SessionedEurekaHttpClient还会持有RetryableEurekaHttpClient对象的引用

//这里存放的实际上是RetryableEurekaHttpClient对象
private final AtomicReference<EurekaHttpClient> eurekaHttpClientRef = new AtomicReference<>();

// 通过clientFactory.newClient获取RetryableEurekaHttpClient
eurekaHttpClient = TransportUtils.getOrSetAnotherClient(eurekaHttpClientRef, clientFactory.newClient());

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IzXgD974-1621563674487)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e69893e6-cb94-4a44-8967-d4cf3e68895a/Untitled.png)]

这里有个精妙的设计,EurekaHtppClientDecorator通过传入匿名内部类RequestExecutor,将行为进行封装就可以在多个EurekaHttpClient进行传递。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-33iN2wid-1621563674488)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9623c00f-ee9b-447f-a548-445533e55b6f/Untitled.png)]


从上图我们可以推算出一个执行流程:

1.SessionedEurekaHttpClient 持有RetryEurekaHttpClient,在执行完成自己的业务后,会将拉取注册里列表的行为交给RetryEurekaHttpClient的去执行

2.RetryEurekaHttpClient持有RedirectingHttpClient,在执行完自己的业务后,会将拉取注册列表的行为交给RedirectingHttpClient去执行

3.RedirectingHttpClient 持有transportClientFactory工厂类,在执行完自己的业务后,会通过该工厂创建MetricsCollectingEurekaHttpClient ,来执行请求的监听和数据统计

4.MetricsCollectingEurekaHttpClient 是持有JerseyApplicationClient工厂的,在执行完自己的业务后,会通过工厂创建JerseyApplicationClient来发送请求到eureka server。

至此,EurekaHttpClient工厂的创建流程 和 EurekaHttpClient的调用链路就解析完成了。

下图在RedirectingEurekaHttpClientJerseyApplicationClient之间少了一个MetricsCollectingEurekaHttpClient。这个忘记画了,也不想改了,就这样吧~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xnkqr2gu-1621563674490)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8c07e99f-a9d1-48e3-b792-5e0e74e32e49/.png)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值