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

news2024/11/25 16:34:12

短连接 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 check
            if (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秒后执行,如果队列不为空立即执行

        @Override
        public 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的信息 在去调用服务端查找具体的配置信息 返回客户端然后做返回和动态刷新以及本地缓存的修改
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1301376.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年国赛高教杯数学建模A题定日镜场的优化设计解题全过程文档及程序

2023年国赛高教杯数学建模 A题 定日镜场的优化设计 原题再现 构建以新能源为主体的新型电力系统&#xff0c;是我国实现“碳达峰”“碳中和”目标的一项重要措施。塔式太阳能光热发电是一种低碳环保的新型清洁能源技术[1]。   定日镜是塔式太阳能光热发电站&#xff08;以下…

【学习笔记】LLM for Education

ChatGPT has entered the classroom: how LLMs could transform education 前言IntroductionThe risks are realEmbracing LLMsIntroducing the AI tutorAugmenting retrievalWill it catch on?总结 前言 一篇来自Nature的文章&#xff0c;探讨了教育行业的不同参与者&#x…

基于以太坊的智能合约开发Solidity(基础篇)

参考教程&#xff1a;基于以太坊的智能合约开发教程【Solidity】_哔哩哔哩_bilibili 1、第一个程序——Helloworld&#xff1a; //声明版本号&#xff08;程序中的版本号要和编译器版本号一致&#xff09; pragma solidity ^0.5.17; //合约 contract HelloWorld {//合约属性变…

详解—[Linux 文件描述符]

一、文件描述符的概念 文件描述符是Linux系统中用于访问文件的一种机制&#xff0c;它是一个非负整数&#xff0c;用于指代被打开的文件。 在Linux中&#xff0c;所有执行I/O操作的系统调用都是通过文件描述符完成的。 文件描述符是一个简单的非负整数&#xff0c;用来表明每一…

告别 Navicat!一款能支持几乎所有数据库的开源工具!

数据库连接工具&#xff0c;后端程序员必须要用到工具&#xff0c;常用的是 Navicat&#xff0c;Navicat是收费工具&#xff0c;今天给大家推荐一款开源免费的数据库连接工具 -- dbeaver。 功能特性 1、几乎支持所有数据库产品&#xff0c;包括&#xff1a;MySQL、SQL Server…

文档或书籍扫描为 PDF:ScanPapyrus Crack

ScanPapyrus 可让您快速轻松地将文档或书籍扫描为 PDF&#xff0c;批处理模式使扫描过程快速高效&#xff0c;自动处理书籍并将其拆分为单独的页面 用于快速扫描文档、书籍或打印照片的扫描仪软件 快速扫描文档 使用此扫描仪软件&#xff0c;您无需在扫描仪和计算机之间来回移动…

如何实现远程公共网络下访问Windows Node.js服务端

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

【Python必做100题】之第二题(找出100以内的偶数并打印)

思路&#xff1a; 1、定义一个空列表来存储取到的偶数 2、每次取到偶数追加到列表的末尾 3、打印的列表即为100以内所有的偶数 重点&#xff1a; 列表追加元素的语法&#xff1a; list.append(i) 代码如下&#xff1a; list [ ] #定义一个空列表来存储偶数 for i in rang…

新公众号没有留言功能怎么办?

为什么公众号没有留言功能&#xff1f;从2018年2月开始&#xff0c;新注册的微信公众号取消了留言功能&#xff0c;原因是为了规避一些营销号通过虚假留言骗取读者信任。不过大部分公众号运营者对TX此举感到失望&#xff0c;一方面大片的留言就像店前排队的顾客&#xff0c;能体…

TeeChart.NET 2023.11.17 Crack

.NET 的 TeeChart 图表控件提供了一个出色的通用组件套件&#xff0c;可满足无数的图表需求&#xff0c;也针对重要的垂直领域&#xff0c;例如金融、科学和统计领域。 数据可视化 数十种完全可定制的交互式图表类型、地图和仪表指示器&#xff0c;以及完整的功能集&#xff0c…

Spring日志完结篇,MyBatis操作数据库(入门)

目录 Spring可以对日志进行分目录打印 日志持久化&#xff08;让日志进行长期的保存&#xff09; MyBatis操作数据库(优秀的持久层框架) MyBatis的写法 开发规范&#xff1a; 单元测试的写法 传递参数 Spring可以对日志进行分目录打印 他的意思是说spring相关只打印INFO…

【Spring教程23】Spring框架实战:从零开始学习SpringMVC 之 SpringMVC简介与SpringMVC概述

目录 1&#xff0c;SpringMVC简介2、SpringMVC概述 欢迎大家回到《Java教程之Spring30天快速入门》&#xff0c;本教程所有示例均基于Maven实现&#xff0c;如果您对Maven还很陌生&#xff0c;请移步本人的博文《如何在windows11下安装Maven并配置以及 IDEA配置Maven环境》&…

Java基础课的中下基础课04

目录 二十三、集合相关 23.1 集合 &#xff08;1&#xff09;集合的分支 23.2 List有序可重复集合 &#xff08;1&#xff09;ArrayList类 &#xff08;2&#xff09;泛型 &#xff08;3&#xff09;ArrayList常用方法 &#xff08;4&#xff09;Vector类 &#xff08;…

【论文阅读笔记】NeRF+Mip-NeRF+Instant-NGP

目录 前言NeRF神经辐射场体渲染连续体渲染体渲染离散化 方法位置编码分层采样体渲染推导公式&#xff08;1&#xff09;到公式&#xff08;2&#xff09;部分代码解读相机变换&#xff08;重要&#xff01;&#xff09; Mip-NerfTo do Instant-NGPTo do 前言 NeRF是NeRF系列的…

字节跳动ZNS SSD应用案例分析

字节给出未来数据中心的发展趋势&#xff0c;包括计算、网络、存储三大组件。 字节目前遇到云存储的痛点主要包括&#xff1a;性能、成本、稳定性、定制化。性能方面&#xff0c;SSD的垃圾回收在传统SSD并不可控。成本方面&#xff0c;传统SSD需要预留一定的OP预留空间。稳定性…

【Marp】基于Markdown-Marp快速制作PPT

【Marp】基于Markdown-Marp快速制作PPT 文章目录 【Marp】基于Markdown-Marp快速制作PPT零、参考资料一、Marp基本语法&#xff08;创建分页&#xff0c;排版图片&#xff0c;更换主题&#xff0c;Marp扩展指令修改样式&#xff09;1、创建新的PPT页面2、插入图片 & 排版图…

Python学习笔记-类

1 定义类 类是函数的集合&#xff0c;class来定义类 pass并没有实际含义&#xff0c;只是为了代码能执行通过&#xff0c;不报错而已&#xff0c;相当于在代码种占一个位置&#xff0c;后续完善 类是对象的加工厂 2.创建对象 carCar()即是创建对象的过程 3、类的成员 3.1 实例…

【CiteSpace】引文可视化分析软件CiteSpace下载与安装

CiteSpace 译“引文空间”&#xff0c;是一款着眼于分析科学分析中蕴含的潜在知识&#xff0c;是在科学计量学、数据可视化背景下逐渐发展起来的引文可视化分析软件。由于是通过可视化的手段来呈现科学知识的结构、规律和分布情况&#xff0c;因此也将通过此类方法分析得到的可…

Unity中C#如何访问并修改Shader材质

文章目录 前言一、我们用点击按钮来改变Shader传入的颜色值1、在渲染GUI时&#xff0c;绘制一个按钮2、我们使用一个公共的成员变量存储需要进行修改的游戏对象3、最后给绘制的按钮点击增加逻辑即可 二、测试使用的代码1、Shader代码&#xff1a;2、C#脚本 前言 我们写好Shade…

MFC CLXHHandleEngine动态库-自定义设置对话框使用

实现的效果如下所示&#xff1a; void CSampleDlg::OnBnClickedButton2() { // TODO: 在此添加控件通知处理程序代码 CSgxMemDialog dlg(180, 100); dlg.SetEnable(true); dlg.SetWindowTitle(_T("自定义对话框")); dlg.AddStatic(1000, //控件资源…