mui的实例项目源码_【SpringCloud】Eureka Client源码分析

本文详细分析了Eureka Client的源码,包括读取应用配置、服务发现、服务注册、初始化定时器等关键功能。重点探讨了DiscoveryClient接口及其在Eureka中的实现,以及服务注册、心跳发送、缓存刷新等操作的实现细节。
摘要由CSDN通过智能技术生成

bdccc2d9a0a82c4bc3d52758ef6dcd4a.gif

【SpringCloud】Eureka Client源码分析

上一节Eureka Server 源码分析讲述了 Eureka Server 的原理及部分源码,今天咱们来看看 Eureka Client 端的源码,功能点类似 Eureka Server

3.7、Eureka Client 源码分析

Eureka Client 通过 Starter 的方式引入依赖, SpringBoot 将会为项目使用以下的自动配置类:

  • EurekaClientAutoConfigurationEureka Client 自动配置类,负责 Eureka Client 中关键Bean的配置和初始化;

  • RibbonEurekaAutoConfigurationRibbon 负载均衡相关配置;

  • EurekaDiscoveryClientConfiguration:配置自动注册、服务发现和应用的健康检查器。

3.7.1、读取应用自身配置信息

DiscoveryClientSpring Cloud 中用于进行服务发现的顶级接口,也是核心接口,在 Netflix Eureka 或者 Alibaba Nacos 或者 Consul 中都有相应的具体实现类。

 public interface DiscoveryClient extends Ordered {
    /** * Default order of the discovery client. */int DEFAULT_ORDER = 0;/** * A human-readable description of the implementation, used in HealthIndicator. * @return The description. */    //获取实现类的描述String description();/** * Gets all ServiceInstances associated with a particular serviceId. * @param serviceId The serviceId to query. * @return A List of ServiceInstance. */    //通过服务id获取服务实例的信息List<ServiceInstance> getInstances(String serviceId);/** * @return All known service IDs. */    //获取所有服务的实例idList<String> getServices();/** * Default implementation for getting order of discovery clients. * @return order */@Overridedefault int getOrder() {
    return DEFAULT_ORDER;}}

而在 Eureka 方面的实现,主要的实现类即为 EurekaDiscoveryClient。但是仔细看 EurekaDiscoveryClient 代码中会发现它会使用原生的 Eureka 中的代码:

 public class EurekaDiscoveryClient implements DiscoveryClient {
        //other...        //引入原生的EurekaClient接口    private final EurekaClient eurekaClient;    @Override    public String description() {
            return DESCRIPTION;    }    @Override    public List<ServiceInstance> getInstances(String serviceId) {
            List<InstanceInfo> infos = this.eurekaClient.getInstancesByVipAddress(serviceId,                                                                              false);        List<ServiceInstance> instances = new ArrayList<>();        for (InstanceInfo info : infos) {
                instances.add(new EurekaServiceInstance(info));        }        return instances;    }    @Override    public List<String> getServices() {
            Applications applications = this.eurekaClient.getApplications();        if (applications == null) {
                return Collections.emptyList();        }        List<Application> registered = applications.getRegisteredApplications();        List<String> names = new ArrayList<>();        for (Application app : registered) {
                if (app.getInstances().isEmpty()) {
                    continue;            }            names.add(app.getName().toLowerCase());        }        return names;    }}

此时的 EurekaClient 接口所在的包为 com.netflix.discovery,也就是说 Spring Cloud 通过内部组合方式调用了原生 Eureka 中的服务发现方法。而该 EurekaClient 接口的实现类默认是 DiscoveryClient 类,而该类属于原生 Eureka 中的服务发现类,所在的包为com.netflix.discovery,是不是有点迷糊了。

867351b96d82ac545c0e353df1e5b69c.png

仔细看代码,就会发现 Spring CloudDiscoveryClient 接口中的几个方法都是依靠 Eureka 原生接口 EurekaClient 来实现的,而原生 EurekaClient 默认指定的实现类为 DiscoveryClient ,所以归根到底主要看 DiscoveryClient 源码。

3.7.2、服务发现:DiscoveryClient

在讲解 Eureka Server 的时候,InstanceRegistry  也实现了 LookupService 接口, 同样原生的 EurekaClient 也实现了该接口,并在原来的基础上新增了很多检索服务的方法,有兴趣的朋友可以查看:

  • 提供了多种方式获取 InstanceInfo,例如根据区域、地址等方式;

  • 提供了为客户端注册和获取服务健康检查处理器的能力。

