Nacos源码解读12——Nacos中长连接的实现

2023-12-11 04:28

本文主要是介绍Nacos源码解读12——Nacos中长连接的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

短连接 VS 长连接

什么是短连接

客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。

长连接

客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。

长连接的好处

假如请求一个普通的网页 但是这个网页有很多个 css js请求 那每次打开一个网页,基本就要建立几个甚至几十个TCP连接,浪费很多网络资源。如果是长连接的话,那么这么多HTTP请求(包括请求网页的内容、CSS文件、JS文件、图片等)都是使用的一个TCP连接,显然可以节省很多资源。

另外一点,长连接并不是永久连接的。如果一段时间内(具体时间可以在header中进行设置,也就是所谓的超时时间),这个连接没有HTTP请求发出的话,那么这个长连接就会被断掉。

短轮询VS 长轮询

什么是轮询

就比如在飞机上想上厕所 却发现厕所有人需要等待 但是你又不想在那等 就只能回去等 ,但是过一段时间在去 还有人 在接着回去 这么周而复始的去回

短轮询

轮询的原理就是客户端以一定的时间间隔向服务端发出请求,频繁的请求保持客户端和服务端同步。
短轮询最大的问题就是客户端发出请求和服务器端的更新并不是一致的。客户端以固定的频率想服务器发出请求,可能服务器端并没有更新,返回的是个空的信息,等服务器端更新的时候,有可能客户端并没有请求,而且只有最后一次请求才能获得最新数据,这样多次请求不仅浪费了资源,而且并不是实际上的实时更新。

优缺点

优点:后端程序编写比较容易
缺点:请求中有大半是无用,浪费带宽和服务器资源。(而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增加了每次传输的数据量)

长轮询

