ZLMediaKit 源码分析——[3] ZLToolKit 中EventPoller之网络事件处理

系列文章目录

第一篇 基于SRS 的 WebRTC 环境搭建
第二篇 基于SRS 实现RTSP接入与WebRTC播放
第三篇 centos下基于ZLMediaKit 的WebRTC 环境搭建
第四篇 WebRTC学习一:获取音频和视频设备
第五篇 WebRTC学习二:WebRTC音视频数据采集
第六篇 WebRTC学习三:WebRTC音视频约束
第七篇 WebRTC学习四:WebRTC常规视觉滤镜
第八篇 WebRTC学习五:从视频中提取图片
第九篇 WebRTC学习六:MediaStream 常用API介绍
第十篇 WebRTC学习七:WebRTC 中 STUN 协议详解
ZLMediaKit源码分析——[1] 开篇:基础库 ZLToolKit 之 onceToken 源码分析
ZLMediaKit源码分析——[2] 从 ZLToolKit 代码看 CPU 亲和性设计
ZLMediaKit源码分析——[3] ZLToolKit 中EventPoller之网络事件处理

文章目录

  • 系列文章目录
  • 前言
  • 一、EventPoller网络事件处理机制
  • 二、EventPoller类图分析
    • 2.1 类图
    • 2.2 相关类功能介绍
  • 三、 事件循环驱动 - runLoop 函数
  • 四、事件监听的添加,修改和删除
    • 4.1 添加事件监听AddEvent
    • 4.2 删除事件监听 delEvent
    • 4.3 修改监听事件类型 modifyEvent
  • 五、数据结构
    • 5.1 _event_map
    • 5.2 _event_cache_expired的之作用
      • 5.2.1 在 delEvent 函数中的作用
      • 5.2.2 在 runLoop 函数中的作用
      • 5.2.3 总结
  • 总结


前言

当我在ZLToolKit里看到事件轮询管理类EventPoller的runLoop函数时,脑海中瞬间浮现出多年前live555里的设计——那里同样有一个doEventLoop()函数,在该函数里执行着网络事件的select操作、异步任务以及延时队列。出于好奇,我仔细研读了EventPoller类的代码。发现如今其IO事件驱动模型采用的是多实例epoll模型,不过网络事件、异步任务和延时队列这些概念依旧存在,这样的设计让人倍感熟悉。那么,今天我就来详细剖析一下ZLM里网络事件处理模型的具体运作机制。


一、EventPoller网络事件处理机制

zlmediakit基于‌事件驱动+非阻塞I/O‌架构,事件处理机制如下:

‌核心机制‌:使用epoll(Linux)或kqueue(BSD)实现I/O多路复用,监控所有socket的可读/可写事件。
‌执行流程‌:
‌事件收集‌:调用epoll_wait等待socket事件(如新连接、数据到达)。
事件触发:调用epoll_ctl添加/修改/删除网络事件。
事件处理:调用回调函数执行任务。
其核心类为 EventPoller,关键函数是 runLoop ,网络事件处理中关键的数据结构为_event_map,_event_cache_expired,以及事件监听,删除,修改的接口构成。

二、EventPoller类图分析

2.1 类图

因为press on上面显示类太多了会看不清晰,这里省略了一些和本节介绍无关的类,保留主要类图如下:
在这里插入图片描述

2.2 相关类功能介绍

AnyStorage 类:这是一个通用的数据存储类,它可以存储任意类型的数据。在 zlmediakit 中,它提供了一种灵活的方式来存储不同类型的值,增强了代码的通用性和可扩展性,方便在不同模块之间传递和管理数据。

ThreadLoadCounter 类:主要用于统计线程的负载情况。它会对线程在运行过程中的各种操作进行计数和时间统计,帮助开发者了解线程的繁忙程度,为系统的性能优化和资源分配提供数据支持。

TaskExecutorInterface 类:作为一个接口类,它定义了任务执行器的基本行为和功能规范。不同的任务执行器可以实现该接口,遵循统一的操作接口,从而实现多态性,方便代码的扩展和维护。其中最重要的就是它提供的4个接口,是异步任务执行的基础。

TaskExecutor 类:是具体的任务执行器类,实现了 TaskExecutorInterface 接口所定义的功能。它负责接收并执行各种任务,合理地分配系统资源,将任务分配到合适的线程或执行环境中进行处理。

