Eureka源码分析
前面对 Eureka 做了比较全面的说明,相信大部分读者都可以搭建 Eureka 框架和根据自己的需求从代码层面选择合适的配置项,同时也能够写一些服务进行开发。
上文也对 Eureka 基础框架、服务治理的机制做了说明,读者可以从中理解 Eureka 的运行原理。但是还不曾涉及代码层面,许多读者想理解源码,却无从下手,因此,在这里对 Eureka 的源码做一个分析,让读者了解服务端与客户端的通信。这里只说明重点,具体读者可以通过设置断点观察。
DiscoveryClient实例
在客户端的启动类上添加注解 @EnableEurekaClient,启动 DiscoveryClient 实例。在进行具体的说明前,我们先看 DiscoveryClient 的类关系图,如图10.18所示。

DiscoveryClient 类是 Spring Cloud 提供的接口,用于定义服务发现的抽象发现,而实现类 EurekaDiscoveryClient 则是对接口的实现,即 Eureka 服务发现。而 EurekaDiscoveryClient 则依赖于 EurekaClient 接口。
DiscoveryClient 类也实现了 EurekaClient 接口,而且是 Netfix 对服务发现的一个实现。因此,真正用于服务发现的类是 DiscoveryClient。对这个类的说明如下。
* The class that is instrumental for interactions with <tt>Eureka Server</tt>.
*
* <p>
* <tt>Eureka Client</tt> is responsible for a) <em>Registering</em> the
* instance with <tt>Eureka Server</tt> b) <em>Renewal</em>of the lease with
* <tt>Eureka Server</tt> c) <em>Cancellation</em> of the lease from
* <tt>Eureka Server</tt> during shutdown
* <p>
* d) <em>Querying</em> the list of services/instances registered with
* <tt>Eureka Server</tt>
* <p>
*
* <p>
* <tt>Eureka Client</tt> needs a configured list of <tt>Eureka Server</tt>
* {@link java.net.URL}s to talk to.These {@link java.net.URL}s are typically amazon elastic eips
* which do not change. All of the functions defined above fail-over to other
* {@link java.net.URL}s specified in the list in the case of failure.
* </p>
上文主要说明了 DiscoveryClient 类的功能。它主要用于与 Eureka Server 互相协作。
Eureka Client 负责向 Server 注册服务实例;向 Server 租约续期;服务关闭期间,向 Server 取消租约;查询 Server 中的服务实例列表,Eureka Client 还需要配置一个 Eureka Server 的 URL 列表。
服务发现
根据 DiscoveryClient 的说明,我们需要进入这个类查找源码。首先,我们需要得到与 Eureka 客户端对话的所有 Eureka Server 的 URL 列表,通过此思路可以找到相应的代码,代码如下所示。
/**
* @deprecated see replacement in {@link com.netflix.discovery. endpoint.EndpointUtils}
* @param instanceZone The zone in which the client resides
* @param preferSameZone true if we have to prefer the same zone as the client, false otherwise
* @return The list of all eureka service urls for the eureka client to talk to
*/
@Deprecated
@Override
public List<String> getServiceUrlsFromConfig(String instanceZone, boolean preferSameZone) {
return EndpointUtils.getServiceUrlsFromConfig(clientConfig, instanceZone, preferSameZone);
}
注意,在这里标注 @deprecated,参照 EndpointUtils。那么我们来看 EndpointUtils 的程序,部分代码如下所示。
public static Map<String, List<String>> getServiceUrlsMapFrom Config(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
Map<String, List<String>> orderedUrls = new LinkedHashMap<>();
String region = getRegion(clientConfig);
String[] availZones = clientConfig.getAvailabilityZones(clientCo nfig.getRegion());
if (availZones == null || availZones.length == 0) {
availZones = new String[1];
availZones[0] = DEFAULT_ZONE;
}
//省略一部分代码
int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 :(myZoneOffset + 1);
while (currentOffset != myZoneOffset) {
zone = availZones[currentOffset];
serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
if (serviceUrls != null) {
orderedUrls.put(zone, serviceUrls);
}
if (currentOffset == (availZones.length - 1)) {
currentOffset = 0;
} else {
currentOffset++;
}
}
//省略一部分代码
return orderedUrls;
}
上面的加粗部分是重点。在这里客户端主要加载两个部分,一个为 region,另一个为 zone。getRegion 函数在这里就不讲了,主要功能是读取 region 并返回,如果不配置则默认为 default。每个微服务对应一个 region。getAvailabilityZones 函数在这里也不讲解了,只说说这里的功能,默认的 zone 为 defaultZone,如果设置了则会通过逗号进行分割。
最后,是 getEurekaServerServiceUrls 方法。这个方法会真正地加载 Server 的具体地址。关于该方法的源码如下所示。
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(URL_SEPARATOR));
}
return new ArrayList<String>();
}