(二)微服务中间键工作原理——nacos服务端服务注册心跳包(/nacos/v1/ns/instance/beat)源码解读

news/2024/7/24 13:09:19/

前言

上节内容我们通过分析nacos客户端源码,了解了nacos客户端是如何向服务端注册服务和发送心跳包的,本节内容话接上一节内容,我们通过分析nacos服务的源码,查看服务端是如何处理客户端注册时候的心跳包的。关于nacos服务端的源码,下载地址为:GitHub - alibaba/nacos: an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.

正文

①找到nacos服务端处理心跳包的接口InstanceController类

接口地址:/nacos/v1/ns/instance/beat

 ②在InstanceController类中的beat方法实现了心跳包的处理逻辑

心跳包的整体处理流程说明:

@CanDistro@PutMapping("/beat")@Secured(action = ActionTypes.WRITE)public ObjectNode beat(HttpServletRequest request) throws Exception {ObjectNode result = JacksonUtils.createEmptyJsonNode();//1.设置默认心跳间隔时间result.put(SwitchEntry.CLIENT_BEAT_INTERVAL, switchDomain.getClientBeatInterval());//2.获取心跳包数据String beat = WebUtils.optional(request, "beat", StringUtils.EMPTY);RsInfo clientBeat = null;if (StringUtils.isNotBlank(beat)) {//3.解析心跳包数据clientBeat = JacksonUtils.toObj(beat, RsInfo.class);}//4.获取集群名称、IP地址、和端口String clusterName = WebUtils.optional(request, CommonParams.CLUSTER_NAME, UtilsAndCommons.DEFAULT_CLUSTER_NAME);String ip = WebUtils.optional(request, "ip", StringUtils.EMPTY);int port = Integer.parseInt(WebUtils.optional(request, "port", "0"));if (clientBeat != null) {if (StringUtils.isNotBlank(clientBeat.getCluster())) {clusterName = clientBeat.getCluster();} else {// fix #2533clientBeat.setCluster(clusterName);}ip = clientBeat.getIp();port = clientBeat.getPort();}//5.获取客户端服务命名空间和服务名称String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);//6.检查服务名称规范,不符合要求抛出异常NamingUtils.checkServiceNameFormat(serviceName);Loggers.SRV_LOG.debug("[CLIENT-BEAT] full arguments: beat: {}, serviceName: {}, namespaceId: {}", clientBeat,serviceName, namespaceId);BeatInfoInstanceBuilder builder = BeatInfoInstanceBuilder.newBuilder();builder.setRequest(request);//7.处理心跳包数据int resultCode = getInstanceOperator().handleBeat(namespaceId, serviceName, ip, port, clusterName, clientBeat, builder);//8.返回处理结果result.put(CommonParams.CODE, resultCode);result.put(SwitchEntry.CLIENT_BEAT_INTERVAL,getInstanceOperator().getHeartBeatInterval(namespaceId, serviceName, ip, port, clusterName));result.put(SwitchEntry.LIGHT_BEAT_ENABLED, switchDomain.isLightBeatEnabled());return result;}

③在InstanceOperatorClientImpl类中handleBeat方法是对心跳包的具体处理流程

handleBeat方法执行流程的说明:

public int handleBeat(String namespaceId, String serviceName, String ip, int port, String cluster,RsInfo clientBeat, BeatInfoInstanceBuilder builder) throws NacosException {//1.根据服务命名空间和服务名称获取服务实例对象Service service = getService(namespaceId, serviceName, true);//2.根据ip和端口号获取客户端IDString clientId = IpPortBasedClient.getClientId(ip + InternetAddressUtil.IP_PORT_SPLITER + port, true);//3.查询注册的客户端IpPortBasedClient client = (IpPortBasedClient) clientManager.getClient(clientId);//4.如果客户端不存在或者客户端服务实例还未发布,注册客户端实例,否则跳过该操作if (null == client || !client.getAllPublishedService().contains(service)) {if (null == clientBeat) {//4.1心跳包不存在,直接返回不存在的提示码return NamingResponseCode.RESOURCE_NOT_FOUND;}//4.2根据心跳包和服务名称构建Instance实例对象Instance instance = builder.setBeatInfo(clientBeat).setServiceName(serviceName).build();//4.3注册Instance实例对象,该方式中存在关于client的实例对象的注册registerInstance(namespaceId, serviceName, instance);//4.4再次获取客户端实例client = (IpPortBasedClient) clientManager.getClient(clientId);}//5.验证服务实例对象是否存在,不存在则抛出服务不存在的异常if (!ServiceManager.getInstance().containSingleton(service)) {throw new NacosException(NacosException.SERVER_ERROR,"service not found: " + serviceName + "@" + namespaceId);}//6.心跳包不存在,则根据传入参数封装客户端心跳包数据if (null == clientBeat) {clientBeat = new RsInfo();clientBeat.setIp(ip);clientBeat.setPort(port);clientBeat.setCluster(cluster);clientBeat.setServiceName(serviceName);}//7.服务健康检查,更新服务的心跳时间,如果服务的健康状态是false,则更新为true,表明服务实例是健康状态ClientBeatProcessorV2 beatProcessor = new ClientBeatProcessorV2(namespaceId, clientBeat, client);HealthCheckReactor.scheduleNow(beatProcessor);//8.更新客户端时间client.setLastUpdatedTime();return NamingResponseCode.OK;}

 registerInstance(namespaceId, serviceName, instance)方法实现了客户端服务和客户端实例的注册

 

下面的方法实现了服务的心跳时间更新和健康状态更新

ClientBeatProcessorV2 beatProcessor = new ClientBeatProcessorV2(namespaceId, clientBeat, client);
HealthCheckReactor.scheduleNow(beatProcessor);

④ InstanceOperatorClientImpl类中的registerInstance方法实现了IpPortBasedClient客户端和Instance服务实例的注册

IpPortBasedClient客户端和Instance服务实例的注册的源码说明:
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {//1.检查服务实例是否合法NamingUtils.checkInstanceIsLegal(instance);//2.获取IpPortBasedClient客户端idboolean ephemeral = instance.isEphemeral();String clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), ephemeral);//3.创建IpPortBasedClient客户端createIpPortClientIfAbsent(clientId);//4.获取服务实例Service service = getService(namespaceId, serviceName, ephemeral);//5.创建Instance实例clientOperationService.registerInstance(service, instance, clientId);}

 ⑤分析IpPortBasedClient客户端实例创建的方法createIpPortClientIfAbsent(clientId);

- IpPortBasedClient客户端存储在一个ConcurrentMap集合中

private final ConcurrentMap<String, IpPortBasedClient> clients = new ConcurrentHashMap<>();

- 判断IpPortBasedClient客户端是否存在,不存在则创建该客户端

private void createIpPortClientIfAbsent(String clientId) {//1.判断IpPortBasedClient客户端是否存在if (!clientManager.contains(clientId)) {ClientAttributes clientAttributes;if (ClientAttributesFilter.threadLocalClientAttributes.get() != null) {clientAttributes = ClientAttributesFilter.threadLocalClientAttributes.get();} else {clientAttributes = new ClientAttributes();}//2.客户端不存在,创建IpPortBasedClient客户端clientManager.clientConnected(clientId, clientAttributes);}}

-调用clientConnected方法,使用客户端工厂类EphemeralIpPortClientFactory创建一个客户端IpPortBasedClient

public boolean clientConnected(String clientId, ClientAttributes attributes) {//使用客户端工厂工具创建一个客户端return clientConnected(clientFactory.newClient(clientId, attributes));
}

-调用clientConnected同名方法,将IpPortBasedClient客户端存储于ConcurrentMap中,并执行init初始化方法

public boolean clientConnected(final Client client) {//将IpPortBasedClient客户端存储到clients的ConcurrentMap中clients.computeIfAbsent(client.getClientId(), s -> {Loggers.SRV_LOG.info("Client connection {} connect", client.getClientId());IpPortBasedClient ipPortBasedClient = (IpPortBasedClient) client;//调用IpPortBasedClient的init方法,实现初始化方法ipPortBasedClient.init();return ipPortBasedClient;});return true;
}

-在init方法中,实现了注册服务心跳包检查和健康检查

public void init() {if (ephemeral) {//心跳包检查beatCheckTask = new ClientBeatCheckTaskV2(this);HealthCheckReactor.scheduleCheck(beatCheckTask);} else {//健康检查healthCheckTaskV2 = new HealthCheckTaskV2(this);HealthCheckReactor.scheduleCheck(healthCheckTaskV2);}
}

- 心跳包检查,在类ClientBeatCheckTaskV2中的doHealthCheck()方法实现心跳包的健康检查

 -在doInterceptor方法中的passIntercept方法实现了客户端检查、健康检查、服务实例检查

-这里我们以服务实例检查为例,ExpiredInstanceChecker中的doCheck方法实现服务实例的过期检查,如果过期则删除实例,默认超过30秒删除实例

public void doCheck(Client client, Service service, HealthCheckInstancePublishInfo instance) {boolean expireInstance = ApplicationUtils.getBean(GlobalConfig.class).isExpireInstance();//如果过期则删除服务注册实例if (expireInstance && isExpireInstance(service, instance)) {deleteIp(client, service, instance);}
}

 

- 在UnhealthyInstanceChecker中的doCheck方法实现服务实例的健康检查,默认时间超过15秒则视实例为不健康

public void doCheck(Client client, Service service, HealthCheckInstancePublishInfo instance) {//判断实例是否健康if (instance.isHealthy() && isUnhealthy(service, instance)) {//如果实例不健康则将实例状态置为falsechangeHealthyStatus(client, service, instance);}
}

⑥服务实例Instance注册 registerInstance

public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {//1.检查服务实例是否合法NamingUtils.checkInstanceIsLegal(instance);//2.获取并创建单实例的服务Service singleton = ServiceManager.getInstance().getSingleton(service);if (!singleton.isEphemeral()) {throw new NacosRuntimeException(NacosException.INVALID_PARAM,String.format("Current service %s is persistent service, can't register ephemeral instance.",singleton.getGroupedServiceName()));}//3.获取客户端,如果不合法则直接返回Client client = clientManager.getClient(clientId);if (!clientIsLegal(client, clientId)) {return;}//4.获取实例发布信息InstancePublishInfo instanceInfo = getPublishInfo(instance);//5.更新客户端信息client.addServiceInstance(singleton, instanceInfo);client.setLastUpdatedTime();client.recalculateRevision();//6.发布客户端服务注册事件NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));NotifyCenter.publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}

结语

至此,关于nacos服务端服务注册心跳包(/nacos/v1/ns/instance/beat)源码解读到这里就结束了,下期见。。。。。。


http://www.ppmy.cn/news/165091.html

相关文章

视频美颜sdk是什么?技术解析与实现原理详解

视频美颜技术的发展则为人们提供了一种美化自己的方式&#xff0c;因此&#xff0c;视频美颜技术成为了一个备受关注的领域。在这个领域中&#xff0c;视频美颜sdk技术则是实现高效美颜的关键因素之一。本文将从技术角度分析视频美颜sdk的实现原理和优势。 一、视频美颜技术的…

有关计算机科学与技术论文

有关计算机科学与技术论文篇一 《 计算方法在计算机科学与技术专业教学改革与实践 》 摘要&#xff1a;介绍了对计算机科学与技术专业的计算方法课程教学改革进行的尝试和探索&#xff0c;结合该专业提出了计算方法课程教学改革的几个重点及策略&#xff0c;优化了教学内容、…

java锁屏,【刷新JAVA锁屏软件 V1.11 (2007-04-02)】-Moto E6手机论坛-ZOL中关村在线

刷新JAVA锁屏软件 V1.11 (2007-04-02) 首先感谢大家对本软件的支持和宽容:) V1.11修改了刚刚一些机友提出的V1.1的问题(原下载链接作废)&#xff0c;主要是某种情况下造成的黑屏的问题。如果大家愿意继续帮忙测试&#xff0c;请下载使用&#xff1b;如果想要等待更加成熟的版本…

phpinfo mysql版本_【请教为何我升级了mysql后phpinfo显示的还是旧版本?】-MySQL论坛-ZOL中关村在线...

重新编译了一下&#xff0c;网站彻底挂了…… [Wed Dec 10 14:33:39 2008] [notice] child pid 1651 exit signal Segmentation fault (11) [Wed Dec 10 14:33:41 2008] [notice] child pid 1332 exit signal Segmentation fault (11) [Wed Dec 10 14:40:53 2008] [notice] chi…

一篇文章搞定《Android嵌套滑动》

一篇文章搞定《Android嵌套滑动》 前言嵌套滑动冲突种类产生原因1、外部与内部滑动方向不一致2、外部与内部滑动方向一致3、多种情况下的嵌套&#xff08;电商首页&#xff09; 解决嵌套滑动的方法1、外部拦截法2、内部拦截法3、现有API框架 外部与内部滑动方向不一致1、ViewPa…

安卓软件与论坛

http://bbs.anzhi.com/forum-75-1.html http://bbs.hiapk.com/forum-429-1.html 安卓论坛&#xff1a;http://www.androidworld.cn/forum.php 准备刷机root&#xff0c;这长时间都不能上网啥的&#xff0c;土阿 呵呵 刷机程序&#xff1a; http://bbs.anzhi.com/forum.php?…

C内存分配的学习帖子(转自zol论坛)

内存分配方式有三种&#xff1a;&#xff08;1&#xff09; 从静态存储区域分配。内存在程序编译的时候就已经分配好&#xff0c;这块内存在程序的整个运行期间都存在。例如全局变量&#xff0c;static 变量。&#xff08;2&#xff09; 在栈上创建。在执行函数时&#xff0c;函…

分享 5 个实用的 Java 开源论坛系统!

最近有点小忙。但是&#xff0c;由于前几天答应了一位读者自己会推荐一些开源的论坛系统&#xff0c;所以&#xff0c;昨晚就简单地熬了个夜&#xff0c;对比了很多个开源论坛系统之后&#xff0c;总结成了这篇文章。 这篇文章我一共推荐了 5 个论坛类开源项目&#xff0c;除了…

Mysql小知识 delete 清空表之后,磁盘空间未发生变化?

1. 删除空洞 1.1 案例展示 首先我们先来看这样一个例子。 我现在有一个名为 sakila 的数据库&#xff0c;该库中有一个 film 表&#xff0c;这个表中有 1000 条记录&#xff0c;我么先来看下这 1000 条记录占用了多少存储空间&#xff1a; 小伙伴们可以看到&#xff0c;这个…

底层网络与平台如何支撑现代化应用,听听 F5 怎么说

出品 | CSDN 云计算 春运期间国内某订票平台最高峰期每小时点击量 59.3 亿次&#xff0c;某大型银行电子渠道交易量每天 1.6 亿笔&#xff0c;这些网络访问压力的背后&#xff0c;都是老牌的应用交付和应用安全厂商 F5 在提供技术支撑。不过在用户对多云、数据与分析量暴增以及…

mybatis是如何集成到spring的之SqlSessionFactoryBean

文章目录 1 前言1.1 集成spring前使用mybatis的方式1.2 集成mybatis到spring的关键步骤 2 SqlSessionFactoryBean对象分析2.1 buildSqlSessionFactory做了什么事情&#xff1f;2.2 为什么是SqlSessionFactoryBean却可以使用SqlSessionFactory&#xff1f; 3 验证demo4 举一反三…

Unity面试题:热更新篇(一)

请简要介绍Unity热更新的原理和实现方式。 答&#xff1a;Unity热更新的原理是通过将游戏的资源和代码分离&#xff0c;将代码部分放置在服务器端&#xff0c;游戏启动时通过网络下载更新的代码并动态加载&#xff0c;以达到实现热更新的目的。实现方式包括AssetBundle、ILRunt…

电脑录视频用什么软件最好?录像软件,3大工具推荐!

案例&#xff1a;电脑屏幕录像软件哪个好用&#xff1f; 【电脑录像工具是我工作必不可缺少的工具&#xff0c;我现在使用的录屏工具性能不太好&#xff0c;很大程度上影响到我的工作效率。我想找一款好用的录屏软件。】 在现代社会中&#xff0c;越来越多的人有电脑录制视频…

记一次 String(-0) 引起的 bug

-0 在js中是存在的&#xff0c;可以通过 var a -0 得到&#xff0c;也可以通过 parseInt(-0.1) 得到 但是存在 -0 0, String(-0) String(0) 的情况 起初&#xff0c;业务中存在一个 给数字转换成 千分位数字字符串的方法 // numInt 为传入的值, 如 1035 let integer pars…

I420转NV21

//I420 To NV21 void I420ToNV21(u8 *pSrcData, u16 wWidth, u16 wHeight, u8 *pDstData[]) {u64 dwSize wWidth * wHeight, i, j;u8 *pSrcData_U pSrcData dwSize;u8 *pSrcData_V pSrcData dwSize (dwSize >> 2);memcpy(pDstData[0], pSrcData, dwSize);//y分量fo…

DELL U2410显示器发红的解决方案

很简单, 换成DVI线连接. 不要用display port

TQ2440 USB驱动

TQ2440 USB驱动是官方提供的一款USB驱动&#xff0c;本站收集提供高速下载&#xff0c;用于解决USB接口不能正常识别&#xff0c;无法正常使用的问题&#xff0c;本动适用于&#xff1a;Windows XP / Windows 7 / Windows 8 / Windows 10 32/64位操作系统。有需要的朋友可以来本…

dvi黑屏解决方法_DVI线导致黑屏故障处理全攻略

笔记本如果遇到黑屏花屏的现象&#xff0c;相信朋友们都会想着把笔记本交给商家去维修&#xff0c;如果尚在保修期还好&#xff0c;过保了问题就大了。笔记本属于精度很高的产品&#xff0c;硬件损坏之后的更换是相当昂贵的。特别是主板损坏&#xff0c;商家的报价大约是2000-3…

dvi黑屏解决方法_DVI线导致黑屏故障处理全攻略分享

今天来聊聊一篇关于DVI线导致黑屏故障处理全攻略分享的文章,现在就为大家来简单介绍下DVI线导致黑屏故障处理全攻略分享,希望对各位小伙伴们有所帮助。 电脑黑屏是一个很常见的故障&#xff0c;引起这个故障的原因也是多种的&#xff0c;但是其中的一个原因是因DVI线导致显示器…

[嵌入式] 重温Mini2440(二)移植Linux-4.9.270

重温Mini2440&#xff08;二&#xff09;移植Linux-4.9.270 一. 获取Linux内核二. 安装编译工具三. 配置内核四. 修改驱动五. 生成uImage六. U-Boot引导内核 一. 获取Linux内核 在The Linux Kernel Archives可以下载到最新的Linux内核 此文使用的版本是&#xff1a; longterm:…