返回介绍

12.2 发现服务源代码剖析

发布于 2025-04-26 13:26:37 字数 7184 浏览 0 评论 0 收藏

使用发现服务时,只要简单地通过注解 @EnableEurekaServer 来标注一个应用为发现服务器,通过注解 @EnableDiscoveryClient 来标注一个应用为发现服务的客户端,服务器就会实现服务注册的功能,客户端将会从服务器中取得已经注册的可用服务列表。

12.2.1 服务端的服务注册功能

服务注册是发现服务的一个主要功能,可以从注解 @EnableEurekaServer 的定义中顺藤摸瓜地找到其实现的源代码,如代码清单 12-4 所示,其中 @Import 将导入一个发现服务的配置:EurekaServerConfiguration。

代码清单 12-4 注解 @EnableEurekaServer 源代码

package org.springframework.cloud.netflix.eureka.server;
......
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EurekaServerConfiguration.class})
public @interface EnableEurekaServer {
}

通过 EurekaServerConfiguration,又引入了一些配置,如增加了监控器和过滤器的配置等,其中一个 InstanceRegistry 的配置将实现对客户端的注册,如代码清单 12-5 所示。

代码清单 12-5 EurekaServerConfiguration 源代码

package org.springframework.cloud.netflix.eureka.server;
......
@Configuration
@Import({EurekaServerInitializerConfiguration.class})
@EnableDiscoveryClient
@EnableConfigurationProperties({EurekaDashboardProperties.class})
public class EurekaServerConfiguration extends WebMvcConfigurerAdapter {
    ......
    @Bean
    public PeerAwareInstanceRegistry peerAwareInstanceRegistry(ServerCodecs
 serverCodecs) {
        this.eurekaClient.getApplications();
        return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClient
Config,serverCodecs, this.eurekaClient, this.expectedNumberOfRenewsPerMin, this.defaultOpenForTrafficCount);
    }
    ......
}

上面的 InstanceRegistry 调用了超类 AbstractInstanceRegistry 的 register 对客户端进行注册,然后将在线的客户端存入注册队列 recentRegisteredQueue 中,如代码清单 12-6 所示。

代码清单 12-6 超类 AbstractInstanceRegistry 源代码片段

package com.netflix.eureka.registry;
......
public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    private static final Logger logger = LoggerFactory.getLogger(AbstractInstance
Registry.class);
    private static final String[] EMPTY_STR_ARRAY = new String[0];
    private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>
 registry = new ConcurrentHashMap();
    protected Map<String, RemoteRegionRegistry> regionNameVSRemoteRegistry = new
HashMap();
    protected final ConcurrentMap<String, InstanceStatus> overriddenInstance
StatusMap;
    private final AbstractInstanceRegistry.CircularQueue<Pair<Long, String>>
 recentRegisteredQueue;
    private final AbstractInstanceRegistry.CircularQueue<Pair<Long, String>>
 recentCanceledQueue;
    private ConcurrentLinkedQueue<AbstractInstanceRegistry.RecentlyChangedItem>
 recentlyChangedQueue;
    ......
    public void register(InstanceInfo r, int leaseDuration, boolean isReplication) {
        try {
            this.read.lock();
            Object gMap = (Map)this.registry.get(r.getAppName());
            EurekaMonitors.REGISTER.increment(isReplication);
            if(gMap == null) {
                ConcurrentHashMap existingLease = new ConcurrentHashMap();
                gMap = (Map)this.registry.putIfAbsent(r.getAppName(), existing
Lease);
                if(gMap == null) {
                    gMap = existingLease;
                }
            }
       ......
      ((Map)gMap).put(r.getId(), lease2);
     AbstractInstanceRegistry.CircularQueue overriddenStatusFromMap1 = this.
recentRegisteredQueue;
    synchronized(this.recentRegisteredQueue) {
                this.recentRegisteredQueue.add(new Pair(Long.valueOf(System.
currentTimeMillis()), r.getAppName() + "(" + r.getId() + ")"));
            }
      ......
   }
}

