SpringCloud-Eureka技术学习及分享(version:2020.0.5)

一.简介

Eureka是Netflix中的一个开源框架,由SpringCloud整合的一个的一个基于服务发现及注册的微服务框架,Eureka主要包含Eureka Server(服务端)及Eureka Client(客户端)两个组件。Eureka Server端提供服务注册、服务续约、服务下线、服务健康检查等功能;Eureka client需要在启动时根据配置的Server端地址去注册自己,并定时向服务端发送心跳并维护本地服务列表。
Eureka满足AP,高可用与可伸缩的Service发现服务,突出可用性。
(CAP原理
P:Partition tolerance,网络分区容错。类似多机房部署,保证服务稳定性。
A: Availability,可用性。
C:Consistency ,一致性。)
在这里插入图片描述

二.Eureka Server端

服务端示例代码开发及配置

  1. 引入POM
    <dependency>
   		<groupId>org.springframework.cloud</groupId>
   		<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
   	</dependency>
  1. 添加配置
spring:
  profiles:
    active: dev
  application:
    name: eurekaserver01
eureka:
  instance:
  #注册名默认是“IP名:应用名:应用端口名”,即${spring.cloud.client.ipAddress}:${spring.application.name}:${spring.application.instance_id:${server.port}}
    instance-id: eurekaserver01
  client:
  #表示是否从Eureka Server获取注册的服务信息
    fetch-registry: false
    # 续约间隔时间 单位为秒
    #registry-fetch-interval-seconds: 30
    #表示是否将自己注册到Eureka Server
    register-with-eureka: false
    service-url:
    # 服务地址  可配置多个中间按照逗号分割
      defaultZone: http://eurekaserver01:7001/eureka/
  server:
    # 关闭自我保护机制
    enable-self-preservation: false
    # 清理间隔(单位:毫秒,默认是60*1000),当服务心跳失效后多久,删除服务
    eviction-interval-timer-in-ms: 3000
  1. 在启动类中添加@EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
public class Eurekaserver01Application {
   public static void main(String[] args) {
   	SpringApplication.run(Eurekaserver01Application.class, args);
   }
}

Eureka Server 源码解析

Eureka Server端的代码不多在org.springframework.cloud.netflix.eureka.server中

服务注册

在这个方法中com.netflix.eureka.registry.AbstractInstanceRegistry#register(com.netflix.appinfo.InstanceInfo, int, boolean),主要做了以下事情:

  1. 发布服务注册监听事件;
  2. 如果是新注册的服务则更新续订客户端数量(在原有的数量上加1)
  3. 重新计算期望收到的续订数(=续订客户端数量*(60/多久续订一次)*自我保护机制阀值)向下取整;
  4. 如果实例状态不等于UNKNOWN,则更新实例状态
  5. 将当前实例添加到注册列表(ConcurrentHashMap<String, Map<String, Lease>> registry)

源码如下:

@Override
/**
*info :表示注册的势力信息
*/
public void register(InstanceInfo info, int leaseDuration, boolean isReplication) {
	//添加服务注册监听事件
	handleRegistration(info, leaseDuration, isReplication);
	//服务注册
	super.register(info, leaseDuration, isReplication);
}
  /**
     * Registers a new instance with a given duration.
     *
     * @see com.netflix.eureka.lease.LeaseManager#register(java.lang.Object, int, boolean)
     */
    public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
        read.lock();
        try {
        	//根据服务名称在注册列表中获取服务租约信息
            Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
            REGISTER.increment(isReplication);
            if (gMap == null) {
                final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();
                
                gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
                if (gMap == null) {
                    gMap = gNewMap;
                }
            }
            Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());
            // 当前租约存在
            if (existingLease != null && (existingLease.getHolder() != null)) {
            	//现有的租约时间戳
                Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
                //本次注册的租约时间
                Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();
                logger.debug("Existing lease found (existing={}, provided={}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);
                if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {
                    logger.warn("There is an existing lease and the existing lease's dirty timestamp {} is greater" +
                            " than the one that is being registered {}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);
                    logger.warn("Using the existing instanceInfo instead of the new instanceInfo as the registrant");
                    registrant = existingLease.getHolder();
                }
            } else {
                // 租约不存在,因此是新的注册
                synchronized (lock) {
                    if (this.expectedNumberOfClientsSendingRenews > 0) {
                        // 注册的客户端数量加1
                        this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
                        updateRenewsPerMinThreshold();
                    }
                }
                logger.debug("No previous lease information found; it is new registration");
            }
            Lease<InstanceInfo> lease = new Lease<>(registrant, leaseDuration);
            if (existingLease != null) {
                lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());
            }
            gMap.put(registrant.getId(), lease);
            recentRegisteredQueue.add(new Pair<Long, String>(
                    System.currentTimeMillis(),
                    registrant.getAppName() + "(" + registrant.getId() + ")"));
            // 如果服务状态不为UNKNOWN,则设置服务状态
            if (!InstanceStatus.UNKNOWN.equals(registrant.getOverriddenStatus())) {
                logger.debug("Found overridden status {} for instance {}. Checking to see if needs to be add to the "
                                + "overrides", registrant.getOverriddenStatus(), registrant.getId());
                if (!overriddenInstanceStatusMap.containsKey(registrant.getId())) {
                    logger.info("Not found overridden id {} and hence adding it", registrant.getId());
                    overriddenInstanceStatusMap.put(registrant.getId(), registrant.getOverriddenStatus());
                }
            }
            InstanceStatus overriddenStatusFromMap = overriddenInstanceStatusMap.get(registrant.getId());
            if (overriddenStatusFromMap != null) {
                logger.info("Storing overridden status {} from map", overriddenStatusFromMap);
                registrant.setOverriddenStatus(overriddenStatusFromMap);
            }

            // 根据覆盖的状态规则设置状态
            InstanceStatus overriddenInstanceStatus = getOverriddenInstanceStatus(registrant, existingLease, isReplication);
            registrant.setStatusWithoutDirty(overriddenInstanceStatus);

            // 如果租约已注册为UP状态,设置租约服务UP时间戳
            if (InstanceStatus.UP.equals(registrant.getStatus())) {
                lease.serviceUp();
            }
            registrant.setActionType(ActionType.ADDED);
            recentlyChangedQueue.add(new RecentlyChangedItem(lease));
            registrant.setLastUpdatedTimestamp();
            invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());
            logger.info("Registered instance {}/{} with status {} (replication={})",
                    registrant.getAppName(), registrant.getId(), registrant.getStatus(), isReplication);
        } finally {
            read.unlock();
        }
    }

服务下线

