java零基础课程百度云,九

} else if (isBlank(info.getIPAddr())) {

return Response.status(400).entity(“Missing ip address”).build();

} else if (isBlank(info.getAppName())) {

return Response.status(400).entity(“Missing appName”).build();

} else if (!appName.equals(info.getAppName())) {

return Response.status(400).entity("Mismatched appName, expecting " + appName + " but was " + info.getAppName()).build();

} else if (info.getDataCenterInfo() == null) {

return Response.status(400).entity(“Missing dataCenterInfo”).build();

} else if (info.getDataCenterInfo().getName() == null) {

return Response.status(400).entity(“Missing dataCenterInfo Name”).build();

}

//处理客户端可能在数据缺失的情况下向错误的DataCenterInfo注册的情况

// handle cases where clients may be registering with bad DataCenterInfo with missing data

DataCenterInfo dataCenterInfo = info.getDataCenterInfo();

if (dataCenterInfo instanceof UniqueIdentifier) {

String dataCenterInfoId = ((UniqueIdentifier) dataCenterInfo).getId();

if (isBlank(dataCenterInfoId)) {

boolean experimental = “true”.equalsIgnoreCase(serverConfig.getExperimental(“registration.validation.dataCenterInfoId”));

if (experimental) {

String entity = “DataCenterInfo of type " + dataCenterInfo.getClass() + " must contain a valid id”;

return Response.status(400).entity(entity).build();

} else if (dataCenterInfo instanceof AmazonInfo) {

AmazonInfo amazonInfo = (AmazonInfo) dataCenterInfo;

String effectiveId = amazonInfo.get(AmazonInfo.MetaDataKey.instanceId);

if (effectiveId == null) {

amazonInfo.getMetadata().put(AmazonInfo.MetaDataKey.instanceId.getName(), info.getId());

}

} else {

logger.warn(“Registering DataCenterInfo of type {} without an appropriate id”, dataCenterInfo.getClass());

}

}

}

//【重要】:这里在调用PeerAwareInstanceRegistry的register注册服务,使用的是实现类:InstanceRegistry

registry.register(info, “true”.equals(isReplication));

return Response.status(204).build(); // 204 to be backwards compatible

}

ApplicationResource.addInstance看方法名就能推测出他是用来注册实例的方法,其中参数InstanceInfo 是客户端提交的注册信息,请求头中isReplicationd为true代表是从其他Eureka Server节点复制实例,如果是isReplication为false,代表是Eureka Client 注册的

在做了一些列参数判断之后,这里在调用PeerAwareInstanceRegistry的register注册服务,使用的是实现类:InstanceRegistry,这个类在之前有介绍过,就是Eureak Server用来实现服务注册,服务发现,服务续约,取消注册等的具体实现,他的继承关系如下:

在这里插入图片描述

跟踪下去,InstanceRegistry .register方法源码如下

public class InstanceRegistry extends PeerAwareInstanceRegistryImpl implements ApplicationContextAware {

public void register(final InstanceInfo info, final boolean isReplication) {

//调用handleRegistration方法,抛出事件:EurekaInstanceRegisteredEvent

this.handleRegistration(info, this.resolveInstanceLeaseDuration(info), isReplication);

//调用父类PeerAwareInstanceRegistryImpl的register方法

super.register(info, isReplication);

}

//服务注册,抛出EurekaInstanceRegisteredEvent事件

private void handleRegistration(InstanceInfo info, int leaseDuration, boolean isReplication) {

this.log("register " + info.getAppName() + ", vip " + info.getVIPAddress() + ", leaseDuration " + leaseDuration + ", isReplication " + isReplication);

this.publishEvent(new EurekaInstanceRegisteredEvent(this, info, leaseDuration, isReplication));

}

…省略…

}

这里在调用handleRegistration方法,抛出事件:EurekaInstanceRegisteredEvent后继续调用了super.register方法,即:PeerAwareInstanceRegistryImpl.register,继续跟踪下去:

@Singleton

