Dubbo的优雅下线原理分析

文/朱季谦
Dubbo如何实现优雅下线?
这个问题困扰了我一阵,既然有优雅下线这种说法,那么,是否有非优雅下线的说法呢?
这,还真有。
可以从linux进程关闭说起,其实,我们经常使用到杀进程的指令背后,就涉及到是否优雅下线的理念。
在日常开发当中,经常用到kill来关掉正在运行的进程,可能你曾看到过一些文章是不推荐使用kill -9 pid的指令来删除进程。当执行该执行时,系统会发出一个SIGKILL信号给将被关掉的进程,接收到该信号的进程,都立即结束运行,假如此时内部仍有请求还没有执行完,那怎么办?你想,整个进程都被立即杀死了,线程作为进程里的某一部分,还能活吗?
打个比方,假如你正在吃东西,物业突然打电话给你,说房子立马就要被炸掉了,你必须立马关门离开,这时,你只能把还没吃完的饭丢下,什么贵重的东西都来不及打理,立马就被迫关门跑路了。
这样强制执行的后果,可能就会造成一些贵重东西的丢失。
这种,就属于非优雅下线,简单,粗暴,不管三七二十一,统统停止关闭。
一般而言,是不推荐使用kill -9 pid来强制杀死进程。
在线上环境,用到更多的,是kill pid指令,这个指令,等同于kill -15 pid指令,因此,当你在网上看到一些介绍kill -15 pid指令时,不用纠结好像没用到过,其实,就是你用到最多的kill pid指令。使用这个指令时,系统会对pid进程发送一个SIGTERM信号,就像给pid打了一个电话,告诉他,你的房子就要到期了,麻烦快点清理好东西搬走。这时,你仍有充裕的时间,把自己的东西打包好,好好清理下房间,没问题了,再搬出去。
换到具体程序代码中,就是执行kill pid指令后,该程序不会立马被强制关闭,而是会接受到一个通知,可以在这个通知方法内,做一些清理操作,若是Dubbo容器,则可以关闭zookeeper注册,暂停新的请求,可以把已经执行一半的请求先执行完成,等等。
这种下线操作,就属于优雅下线。
指令kill -15 pid是操作系统级别的优雅下线操作,那么,在具体进程当中,是如何根据SIGTERM信号来进行具体的优雅下线处理呢?
在Dubbo官网上,关于优雅停机的操作有相关介绍:
优雅停机
Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行。
原理
服务提供方
- 停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。
- 然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。
服务消费方
- 停止时,不再发起新的调用请求,所有新的调用在客户端即报错。
- 然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。
设置方式
设置优雅停机超时时间,缺省超时时间是 10 秒,如果超时则强制关闭。
# dubbo.properties
dubbo.service.shutdown.wait=15000
如果 ShutdownHook 不能生效,可以自行调用,使用tomcat等容器部署的場景,建议通过扩展ContextListener等自行调用以下代码实现优雅停机:
ProtocolConfig.destroyAll();
根据以上信息可以得知,其实Dubbo的优雅实现其实是依赖了JVM的ShutdownHook来实现的,JDK提供了一个在JVM关闭时会执行的方法,可以在该方法当中,执行ProtocolConfig.destroyAll()来实现Dubbo的优雅停机操作,而这个JDK的 ShutdownHook方法,正是在系统执行kill -15 pid时,会执行的方法,这样,我们就可以在该方法里做一些关闭前的清理工作了。
Runtime.getRuntime().addShutdownHook(new Thread(() -> {ProtocolConfig.destroyAll();
}));
这几行代码具体都实现了什么呢?
简单而言,这里通过JDK注册了一个shutdownHook钩子函数,一旦应用停机就会触发该方法,进而执行ProtocolConfig.destroyAll()。
这个ProtocolConfig.destroyAll()源码如下:
public static void destroyAll() {//1.注销注册中心AbstractRegistryFactory.destroyAll();ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);Iterator var1 = loader.getLoadedExtensions().iterator();// 2.循环获取存活的协议while(var1.hasNext()) {String protocolName = (String)var1.next();try {Protocol protocol = (Protocol)loader.getLoadedExtension(protocolName);if (protocol != null) {//关闭暴露协议protocol.destroy();}} catch (Throwable var4) {logger.warn(var4.getMessage(), var4);}
这个destroyAll()里边主要做了两件事:
- 首先注销注册中心,即断开与注册中心的连接,Dubbo注册到ZK的是临时节点,故而当连接断开后,临时节点及底下的数据就会被自动删除;
- 关闭provider和consumer暴露的协议接口,这样,新的请求就无法再继续进行;
下面主要按照这两个模块大体介绍下其底层逻辑:
一、注销注册中心
public static void destroyAll() {if (LOGGER.isInfoEnabled()) {LOGGER.info("Close all registries " + getRegistries());}//加锁,防止关闭多次LOCK.lock();try {Iterator var0 = getRegistries().iterator();//关闭所有已创建的注册中心while(var0.hasNext()) {Registry registry = (Registry)var0.next();try {registry.destroy();} catch (Throwable var6) {LOGGER.error(var6.getMessage(), var6);}}REGISTRIES.clear();} finally {//释放锁LOCK.unlock();}
}
首先获取到所有的注册中心连接,封装成迭代器模式
Iterator var0 = getRegistries().iterator();
接下来,迭代获取每一个注册连接对象进行关闭:
registry.destroy();
该destroy方法定义在接口Node当中,其具体实现将会在对应的Dubbo注册对象里:
public interface Node {URL getUrl();boolean isAvailable();void destroy();
}
这里Dubbo使用的注册中心是Zookeeper,故而destroy会在ZookeeperRegistry类中具体实现:

进入到ZookeeperRegistry类,找到registry.destroy()对应的destroy()方法,可以看到,调用destroy(),其本质是关闭zk客户端连接,当客户端关闭之后,其注册到zk里的生产者或者消费者信息,都会被自动删除。
public void destroy() {super.destroy();try {// 关闭zk客户端this.zkClient.close();} catch (Exception var2) {logger.warn("Failed to close zookeeper client " + this.getUrl() + ", cause: " + var2.getMessage(), var2);}
}
在这里,还有一个需要进一步研究的地方,即 super.destroy(),这个方法实现了什么功能呢?从源码当中,可以看出,其有一行这样的 this.retryFuture.cancel(true)代码,这行代码大概意思是,将失败重试取消方式设置为true,即取消了失败重试的操作,我的理解是,这里是关闭了失败重试,可以在下线过程当中,避免出现因RPC生产者接口缺少而发生反复的失败重试操作,因为到这一步,已经不需要再有失败重试的操作了。
public void destroy() {//移除内存中已经注册的服务,取消所有服务订阅super.destroy();try {//取消失败重试this.retryFuture.cancel(true);} catch (Throwable var2) {this.logger.warn(var2.getMessage(), var2);}
}
注意一点,这里在取消失败重试机制之前,还执行了一行 super.destroy()代码,这行代码的主要功能包括两个:
第一是移除内存中已经注册的服务,第二是取消所有服务订阅。
我们先来看一下其方法详情:
public void destroy() {if (this.logger.isInfoEnabled()) {this.logger.info("Destroy registry:" + this.getUrl());}// 1.移除内存中已经注册的服务Set<URL> destroyRegistered = new HashSet(this.getRegistered());if (!destroyRegistered.isEmpty()) {Iterator var2 = (new HashSet(this.getRegistered())).iterator();while(var2.hasNext()) {URL url = (URL)var2.next();if (url.getParameter("dynamic", true)) {try {this.unregister(url);if (this.logger.isInfoEnabled()) {this.logger.info("Destroy unregister url " + url);}} catch (Throwable var10) {this.logger.warn("Failed to unregister url " + url + " to registry " + this.getUrl() + " on destroy, cause: " + var10.getMessage(), var10);}}}}//2.取消所有的服务订阅Map<URL, Set<NotifyListener>> destroySubscribed = new HashMap(this.getSubscribed());if (!destroySubscribed.isEmpty()) {Iterator var12 = destroySubscribed.entrySet().iterator();while(var12.hasNext()) {Map.Entry<URL, Set<NotifyListener>> entry = (Map.Entry)var12.next();URL url = (URL)entry.getKey();Iterator var6 = ((Set)entry.getValue()).iterator();while(var6.hasNext()) {NotifyListener listener = (NotifyListener)var6.next();try {this.unsubscribe(url, listener);if (this.logger.isInfoEnabled()) {this.logger.info("Destroy unsubscribe url " + url);}} catch (Throwable var9) {this.logger.warn("Failed to unsubscribe url " + url + " to registry " + this.getUrl() + " on destroy, cause: " + var9.getMessage(), var9);}}}}
}
1.移除内存中已经注册的服务
// 1.移除内存中已经注册的服务Set<URL> destroyRegistered = new HashSet(this.getRegistered());if (!destroyRegistered.isEmpty()) {Iterator var2 = (new HashSet(this.getRegistered())).iterator();while(var2.hasNext()) {URL url = (URL)var2.next();if (url.getParameter("dynamic", true)) {try {this.unregister(url);if (this.logger.isInfoEnabled()) {this.logger.info("Destroy unregister url " + url);}} catch (Throwable var10) {this.logger.warn("Failed to unregister url " + url + " to registry " + this.getUrl() + " on destroy, cause: " + var10.getMessage(), var10);}}}}
这部分代码主要是将内存当中的注册信息移除,这部分缓存记录,是在容器启动时,当向注册中心订阅成功后,会同步缓存一份到内存当中。可见,若注册中心挂掉了,Dubbo仍然可以通过缓存获取到远程RPC服务,但是无法获取到新增的RPC服务。
这里主要分析两个方法:this.getRegistered()和 this.unregister(url)。
this.getRegistered()——
private final Set<URL> registered = new ConcurrentHashSet();public Set<URL> getRegistered() {return this.registered;
}
这是获取缓存URL的集合。
this.unregister(url)——
public void unregister(URL url) {if (url == null) {throw new IllegalArgumentException("unregister url == null");} else {if (this.logger.isInfoEnabled()) {this.logger.info("Unregister: " + url);}this.registered.remove(url);}
}
这是将URL从Set集合当中移除的操作。这部分代码其实我有点想明白,为何还需要从Set获取到所有URL,然后再通过迭代器方式一个一个取出去进行移除,直接将Set置空不是更好些吗?当然,这里面应该还有一些我没有考虑到的细节,还有待进一步进行研究。
2.取消所有服务订阅
//2.取消所有的服务订阅Map<URL, Set<NotifyListener>> destroySubscribed = new HashMap(this.getSubscribed());if (!destroySubscribed.isEmpty()) {Iterator var12 = destroySubscribed.entrySet().iterator();while(var12.hasNext()) {Map.Entry<URL, Set<NotifyListener>> entry = (Map.Entry)var12.next();URL url = (URL)entry.getKey();Iterator var6 = ((Set)entry.getValue()).iterator();while(var6.hasNext()) {NotifyListener listener = (NotifyListener)var6.next();try {this.unsubscribe(url, listener);if (this.logger.isInfoEnabled()) {this.logger.info("Destroy unsubscribe url " + url);}} catch (Throwable var9) {this.logger.warn("Failed to unsubscribe url " + url + " to registry " + this.getUrl() + " on destroy, cause: " + var9.getMessage(), var9);}}}}
这部分逻辑与移除内存url都很类型,都是先从缓存里把所有订阅信息都取出来,然后再跌代移除。
二、关闭protocol协议
这部分个关闭,主要是关闭provider和consumer,即对应前边提到的,服务提供方会先标记不再接受新请求,新请求过来直接报错,然后,检查线程池中的线程是否还在运行,如果有,等待线程完成,若超时,则强制关闭;服务消费者则不再发起新请求,同时检测看还有没有请求的响应没有返回,若有,等待返回,若超时,则强制关闭。
下面大概分析一下其源码逻辑。
protocol.destroy(),其方法在接口里定义,具体实现是在RegistryProtocol当中。
@SPI("dubbo")
public interface Protocol {int getDefaultPort();@Adaptive<T> Exporter<T> export(Invoker<T> var1) throws RpcException;@Adaptive<T> Invoker<T> refer(Class<T> var1, URL var2) throws RpcException;void destroy();
}
RegistryProtocol的具体实现如下:
public void destroy() {List<Exporter<?>> exporters = new ArrayList(this.bounds.values());Iterator var2 = exporters.iterator();while(var2.hasNext()) {Exporter<?> exporter = (Exporter)var2.next();exporter.unexport();}this.bounds.clear();
}
这里的核心方法是exporter.unexport(),根据命名就可以推测出,大概就是说不暴露对外接口协议的方法,也就是关闭那些对外暴露的服务。
该exporter.unexport()方法具体实现有两类,一个是DubboExporter,一个是AbstractExporter,这里主要分析下AbstractExporter里面的逻辑。
AbstractExporter内部关于unexport()的方法如下:
public void unexport() {if (!this.unexported) {this.unexported = true;this.getInvoker().destroy();}
}
this.getInvoker().destroy()的实现如下:
public void destroy() {Iterator var1 = (new ArrayList(this.serverMap.keySet())).iterator();String key;//关停所有的Server,provider不再接收新的请求while(var1.hasNext()) {key = (String)var1.next();ExchangeServer server = (ExchangeServer)this.serverMap.remove(key);if (server != null) {try {if (this.logger.isInfoEnabled()) {this.logger.info("Close dubbo server: " + server.getLocalAddress());}// HeaderExchangeServer中会停止发送心态的任务,关闭channelserver.close(getServerShutdownTimeout());} catch (Throwable var7) {this.logger.warn(var7.getMessage(), var7);}}}var1 = (new ArrayList(this.referenceClientMap.keySet())).iterator();ExchangeClient client;//关停所有Client,consumer将不再发送新的请求while(var1.hasNext()) {key = (String)var1.next();client = (ExchangeClient)this.referenceClientMap.remove(key);if (client != null) {try {if (this.logger.isInfoEnabled()) {this.logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress());}// HeaderExchangeClient中会停止发送心态的任务,关闭channelclient.close();} catch (Throwable var6) {this.logger.warn(var6.getMessage(), var6);}}}
......
}
总结一下,Dubbo的优雅下线,若是通过JDK的shutdownHook来完成优雅停机的,这时当用户对该Dubbo进行执行kill pid后,在关闭JVM时会发起一个线程执行ShutdownHook,进而执行 ProtocolConfig.destroyAll()方法,该方法在关掉进行前,主要做了以下一些清理工作:
1、关闭zk客户端
2、 客户端断开ZK连接后,ZK会自动删除临时注册节点
3、 取消重试机制
4 、清除内存中已经注册的服务
5、 取消所有的服务订阅
6、关闭provider和consumer,停止新的请求
后面还有一步没分析到,是若仍有在执行的线程,会等待其执行完成。
最后,在清理完一系列工作后,就可以关闭该进程了。
这就是Dubbo的优雅下线大概的原理。
相关文章:
Dubbo的优雅下线原理分析
文/朱季谦 Dubbo如何实现优雅下线? 这个问题困扰了我一阵,既然有优雅下线这种说法,那么,是否有非优雅下线的说法呢? 这,还真有。 可以从linux进程关闭说起,其实,我们经常使用到杀…...
leetcode做题笔记2342. 数位和相等数对的最大和
给你一个下标从 0 开始的数组 nums ,数组中的元素都是 正 整数。请你选出两个下标 i 和 j(i ! j),且 nums[i] 的数位和 与 nums[j] 的数位和相等。 请你找出所有满足条件的下标 i 和 j ,找出并返回 nums[i] nums[j]…...
c# YOLOV5目标检测部署
using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Dnn; using Emgu.CV.Structure; using Emgu.CV.Util...
学习笔记6——垃圾回收
学习笔记系列开头惯例发布一些寻亲消息 链接:https://baobeihuijia.com/bbhj/contents/3/190801.html java垃圾回收(stop the world) 专注于堆和方法区的垃圾回收,年轻代,老年代,永久代判断对象是否还存…...
3.1 Windows驱动开发:内核远程堆分配与销毁
在开始学习内核内存读写篇之前,我们先来实现一个简单的内存分配销毁堆的功能,在内核空间内用户依然可以动态的申请与销毁一段可控的堆空间,一般而言内核中提供了ZwAllocateVirtualMemory这个函数用于专门分配虚拟空间,而与之相对应…...
C++: 模板初阶
文章目录 一. 泛型编程二. 函数模板函数模板的原理函数模板的实例化隐式实例化: 让编译器根据实参推演模板参数的实际类型显示实例化: 在函数名后的<>中制定模板参数的世纪类型 模板参数的匹配原则 三. 类模板类模板的定义格式类模板的实例化 一. 泛型编程 如何实现一个…...
人工智能基础_机器学习036_多项式回归升维实战3_使用线性回归模型_对天猫双十一销量数据进行预测_拟合---人工智能工作笔记0076
首先我们拿到双十一从2009年到2018年的数据 可以看到上面是代码,我们自己去写一下 首先导包,和准备数据 from sklearn.linear_model import SGDRegressor import numpy as np import matplotlib.pyplot as plt X=np.arange(2009.2020)#左闭右开,2009到2019 获取从2009到202…...
【算法挨揍日记】day29——139. 单词拆分、467. 环绕字符串中唯一的子字符串
139. 单词拆分 139. 单词拆分 题目描述: 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。 解题思路&am…...
YOLOv8-Seg改进:轻量级Backbone改进 | VanillaNet极简神经网络模型 | 华为诺亚2023
🚀🚀🚀本文改进:一种极简的神经网络模型 VanillaNet,支持vanillanet_5, vanillanet_6, vanillanet_7, vanillanet_8, vanillanet_9, vanillanet_10, vanillanet_11等版本,相比较yolov8-seg各个版本如下: layersparametersgradientsGFLOPsvanillanet_521230017523...
解决Requests中使用httpbin服务器问题:自定义URL的实现与验证
问题背景 在使用Python的Requests模块进行单元测试时,可能会遇到无法使用本地运行的httpbin服务器进行测试的问题。这是因为测试脚本允许通过环境变量HTTPBIN_URL指定用于测试的本地httpbin实例,但在某些测试用例中,URL是硬编码为httpbin.or…...
软考-高级-系统架构设计师教程(清华第2版)【第17章 通信系统架构设计理论与实践(P614~646)-思维导图】
软考-高级-系统架构设计师教程(清华第2版)【第17章 通信系统架构设计理论与实践(P614~646)-思维导图】 课本里章节里所有蓝色字体的思维导图...
【MATLAB源码-第82期】基于matlab的OFDM系统载波频移偏差(CFO)估计,对比三种不同的方法。
操作环境: MATLAB 2013b 1、算法描述 正交频分复用(OFDM)系统中的载波频率偏移(CFO)估计是一项关键技术,用于确保数据传输的准确性和效率。CFO通常由于振荡器频率不匹配和多普勒频移引起。不同的CFO估计…...
Docker Swarm: 容器编排的力量和优势深度解析
文章目录 Docker Swarm的核心概念1. 节点(Node)2. 服务(Service)3. 栈(Stack) 使用Docker Swarm1. 初始化Swarm2. 加入节点3. 创建服务4. 扩展和缩减服务5. 管理栈6. 管理服务更新 Docker Swarm的优势深度解…...
调整Windows键盘上只能看到拼音而无法看到实际的文本以及关闭输入法悬浮窗方法
一、输入法设置 如果您在键盘上只能看到拼音而无法看到实际的文本,这可能是因为您的输入法设置为中文拼音输入法或其他仅显示拼音的输入法。 要解决这个问题,您可以尝试以下方法: 1. 切换输入法:按下 Shift Alt 组合键或 Wind…...
【微软技术栈】C#.NET 中的管道操作
C#.NET 管道为进程间通信提供了平台。 管道分为两种类型: 匿名管道。 匿名管道在本地计算机上提供进程间通信。 与命名管道相比,虽然匿名管道需要的开销更少,但提供的服务有限。 匿名管道是单向的,不能通过网络使用。 仅支持一个服…...
Python学习笔记--进程
进程 Python 中的多线程其实并不是真正的多线程,如果想要充分地使用多核 CPU 的资源,在 Python 中大部分情况需要使用多进程。 Python 提供了非常好用的多进程包 multiprocessing,只需要定义一个函数,Python 会完成其他所有事情。 借助这个包,可以轻松完成从单进程到并…...
比亚迪刀片电池与特斯拉4680电池比较
1 电池材料 比亚迪刀片电池采用的磷酸铁锂LFP(LiFePO4),特斯拉的4680电池采用的三元锂。 磷酸铁锂:循环寿命长,安全性能好,价格低廉,但是能量密度低,导电性能差,低温表现…...
在写windows C++代码的时候,从代码安全角度考虑,我们应该注意什么?
在写windows C代码的时候,从代码安全角度考虑,我们应该注意什么?分别是:输入验证、内存管理、错误处理、并发和线程安全、使用安全的API、避免使用不安全的函数、最小权限原则。 一、输入验证 1. 用户输入验证 #include <io…...
【草料】uni-app ts vue 小程序 如何如何通过草料生成对应的模块化二维码
一、查看uni-app项目 1、找到路径 可以看到项目从 src-race-pages-group 这个使我们目标的查询页面 下面我们将这个路径copy到草料内 2、找到进入页面入参 一般我们都会选择 onload() 函数下的入参 这里我们参数的是 id 二、草料 建议看完这里的教程文档 十分清晰!…...
CMS与FullGC
JVM中的CMS(Concurrent Mark Sweep)GC和Full GC(Full Garbage Collection)是两种不同的垃圾回收算法。 CMS GC:CMS GC是一种并发的垃圾回收算法,它在运行期间与应用程序线程并发工作,尽可能减少…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...
云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