页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可以发送。
发送完数据之后,浏览器关闭连接,随即又发送一个到服务器的新请求。这一过程在页面打开期间一直持续不断。(在服务端hold住Http请求(死循环或者sleep等等方式),等到目标时间发生,返回Http响应。

长轮询优缺点

优点:在无消息的情况下不会频繁的请求,节省了网络流量,解决了服务端一直疲于接受请求的窘境。
缺点:服务器hold连接会消耗资源,需要同时维护多个线程,服务器所能承载的TCP连接数是有上限的,这种轮询很容易把连接数顶满。

Nacos是怎么处理服务配置修改刷新

1.X版本

在这里插入图片描述
客户端启动的时候会初始化一个长连接的线程池定时去 发送pull请求 会发送一个HTTP请求到服务端 在服务端hold住Http请求 超时时间是30秒
30s内 有可能触发变化 push数据到客户端
30s内 数据没有变更超时返回

缺点

30 秒定期创建销毁连接,GC压力大

2.X版本

Nacos 2.x 相比上面 30s ⼀次的长轮训,升级成长链接模式,配置变更,启动建立长链接,配置变
更服务端推送变更配置列表,然后 SDK 拉取配置更新,因此通信效率大幅提升

Nacos长连接源码解析

这里只抓重点 想看详情的话看前面几章

客户端变更推送配置变更事件

    /*** adaptor to config module ,when server side config change ,invoke this method.** @param groupKey groupKey*/public void configDataChanged(String groupKey, String dataId, String group, String tenant, boolean isBeta,List<String> betaIps, String tag) {//获取注册的Client列表Set<String> listeners = configChangeListenContext.getListeners(groupKey);if (CollectionUtils.isEmpty(listeners)) {return;}int notifyClientCount = 0;//遍历client列表for (final String client : listeners) {//拿到grpc连接Connection connection = connectionManager.getConnection(client);if (connection == null) {continue;}ConnectionMeta metaInfo = connection.getMetaInfo();//beta ips check.String clientIp = metaInfo.getClientIp();String clientTag = metaInfo.getTag();if (isBeta && betaIps != null && !betaIps.contains(clientIp)) {continue;}//tag checkif (StringUtils.isNotBlank(tag) && !tag.equals(clientTag)) {continue;}//构建请求参数ConfigChangeNotifyRequest notifyRequest = ConfigChangeNotifyRequest.build(dataId, group, tenant);//构建推送任务RpcPushTask rpcPushRetryTask = new RpcPushTask(notifyRequest, 50, client, clientIp, metaInfo.getAppName());//推送任务 向客户端发送变更通知push(rpcPushRetryTask);notifyClientCount++;}Loggers.REMOTE_PUSH.info("push [{}] clients ,groupKey=[{}]", notifyClientCount, groupKey);}

ClientWorker 这里最终会调用 notifyListenConfig方法 这个方法实际上就是往listenExecutebell这个阻塞队列中去offer一个object对象

 private void initRpcClientHandler(final RpcClient rpcClientInner) {rpcClientInner.registerServerRequestHandler((request) -> {if (request instanceof ConfigChangeNotifyRequest) {ConfigChangeNotifyRequest configChangeNotifyRequest = (ConfigChangeNotifyRequest) request;LOGGER.info("[{}] [server-push] config changed. dataId={}, group={},tenant={}",rpcClientInner.getName(), configChangeNotifyRequest.getDataId(),configChangeNotifyRequest.getGroup(), configChangeNotifyRequest.getTenant());String groupKey = GroupKey.getKeyTenant(configChangeNotifyRequest.getDataId(), configChangeNotifyRequest.getGroup(),configChangeNotifyRequest.getTenant());CacheData cacheData = cacheMap.get().get(groupKey);if (cacheData != null) {synchronized (cacheData) {cacheData.getLastModifiedTs().set(System.currentTimeMillis());cacheData.setSyncWithServer(false);//向阻塞队列中添加元素 触发长连接的执行notifyListenConfig();}}//返回客户端响应return new ConfigChangeNotifyResponse();}return null;});}

客户端处理变更事件

客户端启动的时候会创建 NaocsConfigService 他的构造方法中会创建一个ClientWorker并启动 然后会执行 startInternal方法 这个方法中会看到 从listenExecutebell中拿数据 listenExecutebell在上文服务配置发生变更的时候会往里面塞一个object 对象 所以这里就能poll队列中出来 如果队列为空等待5秒后执行,如果队列不为空立即执行

        @Overridepublic void startInternal() {executor.schedule(() -> {while (!executor.isShutdown() && !executor.isTerminated()) {try {listenExecutebell.poll(5L, TimeUnit.SECONDS);if (executor.isShutdown() || executor.isTerminated()) {continue;}executeConfigListen();} catch (Exception e) {LOGGER.error("[ rpc listen execute ] [rpc listen] exception", e);}}}, 0L, TimeUnit.MILLISECONDS);}

executeConfigListen 方法详细看 https://blog.csdn.net/qq_41956309/article/details/134904263这段 下面简述一下
其实就是将3000个配置信息封装成一个CacheData 共用一个TaskId 一个TaskId 对应一个Client连接 然后会发送到服务端 通过比较md5的方式来判断哪些配置发生了变更 然后会返回变更的key 然后客户端遍历返回的有变更信息的key的信息 在去调用服务端查找具体的配置信息 返回客户端然后做返回和动态刷新以及本地缓存的修改
在这里插入图片描述

这篇关于Nacos源码解读12——Nacos中长连接的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/479442

相关文章

springboot+redis实现订单过期(超时取消)功能的方法详解

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代... 目录一、Redis键过期回调方案(推荐)1. 配置Redis监听器2. 监听键过期事件3. Redi

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

基于SpringBoot实现分布式锁的三种方法

《基于SpringBoot实现分布式锁的三种方法》这篇文章主要为大家详细介绍了基于SpringBoot实现分布式锁的三种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、基于Redis原生命令实现分布式锁1. 基础版Redis分布式锁2. 可重入锁实现二、使用Redisso

SpringBoo WebFlux+MongoDB实现非阻塞API过程

《SpringBooWebFlux+MongoDB实现非阻塞API过程》本文介绍了如何使用SpringBootWebFlux和MongoDB实现非阻塞API,通过响应式编程提高系统的吞吐量和响应性能... 目录一、引言二、响应式编程基础2.1 响应式编程概念2.2 响应式编程的优势2.3 响应式编程相关技术

Mybatis对MySQL if 函数的不支持问题解读

《Mybatis对MySQLif函数的不支持问题解读》接手项目后,为了实现多租户功能,引入了Mybatis-plus,发现之前运行正常的SQL语句报错,原因是Mybatis不支持MySQL的if函... 目录MyBATis对mysql if 函数的不支持问题描述经过查询网上搜索资料找到原因解决方案总结Myb

C#实现将XML数据自动化地写入Excel文件

《C#实现将XML数据自动化地写入Excel文件》在现代企业级应用中,数据处理与报表生成是核心环节,本文将深入探讨如何利用C#和一款优秀的库,将XML数据自动化地写入Excel文件,有需要的小伙伴可以... 目录理解XML数据结构与Excel的对应关系引入高效工具:使用Spire.XLS for .NETC

Nginx更新SSL证书的实现步骤

《Nginx更新SSL证书的实现步骤》本文主要介绍了Nginx更新SSL证书的实现步骤,包括下载新证书、备份旧证书、配置新证书、验证配置及遇到问题时的解决方法,感兴趣的了解一下... 目录1 下载最新的SSL证书文件2 备份旧的SSL证书文件3 配置新证书4 验证配置5 遇到的http://www.cppc

Nginx之https证书配置实现

《Nginx之https证书配置实现》本文主要介绍了Nginx之https证书配置的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起... 目录背景介绍为什么不能部署在 IIS 或 NAT 设备上?具体实现证书获取nginx配置扩展结果验证

SpringBoot整合 Quartz实现定时推送实战指南

《SpringBoot整合Quartz实现定时推送实战指南》文章介绍了SpringBoot中使用Quartz动态定时任务和任务持久化实现多条不确定结束时间并提前N分钟推送的方案,本文结合实例代码给大... 目录前言一、Quartz 是什么?1、核心定位:解决什么问题?2、Quartz 核心组件二、使用步骤1

使用Redis实现会话管理的示例代码

《使用Redis实现会话管理的示例代码》文章介绍了如何使用Redis实现会话管理,包括会话的创建、读取、更新和删除操作,通过设置会话超时时间并重置,可以确保会话在用户持续活动期间不会过期,此外,展示了... 目录1. 会话管理的基本概念2. 使用Redis实现会话管理2.1 引入依赖2.2 会话管理基本操作