除去一般的检索服务的接口,主要关注 EurekaClient中的两个接口方法,分别是:

 //DiscoveryClient#registerHealthCheck// 为Eureka Client注册健康检查处理器public void registerHealthCheck(HealthCheckHandler healthCheckHandler) {
        if (instanceInfo == null) {
            logger.error("Cannot register a healthcheck handler when instance info is null!");    }    if (healthCheckHandler != null) {
            this.healthCheckHandlerRef.set(healthCheckHandler);        // schedule an onDemand update of the instanceInfo when a new healthcheck handler is registered        if (instanceInfoReplicator != null) {
                instanceInfoReplicator.onDemandUpdate();        }    }}//DiscoveryClient#registerEventListener// 监听Client服务实例信息的更新public void registerEventListener(EurekaEventListener eventListener) {
        this.eventListeners.add(eventListener);}

Eureka Server 一般通过心跳 (heartbeat)来识别一个实例的状态。Eureka Client 中存在一个定时任务定时通过 HealthCheckHandlerClient 检测当前 Client 的状态 ,如 Client 的状态发生改变, 将会触发新的注册事件 ,更新 Eureka Server 注册表中该服务实例的相关信息。

HealthCheckHandler接口代码如下:

 public interface HealthCheckHandler {
        InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus);}

spring-cloud-netflix-eureka-client中的实现主要是EurekaHealthCheckHandler, 它主要使用了spring-cloud-actuator中的 HealthAggregatorHealthIndicator,用于监测服务实例的状态。

EurekaEventListener注册的事件监听模式属于观察者模式,当服务实例的状态发生改变的时候,就会触发事件,仔细观察 EurekaClient中有个方法:

 //DiscoveryClient#fireEventprotected void fireEvent(final EurekaEvent event) {
        for (EurekaEventListener listener : eventListeners) {
            try {
                listener.onEvent(event);        } catch (Exception e) {
                logger.info("Event {} throw an exception for listener {}", event, listener, e.getMessage());        }    }}

fireEvent方法即为触发的事件。

3.7.3、DiscoveryClient构造函数

DiscoveryClient 构造函数中,Eureka Client 会执行从 Eureka Server 中拉取注册表信息、服务注册、 初始化发送心跳、缓存刷新( 重新拉取注册表信息 )和按需注册定时任务等操作,可以说 DiscoveryClient 的构造函数贯穿 Eureka Client 启动阶段的各项工作。

 @InjectDiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,                Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
        if (args != null) {
            this.healthCheckHandlerProvider = args.healthCheckHandlerProvider;        this.healthCheckCallbackProvider = args.healthCheckCallbackProvider;        this.eventListeners.addAll(args.getEventListeners());        this.preRegistrationHandler = args.preRegistrationHandler;    } else {
            this.healthCheckCallbackProvider = null;        this.healthCheckHandlerProvider = null;        this.preRegistrationHandler = null;    }    this.applicationInfoManager = applicationInfoManager;    InstanceInfo myInfo = applicationInfoManager.getInfo();    clientConfig = config;    staticClientConfig = clientConfig;    transportConfig = config.getTransportConfig();    instanceInfo = myInfo;    if (myInfo != null) {
            appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId();    } else {
            logger.warn("Setting instanceInfo to a passed in null value");    }    this.backupRegistryProvider = backupRegistryProvider;    this.endpointRandomizer = endpointRandomizer;    this.urlRandomizer = new EndpointUtils.InstanceInfoBasedUrlRandomizer(instanceInfo);    localRegionApps.set(new Applications());    fetchRegistryGeneration = new AtomicLong(0);    remoteRegionsToFetch = new AtomicReference<String>(clientConfig.fetchRegistryForRemoteRegions());    remoteRegionsRef = new AtomicReference<>(remoteRegionsToFetch.get() == null ? null : remoteRegionsToFetch.get().split(","));    if (config.shouldFetchRegistry()) {
            this.registryStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{
    15L, 30L, 60L, 120L, 240L, 480L});    } else {
            this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;    }    if (config.shouldRegisterWithEureka()) {
            this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{
    15L, 30L, 60L, 120L, 240L, 480L});    } else {
            this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;    }    logger.info("Initializing Eureka in region {}", clientConfig.getRegion());    if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) {
            logger.info("Client configured to neither register nor query for data.");        scheduler = null;        heartbeatExecutor = null;        cacheRefreshExecutor = null;        eurekaTransport = null;        instanceRegionChecker = new InstanceRegionChecker(new PropertyBasedAzToRegionMapper(config), clientConfig.getRegion());        // This is a bit of hack to allow for existing code using DiscoveryManager.getInstance()        // to work with DI'd DiscoveryClient        DiscoveryManager.getInstance().setDiscoveryClient(this);        DiscoveryManager.getInstance().setEurekaClientConfig(config);        initTimestampMs = System.currentTimeMillis();        logger.info("Discovery Client initialized at timestamp {} with initial instances count: {}",                    initTimestampMs, this.getApplications().size());        return;  // no need to setup up an network tasks and we are done    }    try {
            // default size of 2 - 1 each for heartbeat and cacheRefresh        scheduler = Executors.newScheduledThreadPool(2,
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值