源码篇--Nacos服务--中章(8):Nacos服务端感知客户端实例变更-3

文章目录

  • 前言
  • 一、客户端实例变更:
  • 二、实例变更感知:
    • 2.1 实例注册信息通知:
      • 2.1.1 接收DistroDataRequest 请求:
      • 2.1.2 onReceive 处理请求:
      • 2.1.3 processData 处理请求:
      • 2.1.4 handlerClientSyncData 处理数据:
      • 2.1.5 upgradeClient 数据对比和更新:
    • 2.2 服务端节点启动,全量拉取数据:
      • 2.2.1 DistroProtocol对象创建触发全量拉取:
      • 2.2.2 DistroLoadDataTask 全量任务的执行:
      • 2.2.3 load 拉取其它节点实例信息:
      • 2.2.4 loadAllDataSnapshotFromRemote 拉取&处理:
        • 2.2.4.1 获取数据和处理:
        • 2.2.4.2 获取数据:
    • 2.3 集群节点心跳监测:
  • 总结


前言

Nacos 集群中的节点通过distro 协议,grpc 通信互相同步节点中的实例信息;本文对服务端实例同步的3种场景进行介绍;服务端版本 3.0.13。


一、客户端实例变更:

我们知道客户端在启动的时候会与服务端建立grpc 连接,并且在启动完成向其注册实例信息,此时客户端的实例只被保存在服务端的某一个节点上,需要将改实例信息发送到集群中的其它节点;

我们知道Nacos 是集群的,支持进行水平扩展,所以在向集群内添加节点时,新加入的节点也需要获取到其它节点保存的实例信息,并保存到自己的节点上;

Nacos 集群中的节点,可能分布在不同的环境,节点之间需要网络进行连接,此时就不可能避免的出现,集群内某个节点短暂失联的情况,当网络恢复正常后,落后的节点就需要同步其它节点的信息,并覆盖本地的实例信息,从而达到实例信息数据的一致性;

本文对以上提到的3中场景展开进行介绍。

二、实例变更感知:

2.1 实例注册信息通知:

当客户端注册到集群中的某个节点,该节点需要在保存实例注册信息后,也需要负责将注册的实例信息同步到集群内的其它节点;

在这里插入图片描述
在:源码篇–Nacos服务–中章(8):Nacos服务端感知客户端注册-2 ,介绍了集群内节点通信通道的建立;现在就可以使用改通道向集群内其它运行的节点同步客户端实例信息;

2.1.1 接收DistroDataRequest 请求:

DistroDataRequestHandler distro 协议处理类负责对集群内发送的 DistroDataRequest (实例变更请求)请求进行处理;

