注册中心eureka的介绍及源码探索

1. 背景

1.1. 注册中心是什么

注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用。
在这里插入图片描述

1.2. 为什么需要注册中心

在分布式系统中,服务可能有上千个,然后每个服务都有好几个实例,如果通过 ip + port 进行服务之间通信则会使系统变得难维护,并且还需要考虑其他复杂的问题:

  1. 服务注册后,如何被及时发现
  2. 服务宕机后,如何及时下线
  3. 服务如何有效的水平扩展
  4. 如何获取服务列表
  5. 注册中心如何实现自身的高可用

2. Eureka

2.1. 世面上的流行的注册中心

组件名称 组件简介
Zookeeper zookeeper是一个分布式协调工具,可以实现注册中心功能
Eureka springcloud的注册中心
Consul Consul 简化了分布式环境中的服务的注册和发现流程,国外比较流行
Nacos Nacos 致力于帮助您发现、配置和管理微服务。SpringCloudAlibaba

2.2. Eureka

https://github.com/Netflix/eureka
在这里插入图片描述
服务注册中心(可以是一个集群),对外暴露自己的地址

注册中心有 Eureka Service, Eureka Client,Eureka Client又分为提供者和消费者;

(某一个服务既可以是提供者也可以是消费者)

服务提供者

  • 服务注册: 启动的时候会通过发送REST请求的方式将自己注册到Eureka Server上,同时带上了自身服务的一些元数据信息。
  • 服务续约: 在注册完服务之后,服务提供者会维护一个心跳(默认30S) 用来持续告诉Eureka Server: "我还活着 ”
  • 服务下线: 当服务实例进行正常的关闭操作时,它会触发一个服务下线的REST请求 给Eureka Server, 告诉服务注册中心:“我要下线了 ”。

服务消费者

  • 获取服务: 服务消费者(Eureka Client)在启动的时候,会发送一个REST请求给Eureka Server,获 取上面注册的服务清单,并且缓存在Eureka Client本地,默认缓存30秒 (eureka.client.registryFetchIntervalSeconds)。同时,为了性能考虑,Eureka Server也会维护一份只读的服务清单缓存,该缓存每隔30秒更新一次。
  • 服务调用: 服务消费者在获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元数据信息。在进行服务调用的时候,优先访问同处一个Zone中的服务提供方。

Eureka Server(服务注册中心)

  • 失效剔除:【在关闭自我保护才有效】 默认每隔一段时间(默认为60秒) 将当前清单中超时(默认为90秒)没有续约的服务剔除出去。

  • 自我保护: EurekaServer 在运行期间,如果在15分钟内超过85%的客户端节点都没有正常的心跳(通常由于网络不稳定导致)。 Eureka Server会将当前的实例注册信息保护起来, 让这些实例不会过期,尽可能保护这些注册信息。此时会出现以下几种情况:

    • Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
    • Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
    • 当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。

    因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像ZK那样如果有一半不可用的情况会导 致整个集群不可用而变成瘫痪。

3.1. 服务注册

3.1.1. Eureka-Client

啥时候会被注册

  1. 当客户端刚刚启动的时候
  2. 当客户端的instance信息发生改动
    在这里插入图片描述

当我们的客户端引入了Eureka-Client,当主方法启动时,@SpringBootApplication会扫描所有的META-INF/spring.factories文件下的 xxxAutoConfiguration。这时候 EurekaClientAutoConfiguration 也会被加载。

上面这段代码,很简单,就是实例化了一个Bean,主要是这个Bean实现了SmartLifecycle, 当重写方法 isAutoStartup() 返回值为true,会启动start()方法。
下面可以详细看看这个代码。

EurekaClientAutoConfiguration.java
@Bean
public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(ApplicationContext context, EurekaServiceRegistry registry, EurekaRegistration registration) {
   

   // 重点代码
   return new EurekaAutoServiceRegistration(context, registry, registration);
}