public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {

/**

注册服务信息 InstanceInfo,并将此信息InstanceInfo复制到所有对等的eureka server节点。

如果这是来自其他副本节点的复制事件,则不会复制它。

  • Registers the information about the {@link InstanceInfo} and replicates

  • this information to all peer eureka nodes. If this is replication event

  • from other replica nodes then it is not replicated.

  • @param info

  •        the {@link InstanceInfo} to be registered and replicated.
    
  • @param isReplication

  •        true if this is a replication event from other replica nodes,
    
  •        false otherwise.
    

*/

@Override

public void register(final InstanceInfo info, final boolean isReplication) {

//租期失效时间 90 s

int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;

if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {

//如果服务的租期失效时间大于默认的90s,则重新赋值租期时间

leaseDuration = info.getLeaseInfo().getDurationInSecs();

}

//服务注册

super.register(info, leaseDuration, isReplication);

//把注册的服务信息复制到其他的Eureka Server 节点,注意这里是Action.Register

replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);

}

}

该方法做了3个事情,

  • 1是更新租期失效时间,

  • 2是调用super.register服务注册(AbstractInstanceRegistry.register)

  • 3是调用replicateToPeers把服务实例拷贝到其他的Eureak Server节点

我们先看下super.register方法即:AbstractInstanceRegistry.register方法的源码

/**

  • 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) {

try {

//获取锁:ReentrantReadWriteLock.lock()

read.lock();

//根据注册的服务的名字取本地服务注册表中获取服务注册信息,如果该服务已经被注册了,那么registry中将会存在它

Map<String, Lease> gMap = registry.get(registrant.getAppName());

REGISTER.increment(isReplication);

if (gMap == null) {

//如果该服务实例没被注册,就把服务实例注册到本地的registry中,本质是一个Map

final ConcurrentHashMap<String, Lease> gNewMap = new ConcurrentHashMap<String, Lease>();

//没有的话就添加到registry中

gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);

if (gMap == null) {

gMap = gNewMap;

}

}

//根据服务实例id获取服务的租约对象

Lease existingLease = gMap.get(registrant.getId());

// Retain the last dirty timestamp without overwriting it, if there is already a lease

//如果已经有租约,则保留最后的脏时间戳而不覆盖它

if (existingLease != null && (existingLease.getHolder() != null)) {

//registry中已经存在的当前服务的最后修改时间的时间戳

Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();

//提交注册的当前服务的最后修改时间

Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();

logger.debug(“Existing lease found (existing={}, provided={}”, existingLastDirtyTimestamp, registrationLastDirtyTimestamp);

//因为如果时间戳相等,我们仍然采用远程传输的InstanceInfo而不是服务器本地副本

// this is a > instead of a >= because if the timestamps are equal, we still take the remote transmitted

// InstanceInfo instead of the server local copy.

//如果已存在的该服务的修改时间 大于 当前提交注册的该服务的最后修改时间,

//则采用registy中已存在的服务为准,因为要选择修改时间靠后的

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”);

//使用现有的instanceInfo代替新的instanceInfo作为注册者

registrant = existingLease.getHolder();

}

} else {

//执行到这里,说明该服务是新注册

// The lease does not exist and hence it is a new registration

synchronized (lock) {

//这里在计算服务的续约频率值

if (this.expectedNumberOfRenewsPerMin > 0) {

// Since the client wants to cancel it, reduce the threshold

// (1

// for 30 seconds, 2 for a minute)

//(expectedNumberOfRenewsPerMin)期待的每分钟续订次数,默认是30s/个,给他增加到2,每分钟2个请求

this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2;

//修改numberOfRenewsPerMinThreshold每分钟续约阀值 = 2 *(85%),

//RenewalPercentThreshold是获取续订阈值百分比

this.numberOfRenewsPerMinThreshold =

(int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());

}

}

logger.debug(“No previous lease information found; it is new registration”);

}

//创建租约对象,把注册实例和租期放进去

Lease lease = new Lease(registrant, leaseDuration);

if (existingLease != null) {

//设置服务上线时间

lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());

}

//以注册的实例的ID为key把服务实例存封装到 Map

gMap.put(registrant.getId(), lease);

//添加到注册队列

synchronized (recentRegisteredQueue) {

recentRegisteredQueue.add(new Pair<Long, String>(

System.currentTimeMillis(),

registrant.getAppName() + “(” + registrant.getId() + “)”));

}

// This is where the initial state transfer of overridden status happens

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());

//添加服务的OverriddenStatus

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);

}

//根据覆盖的状态规则设置状态

// Set the status based on the overridden status rules

InstanceStatus overriddenInstanceStatus = getOverriddenInstanceStatus(registrant, existingLease, isReplication);

registrant.setStatusWithoutDirty(overriddenInstanceStatus);

// If the lease is registered with UP status, set lease service up timestamp

//如果租约已注册为UP状态,请设置租约服务启动时间戳记

if (InstanceStatus.UP.equals(registrant.getStatus())) {

//更新服务上线时间戳

lease.serviceUp();

}

//服务动作:ADDED添加,MODIFIED修改,DELETED删除

registrant.setActionType(ActionType.ADDED);

//添加到最近更改的队列

recentlyChangedQueue.add(new RecentlyChangedItem(lease));

//最后更新时间

registrant.setLastUpdatedTimestamp();

//使当前应用的ResponseCache失效

invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());

logger.info(“Registered instance {}/{} with status {} (replication={})”,

registrant.getAppName(), registrant.getId(), registrant.getStatus(), isReplication);

} finally {

read.unlock();

}

}

总结一下,这个方法做了什么呢

  1. 判断当前服务是否已经被注册,如果是,则以最后更新时间为准,选择更新时间靠后的服务实例进行注册

  2. 维护实例的租约信息Lease,并放到Eureka Server本地维护维护的registry注册表中,本质是一个Map(ConcurrentHashMap<String, Map<String, Lease>>)

  3. 如果是服务是新注册的,把注册的实例封装成Leaset存储到registry注册表中,并更新每分钟续约阀值numberOfRenewsPerMinThreshold

  4. 维护了两个队列,recentRegisteredQueue最近注册队列,recentlyChangedQueue最近更改队列,这个队列可以用来获取最近操作的信息。

  5. 维护当前实例的OverriddenStatus

  6. 更新服务实例的最后更新时间戳

  7. 使ResponseCache缓存失效

该方法结束,我们回到PeerAwareInstanceRegistryImpl.register方法中,继续跟踪replicateToPeers 方法

@Singleton

public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {

/**

  • Replicates all eureka actions to peer eureka nodes except for replication

  • traffic to this node.

  • 将所有eureka操作复制到对等eureka节点

*/

