首页 Eureka源码02-EurekaHttpClient装饰模式的使用
文章
取消

Eureka源码02-EurekaHttpClient装饰模式的使用

1. EurekaTransport

EurekaTransport 持有了用于 http 通讯的组件,用于向 eureka server 发送服务注册等信息的有 registrationClient,用于向 eureka server 查询服务信息的有 queryClient。bootstrapResolver 这个是用于对 service-url.defaultZone 这个配置或者其他的 zone 进行解析。

1
2
3
4
5
6
7
8
9
10
     private static final class EurekaTransport {
         private ClosableResolver bootstrapResolver;
         private TransportClientFactory transportClientFactory;

         private EurekaHttpClient registrationClient;
         private EurekaHttpClientFactory registrationClientFactory;

         private EurekaHttpClient queryClient;
         private EurekaHttpClientFactory queryClientFactory;
    }

2. ClosableResolver

ClosableResolver 的实现类为 ConfigClusterResolver,在 getClusterEndpoints -> getClusterEndpointsFromConfig -> EndpointUtils.getServiceUrlsMapFromConfig -> EurekaClientConfig.getEurekaServerServiceUrls 从配置文件中获取到了 eureka 集群信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    public List<String> getEurekaServerServiceUrls(String myZone) {
        String serviceUrls = configInstance.getStringProperty(
                namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + "." + myZone, null).get();
        if (serviceUrls == null || serviceUrls.isEmpty()) {
            serviceUrls = configInstance.getStringProperty(
                    namespace + CONFIG_EUREKA_SERVER_SERVICE_URL_PREFIX + ".default", null).get();

        }
        if (serviceUrls != null) {
            return Arrays.asList(serviceUrls.split(","));
        }

        return new ArrayList<String>();
    }

3. registrationClient

如果registration.enabled = true则需要向 eureka server 进行注册,那么就会创建 registrationClient。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    if (clientConfig.shouldRegisterWithEureka()) {
        EurekaHttpClientFactory newRegistrationClientFactory = null;
        EurekaHttpClient newRegistrationClient = null;
        try {
            newRegistrationClientFactory = EurekaHttpClients.registrationClientFactory(
                    eurekaTransport.bootstrapResolver,
                    eurekaTransport.transportClientFactory,
                    transportConfig
            );
            newRegistrationClient = newRegistrationClientFactory.newClient();
        } catch (Exception e) {
            logger.warn("Transport initialization failure", e);
        }
        eurekaTransport.registrationClientFactory = newRegistrationClientFactory;
        eurekaTransport.registrationClient = newRegistrationClient;
    }

newRegistrationClientFactory 用于创建 newRegistrationClient。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    static EurekaHttpClientFactory canonicalClientFactory(final String name,
                                                          final EurekaTransportConfig transportConfig,
                                                          final ClusterResolver<EurekaEndpoint> clusterResolver,
                                                          final TransportClientFactory transportClientFactory) {

        return new EurekaHttpClientFactory() {
            @Override
            public EurekaHttpClient newClient() {
                return new SessionedEurekaHttpClient(
                        name,
                        RetryableEurekaHttpClient.createFactory(
                                name,
                                transportConfig,
                                clusterResolver,
                                RedirectingEurekaHttpClient.createFactory(transportClientFactory),
                                ServerStatusEvaluators.legacyEvaluator()),
                        transportConfig.getSessionedClientReconnectIntervalSeconds() * 1000
                );
            }

            @Override
            public void shutdown() {
                wrapClosable(clusterResolver).shutdown();
            }
        };
    }

我们可以看到当调用 factory.newClient 函数时,最终创建的是一个 SessionedEurekaHttpClient。至于 RetryableEurekaHttpClient 和 RedirectingEurekaHttpClient 则是在执行 execute 函数时创建的。

现在我们看到了三种代理的 EurekaHttpClient,但是 MetricsCollectingEurekaHttpClient 是在哪里创建的呢?

在创建 RedirectingEurekaHttpClient 的时候,最终会使用到 eurekaTransport.transportClientFactory,也就是 Jersey1TransportClientFactories。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    public TransportClientFactory newTransportClientFactory(final Collection<ClientFilter> additionalFilters,
                                                                   final EurekaJerseyClient providedJerseyClient) {
        ApacheHttpClient4 apacheHttpClient = providedJerseyClient.getClient();
        if (additionalFilters != null) {
            for (ClientFilter filter : additionalFilters) {
                if (filter != null) {
                    apacheHttpClient.addFilter(filter);
                }
            }
        }

        final TransportClientFactory jerseyFactory = new JerseyEurekaHttpClientFactory(providedJerseyClient, false);
        final TransportClientFactory metricsFactory = MetricsCollectingEurekaHttpClient.createFactory(jerseyFactory);

        return new TransportClientFactory() {
            @Override
            public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
                return metricsFactory.newClient(serviceUrl);
            }

            @Override
            public void shutdown() {
                metricsFactory.shutdown();
                jerseyFactory.shutdown();
            }
        };
    }

至此,我们可以得到 EurekaHttpClient 的调用顺序,SessionedEurekaHttpClient -> RetryableEurekaHttpClient -> RedirectingEurekaHttpClient -> MetricsCollectingEurekaHttpClient -> JerseyApplicationClient

4. EurekaHttpClientDecorator

就以从实现 EurekaHttpClient 接口的 register 函数举例,将 register 的行为下放到了实现类。在子类中,当调用 RequestExecutor#execute 时, 会委托给代理进行 register,这样的话装饰器可以在实际调用函数前后做一些装饰操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    protected abstract <R> EurekaHttpResponse<R> execute(RequestExecutor<R> requestExecutor);

    @Override
    public EurekaHttpResponse<Void> register(final InstanceInfo info) {
        return execute(new RequestExecutor<Void>() {
            @Override
            public EurekaHttpResponse<Void> execute(EurekaHttpClient delegate) {
                return delegate.register(info);
            }

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

5. EurekaHttpClientDecorator实现类的作用

  • MetricsCollectingEurekaHttpClient: 对不同的请求结果进行统计。
  • RedirectingEurekaHttpClient:在创建 client 的时候,会进行一次请求,如果返回结果需要进行重定向,就会根据重新重定向地址重新生成一个client
  • RetryableEurekaHttpClient:如果请求失败,则会换一个 eureka server 进行重试
  • SessionedEurekaHttpClient: 超过一定时间没有使用过这个 client,就会重新创建一个新的 client。防止一直使用相同的 client。
本文由作者按照 CC BY 4.0 进行授权