微服务上下线动态感知实现的技术解析
序言
随着微服务架构的广泛应用,服务的动态管理和监控变得尤为重要。在微服务架构中,服务的上下线是一个常见的操作,如何实时感知这些变化,确保系统的稳定性和可靠性,成为了一个关键技术挑战。本文将深入探讨微服务上下线动态感知的实现方式,从技术基础、场景案例、解决思路和底层原理等多个维度进行阐述,并分别使用Java和Python进行演示介绍。
场景案例描述
场景一:业务系统微服务上下线挑战
某业务系统采用Spring Boot和Spring Cloud框架,服务发布流程中经常遇到以下问题:
- 过早销毁对象:服务正在处理请求时,由于服务实例被过早销毁,导致请求报错。
- 服务未及时下线:调用方未能及时感知服务已在下线,仍发送请求至已下线的服务实例,导致请求失败。
- 过早注册服务:服务未初始化完成即被注册到注册中心,导致接口响应时间突增甚至超时。
场景二:大规模微服务无损上下线需求
在云原生环境下,某客户在生产环境中使用Spring Cloud应用时,发现发布过程中出现了大量错误,如ServiceUnavailable
。分析后发现,问题的根源在于某些消费者未能及时收到提供者的下线通知,导致请求仍然被发送到已下线的服务实例。
解决业务场景思路
针对上述场景,我们需要实现微服务的优雅上下线,确保服务在上下线过程中不会中断现有请求,并能及时通知调用方更新服务列表。以下是解决这些问题的关键思路:
优雅上线
- 资源初始化:通过预热功能实现服务资源的初始化。预热模块可以是可插拔的,业务方可以根据需要自定义预热逻辑,或者使用线上请求回放预热。
- 服务注册:在服务初始化完成后,再将其注册到注册中心,避免过早注册导致的接口响应时间问题。
优雅下线
- 取消注册与标记下线:服务实例在下线前,先向注册中心取消注册,并标记自己为下线状态。
- 请求处理:在标记为下线状态后,服务实例会阻塞一段时间(如5秒),以确保正在处理的请求能够成功返回。
- 负载剔除:服务调用方在收到下线标记后,将该实例从可用服务列表中剔除,确保后续请求不再打到该实例上。
实现无损上下线
- 主动注销与通知:在服务端内置HttpServer,提供
/offline
接口,用于接收主动注销的通知。在K8s的Prestop钩子中触发该接口,实现服务提前注销。 - 长连接维护:对于长连接框架(如Dubbo),可以在服务提供者中维护与服务消费者的连接集合,在收到offline命令后,向所有连接发送只读信号,确保不再接收新请求。
- 自适应等待:服务端在收到下线通知后,采用自适应等待策略,确保所有在途请求处理完成后再进行停机。
底层原理介绍
服务注册与发现
服务注册与发现是微服务架构中实现服务动态感知的基础。常见的服务注册中心有Eureka、Consul、Nacos等。
服务注册
当一个新的微服务实例启动时,它会向服务注册中心注册自己的信息,包括服务名称、IP地址、端口号等。这些信息将被存储在注册中心的目录中,供其他服务查询。
服务发现
其他服务实例通过查询注册中心,可以获取到目标服务的地址信息,从而实现服务的调用。注册中心通常提供RESTful API或客户端库,方便服务实例进行查询。
心跳机制
已注册的服务实例会定期向注册中心发送心跳包,以表明自己仍然存活。如果注册中心在一定时间内未收到某个服务实例的心跳包,将认为该实例已经下线,并将其从注册列表中移除。
事件通知机制
当服务注册中心感知到服务的上下线变化时,会通过事件通知机制及时通知订阅了该服务的其他微服务实例。这样,订阅者可以实时更新自己的本地缓存或服务列表,确保服务调用的准确性。
优雅上下线实现细节
优雅上线
- 预热逻辑:
-
- 自定义预热:业务方根据实际需求扩展预热逻辑,如加载缓存数据、初始化数据库连接等。
- 线上请求回放预热:通过配置预热接口,拉取线上请求对本地服务进行预热。当接口调用达到预热次数后,服务再注册到注册中心。
- 服务注册组件:
-
- 在Spring Cloud Netflix基础上,定制开发
GracefulServiceRegistration
组件,负责触发预热逻辑和服务注册。 WarmUp
接口用于定义预热逻辑,业务方可以实现该接口并注册到Spring容器中。
- 在Spring Cloud Netflix基础上,定制开发
优雅下线
- 取消注册与标记下线:
-
- 在服务下线前,通过
GracefulServiceRegistration
组件取消注册,并标记服务为下线状态。 - 使用
InvokePlugin
插件检查服务状态,如果服务处于下线中,则直接返回下线标记。
- 在服务下线前,通过
- 请求处理与等待:
-
- 服务实例在标记为下线状态后,会阻塞当前线程一段时间(如5秒),确保正在处理的请求能够成功返回。
- 如果在此期间收到新请求,将直接返回下线标记。
- 负载剔除:
-
- 服务调用方在收到下线标记后,将该实例从可用服务列表中剔除。
- 使用独立的任务线程处理失活队列,并维护可用注册表,确保后续请求不再打到已下线的实例上。
无损上下线技术实现
主动注销与通知
- 内置HttpServer:
-
- 在服务提供者进程中内置HttpServer,提供
/offline
接口用于接收主动注销的通知。 - 在K8s的Prestop钩子中配置
curl http://localhost:20001/offline
触发主动注销。
- 在服务提供者进程中内置HttpServer,提供
- 长连接维护:
-
- 对于长连接框架(如Dubbo),在服务提供者中维护与服务消费者的连接集合。
- 在收到offline命令后,向所有连接发送只读信号,确保不再接收新请求。
- 自适应等待:
-
- 服务端在收到下线通知后,采用自适应等待策略。
- 统计并计算进入服务提供者和调用完成的流量,确保所有在途请求处理完成后再进行停机。
关键技术点解析
Zookeeper在动态感知中的应用
Zookeeper作为一种分布式协调服务,也可以用于实现微服务的动态感知。通过Zookeeper的watch机制,可以实时监控服务节点的变化。
- 节点创建与删除:
-
- 当服务上线时,在Zookeeper中创建一个临时节点表示该服务实例。
- 当服务下线时,该临时节点会自动删除。
- 客户端监听:
-
- 客户端通过监听Zookeeper中的特定节点(如服务列表节点),可以实时感知服务实例的上下线。
- 当监听到节点变化时,客户端会更新本地服务列表,确保服务调用的准确性。
Spring Cloud与Eureka的集成
Spring Cloud提供了对Eureka的集成支持,使得在Spring Boot应用中实现服务注册与发现变得非常简单。
- 依赖引入:
-
- 在Spring Boot项目的
pom.xml
文件中引入Spring Cloud Starter Netflix Eureka Client依赖。
- 在Spring Boot项目的
- 配置Eureka客户端:
-
- 在
application.yml
或application.properties
文件中配置Eureka客户端的相关属性,如服务名称、Eureka服务器地址等。
- 在
- 启用Eureka客户端:
-
- 在Spring Boot应用的启动类上添加
@EnableEurekaClient
注解,启用Eureka客户端功能。
- 在Spring Boot应用的启动类上添加
- 服务注册与发现:
-
- 启动应用后,服务实例会自动向Eureka服务器注册。
- 其他服务实例可以通过Eureka服务器查询目标服务的地址信息,实现服务调用。
K8s Prestop钩子的应用
在Kubernetes中,可以使用Prestop钩子在容器停止之前执行一些清理或通知操作。这对于实现微服务的无损下线非常有用。
- 配置Prestop钩子:
-
- 在Kubernetes的Pod定义文件中,为容器配置Prestop钩子。
- 钩子可以是一个执行脚本或命令,用于在服务实例下线前进行必要的操作。
- 触发主动注销:
-
- 在Prestop钩子中触发服务实例的主动注销操作。
- 通过调用服务提供者内置的
/offline
接口或向注册中心发送取消注册请求来实现。
- 等待请求处理完成:
-
- 在触发注销操作后,服务实例会进入等待状态,确保所有在途请求处理完成后再进行停机。
演示介绍
Java 演示
以下是一个使用Java和Spring Cloud Eureka实现微服务上下线动态感知的示例。
引入依赖
xml复制代码
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置Eureka客户端
yaml复制代码
spring:
application:
name: demo-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
lease-renewal-interval-in-seconds: 5 # 心跳间隔时间
lease-expiration-duration-in-seconds: 15 # 服务失效时间
启动类
java复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class DemoServiceApplication {
public static void main(String[] args) {SpringApplication.run(DemoServiceApplication.class, args);}
}
控制器
java复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@GetMapping("/hello")
public String hello() {
return "Hello from Demo Service!";}
}
启动Eureka服务器
如果没有现成的Eureka服务器,可以创建一个Spring Boot项目并配置Eureka服务器。
yaml复制代码
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
spring:
application:
name: eureka-server
在启动类上添加@EnableEurekaServer
注解:
java复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);}
}
启动Eureka服务器和DemoServiceApplication,访问Eureka控制台(通常是http://localhost:8761
),应该能够看到demo-service
已经注册成功。访问http://localhost:<port>/hello
(<port>
是demo-service
的端口),应该能够看到返回的“Hello from Demo Service!”。
现在,当你启动或停止demo-service
实例时,Eureka服务器会实时地感知到这一变化,并更新其注册列表。其他微服务可以通过Eureka服务器查询最新的服务地址信息,从而实现动态的服务发现和调用。
Python 演示
以下是一个使用Python和Consul实现微服务上下线动态感知的示例。
安装Consul客户端
首先,需要安装python-consul
库:
bash复制代码 pip install python-consul
注册服务
python复制代码
import consul
import time
import random
# 连接到Consul代理
c = consul.Consul(host='127.0.0.1', port=8500)
# 注册服务
def register_service(name, port):c.agent.service.register(name, service_id=f"{name}_{port}", address='127.0.0.1', port=port, check={
'ttl': '10s'})
# 模拟服务运行
def run_service(name, port):register_service(name, port)
try:
while True:
print(f"{name} is running on port {port}")time.sleep(random.randint(1, 10))
except KeyboardInterrupt:deregister_service(name, port)
# 注销服务
def deregister_service(name, port):c.agent.service.deregister(f"{name}_{port}")
if __name__ == "__main__":service_name = "demo-service"service_port = 8080run_service(service_name, service_port)
发现和调用服务
python复制代码
import consul
import requests
# 连接到Consul代理
c = consul.Consul(host='127.0.0.1', port=8500)
# 发现服务
def discover_services(service_name):index, services = c.catalog.service(service_name)
return [(service['Address'], service['ServicePort']) for service in services]
# 调用服务
def call_service(service_name):services = discover_services(service_name)
if not services:
print(f"No available instances of {service_name}")
returnaddress, port = random.choice(services)url = f"http://{address}:{port}/hello"response = requests.get(url)
print(f"Called {url}, response: {response.text}")
if __name__ == "__main__":service_name = "demo-service"call_service(service_name)
在上面的示例中,我们首先注册了一个名为demo-service
的服务,并模拟了其运行过程。然后,我们编写了一个发现服务的函数discover_services
,用于查询Consul中的服务实例。最后,我们编写了一个调用服务的函数call_service
,它随机选择一个可用的服务实例并发送HTTP GET请求。
结论
微服务上下线动态感知是微服务架构中一个非常重要的功能,它确保了系统的可用性和负载均衡。通过服务注册与发现机制、心跳机制、事件通知机制以及优雅上下线策略的实现,我们可以有效地感知和处理服务的上下线变化。同时,结合Zookeeper、Spring Cloud与Eureka、Consul等技术和工具,我们可以更加灵活地构建和管理微服务系统。在未来的发展中,随着技术的不断进步和应用场景的不断拓展,微服务上下线动态感知的实现方式也将更加多样化和智能化。
相关文章:
微服务上下线动态感知实现的技术解析
序言 随着微服务架构的广泛应用,服务的动态管理和监控变得尤为重要。在微服务架构中,服务的上下线是一个常见的操作,如何实时感知这些变化,确保系统的稳定性和可靠性,成为了一个关键技术挑战。本文将深入探讨微服务上…...
指针与引用错题汇总
int *p[3]; // 定义一个包含 3 个指向 int 的指针的数组int a 10, b 20, c 30; p[0] &a; // p[0] 指向 a p[1] &b; // p[1] 指向 b p[2] &c; // p[2] 指向 c // 访问指针所指向的值 printf("%d %d %d\n", *p[0], *p[1], *p[2]); // 输出: 10 20 30…...
短视频账号矩阵系统源码--独立saas技术部署
短视频矩阵系统通过多账号在多个平台上发布内容,形成一种网络效应。对于抖音平台而言,技术公司需具备特定接口权限方能进行开发工作。然而,视频发布及企业号评论与回复等功能的接口权限往往难以获取。通过构建抖音账号矩阵,利用多…...
leaflet 介绍
目录 一、leaflet 官网 二、leaflet 在项目中的引用 1、在head中引入 2、在main.js中引入 leaflet目前版本是1.9.4,在leaflet插件库中,很多插件因长时间未更新,适配的是1.7版本的,在选用插件的时候要查看版本适配。 leaflet详…...
总结贴:Servlet过滤器、MVC拦截器
一:Servlet过滤器 1.1解析 Filter 即为过滤,用于请求到达Servlet之前(Request),以及再Servlet方法执行完之后返回客户端进行后处理(HttpServletResponse)。简单说就是对请求进行预处理,对响应进行后处理 在请求到达Servlet之前,可以经过多个Filt…...
鸿蒙开发:自定义一个任意位置弹出的Dialog
前言 鸿蒙开发中,一直有个问题困扰着自己,想必也困扰着大多数开发者,那就是,系统提供的dialog自定义弹窗,无法实现在任意位置进行弹出,仅限于CustomDialog和Component struct的成员变量,这就导致…...
在Windows下编译支持https的wsdl2h
下载源码 在官网下载源码 安装Openssl 下载OpenSSL并安装,安装完成后需要将OpenSSL的路径添加到环境变量中 配置VS 1、打开工程 2、因为前面安装的OpenSLL是64位的,因此需要创建一个X64的配置 打开配置管理器,然后选择新建࿰…...
PHP和GD库如何根据像素绘制图形
使用PHP和GD库,你可以根据像素绘制各种图形,比如点、线、矩形、圆形等。GD库是PHP的一个扩展,它提供了一系列用于创建和处理图像的函数。以下是一个简单的示例,展示如何使用GD库根据像素绘制图形。 安装GD库 首先,确…...
webpack(react)基本构建
文章目录 概要整体架构流程技术名词解释技术细节小结 概要 Webpack 是一个现代 JavaScript 应用程序的静态模块打包工具。它的主要功能是将各种资源(如 JavaScript、CSS、图片等)视为模块,并将它们打包成一个或多个输出文件,以便…...
《Opencv》基础操作<1>
目录 一、Opencv简介 主要特点: 应用领域: 二、基础操作 1、模块导入 2、图片的读取和显示 (1)、读取 (2)、显示 3、 图片的保存 4、获取图像的基本属性 5、图像转灰度图 6、图像的截取 7、图…...
Oracle 11g R2 RAC 到单实例 Data Guard 搭建(RMAN备份方式)
一、配置方案 环境说明 角色主库主库备库主机名rac01rac02racdg公网IP10.10.10.14110.10.10.14310.10.10.191VIP10.10.10.14210.10.10.144-SCAN10.10.10.14010.10.10.140-INSTANCE_NAMEorcl1orcl2orclDB_NAMEorclorclorclSERVICE_NAMEorclorclorclDB_UNIQUE_NAMEorclorclorcl…...
HTTPS 加密
HTTPS 加密技术 1. HTTPS 概述 HTTPS(HyperText Transfer Protocol Secure)是 HTTP 协议的安全版本,利用 SSL/TLS 协议对通信进行加密,确保数据的机密性、完整性和身份认证。HTTPS 在保护敏感数据的传输(如登录凭证、…...
泛微e9开发 编写前端请求后端接口方法以及编写后端接口
泛微e9开发 前端请求后端接口以及后端发布接口 前端请求后端接口 前端发起get请求 fetch(/api/youpath, {method: GET, // 默认 GET 方法,可以省略headers: {Content-Type: application/json, // 通常 GET 请求无需指定 body,Content-Type 不太重要},…...
Linux —— 《线程控制》
文章目录 前言:为什么要链接pthread库?线程控制:线程创建:start_routine?传递自定义类型同一份栈空间? 线程等待:返回值与参数?创建多线程 线程终止线程分离 前言: 上一文我们学习…...
基于HTML+CSS的房地产销售网站设计与实现
摘 要 房地产销售系统,在二十年来互联网时代下有着巨大的意义,随着互联网不断的发展扩大,一个方便直 观的房地产管理系统的网站开发是多么地有意义,不仅打破了传统的线下看房,线下获取资讯,也给房地产从业…...
操作系统 | 学习笔记 | 王道 | 2.4死锁
2.4 死锁 文章目录 2.4 死锁2.4.1 死锁的概念2.4.2 死锁预防2.4.3 死锁避免2.4.4 死锁检测和解除 2.4.1 死锁的概念 死锁的定义 在并发环境下,各进程因竞争资源而造成的一种互相等待对方手里的资源,导致各进程都阻塞,都无法向前推进的现象&am…...
【FPGA开发】Vivado自定义封装IP核,绑定总线
支持单个文件的封装、整个工程的封装,这里用单个文件举例。 在文件工程目录下,自建一个文件夹,里面放上需要封装的verilog文件。 选择第三个,指定路径封装,找到文件所在目录 取个名,选择封装IP的路径 会…...
python的3D可视化库vedo-3 (visual模块)点对象的属性、光效、附注
文章目录 3 PointsVisual的方法3.1 对象属性3.1.1 顶点大小3.1.2 复制属性3.1.3 颜色设置3.1.4透明度设置 3.2 对象光效3.2.1 点的形状3.2.2 点的表面光效 3.3 尾随线和投影3.3.1 尾随线3.3.2 投影 3.4 给对象附加文字说明3.4.1 标注3.4.2 2D标注3.4.3 气泡说明3.4.4 旗标说明3…...
llamaindex实战-ChatEngine-ReAct Agent模式
概述 ReAct 是一种基于Agent的聊天模式,构建在数据查询引擎之上。对于每次聊天交互,代理都会进入一个 ReAct 循环: 首先决定是否使用查询引擎工具并提出适当的输入 (可选)使用查询引擎工具并观察其输出 决定是否重复…...
redis快速进门
、数据库类型认识 关系型数据库 关系型数据库是一个结构化的数据库,创建在关系模型(二维表格模型)基础上,一般面向于记录。 SQL 语句(标准数据查询语言)就是一种基于关系型数据库的语言,用于执行…...
从0开始linux(39)——线程(2)线程控制
欢迎来到博主的专栏:从0开始linux 博主ID:代码小豪 文章目录 线程创建线程标识符线程参数多线程竞争资源 回收线程detach 线程退出pthread_cancel 线程创建 线程创建的函数为pthread_create。该函数是包含在posix线程库当中,posix线程是C语言…...
International Journal of Medical Informatics投稿经历时间节点
20240423,完成投稿 20240612,按编辑要求修改后再投, with editor 20240613,under review,completed 0, accepted 0, invitation 2. 20240620, under review,completed 0, accepted 1, invitation 2. 20240626, unde…...
BUUCTF—Reverse—Java逆向解密(10)
程序员小张不小心弄丢了加密文件用的秘钥,已知还好小张曾经编写了一个秘钥验证算法,聪明的你能帮小张找到秘钥吗? 注意:得到的 flag 请包上 flag{} 提交 需要用专门的Java反编译软件:jd-gui 下载文件,发现是个class文…...
CLIP-MMA: Multi-Modal Adapter for Vision-Language Models
当前的问题 CLIP-Adapter仅单独调整图像和文本嵌入,忽略了不同模态之间的交互作用。此外,适应性参数容易过拟合训练数据,导致新任务泛化能力的损失。 动机 图1所示。多模态适配器说明。 通过一种基于注意力的 Adapter ,作者称之…...
三维扫描仪-3d扫描建模设备自动检测尺寸
在现代工业制造领域,三维扫描仪已成为实现高精度尺寸检测的关键设备。CASAIM自动化智能检测系统以其自动化三维立体扫描技术,为产品尺寸的自动检测提供了高效、可靠的解决方案。 CASAIM自动化智能检测系统通过非接触式测量方式,通过激光扫描…...
vue3+ant design vue实现日期选择器默认显示当前年,并限制用户只能选择当前年及之前~
1、思路:之前想拿当前年直接做赋值操作,实际上是行不通的,因为组件本身有数据格式限制,会出现报错,然后索性直接获取当前日期(YYYY-MM-DD)赋值给日期组件,这样不管你用的是年&#x…...
【electron-vite】搭建electron+vue3框架基础
一、拉取项目 electron-vite 中文文档地址: https://cn-evite.netlify.app/guide/ 官网网址:https://evite.netlify.app/ 版本 vue版本:vue3 构建工具:vite 框架类型:Electron JS语法:TypeScript &…...
05《存储器层次结构与接口》计算机组成与体系结构 系列课
目录 存储器层次结构概述 层次结构的定义 存储器的排名 存储器接口 处理器与存储器的速度匹配 存储器接口的定义 存储器访问命中率 两种接口 第1种方式:并行 命中率的计算 存储器访问时间 第2种方式:逐级 结语 大家好,欢迎回来。…...
elasticsearch报错fully-formed single-node cluster with cluster UUID
1.问题描述 k8s集群内部署的es中间件起不来,查看日志发现如下警告,节点发现功能开启,但是目前我是单节点服务,所以尝试编辑sts将节点发现功能去掉或者在部署时将你的sts的yaml文件和chart文件修改重新部署以去掉该功能 {"t…...
Milvus×Florence:一文读懂如何构建多任务视觉模型
近两年来多任务学习(Multi-task learning)正取代传统的单任务学习(single-task learning),逐渐成为人工智能领域的主流研究方向。其原因在于,多任务学习可以让我们以最少的人力投入,获得尽可能多…...
衡水哪儿专业做网站/搜索风云榜百度
test test 是正则表达式的方法,参数是字符串,返回的是布尔值(true或false),查找对应的字符串是否存在 exec RegExpObject.exec(string) exec是正则表达式的方法,它的参数是字符串,查找并返回当…...
python html 界面开发/草根seo视频大全网站
一、字符转换函数1、ASCII()返回字符表达式最左端字符的ASCII 码值。在ASCII()函数中,纯数字的字符串可不用‘’括起来,但含其它字符的字符串必须用‘’括起来使用,否则会出错。2、CHAR()将ASCII 码转换为字符。如果没…...
布吉公司做网站/国内永久免费建站
前面的一篇文章Direct 3D基础介绍了一些基本概念,叙述了如何在显示器上直接绘制具有立体感的2D图形。上面的方法是不现实的,因为预先根据透视原理人工计算出3D物体在显示屏幕上显示的坐标然后再绘制的这种方式如果涉及从不同角度观察的3D物体的话&#x…...
怎么在百度上搜到自己的网站/网站搜什么关键词
gettext 是GNU 提供的一套 国际化与本地化处理的相关函数库。大多数语言都有对应的gettext实现。本文主要使用jed 来实现gettext 一系列方法对应的功能。pot/po文件pot文件 是po文件的模板文件,一般是通过 xgettext 程序生成出来的。po文件 是根据pot文件通过msgini…...
枣庄市庄里水库建设管理处网站/网络视频营销
CPrimer第五版 习题答案 【总目录】:https://blog.csdn.net/Dust_Evc/article/details/114334124 练习4.1 表达式 5 10 * 20 / 2 的求值结果是多少? 105。 练习4.2 根据4.12节中的表,在下述表达式的合理位置添加括号,使得添加括…...
wordpress 支持小工具/百度推广是什么意思
目录Trie 树(字典树)1 10 个频繁出现的词2 寻找热门查询Trie 树(字典树) Trie 树,即字典树,又称单词查找树或键树,是一种树形结构。 典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被…...