private void replicateToPeers(Action action, String appName, String id,

InstanceInfo info /* optional */,

InstanceStatus newStatus /* optional */, boolean isReplication) {

//开始计时

Stopwatch tracer = action.getTimer().start();

try {

//是否是其他节点复制过来的

if (isReplication) {

//最后一分钟的复制次数+1

numberOfReplicationsLastMin.increment();

}

// If it is a replication already, do not replicate again as this will create a poison replication

//如果已经是复制,则不要再次复制

if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {

return;

}

//遍历集群所有节点

for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) {

// If the url represents this host, do not replicate to yourself.

//如果该URL代表此主机,请不要复制到您自己,当前节点不复制

if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {

continue;

}

//复制实例到其他某个Eureka

replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node);

}

} finally {

tracer.stop();

}

}

继续跟踪replicateInstanceActionsToPeers的源码

/**

  • Replicates all instance changes to peer eureka nodes except for

  • replication traffic to this node.

*/

private void replicateInstanceActionsToPeers(Action action, String appName,

String id, InstanceInfo info, InstanceStatus newStatus,

PeerEurekaNode node) {

try {

InstanceInfo infoFromRegistry = null;

CurrentRequestVersion.set(Version.V2);

//判断请求的是什么操作

switch (action) {

//取消注册,调用 PeerEurekaNode.cancel

case Cancel:

node.cancel(appName, id);

break;

//心跳请求,调用PeerEurekaNode.heartbeat

case Heartbeat:

InstanceStatus overriddenStatus = overriddenInstanceStatusMap.get(id);

infoFromRegistry = getInstanceByAppAndId(appName, id, false);

node.heartbeat(appName, id, infoFromRegistry, overriddenStatus, false);

break;

//服务注册调用PeerEurekaNode.register

case Register:

node.register(info);

break;

//状态修改调用PeerEurekaNode.statusUpdate

case StatusUpdate:

infoFromRegistry = getInstanceByAppAndId(appName, id, false);

node.statusUpdate(appName, id, newStatus, infoFromRegistry);

break;

//状态删除调用PeerEurekaNode.deleteStatusOverride

case DeleteStatusOverride:

infoFromRegistry = getInstanceByAppAndId(appName, id, false);

node.deleteStatusOverride(appName, id, infoFromRegistry);

break;

}

} catch (Throwable t) {

logger.error(“Cannot replicate information to {} for action {}”, node.getServiceUrl(), action.name(), t);

}

}