EventPoller 类:是整个事件处理的核心类,负责事件的轮询和管理。它能够监听网络套接字的各种事件,通过事件循环不断检查事件状态,并在事件发生时触发相应的处理逻辑。

三、 事件循环驱动 - runLoop 函数

EventPoller::runLoop 函数是 EventPoller 类的核心函数,它实现了事件循环的逻辑,不断地监听网络套接字的事件并进行处理。

void EventPoller::runLoop(bool blocked, bool ref_self) {if (blocked) {if (ref_self) {s_current_poller = shared_from_this();}_sem_run_started.post();_exit_flag = false;uint64_t minDelay;
#if defined(HAS_EPOLL)struct epoll_event events[EPOLL_SIZE];while (!_exit_flag) {minDelay = getMinDelay();startSleep();//用于统计当前线程负载情况int ret = epoll_wait(_event_fd, events, EPOLL_SIZE, minDelay ? minDelay : -1);sleepWakeUp();//用于统计当前线程负载情况if (ret <= 0) {//超时或被打断continue;}_event_cache_expired.clear();for (int i = 0; i < ret; ++i) {struct epoll_event &ev = events[i];int fd = ev.data.fd;if (_event_cache_expired.count(fd)) {//event cache refreshcontinue;}auto it = _event_map.find(fd);if (it == _event_map.end()) {epoll_ctl(_event_fd, EPOLL_CTL_DEL, fd, nullptr);continue;}auto cb = it->second;try {(*cb)(toPoller(ev.events)); // 将epoll事件类型转换回自定事件类型} catch (std::exception &ex) {ErrorL << "Exception occurred when do event task: " << ex.what();}}}
#elif defined(HAS_KQUEUE)// ... 其他系统的处理代码
#else// ... 其他系统的处理代码
#endif //HAS_EPOLL} else {_loop_thread = new thread(&EventPoller::runLoop, this, true, ref_self);_sem_run_started.wait();}
}

先屏蔽掉其它系统的网络处理,可以看到该函数处理主要流程如下:
1、初始化和循环条件判断:首先进行一些初始化操作,如设置当前的 EventPoller 实例,将退出标志 _exit_flag 置为 false,表示开始事件循环。
2、获取最小延迟时间:调用 getMinDelay() 函数获取最小延迟时间,用于 epoll_wait 的超时设置。
3、调用 epoll_wait 监听事件:使用 epoll_wait 函数监听网络事件,当有事件发生时,该函数会返回发生事件的数量。
4、事件处理:遍历发生事件的数组,对于每个事件,检查其对应的文件描述符是否在 _event_map 中。如果存在,则调用相应的回调函数进行处理;如果不存在,则从 epoll 中删除该文件描述符。

四、事件监听的添加,修改和删除

4.1 添加事件监听AddEvent

int EventPoller::addEvent(int fd, int event, PollEventCB cb) {// 时间检查TimeTicker();// 回调函数检查if (!cb) {WarnL << "PollEventCB is empty";return -1;}// 当前线程检查,// 如果是当前线程:根据不同的操作系统平台选择不同的事件通知机制来添加事件监听。if (isCurrentThread()) {
#if defined(HAS_EPOLL)struct epoll_event ev = {0};ev.events = toEpoll(event) ;ev.data.fd = fd;int ret = epoll_ctl(_event_fd, EPOLL_CTL_ADD, fd, &ev);if (ret != -1) {_event_map.emplace(fd, std::make_shared<PollEventCB>(std::move(cb)));}return ret;
#elif defined(HAS_KQUEUE)struct kevent kev[2];int index = 0;if (event & Event_Read) {EV_SET(&kev[index++], fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, nullptr);}if (event & Event_Write) {EV_SET(&kev[index++], fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, nullptr);}int ret = kevent(_event_fd, kev, index, nullptr, 0, nullptr);if (ret != -1) {_event_map.emplace(fd, std::make_shared<PollEventCB>(std::move(cb)));}return ret;
#else
#ifndef _WIN32// win32平台,socket套接字不等于文件描述符,所以可能不适用这个限制if (fd >= FD_SETSIZE) {WarnL << "select() can not watch fd bigger than " << FD_SETSIZE;return -1;}
#endifauto record = std::make_shared<Poll_Record>();record->fd = fd;record->event = event;record->call_back = std::move(cb);_event_map.emplace(fd, record);return 0;
#endif}// 如果不是当前线程,发起异步操作:// 使用 async 函数异步调用 addEvent 函数,将事件添加操作放到合适的线程中执行,并立即返回 0。async([this, fd, event, cb]() mutable {addEvent(fd, event, std::move(cb));});return 0;
}

EventPoller::addEvent 函数的主要功能是向事件轮询器中添加一个文件描述符(fd)的事件监听,并关联一个回调函数(cb)。该函数会根据不同的操作系统平台,采用不同的事件通知机制(如 epoll、kqueue 或 select)来添加事件监听,同时会处理回调函数为空以及非当前线程调用的情况。

4.2 删除事件监听 delEvent

int EventPoller::delEvent(int fd, PollCompleteCB cb) {// 时间检查TimeTicker();// 回调函数检查if (!cb) {cb = [](bool success) {};}// 当前线程检查// 如果是当前线程:根据不同的操作系统平台选择不同的事件通知机制来删除事件监听。if (isCurrentThread()) {
#if defined(HAS_EPOLL)int ret = -1;if (_event_map.erase(fd)) {_event_cache_expired.emplace(fd);ret = epoll_ctl(_event_fd, EPOLL_CTL_DEL, fd, nullptr);}cb(ret != -1);return ret;
#elif defined(HAS_KQUEUE)int ret = -1;if (_event_map.erase(fd)) {_event_cache_expired.emplace(fd);struct kevent kev[2];int index = 0;EV_SET(&kev[index++], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr);EV_SET(&kev[index++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr);ret = kevent(_event_fd, kev, index, nullptr, 0, nullptr);}cb(ret != -1);return ret;
#elseint ret = -1;if (_event_map.erase(fd)) {_event_cache_expired.emplace(fd);ret = 0;}cb(ret != -1);return ret;
#endif //HAS_EPOLL}// 跨线程操作// 使用 async 函数异步调用 delEvent 函数,将事件删除操作放到合适的线程中执行,并立即返回 0async([this, fd, cb]() mutable {delEvent(fd, std::move(cb));});return 0;
}

EventPoller::delEvent 函数的主要功能是从事件轮询器中删除指定文件描述符(fd)的事件监听,并在操作完成后调用一个完成回调函数(cb)。该函数会根据不同的操作系统平台,采用不同的事件通知机制(如 epoll、kqueue)来删除事件监听,同时会处理回调函数为空以及非当前线程调用的情况。

4.3 修改监听事件类型 modifyEvent

int EventPoller::modifyEvent(int fd, int event, PollCompleteCB cb) {// 时间检查TimeTicker();// 回调函数检查与默认设置if (!cb) {cb = [](bool success) {};}// 当前线程检查// 如果是当前线程:根据不同的操作系统平台选择不同的事件通知机制来修改事件监听。if (isCurrentThread()) {
#if defined(HAS_EPOLL)struct epoll_event ev = { 0 };ev.events = toEpoll(event);ev.data.fd = fd;auto ret = epoll_ctl(_event_fd, EPOLL_CTL_MOD, fd, &ev);cb(ret != -1);return ret;
#elif defined(HAS_KQUEUE)struct kevent kev[2];int index = 0;EV_SET(&kev[index++], fd, EVFILT_READ, event & Event_Read ? EV_ADD | EV_CLEAR : EV_DELETE, 0, 0, nullptr);EV_SET(&kev[index++], fd, EVFILT_WRITE, event & Event_Write ? EV_ADD | EV_CLEAR : EV_DELETE, 0, 0, nullptr);int ret = kevent(_event_fd, kev, index, nullptr, 0, nullptr);cb(ret != -1);return ret;
#elseauto it = _event_map.find(fd);if (it != _event_map.end()) {it->second->event = event;}cb(it != _event_map.end());return it != _event_map.end() ? 0 : -1;
#endif // HAS_EPOLL}// 如果不是当前线程, 发起异步操作:// 使用 async 函数异步调用 modifyEvent 函数,将事件修改操作放到合适的线程中执行。async([this, fd, event, cb]() mutable {modifyEvent(fd, event, std::move(cb));});return 0;
}

EventPoller::modifyEvent 函数的主要功能是修改指定文件描述符(fd)对应的事件监听设置,并在操作完成后调用一个完成回调函数(cb)。该函数会根据不同的操作系统平台,采用不同的事件通知机制(如 epoll、kqueue)来修改事件监听,同时会处理回调函数为空以及非当前线程调用的情况。

五、数据结构

5.1 _event_map

在 EventPoller 类中,有一个重要的数据结构 std::unordered_map<int, std::shared_ptr > _event_map,它用于存储网络事件的相关信息。

键(Key):为文件描述符(int 类型),每个文件描述符对应一个网络套接字。
值(Value):为 std::shared_ptr 类型,它是一个智能指针,指向一个事件处理回调函数。当该文件描述符对应的网络事件发生时,会调用该回调函数进行处理。
通过这个数据结构,EventPoller 能够快速地查找和处理不同文件描述符的网络事件,提高事件处理的效率。

5.2 _event_cache_expired的之作用

在EventPoller 类中,_event_cache_expired完整声明为std::unordered_set _event_cache_expired;

5.2.1 在 delEvent 函数中的作用

int EventPoller::delEvent(int fd, PollCompleteCB cb) {// ...if (_event_map.erase(fd)) {_event_cache_expired.emplace(fd);// ...}// ...
}

标记待删除的文件描述符:当调用 delEvent 函数删除一个文件描述符对应的事件时,如果该文件描述符存在于 _event_map 中(即 _event_map.erase(fd) 返回 true),会将这个文件描述符添加到 _event_cache_expired 集合里。这样做是为了在后续的事件处理循环中标记这个文件描述符已经被删除,以便后续可以跳过对它的处理。

避免误处理已删除的事件:在多线程环境下,删除事件和处理事件可能会并发执行。将待删除的文件描述符添加到 _event_cache_expired 中,可以确保即使在删除操作还未完全完成时,事件处理循环也能识别并跳过这些文件描述符,避免对已删除的事件进行误处理。

5.2.2 在 runLoop 函数中的作用

void EventPoller::runLoop(bool blocked, bool ref_self) {// ..._event_cache_expired.clear();for (int i = 0; i < ret; ++i) {struct epoll_event &ev = events[i];int fd = ev.data.fd;if (_event_cache_expired.count(fd)) {continue;}// ...}// ...
}

清空过期事件缓存:在每次 epoll_wait 调用返回且有事件发生时,首先会调用 _event_cache_expired.clear() 清空集合。这是因为每次新的事件轮询周期开始时,需要重新判断哪些文件描述符是需要跳过的,避免上一轮的过期标记影响当前轮次的事件处理。

过滤已标记的文件描述符:在遍历 epoll_wait 返回的事件数组时,对于每个事件对应的文件描述符 fd,会检查它是否存在于 _event_cache_expired 集合中。如果存在,说明这个文件描述符已经在 delEvent 函数中被标记为待删除或需要跳过,此时会直接跳过对该事件的处理,继续处理下一个事件,从而提高事件处理的效率和准确性。

5.2.3 总结

_event_cache_expired 集合在整个事件处理机制中起到了一个中间标记的作用,它在 delEvent 函数中标记需要删除或跳过的文件描述符,在 runLoop 函数中过滤这些标记过的文件描述符,避免重复处理,确保事件处理的正确性和高效性,尤其在多线程环境下可以有效避免并发操作带来的问题。


总结

本文深入分析了开源 zlmediakit 中针对网络套接字的事件监听和事件循环驱动机制。通过对 EventPoller 类和其基类的详细剖析,我们了解了其核心函数 runLoop 的工作原理,以及相关函数和数据结构的作用。在 Linux 系统下,epoll 机制的使用使得网络事件的处理更加高效和灵活。同时,AddEvent()、ModifyEvent() 和 delEvent() 函数提供了对网络事件监听的添加、修改和删除操作,方便开发者根据业务需求进行动态调整。_event_map 数据结构则为网络事件的管理提供了有效的支持。_event_cache_expired 作为 std::unordered_set 类型变量,在事件处理流程中起关键中间标记作用,在 delEvent 函数里标记待删除文件描述符以避免多线程下误处理已删事件,在 runLoop 函数中先清空集合再过滤标记过的描述符来提升事件处理效率与准确性。

通过对这些机制的理解,开发者可以更好地利用 zlmediakit 进行网络编程,实现高效、稳定的网络通信。后续我们还将对异步事件和延时任务进行专门的分析,敬请期待。

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

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

相关文章

【分布式】分布式限流方案解析

文章目录 固定窗口限流方案​实现方式​优点​缺点​ 滑动窗口限流方案​实现方式​优点​缺点​ 令牌桶限流方案​实现方式​优点​缺点​ 漏斗限流方案​实现方式​优点​缺点​ 在分布式系统蓬勃发展的当下&#xff0c;系统面临的流量挑战日益复杂。为确保系统在高并发场景下…

WPS JS宏编程教程(从基础到进阶)-- 第三部分:JS宏编程语言开发基础

第三部分:JS宏编程语言开发基础 @[TOC](第三部分:JS宏编程语言开发基础)**第三部分:JS宏编程语言开发基础**1. 变量与数据类型**变量声明:三种方式****示例代码****数据类型判断****实战:动态处理单元格类型**2. 运算符全解析**算术运算符****易错点:字符串拼接 vs 数值相…

Python - 爬虫-网页抓取数据-库urllib

urllib库是Python内置的HTTP请求库。无需额外安装&#xff0c;可以直接使用。urllib库包含以下四个模块。 urllib.request - 打开和读取 URL。urllib.error - 包含 urllib.request 抛出的异常。urllib.parse - 解析 URL。urllib.robotparser - 解析 robots.txt 文件。 1、reque…

C++进阶知识复习 1~15

C 进阶总复习 &#xff08;1~15&#xff09; 目的1. 介绍下程序从编写到可执行的整个过程2. C中的auto和decltype的区别3. 介绍下多态的实现原理4. C中的new[] 和delete[] 为什么一定要配对使用&#xff1f;5. C中malloc申请的内存 可以使用delete释放嘛6. 什么情况下会出现内存…

输电线路航空标志球:低空飞行的安全路标 / 恒峰智慧科技

在现代社会&#xff0c;随着航空业的快速发展&#xff0c;低空飞行活动日益频繁。为了确保飞行安全&#xff0c;避免飞机与高压电线等障碍物发生碰撞&#xff0c;输电线路航空标志球应运而生。这种装置被广泛应用于高压输电线路上&#xff0c;尤其是超高压和跨江输电线&#xf…

Debian/Ubuntu的networking的`/etc/network/interfaces`配置文件详解

Debian/Ubuntu的networking的/etc/network/interfaces配置文件详解 Debian/Ubuntu 的 /etc/network/interfaces 配置文件详解 在 Debian/Ubuntu 系统中&#xff0c;/etc/network/interfaces 是传统网络接口配置文件&#xff0c;用于定义网络接口的静态/动态配置。以下是逐项解…

OpenCV 图形API(或称G-API)(1)

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 引言 OpenCV 图形API&#xff08;或称G-API&#xff09;是一个新的OpenCV模块&#xff0c;旨在使常规图像处理更快且更便携。通过引入一种新的基于图的执行…

Leetcode 3505. Minimum Operations to Make Elements Within K Subarrays Equal

Leetcode 3505. Minimum Operations to Make Elements Within K Subarrays Equal 1. 解题思路2. 代码实现 题目链接&#xff1a;3505. Minimum Operations to Make Elements Within K Subarrays Equal 1. 解题思路 这一题大的思路上不难想到就是一个动态规划的思路。我们分别…

win10之mysql server 8.0.41安装

一 mysql server 下载 官网下载地址页面 https://dev.mysql.com/downloads/mysql/二 免装版使用步骤 1 解压 下载完成后,解压文件夹,如下所示: 2 执行安装命令 D:\soft\mysql\mysql-8.0.41-winx64\mysql-8.0.41-winx64\bin>mysqld --install Service successfully in…

第十二届蓝桥杯省赛软件类(cc++组)

第一题&#xff08;空间&#xff09; 解题思路 答案 #include <stdio.h>int main() {// 计算256MB对应的字节数&#xff0c;1MB 1024KB&#xff0c;1KB 1024Blong long total_bytes 256 * 1024 * 1024; // 每个32位二进制整数占4个字节&#xff08;32 / 8 4&#xf…

C++ 新特性 | C++ 11 | 移动语义

文章目录 一、移动语义1、为什么需要移动语义&#xff1f;2、怎么“偷”&#xff1f;——右值引用&#xff08;&&&#xff09;3、如何实现移动语义&#xff1f;——移动构造函数/赋值4、什么时候触发移动&#xff1f;5、移动 vs 拷贝 一、移动语义 1、为什么需要移动语…

wsl下ubuntu安装宝塔

在 WSL (Windows Subsystem for Linux) 下的 Ubuntu 中安装宝塔面板的步骤如下&#xff1a; 1. 确保 WSL 环境正常 已安装 WSL 2 并启用 Ubuntu 发行版&#xff08;推荐 Ubuntu 20.04/22.04&#xff09;。 在 PowerShell 中检查 WSL 版本&#xff1a; wsl --list --verbose 如…

UDP网络通信

UDP网络通信&#xff1a; 步骤1 创建套接字&#xff1a; #include <sys/types.h> #include <sys/socket.h>int socket(int domain, int type, int protocol);参数一 domain&#xff1a; AF_UNIX Local communication unix(7) 本地通信 AF_INET IPv4 Inte…

教你快速理解linux中的NUMA节点探测是干什么用的?

想象一个大城市被划分成几个区&#xff08;比如东区、西区&#xff09;。每个区有自己的超市&#xff08;内存&#xff09;&#xff0c;居民&#xff08;CPU&#xff09;去本区的超市买东西最快&#xff0c;去其他区的超市会慢一些。 NUMA节点探测&#xff0c;就是Linux系统在…

使用 Less 实现 PC 和移动端样式适配

&#x1f310; 使用 Less 实现 PC 和移动端样式适配 —— 以 position 属性为例 在前端开发中&#xff0c;我们常常会遇到这样一个场景&#xff1a; 在 PC 页面中需要某个元素是 position: relative;&#xff0c;但在移动端却希望它是 position: inherit;&#xff0c;以便更灵…

企业战略管理(设计与工程师类)-2-战略规划及管理过程-1-概述

战略管理过程 参考资料&#xff1a; 战略管理 - 清华大学- 蔡临宁公司战略与风险管理 - 华中科技大学 - 贺远琼战略管理 - 北京理工大学 - 杨万荣DeepSeek - 深度思考与联网检索 AFI框架 战略管理最典型的就是采用传统的AFI通用战略管理框架&#xff08;模型&#xff09;&a…

Swoole 的 Hyperf 框架和 Go 的 Gin 框架高并发原理以及技术实现对比分析

Swoole 的 Hyperf 框架和 Go 的 Gin 框架虽然都支持高并发&#xff0c;但它们的实现原理、底层机制和适用场景有显著差异。以下从 高并发原理、技术实现区别、优缺点 三个方面详细分析&#xff1a; 一、高并发实现原理 1. Hyperf (PHP Swoole) Hyperf 的高并发能力基于 Swoo…

【教程】如何利用bbbrisk一步一步实现评分卡

利用bbbrisk一步一步实现评分卡 一、什么是评分卡1.1.什么是评分卡1.2.评分卡有哪些 二、评分卡怎么弄出来的2.1.如何制作评分卡2.2.制作评分卡的流程 三、变量的分箱3.1.数据介绍3.2.变量自动分箱3.3.变量的筛选 四、构建评分卡4.1.评分卡实现代码4.2.评分卡表4.3.阈值表与分数…

AI日报 - 2025年4月2日

&#x1f31f; 今日概览&#xff08;60秒速览&#xff09; ▎&#x1f916; AGI突破 | 研究揭示零RL训练可诱发模型顿悟&#xff0c;Anthropic发布Claude 3.5内部机制研究&#xff0c;简化语言模型推理优化新方法提出。 DeepSeek-R1无需额外指令即可深度推理&#xff1b;Anthro…

探索 Kubernetes 网络穿透:如何从外部访问 K8s Pod 地址

文章目录 探索 Kubernetes 网络穿透&#xff1a;如何从外部访问 K8s Pod 地址为什么需要外部访问 Pod 地址&#xff1f;常见的网络穿透方案NodePortLoadBalancerIngressPort-ForwardHostNetworkkt-connect&#xff1a;为开发调试提供便捷穿透 实践建议与注意事项各方案对比表总…