com.netflix.eureka.registry.AbstractInstanceRegistry#cancel
主要做了以下事情:

  1. 发布服务下线事件
  2. 根据实例ID将当前服务在注册列表中删除
  3. 根据实例ID删除状态列表
  4. 设置实例的 actionType为DELETED
  5. 如果是新注册的服务则更新续订客户端数量(expectedNumberOfClientsSendingRenews 在原有的数量上减1)
  6. 重新计算期望收到的续订数(=续订客户端数量*(60/多久续订一次)*自我保护机制阀值)向下取整;
    源码如下:
	@Override
	public boolean cancel(String appName, String serverId, boolean isReplication) {
		handleCancelation(appName, serverId, isReplication);
		return super.cancel(appName, serverId, isReplication);
	}
	 @Override
    public boolean cancel(final String appName, final String id,
                          final boolean isReplication) {
        if (super.cancel(appName, id, isReplication)) {
            replicateToPeers(Action.Cancel, appName, id, null, null, isReplication);
            return true;
        }
        return false;
    }
	 /**
     * {@link #cancel(String, String, boolean)} method is overridden by {@link PeerAwareInstanceRegistry}, so each
     * cancel request is replicated to the peers. This is however not desired for expires which would be counted
     * in the remote peers as valid cancellations, so self preservation mode would not kick-in.
     */
    protected boolean internalCancel(String appName, String id, boolean isReplication) {
        read.lock();
        try {
            CANCEL.increment(isReplication);
            Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
            Lease<InstanceInfo> leaseToCancel = null;
            if (gMap != null) {
                leaseToCancel = gMap.remove(id);
            }
            recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
            InstanceStatus instanceStatus = overriddenInstanceStatusMap.remove(id);
            if (instanceStatus != null) {
                logger.debug("Removed instance id {} from the overridden map which has value {}", id, instanceStatus.name());
            }
            if (leaseToCancel == null) {
                CANCEL_NOT_FOUND.increment(isReplication);
                logger.warn("DS: Registry: cancel failed because Lease is not registered for: {}/{}", appName, id);
                return false;
            } else {
                leaseToCancel.cancel();
                InstanceInfo instanceInfo = leaseToCancel.getHolder();
                String vip = null;
                String svip = null;
                if (instanceInfo != null) {
                    instanceInfo.setActionType(ActionType.DELETED);
                    recentlyChangedQueue.add(new RecentlyChangedItem(leaseToCancel));
                    instanceInfo.setLastUpdatedTimestamp();
                    vip = instanceInfo.getVIPAddress();
                    svip = instanceInfo.getSecureVipAddress();
                }
                invalidateCache(appName, vip, svip);
                logger.info("Cancelled instance {}/{} (replication={})", appName, id, isReplication);
            }
        } finally {
            read.unlock();
        }
        synchronized (lock) {
            if (this.expectedNumberOfClientsSendingRenews > 0) {
                // Since the client wants to cancel it, reduce the number of clients to send renews.
                this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1;
                updateRenewsPerMinThreshold();
            }
        }
        return true;
    }

服务续约

com.netflix.eureka.registry.AbstractInstanceRegistry#renew

	//服务续约  appName 服务名称   serverId 服务ID  isReplication 是否是服务端的复制  还是客户端的续约操作
	 public boolean renew(String appName, String id, boolean isReplication) {
	 	//计数器加1 ,如果当前是服务器副本备份  则counter加1 否则counter(当前服务名对应的区域计数器)加1且myZoneCounter(当前服务自己的计数器)加1 
        RENEW.increment(isReplication);
        //根据服务名称从注册列表中取出对应的的服务列表
        Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
        Lease<InstanceInfo> leaseToRenew = null;
        if (gMap != null) {
        	//根据服务ID取出服务实例信息
            leaseToRenew = gMap.get(id);
        }
        if (leaseToRenew == null) {
        	//续约失败加1  
            RENEW_NOT_FOUND.increment(isReplication);
            logger.warn("DS: Registry: lease doesn't exist, registering resource: {} - {}", appName, id);
            return false;
        } else {
        	//服务实例信息
            InstanceInfo instanceInfo = leaseToRenew.getHolder();
            if (instanceInfo != null) {
                // touchASGCache(instanceInfo.getASGName());
                //匹配当前的实例状态  并返回匹配后的实例状态
                InstanceStatus overriddenInstanceStatus = this.getOverriddenInstanceStatus(
                        instanceInfo, leaseToRenew, isReplication);
                 //如果匹配后的实例状态为已下线  则续约失败加1       
                if (overriddenInstanceStatus == InstanceStatus.UNKNOWN) {
                    logger.info("Instance status UNKNOWN possibly due to deleted override for instance {}"
                            + "; re-register required", instanceInfo.getId());
                    RENEW_NOT_FOUND.increment(isReplication);
                    return false;
                }
                //如果当前实例状态与匹配后的实例状态补相等  则修改实例状态为匹配后的实例状态
                if (!instanceInfo.getStatus().equals(overriddenInstanceStatus)) {
                    logger.info(
                            "The instance status {} is different from overridden instance status {} for instance {}. "
                                    + "Hence setting the status to overridden status", instanceInfo.getStatus().name(),
                                    overriddenInstanceStatus.name(),
                                    instanceInfo.getId());
                    instanceInfo.setStatusWithoutDirty(overriddenInstanceStatus);

                }
            }
            //最后一分钟的实例续约计数器加1
            renewsLastMin.increment();
            //修改最后一次续约时间
            leaseToRenew.renew();
            return true;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值