客户端在发现服务器中注册之后,就可以在发现服务器的控制台上,查看在线的客户端列表。这里有一个缺陷,如果客户端关闭了,在发现服务器的控制台中,还能查到这个客户端,只有重启发现服务器,才能更新这个客户端列表。

12.2.2 客户端注册和提取服务列表

客户端除了自身在发现服务器上注册之外,它还要从服务器中取得已经注册的其他客户端,以得到一个可用的服务列表(其他注册的客户端)。这部分的核心源代码可以在 com.netflix.discovery.DiscoveryClient 中找到。

客户端自身注册的实现方法如代码清单 12-7 所示。这里,主要是将客户端的名称、IP 地址和端口等信息通过一个 instanceInfo 对象发给发现服务器进行注册。

代码清单 12-7 客户端注册源代码

package com.netflix.discovery;
......
public class DiscoveryClient implements EurekaClient {
......
    boolean register() throws Throwable {
        logger.info("DiscoveryClient_" + this.appPathIdentifier + ": registering
 service...");
        if(this.shouldUseExperimentalTransportForRegistration()) {
            EurekaHttpResponse response1;
            try {
                response1 = this.eurekaTransport.registrationClient.register
(this.instanceInfo);
            } catch (Exception var7) {
                logger.warn("{} - registration failed {}", new Object[]{"Discovery
Client_" + this.appPathIdentifier, var7.getMessage(), var7});
                throw var7;
            }
            this.isRegisteredWithDiscovery = true;
            if(logger.isInfoEnabled()) {
                logger.info("{} - registration status: {}", "DiscoveryClient_"
 + this.appPathIdentifier, Integer.valueOf(response1.getStatusCode()));
            }
            return response1.getStatusCode() == 204;
        } else {
            ClientResponse response = null;
            boolean e;
            try {
                response = this.makeRemoteCall(DiscoveryClient.Action.Register);
                this.isRegisteredWithDiscovery = true;
                logger.info("{} - registration status: {}", "DiscoveryClient_"
 + this.appPathIdentifier, response != null?Integer.valueOf(response.getStatus()):"not sent");
                e = response != null && response.getStatus() == 204;
            } catch (Throwable var8) {
                logger.warn("{} - registration failed {}", new Object[]{"Disco
very|




Client_" + this.appPathIdentifier, var8.getMessage(), var8});
                throw var8;
            } finally {
                this.closeResponse(response);
            }
            return e;
        }
    }
    ......
}

客户端执行注册使用计划任务的方式来实现,而客户端从发现服务器中更新其他在线的客户端列表,也使用了一个定时任务来管理。代码清单 12-8 使用一个定时任务 TimerTask 定时从发现服务器中取得其他在线的客户端列表,以备使用。

代码清单 12-8 更新客户端列表定时器源代码

package com.netflix.discovery;
......
public class DiscoveryClient implements EurekaClient {
......
    private TimerTask getServiceUrlUpdateTask(final String zone) {
        return new TimerTask() {
            public void run() {
                try {
                    List e = DiscoveryClient.this.timedGetDiscoveryServiceUrls
(zone);
                    if(e.isEmpty()) {
                        DiscoveryClient.logger.warn("The service url list is
 empty");
                        return;
                    }
                    if(!e.equals(DiscoveryClient.this.eurekaServiceUrls.get
())) {
                        DiscoveryClient.logger.info("Updating the serviceUrls
 as they seem to have changed from {} to {} ", Arrays.toString(((List)DiscoveryClient.this.eurekaServiceUrls.get()).toArray()), Arrays.toString(e.toArray()));
                        DiscoveryClient.this.eurekaServiceUrls.set(e);
                    }
                } catch (Throwable var2) {
                    DiscoveryClient.logger.error("Cannot get the eureka service
 urls :", var2);
                }
            }
        };
}
......
}

单纯的发现服务,并不能看出它有多大的用途,它只有与动态路由、负载均衡和监控服务等一起使用,才能发挥其强大的功能。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。