- 前言
- 第一部分 基础应用开发
- 第 1 章 Spring Boot 入门
- 第 2 章 在 Spring Boot 中使用数据库
- 第 3 章 Spring Boot 界面设计
- 第 4 章 提高数据库访问性能
- 第 5 章 Spring Boot 安全设计
- 第二部分 分布式应用开发
- 第 6 章 Spring Boot SSO
- 第 7 章 使用分布式文件系统
- 第 8 章 云应用开发
- 第 9 章 构建高性能的服务平台
- 第三部分 核心技术源代码分析
- 第 10 章 Spring Boot 自动配置实现原理
- 第 11 章 Spring Boot 数据访问实现原理
- 第 12 章 微服务核心技术实现原理
- 附录 A 安装 Neo4j
- 附录 B 安装 MongoDB
- 附录 C 安装 Redis
- 附录 D 安装 RabbitMQ
- 结束语
12.2 发现服务源代码剖析
使用发现服务时,只要简单地通过注解 @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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论