EurekaAutoServiceRegistration.java
public class EurekaAutoServiceRegistration implements AutoServiceRegistration, SmartLifecycle, Ordered {
    
    
	@Override
	public void start() {
   
		// ...
		
		// 该实例还未启动
		if (!this.running.get() && this.registration.getNonSecurePort() > 0) {
   
            // 重点;自动去注册服务
			this.serviceRegistry.register(this.registration);
            // 发布 节点注册事件
			this.context.publishEvent(
					new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig()));
			this.running.set(true);
		}
	}
	
	@Override
	public boolean isAutoStartup() {
   
		return true;
	}
}


InstanceInfoReplicator.java
public boolean onDemandUpdate() {
   
    if (rateLimiter.acquire(burstSize, allowedRatePerMinute)) {
   
        if (!scheduler.isShutdown()) {
   
            scheduler.submit(new Runnable() {
   
                @Override
                public void run() {
   
                    // ...
                    
                    // 调用run方法
                    InstanceInfoReplicator.this.run();
                }
            });
            return true;
        } else {
   
            return false;
        }
    } else {
   
        return false;
    }
}

public void run() {
   
    try {
   
         // 刷新实例信息。
        discoveryClient.refreshInstanceInfo();
        
        Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
        if (dirtyTimestamp != null) {
   
        
            // 注册自己的服务
            discoveryClient.register();
            instanceInfo.unsetIsDirty(dirtyTimestamp);
        }
    } catch (Throwable t) {
   
    
    } finally {
   
        Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
        scheduledPeriodicRef.set(next);
    }
}

第二种是当我们客户端instance信息发生变化

private void initScheduledTasks() {
   
    //省略, 刷新缓存的定时器
    
    // 监听instance的状态变更
    instanceInfoReplicator = new InstanceInfoReplicator(
            this,
            instanceInfo,
            clientConfig.getInstanceInfoReplicationIntervalSeconds(),
            2); // burstSize

    statusChangeListener = new ApplicationInfoManager.StatusChangeListener() {
   
        @Override
        public String getId() {
   
            return "statusChangeListener";
        }

        @Override
        public void notify(StatusChangeEvent statusChangeEvent) {
   
            // ...
            // 调用方法
            instanceInfoReplicator.onDemandUpdate();
        }
    };

    if (clientConfig.shouldOnDemandUpdateStatusChange()) {
   
        applicationInfoManager.registerStatusChangeListener(statusChangeListener);
    }

    instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
}

总结 :服务注册分为2种。
第一种: 当应用启动的时候,如果应用开启了自动注册(默认开启), 那么在自动配置类加载的时候,会通过EurekaAutoServiceRegistration实例化的时候,去改变instance的status,然后调用注册。
第二种: 主要应用于启动之后,当应用的信息发生改变之后,每40每秒执行一次的线程,检测到了,也会自动去注册一次。

DiscoveryClient.register()

DiscoveryClient.java

boolean register() throws Throwable {
   
    EurekaHttpResponse<Void> httpResponse;
    try {
   
        //发起HTTP请求
        httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
    } catch (Exception e) {
   
        
    }
    return httpResponse.getStatusCode() == 204;
}

使用的Jersey框架来完成http的请求调用

AbstractJerseyEurekaHttpClient.java
@Override
public EurekaHttpResponse<Void> register(InstanceInfo info) {
   
    // 请求url
    String urlPath = "apps/" + info.getAppName();
    ClientResponse response = null;
    try {
   
        Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder();
        addExtraHeaders(resourceBuilder);
        response = resourceBuilder
                .header("Accept-Encoding", "gzip")
                .type(MediaType.APPLICATION_JSON_TYPE)
                .accept(MediaType.APPLICATION_JSON)
                 // post请求;请求参数
                .post(ClientResponse.class, info);
        return anEurekaHttpResponse(response.getStatus()).headers(
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gimtom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值