1.背景
在研究从eureka server拉取服务注册信息列表时,源码阅读到拉取全量列表时,需要调用EurekaHttpClient
进行。
方法:com.netflix.discovery.DiscoveryClient#getAndStoreFullRegistry
疑惑代码:eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())
这里的queryClient返回的竟然不是JerseyApplicationClient
而是SessionedEurekaHttpClient
。
但是通过代码调试还是走到了JerseyApplicationClient
。
那么如何从SessionedEurekaHttpClient
一步步到JerseyApplicationClient
的呢?
以下是EurekaHttpClient
的子类,在从SessionedEurekaHttpClient
到JerseyApplicationClient
的过程中,我们到底会用到哪些子类,下面仔细梳理一下。
2.EurekaHttpClient工厂的疯狂套娃
上图是我整理的从eureka server拉取注册列表的EurekaHttpClient的创建过程,在上面我们可以看到每个EurekaHttpClient
对象都对应一个工厂,而每个工厂都会引用其他工厂来形成套娃。
在上图中,transportClientFactory
引用了metricsFactory
,metricsFactory
引用了jerseyFactory
工厂。
transportClientFactory 、metricsFactory 和jerseyFactory 的关系:
- jerseyFactory 用于创建JerseyApplicationClient,JerseyApplicationClient主要是用于发送请求的
metricsFactory
用于创建MetricsCollectingEurekaHttpClient
,这个类也是继承自EurekaHttpClientDecorator
,MetricsCollectingEurekaHttpClient
的主要作用是收集和统计JerseyApplicationClient
发送请求和响应的行为信息。transportClientFactory
是对metricsFactory
进行了一层封装,这个类只是重写了shutdown方法,进行metricsFactory
和jerseyFactory
的shutdown
处理。
剩下的三个工厂是一个链式处理:
eurekaTransport.queryClientFactory
工厂(工厂1
)负责创建SessionedEurekaHttpClient
,SessionedEurekaHttpClient
主要负责超时重建RetryEurekaHttpClient
。SessionedEurekaHttpClient
持有RetryEurekaHttpClient
的工厂(工厂2
)类,在执行完自己的业务后,就将行为传递给RetryEurekaHttpClient
处理RetryEurekaHttpClient
负责请求重试和多服务器节点失败轮询。RetryEurekaHttpClient
持有RedirectingEurekaHttpClient
工厂(工厂3
)类,RetryEurekaHttpClient
在执行完自己的业务后,会将行为传递给RetryEurekaHttpClient
处理RedirectingEurekaHttpClient
持有transportClientFactory
工厂对象,RedirectingEurekaHttpClient
主要用于控制重定向次数,超过限次的重定向会进行失败处理。RedirectingEurekaHttpClient
在处理完自己的业务后,会将行为交给transportClientFactory
创建的EurekaHttpClient对象处理,这时候便与上面的三个工厂类进行关联了。
其实可以看到其实是5个EurekaHttpClient
的链式处理,只不过是通过TransportClientFactory
来进行串联的。
SessionedEurekaHttpClient
负责RetryEurekaHttpClient的超时重建RetryEurekaHttpClient
负责请求失败重试RedirectingEurekaHttpClient
负责死循环重定向或者超次数重定向断开处理MetricsCollectingEurekaHttpClient
负责进行面板信息统计处理JerseyApplicationClient
负责发送http请求到eureka server
3.源码定位于剖析
eurekaTransport.queryClient.getApplications(String[] regions)
这个方法是用来拉取注册表的,而EurekaHttpClient对象是存储在eurekaTransport.queryClient
中,它的创建是在com.netflix.discovery.DiscoveryClient#scheduleServerEndpointTask
方法内完成的
而newQueryClient
实际上是一个SessionedEurekaHttpClient
。
从上面看到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());
这里有个精妙的设计,EurekaHtppClientDecorator通过传入匿名内部类RequestExecutor,将行为进行封装就可以在多个EurekaHttpClient进行传递。
从上图我们可以推算出一个执行流程:
1.SessionedEurekaHttpClient
持有RetryEurekaHttpClient
,在执行完成自己的业务后,会将拉取注册里列表的行为交给RetryEurekaHttpClient
的去执行
2.RetryEurekaHttpClient
持有RedirectingHttpClient
,在执行完自己的业务后,会将拉取注册列表的行为交给RedirectingHttpClient
去执行
3.RedirectingHttpClient
持有transportClientFactory
工厂类,在执行完自己的业务后,会通过该工厂创建MetricsCollectingEurekaHttpClient
,来执行请求的监听和数据统计
4.MetricsCollectingEurekaHttpClient
是持有JerseyApplicationClient
工厂的,在执行完自己的业务后,会通过工厂创建JerseyApplicationClient
来发送请求到eureka server。
至此,EurekaHttpClient
工厂的创建流程 和 EurekaHttpClient
的调用链路就解析完成了。
下图在RedirectingEurekaHttpClient
和JerseyApplicationClient
之间少了一个MetricsCollectingEurekaHttpClient
。这个忘记画了,也不想改了,就这样吧~