@Override
public DistroDataResponse handle(DistroDataRequest request, RequestMeta meta) throws NacosException {try {// 获取操作类型switch (request.getDataOperation()) {case VERIFY:return handleVerify(request.getDistroData(), meta);case SNAPSHOT:return handleSnapshot();case ADD:case CHANGE:case DELETE:// 实例变化类型return handleSyncData(request.getDistroData());case QUERY:return handleQueryData(request.getDistroData());default:return new DistroDataResponse();}} catch (Exception e) {Loggers.DISTRO.error("[DISTRO-FAILED] distro handle with exception", e);DistroDataResponse result = new DistroDataResponse();result.setErrorCode(ResponseCode.FAIL.getCode());result.setMessage("handle distro request with exception");return result;}
}
private DistroDataResponse handleSyncData(DistroData distroData) {DistroDataResponse result = new DistroDataResponse();// 请求处理if (!distroProtocol.onReceive(distroData)) {result.setErrorCode(ResponseCode.FAIL.getCode());result.setMessage("[DISTRO-FAILED] distro data handle failed");}return result;
}

2.1.2 onReceive 处理请求:

从请求的DistroData 参数中解析出来发送端注册的客户端实例信息,然后与本节点进行对比,完成对本节点注册实例的更新;

/*** Receive synced distro data, find processor to process.** @param distroData Received data* @return true if handle receive data successfully, otherwise false*/
public boolean onReceive(DistroData distroData) {Loggers.DISTRO.info("[DISTRO] Receive distro data type: {}, key: {}", distroData.getType(),distroData.getDistroKey());// 获取资源类型: Nacos:Naming:v2:ClientDataString resourceType = distroData.getDistroKey().getResourceType();// 获取资源处理器DistroDataProcessor dataProcessor = distroComponentHolder.findDataProcessor(resourceType);if (null == dataProcessor) {Loggers.DISTRO.warn("[DISTRO] Can't find data process for received data {}", resourceType);return false;}// 数据处理return dataProcessor.processData(distroData);
}

2.1.3 processData 处理请求:

对CHANGE 事件做出处理,先反序列化得到原始数据,然后进行处理;

@Override
public boolean processData(DistroData distroData) {switch (distroData.getType()) {case ADD:case CHANGE:// 反序列化,传入的调用sync 接口的节点下注册的实例信息ClientSyncData clientSyncData = ApplicationUtils.getBean(Serializer.class).deserialize(distroData.getContent(), ClientSyncData.class);// 对实例信息进行处理handlerClientSyncData(clientSyncData);return true;case DELETE:String deleteClientId = distroData.getDistroKey().getResourceKey();Loggers.DISTRO.info("[Client-Delete] Received distro client sync data {}", deleteClientId);clientManager.clientDisconnected(deleteClientId);return true;default:return false;}
}

2.1.4 handlerClientSyncData 处理数据:

创建本节点client(对应发送端),集群内对每个节点(除了自己之外)都会建立对应的client 客户端(根据客户端的id 进行区分),后续可以使用其客户端,进行请求的发送;

private void handlerClientSyncData(ClientSyncData clientSyncData) {Loggers.DISTRO.info("[Client-Add] Received distro client sync data {}, revision={}", clientSyncData.getClientId(),clientSyncData.getAttributes().getClientAttribute(ClientConstants.REVISION, 0L));// 创建与发送请求节点服务的client 对象clientManager.syncClientConnected(clientSyncData.getClientId(), clientSyncData.getAttributes());Client client = clientManager.getClient(clientSyncData.getClientId());// 数据更新upgradeClient(client, clientSyncData);
}

2.1.5 upgradeClient 数据对比和更新:

先保存发送过来的客户端信息,然后在与本地保存的客户端信息进行对比,剔除掉本地过时的客户端实例信息;

private void upgradeClient(Client client, ClientSyncData clientSyncData) {Set<Service> syncedService = new HashSet<>();// process batch instance sync logicprocessBatchInstanceDistroData(syncedService, client, clientSyncData);List<String> namespaces = clientSyncData.getNamespaces();List<String> groupNames = clientSyncData.getGroupNames();List<String> serviceNames = clientSyncData.getServiceNames();List<InstancePublishInfo> instances = clientSyncData.getInstancePublishInfos();for (int i = 0; i < namespaces.size(); i++) {// 遍历命名空间// 组装服务实例Service service = Service.newService(namespaces.get(i), groupNames.get(i), serviceNames.get(i));// 获取本节点之前已经存在的服务注册实例Service singleton = ServiceManager.getInstance().getSingleton(service);syncedService.add(singleton);// 获取服务实例信息InstancePublishInfo instancePublishInfo = instances.get(i);if (!instancePublishInfo.equals(client.getInstancePublishInfo(singleton))) {// 与本地缓存的 服务实例进行对比// 不相同,则在服务实例添加到 改client  下 ConcurrentHashMap<Service, InstancePublishInfo> publishers 中client.addServiceInstance(singleton, instancePublishInfo);// 发布服务实例注册事件NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, client.getClientId()));NotifyCenter.publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instancePublishInfo.getMetadataId(), false));}}for (Service each : client.getAllPublishedService()) {// 遍历本节点的所有服务注册实例keyif (!syncedService.contains(each)) {// 本节点已经失效的服务注册实例,进行实例异常client.removeServiceInstance(each);NotifyCenter.publishEvent(new ClientOperationEvent.ClientDeregisterServiceEvent(each, client.getClientId()));}}client.setRevision(clientSyncData.getAttributes().<Integer>getClientAttribute(ClientConstants.REVISION, 0));
}

2.2 服务端节点启动,全量拉取数据:

在这里插入图片描述

集群内某个节点在启动时,全量拉取其它节点的实例信息,进行整理并保存到该节点的实例注册信息中;改部分的工作是在 DistroProtocol 对象构建时通过startLoadTask() 方法进行的;

2.2.1 DistroProtocol对象创建触发全量拉取:

public DistroProtocol(ServerMemberManager memberManager, DistroComponentHolder distroComponentHolder,DistroTaskEngineHolder distroTaskEngineHolder) {this.memberManager = memberManager;this.distroComponentHolder = distroComponentHolder;this.distroTaskEngineHolder = distroTaskEngineHolder;// 开始任务startDistroTask();
}private void startDistroTask() {if (EnvUtil.getStandaloneMode()) {isInitialized = true;return;}// 校验的定时任务 每隔5s 发送一次startVerifyTask();// 启动加载任务startLoadTask();
}
private void startLoadTask() {DistroCallback loadCallback = new DistroCallback() {@Overridepublic void onSuccess() {isInitialized = true;}@Overridepublic void onFailed(Throwable throwable) {isInitialized = false;}};// 值执行一次GlobalExecutor.submitLoadDataTask(new DistroLoadDataTask(memberManager, distroComponentHolder, DistroConfig.getInstance(), loadCallback));
}

2.2.2 DistroLoadDataTask 全量任务的执行:

public DistroLoadDataTask(ServerMemberManager memberManager, DistroComponentHolder distroComponentHolder,DistroConfig distroConfig, DistroCallback loadCallback) {this.memberManager = memberManager;this.distroComponentHolder = distroComponentHolder;this.distroConfig = distroConfig;this.loadCallback = loadCallback;loadCompletedMap = new HashMap<>(1);
}@Override
public void run() {try {// 加载load();if (!checkCompleted()) {GlobalExecutor.submitLoadDataTask(this, distroConfig.getLoadDataRetryDelayMillis());} else {loadCallback.onSuccess();Loggers.DISTRO.info("[DISTRO-INIT] load snapshot data success");}} catch (Exception e) {loadCallback.onFailed(e);Loggers.DISTRO.error("[DISTRO-INIT] load snapshot data failed. ", e);}
}

2.2.3 load 拉取其它节点实例信息:

遍历集群内除了自己的节点,只要有一个节点返回了注册的实例信息就可以进行本节点实例信息的更新;

private void load() throws Exception {// 集群内只有自己,不需要加载数据while (memberManager.allMembersWithoutSelf().isEmpty()) {Loggers.DISTRO.info("[DISTRO-INIT] waiting server list init...");TimeUnit.SECONDS.sleep(1);}// 数据存储对象是否为空while (distroComponentHolder.getDataStorageTypes().isEmpty()) {Loggers.DISTRO.info("[DISTRO-INIT] waiting distro data storage register...");TimeUnit.SECONDS.sleep(1);}for (String each : distroComponentHolder.getDataStorageTypes()) {if (!loadCompletedMap.containsKey(each) || !loadCompletedMap.get(each)) {// 从远处加载快照数据loadCompletedMap.put(each, loadAllDataSnapshotFromRemote(each));}}}

2.2.4 loadAllDataSnapshotFromRemote 拉取&处理:

向集群中的某一节点发送 DistroDataRequest 事件是 SNAPSHOT,获取到返回的注册实例信息;反序列化得到原始数据,进行改节点的实例信息保存;

2.2.4.1 获取数据和处理:
private boolean loadAllDataSnapshotFromRemote(String resourceType) {// 传输代理类DistroTransportAgent transportAgent = distroComponentHolder.findTransportAgent(resourceType);// 数据处理器DistroDataProcessor dataProcessor = distroComponentHolder.findDataProcessor(resourceType);if (null == transportAgent || null == dataProcessor) {Loggers.DISTRO.warn("[DISTRO-INIT] Can't find component for type {}, transportAgent: {}, dataProcessor: {}",resourceType, transportAgent, dataProcessor);return false;}for (Member each : memberManager.allMembersWithoutSelf()) {// 遍历集群内其它节点long startTime = System.currentTimeMillis();try {Loggers.DISTRO.info("[DISTRO-INIT] load snapshot {} from {}", resourceType, each.getAddress());// 从集群内其它节点获取注册的信息DistroData distroData = transportAgent.getDatumSnapshot(each.getAddress());Loggers.DISTRO.info("[DISTRO-INIT] it took {} ms to load snapshot {} from {} and snapshot size is {}.",System.currentTimeMillis() - startTime, resourceType, each.getAddress(),getDistroDataLength(distroData));// 处理boolean result = dataProcessor.processSnapshot(distroData);Loggers.DISTRO.info("[DISTRO-INIT] load snapshot {} from {} result: {}", resourceType, each.getAddress(),result);if (result) {// 设置数据处理完毕标识distroComponentHolder.findDataStorage(resourceType).finishInitial();return true;}} catch (Exception e) {Loggers.DISTRO.error("[DISTRO-INIT] load snapshot {} from {} failed.", resourceType, each.getAddress(), e);}}return false;
}
2.2.4.2 获取数据:

(1) DistroDataRequest 请求发送获取数据

 @Overridepublic DistroData getDatumSnapshot(String targetServer) {// 获取集群内改节点信息Member member = memberManager.find(targetServer);if (checkTargetServerStatusUnhealthy(member)) {throw new DistroException(String.format("[DISTRO] Cancel get snapshot caused by target server %s unhealthy", targetServer));}// 构建 DistroDataRequest 对象DistroDataRequest request = new DistroDataRequest();request.setDataOperation(DataOperation.SNAPSHOT);try {// 通过 grpc 发送普通的request 请求Response response = clusterRpcClientProxy.sendRequest(member, request, DistroConfig.getInstance().getLoadDataTimeoutMillis());if (checkResponse(response)) {return ((DistroDataResponse) response).getDistroData();} else {throw new DistroException(String.format("[DISTRO-FAILED] Get snapshot request to %s failed, code: %d, message: %s",targetServer, response.getErrorCode(), response.getMessage()));}} catch (NacosException e) {throw new DistroException("[DISTRO-FAILED] Get distro snapshot failed! ", e);}}

(2)集群其它节点接收DistroDataRequest 并处理SNAPSHOT 事件:

DistroDataRequestHandler #handle 负责请求的处理;

@Override
public DistroDataResponse handle(DistroDataRequest request, RequestMeta meta) throws NacosException {try {// 获取操作类型switch (request.getDataOperation()) {case VERIFY:return handleVerify(request.getDistroData(), meta);case SNAPSHOT:// 返回改节点下的注册实例信息return handleSnapshot();case ADD:case CHANGE:case DELETE:// 实例变化类型return handleSyncData(request.getDistroData());case QUERY:return handleQueryData(request.getDistroData());default:return new DistroDataResponse();}} catch (Exception e) {Loggers.DISTRO.error("[DISTRO-FAILED] distro handle with exception", e);DistroDataResponse result = new DistroDataResponse();result.setErrorCode(ResponseCode.FAIL.getCode());result.setMessage("handle distro request with exception");return result;}
}

快照信息获取1:

private DistroDataResponse handleSnapshot() {DistroDataResponse result = new DistroDataResponse();// 获取快照信息DistroData distroData = distroProtocol.onSnapshot(DistroClientDataProcessor.TYPE);result.setDistroData(distroData);return result;
}

快照信息获取2:

/**
* Query all datum snapshot.** @param type datum type* @return all datum snapshot*/public DistroData onSnapshot(String type) {DistroDataStorage distroDataStorage = distroComponentHolder.findDataStorage(type);if (null == distroDataStorage) {Loggers.DISTRO.warn("[DISTRO] Can't find data storage for received key {}", type);return new DistroData(new DistroKey("snapshot", type), new byte[0]);}return distroDataStorage.getDatumSnapshot();}

快照信息获取3:

@Override
public DistroData getDatumSnapshot() {List<ClientSyncData> datum = new LinkedList<>();// 遍历注册的客户端信息for (String each : clientManager.allClientId()) {Client client = clientManager.getClient(each);if (null == client || !client.isEphemeral()) {continue;}datum.add(client.generateSyncData());}ClientSyncDatumSnapshot snapshot = new ClientSyncDatumSnapshot();snapshot.setClientSyncDataList(datum);byte[] data = ApplicationUtils.getBean(Serializer.class).serialize(snapshot);return new DistroData(new DistroKey(DataOperation.SNAPSHOT.name(), TYPE), data);
}@Overridepublic ClientSyncData generateSyncData() {List<String> namespaces = new LinkedList<>();List<String> groupNames = new LinkedList<>();List<String> serviceNames = new LinkedList<>();List<String> batchNamespaces = new LinkedList<>();List<String> batchGroupNames = new LinkedList<>();List<String> batchServiceNames = new LinkedList<>();List<InstancePublishInfo> instances = new LinkedList<>();List<BatchInstancePublishInfo> batchInstancePublishInfos = new LinkedList<>();BatchInstanceData  batchInstanceData = new BatchInstanceData();// 遍历改服务端下注册的客户端实例for (Map.Entry<Service, InstancePublishInfo> entry : publishers.entrySet()) {InstancePublishInfo instancePublishInfo = entry.getValue();if (instancePublishInfo instanceof BatchInstancePublishInfo) {BatchInstancePublishInfo batchInstance = (BatchInstancePublishInfo) instancePublishInfo;batchInstancePublishInfos.add(batchInstance);buildBatchInstanceData(batchInstanceData, batchNamespaces, batchGroupNames, batchServiceNames, entry);batchInstanceData.setBatchInstancePublishInfos(batchInstancePublishInfos);} else {namespaces.add(entry.getKey().getNamespace());groupNames.add(entry.getKey().getGroup());serviceNames.add(entry.getKey().getName());instances.add(entry.getValue());}}// 返回当前服务端下注册的所有客户端实例ClientSyncData data = new ClientSyncData(getClientId(), namespaces, groupNames, serviceNames, instances, batchInstanceData);data.getAttributes().addClientAttribute(REVISION, getRevision());return data;
}

2.3 集群节点心跳监测:

篇幅原因,此章节放到 源码篇–Nacos服务–中章(8):Nacos服务端感知客户端实例变更(集群数据校验)-4 ,进行介绍。


总结

本文对Nacos 集群内实例注册的感知,对实例的注册;Nacos 集群节点启动,实例信息的同步进行介绍。

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

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

相关文章

k8s集群Grafana精选dashboard页面

文章目录 参考文档 Grafana自选模板推荐模板&#xff1a;13332、13824、14518Grafana默认配置我们选择 Node Exporter/Nodes 的 Dashboard 进去&#xff1a;点击 Kubernetes/Networking/Cluster 进去使用模板查看结果 Grafana接入Prometheus数据Grafana添加监控模板导入 1860_r…

体验用AI写代码

近两年&#xff0c;AI确实迎来了大爆发&#xff0c;2023年也成了AI时代的元年&#xff0c;去年下半年的时候&#xff0c;国内月之暗面出品的Kimi成了新的AI热门&#xff0c;也体验用Kimi修改论文&#xff0c;以及用图片生成代码&#xff0c;代码准确度还是蛮高的。做为SRE&…

使用Python的Tkinter库创建你的第一个桌面应用程序

文章目录 准备工作创建窗口和按钮代码解释运行你的应用程序结论 在本教程中&#xff0c;我们将介绍如何使用Python的Tkinter库创建一个简单的桌面应用程序。我们将会创建一个包含一个按钮的窗口&#xff0c;点击按钮时会在窗口上显示一条消息。 准备工作 首先&#xff0c;确保…

扩展大型视觉-语言模型的视觉词汇:Vary 方法

在人工智能领域&#xff0c;大型视觉-语言模型&#xff08;LVLMs&#xff09;正变得越来越重要&#xff0c;它们能够处理多种视觉和语言任务&#xff0c;如视觉问答&#xff08;VQA&#xff09;、图像字幕生成和光学字符识别&#xff08;OCR&#xff09;。然而&#xff0c;现有…

Docker--compose概述与部署

目录 一、概述 1. Compose简介 1.1 docker compose常用命令 1.2 Compose配置常用字段 2. YAML简介 2.1 YAML支持的数据结构 2.2 YML文件编写注意事项 2.3 Docker Compose文件结构 3. Docker-Compose安装 ​编辑 4.docker Compose撰写nginx 镜像 1. 准备环境 ​编辑…

Zabbix 安装部署说明文档

Zabbix是一个开源的网络监控和管理系统&#xff0c;其架构设计用于提供企业级的监控解决方案。以下是Zabbix的主要组件&#xff1a; 1.Zabbix Server&#xff1a;这是Zabbix系统的核心组件&#xff0c;负责接收Agent程序报告的系统可用性、系统完整性和统计数据。Zabbix Serve…

CSS 06

精灵图 为什么要使用精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度,因此&#xff0c;为了有效地减少服务…

【深度学习】YOLOv5,金属表面的缺陷检测,GC10-DET数据集

目录&#xff1a; 文章目录 数据集数据集转换下载yolov5创建 dataset.yaml训练参数开始训练数据分布训练结果问询、帮助 数据集 数据集地址&#xff1a; https://github.com/lvxiaoming2019/GC10-DET-Metallic-Surface-Defect-Datasets 数据集下载方式&#xff1a; Downlo…

有趣的大模型之我见 | Claude AI

最近我的朋友圈被 Claude 3 严重刷屏。因为它在 Performance Benchmark 和 Vision Capabilities 中的表现荣登第一。 对 Claude AI 的喜欢是从它第二个版本出来。仅从我个人的简单应用场景的体验是&#xff0c;Claude 对于 prompt 的理解度&#xff0c;尤其是对中文的理解度高…

PHP源码_在线艺术字体在线生成转换设计网站源码

最全的字体转换器在线转换、艺术字体在线生成器和字体下载&#xff0c;包括书法字体在线转换、毛笔字在线生成器&#xff0c;更有草书字体、篆体字、连笔字、POP字体转换器等中文和英文字体。 支持自己添加字体&#xff0c;在线艺术字体转换器&#xff0c;织梦内核艺术字体在线…

.NET操作 Access (MSAccess)

注意&#xff1a;新项目推荐 Sqlite &#xff0c;Access需要注意的东西太多了&#xff0c;比如OFFICE版本&#xff0c;是X86还是X64 连接字符串 ProviderMicrosoft.ACE.OleDB.15.0;Data Source"GetCurrentProjectPath"\\test.accdb//不同的office版本 连接字符串有…

MySQL Workbench 数据库常用操作

大家好哦&#xff0c;我是程序员徐师兄&#xff0c;今天为大家打来的是MySQL Workbench 数据库常用操作。 文章目录 一、连接数据库二、进入数据库三、创建数据库四、设置默认数据库五、创建数据表六、查看表数据七、查看数据表 一、连接数据库 二、进入数据库 三、创建数据库 …

Shopee怎么选品成功率高达80%?请学

电商圈内流传着一句话&#xff1a;三分靠运营&#xff0c;七分靠选品。 选品在电商项目中至关重要&#xff0c;也是一个非常考验技巧和经验的环节。选品选择得好&#xff0c;后续的每一步都会变得相对轻松。 那么要怎么在众多商品中脱颖而出&#xff0c;提高在Shopee平台上选…

Llama images - 记录我看到的那些羊驼

来自 &#xff1a; DREAM: Distributed RAG Experimentation Framework

如何用智能获客开启新商机?揭秘赢销侠软件的奇效

在当今数字化竞争日益激烈的商业环境中&#xff0c;企业为了生存和发展&#xff0c;必须寻找新的途径以获取潜在客户。智能获客作为一种新型的营销方式&#xff0c;正以其高效、精准的特点改变着传统的市场开拓模式。而在这个过程中&#xff0c;自动获客软件的作用愈发凸显&…

Adobe推出AI视频超分辨率工具VideoGigaGAN

&#x1f989; AI新闻 &#x1f680; Adobe推出AI视频超分辨率工具VideoGigaGAN 摘要&#xff1a;Adobe公司最新推出的AI工具VideoGigaGAN&#xff0c;利用上采样技术将视频分辨率从128128提升至10241024。这一工具基于GigaGAN模型开发&#xff0c;专注于生成视频超分辨率&am…

《深入解析windows操作系统》第1章读书笔记

1、服务&#xff1a;指操作系统中可以被调用的例程&#xff08;即函数&#xff0c;等价于系统调用&#xff1f;&#xff09;、设备驱动程序或者服务器进程。一些特定术语如下&#xff1a; 1&#xff09;Windows API函数&#xff1a;指Windows API中已经被文档化、可被调用的子例…

TCP重传,滑动窗口,流量控制,拥塞控制

TCP重传&#xff0c;滑动窗口&#xff0c;流量控制&#xff0c;拥塞控制 TCP重传机制&#xff1a; 超时重传快速重传SACKD-SACK 通过序列号与确认应答判断是否要重传 超时重传&#xff1a; 超过指定时间没有收到确认应答报文&#xff0c;就会重发该数据 触发超时重传的情况…

SpirngBoot整合快递100

目录 一、注册快递100 二、技术文档地址 三、需要认证的key和comcumer 四、spring boot 整合快递 100使用 4.1 引入快递100和hutool的依赖 4.2 将key和comcumer写入application.properties文件中 4.3 新建一个modle,用于将查出来的json数据转成对象 4.4 新建一个controll…

MySql-日期分组

一、分别统计各时间各类型数据条数 数据库的 request_time字段 数据类型&#xff1a;timestamp 默认值&#xff1a;CURRENT_TIMESTAMP 例子&#xff1a; 2024-01-26 08:25:48 原数据&#xff1a; 1、将数据按照日期&#xff08;年月日&#xff09;形式输出 按照request_…