这里在根据请求的动作类型选择PeerEurekaNode的不同方法,我们这里是服务注册.register,调用的是 PeerEurekaNode.register ,源码如下:

/**

  • Sends the registration information of {@link InstanceInfo} receiving by

  • this node to the peer node represented by this class.

  • @param info

  •        the instance information {@link InstanceInfo} of any instance
    
  •        that is send to this instance.
    
  • @throws Exception

*/

public void register(final InstanceInfo info) throws Exception {

//到期时间当,前时间加上30s过期

long expiryTime = System.currentTimeMillis() + getLeaseRenewalOf(info);

//封装InstanceReplicationTask 实例赋值任务到调度器中

batchingDispatcher.process(

taskId(“register”, info),

new InstanceReplicationTask(targetHost, Action.Register, info, null, true) {

public EurekaHttpResponse execute() {

//复制器客户端 HttpReplicationClient(JerseyReplicationClient),执行注册

//调用AbstractJerseyEurekaHttpClient#register

return replicationClient.register(info);

}

},

expiryTime

);

}

把InstanceInfo注册实例封装成InstanceReplicationTask实例复制任务,交给batchingDispatcher批量任务调度器去执行,replicationClient是HttpReplicationClient它的默认实现是JerseyEurekaHttpClient,底层会调用AbstractJerseyEurekaHttpClient#register的方法完成实例的注册,这里其实就和当时我们分析Eureka Client 服务注册的最后注册请求一样了

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后希望可以帮助到大家!

千千万万要记得:多刷题!!多刷题!!

之前算法是我的硬伤,后面硬啃了好长一段时间才补回来,算法才是程序员的灵魂!!!!

篇幅有限,以下只能截图分享部分的资源!!

(1)多线程(这里以多线程为代表,其实整理了一本JAVA核心架构笔记集)

image

(2)刷的算法题(还有左神的算法笔记)

image

(3)面经+真题解析+对应的相关笔记(很全面)

image

(4)视频学习(部分)

ps:当你觉得学不进或者累了的时候,视频是个不错的选择

在这里,最后只一句话:祝大家offer拿到手软!!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
识点,真正体系化!**

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后希望可以帮助到大家!

千千万万要记得:多刷题!!多刷题!!

之前算法是我的硬伤,后面硬啃了好长一段时间才补回来,算法才是程序员的灵魂!!!!

篇幅有限,以下只能截图分享部分的资源!!

(1)多线程(这里以多线程为代表,其实整理了一本JAVA核心架构笔记集)

[外链图片转存中…(img-u3ybTlCQ-1713312823837)]

(2)刷的算法题(还有左神的算法笔记)

[外链图片转存中…(img-lfvYPGIv-1713312823837)]

(3)面经+真题解析+对应的相关笔记(很全面)

[外链图片转存中…(img-CsPHWyWG-1713312823838)]

(4)视频学习(部分)

ps:当你觉得学不进或者累了的时候,视频是个不错的选择

在这里,最后只一句话:祝大家offer拿到手软!!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 12
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值