ZLMediaKit 服务启动(1)

4 阅读11分钟

下面我按**“你贴出来的这篇博客 + 你两张类图” ,用初学者能跟着源码跑通的方式,把服务启动**这一章的知识讲透。你可以把它当成一张“从 main() 到 RtspSession::onRecv() 的全链路地图”。


0. 先用两张类图建立脑内模型

类图1(线程/事件循环/Server/TcpServer)

你要记住这条继承/组合链:

  • TaskExecutorInterface:定义 async/sync 这类“把任务丢到某线程执行”的接口
  • TaskExecutor:实现了负载统计(靠 ThreadLoadCounter
  • EventPoller:一个 Reactor(一个线程 + 一个 epoll_wait 循环 + 任务队列)
  • TaskExecutorGetterImp / EventPollerPool:管理多个 EventPoller,并按负载挑“最空闲的 poller”
  • Server:只有一个核心成员 EventPoller::Ptr _poller(服务在哪个 poller 线程上运行)
  • TcpServer:真正的 TCP 监听/accept + 创建 Session + 连接分发

一句话:EventPollerPool 提供多条 IO 线程;TcpServer 把连接分摊到这些线程里。

类图2(Socket)

你要记住 Socket 体系的三点:

  • Socket 里最关键的是三类回调:

    • _on_before_accept:accept 前“先创建 peer Socket”(用于负载均衡挑 poller)
    • _on_accept:accept 成功后“通知上层创建 Session”
    • _on_read:读到数据后“把数据上抛给 Session::onRecv”
  • SockFD:封装 fd + sock 类型 + poller 指针

  • Socket::attachEvent() :把 peer fd 加进 poller 的 epoll,并开始触发 onRead/onWrite/onError


1. 服务启动全流程(按你博客的“服务创建流程”逐步解释)

Step 1:main.cpp 创建 TcpServer 并 start()

auto rtspSrv = std::make_shared<TcpServer>();
rtspSrv->start<RtspSession>(rtspPort);

关键点:start<SessionType> 决定了 accept 后创建什么 Session。
这里就是 RtspSession


Step 2:TcpServer 构造 -> 顺带把 EventPollerPool 初始化了

TcpServer 构造会先构造父类 Server(poller)

Server::Server(EventPoller::Ptr poller) {
  _poller = poller ? poller : EventPollerPool::Instance().getPoller();
}

这意味着:

  • 你没传 poller 时,第一次用到 EventPollerPool::Instance() 就会触发线程池初始化
  • pool 会创建 N 个 EventPoller(N≈CPU核数,或命令行 -t 指定)

Step 3:EventPollerPool::addPoller() 做了什么?

它会循环创建 EventPoller("event poller i"),并为每个 poller 启一个线程跑 runLoop()

核心理解:

  • 每个 EventPoller 对应一个线程
  • 每个 EventPoller 在构造时创建 epoll fd,并把内部 pipe 的读端注册进 epoll(用于“唤醒线程执行任务”)
  • async() 的实现本质是:把 task 放进队列 -> 往 pipe 写 1 字节 -> epoll_wait 被唤醒 -> 执行 task

这就是为什么 ZLToolKit 的“跨线程投递任务”很稳:不用锁住 epoll_wait,直接 pipe 唤醒。


Step 4:TcpServer::setOnCreateSocket(nullptr) 注册默认“造 Socket”的工厂

构造 TcpServer 时会做:

_on_create_socket = [](const EventPoller::Ptr &poller) {
  return Socket::createSocket(poller, false);
};

意义很大:以后 TcpServer 要造任何 Socket(监听 socket / peer socket),都走这个回调。

注意 enable_mutex=false:设计目标是尽量让 socket 逻辑在所属 poller 单线程里跑,减少锁开销(高性能)。


Step 5:TcpServer::start() 注册 _session_alloc(Session 工厂)

start<SessionType> 的关键是这段:

  • auto session = std::make_shared<SessionType>(sock);
  • session->setOnCreateSocket(server->_on_create_socket);
  • return std::make_shared<SessionHelper>(server, session);

这里你要抓住三件事:

  1. accept 之后,TcpServer 会用 _session_alloc 把 Socket 变成 Session(例如 RtspSession)

  2. Session 继承自 SocketHelper,所以它也有 _on_create_socket

    • 它被设置成 TcpServer 的 _on_create_socket
    • 以后 Session 内部如果需要再创建 socket(比如主动连、代理等),会用这套工厂
  3. SessionHelper 会把 session 放进全局 SessionMap,并且 TcpServer 也会把 helper 放进自己的 _session_map 做生命周期管理

记忆法: _session_alloc = “连接进来后,我应该创建什么会话对象”。


Step 6:TcpServer::start_l() -> setupEvent() 创建“监听 Socket”并挂回调

start_l()第一步就是 setupEvent()

6.1 创建监听 socket:绑定的是 Server::_poller
_socket = createSocket(_poller);

所以监听 socket 默认属于“创建这个 TcpServer 时拿到的 poller”。

6.2 挂两个回调(类图2 的核心)
  • setOnBeforeAccept(...) -> 设置 Socket::_on_before_accept
  • setOnAccept(...) -> 设置 Socket::_on_accept

这两个回调就是 accept 链路的分水岭


2. 连接进来后:从 listen fd 到 RtspSession::onRecv()

Step 7:listen() 会把监听 fd 加入 epoll,并把回调设为 Socket::onAccept()

Socket::listen(sock) 内部会调用:

_poller->addEvent(listen_fd, READ|ERROR, [this]{ onAccept(...); });

所以:epoll 触发 listen_fd 可读 = 有新连接 = 调 Socket::onAccept()


Step 8:Socket::onAccept() 的核心逻辑(你贴的代码非常关键)

它做的事情可以浓缩成 4 步:

8.1 accept() 拿到客户端 fd
fd = accept(listenfd, nullptr, nullptr);
8.2 创建 peer_sock(先负载均衡,失败再保底)

优先走:

peer_sock = _on_before_accept(_poller);

而你前面注册的 _on_before_accept 实际调用:

createSocket(EventPollerPool::Instance().getPoller(false));

也就是说:peer socket 会绑定“当前最空闲的 poller”

如果失败(一般不走)才保底:

peer_sock = Socket::createSocket(_poller, false); // 用父 poller

这就是你博客一直强调的:负载均衡分配 poller

8.3 设置 peer fd(同时拿到对端 ip/port)

peer_sock->setPeerSock(fd) 会把 fd 封装成 SockFD 并挂到 peer_sock 上。

8.4 先触发 _on_accept,再 attachEvent(这个顺序很讲究)

代码里用 shared_ptr<void> completed 的析构来保证顺序:

  • _on_accept(peer_sock, completed):让上层创建 Session、设置 onRead/onErr
  • _on_accept 完成后,completed 释放触发析构:执行 attachEvent(peer_sock_fd)
  • attachEvent 才把 peer fd 加到 epoll,并开始收数据

目的:避免“还没设置 onRead 就收到数据事件”


Step 9:TcpServer::setupEvent() 里 _on_accept 做了啥?

你贴的逻辑很关键:

  1. auto ptr = sock->getPoller().get();

  2. auto server = strong_self->getServer(ptr);

    • 这一步会根据 poller 找到对应的 clone 出来的 TcpServer
  3. ptr->async([server]{ server->onAcceptConnection(sock); });

    • 把真正的 session 创建动作,投递到 peer_sock 所属的 poller 线程执行

这一步解决了一个很关键的问题: “连接归哪个 poller,就在哪个线程里创建 session 和管理 session_map”。


Step 10:TcpServer::onAcceptConnection() -> 创建 Session + 绑定 onRead

onAcceptConnection 里两件事最重要:

  1. _session_alloc(server, sock) 创建 RtspSession + SessionHelper

  2. 设置 sock->setOnRead(...),回调里调用:

    • strong_session->onRecv(buf);
      最终就是 RtspSession::onRecv()

所以完整链路是:

epoll(监听fd) -> Socket::onAccept() -> (负载均衡创建 peer socket) -> TcpServer::onAcceptConnection() -> sock->setOnRead -> RtspSession::onRecv


3. 为什么要 “clone 服务” 这一套?(你博客第10步)

start_l() 后半段会:

  • 遍历 pool 里的所有 poller
  • 给每个 poller 创建一个 TcpServer(onCreatServer(poller)
  • cloneFrom(*this):在那个 poller 上重新 setupEvent,然后把监听 socket “clone”过去并 listen

你可以把它理解成:

  • 每个 poller 上都有一个“同款 TcpServer”实例
  • 这些 clone server 共享同一个 listen fd(底层通过 SockFD/SockNum 的复制共享)
  • 然后每个 poller 都对 listen fd 执行 addEvent,这样多个线程都能等待 accept 事件

这就是经典的多线程 accept 模型(代码里 addEvent 使用了 epoll 的 EPOLLEXCLUSIVE,也是为了解决惊群/争抢问题)。


4. 初学者最容易混淆的 3 个点(我帮你拎出来)

混淆点 1:TcpServer::_on_create_socket vs Socket::_on_before_accept vs SocketHelper::_on_create_socket

  • TcpServer::_on_create_socket:TcpServer 的“造 Socket 工厂”(绑定 poller / 关 mutex)
  • Socket::_on_before_accept:监听 socket 在 accept 前调用,用来“挑空闲 poller 创建 peer socket”
  • SocketHelper::_on_create_socket:挂在 Session 上的“造 Socket 工厂”,一般用于 Session 内部需要再建连接时(默认继承父 poller,不做负载均衡逻辑)

混淆点 2:为什么 onAccept 里又 async 一次?

因为 accept 事件发生在“某个 poller 的监听 socket”上,但 peer socket 可能被分配到了另一个 poller。
所以必须把 onAcceptConnection() 投递到 peer socket 的 poller 线程执行,才能保证线程一致性。

混淆点 3:Timer(2s) 和 _is_on_manager 是干嘛的?

TcpServer 会定时 onManagerSession() 遍历 session 做维护。
遍历过程中如果同时发生 onErr 导致删除 session,会有迭代器失效风险,所以用 _is_on_manager + async erase 规避。


5. 你照着做就能“真正掌握”的 3 个练习

  1. 确认连接负载均衡是否生效
    TcpServer::onBeforeAcceptConnection() 打断点,观察 getPoller(false) 每次返回的 poller 名称(event poller i)。
  2. 确认“先 onAccept 再 attachEvent”的顺序
    Socket::onAccept()_on_accept(...)attachEvent(...) 各打一个 log,你会看到 attachEvent 总是后发生。
  3. 确认数据最终进入 RtspSession::onRecv()
    TcpServer::onAcceptConnection()sock->setOnRead(...) 回调里打断点,然后跟到 RtspSession::onRecv()

如果你想继续下一篇《推流创建》,你只要告诉我:你更关心 RTSP 推流(ANNOUNCE/RECORD) 还是 RTMP 推流(publish) ,我就按同样方式把“从 onRecv 解析协议 -> 创建 MediaSource -> 注册到全局 -> 开始收 RTP/FLV”这条链路教给你。 下面给你一份**“ZLMediaKit / ZLToolKit 服务启动(以 RTSP TcpServer 为例)”的详细流程**,从 main() 一路到 accept 新连接、创建 RtspSession、读到数据进入 RtspSession::onRecv() 。你可以把它当成读源码的路线图。


1) main.cpp:创建并启动 TcpServer

  1. 读取配置拿到 rtspPort
  2. auto rtspSrv = std::make_shared<TcpServer>();
  3. rtspSrv->start<RtspSession>(rtspPort);

关键:模板参数 RtspSession 决定 accept 后创建的 Session 类型。


2) 构造 TcpServer:同时构造父类 Server,拿到主 poller

  1. TcpServer::TcpServer(poller=nullptr) : Server(poller) { setOnCreateSocket(nullptr); }

  2. Server::Server(poller)

    • 如果传入 poller 为空:

      • EventPollerPool::Instance().getPoller() 取一个 poller
      • 第一次调用 Instance() 会触发 EventPollerPool 初始化(创建线程池)
    • 得到 Server::_poller


3) EventPollerPool 初始化:创建 N 个 EventPoller 线程(epoll线程)

  1. EventPollerPool::EventPollerPool() -> addPoller("event poller", size, ...)

  2. TaskExecutorGetterImp::addPoller(...)

    • size = (传入size>0)?size:hardware_concurrency()

    • 循环 i=0..size-1:

      1. new EventPoller("event poller i")
      2. poller->runLoop(false, register_thread) 创建线程运行 epoll loop
      3. poller->async(...) 设置线程名/CPU亲和性(可选)
      4. 把 poller 放入 _threads

4) EventPoller 构造:创建 epoll + 内部管道事件

  1. EventPoller::EventPoller(...)

    • 创建 _epoll_fd = epoll_create(...)
    • 建内部 pipe(用于唤醒 poller 执行 async 任务)
    • addEvent(pipe.readFD, READ, onPipeEvent) 注册 pipe 读事件

5) TcpServer 注册 “创建 Socket 的工厂回调” _on_create_socket

  1. TcpServer::setOnCreateSocket(nullptr)

    • cb 为空时设置默认:

      • _on_create_socket = [](poller){ return Socket::createSocket(poller, false); }
    • 并同步到已 clone 的 server(若已有)

这个回调未来会用于:创建监听 Socket、创建客户端 peer Socket。


6) TcpServer::start:注册 Session 工厂 _session_alloc

  1. TcpServer::start<RtspSession>(port, host, backlog)

    • 注册 _session_alloc = [](server, sock){ ... }

      1. auto session = std::make_shared<RtspSession>(sock)
      2. session->setOnCreateSocket(server->_on_create_socket)(Session继承SocketHelper)
      3. return std::make_shared<SessionHelper>(server, session)
    • 调用 start_l(port, host, backlog)


7) start_l:创建监听 socket、listen、定时管理、clone 多个 server

  1. TcpServer::start_l(...) 做 4 件大事:

7.1 setupEvent:创建监听 Socket + 注册 accept 相关回调

  1. setupEvent()
  1. _socket = createSocket(_poller)

    • createSocket(poller) 其实就是 _on_create_socket(poller)
    • 所以监听 socket 绑定 Server::_poller
  2. 注册 onBeforeAccept(给 Socket::_on_before_accept):

    • 回调调用 TcpServer::onBeforeAcceptConnection(poller)
  3. 注册 onAccept(给 Socket::_on_accept):

    • 回调里根据 sock->getPoller() 找到对应的 clone server
    • ptr->async([server]{ server->onAcceptConnection(sock); })

7.2 listen:启动监听,并把 listen fd 加入 epoll

  1. _socket->listen(port, host, backlog)
  • 底层 SockUtil::listen() 创建 listen fd 并 bind/listen

  • Socket::listen(SockFD)

    • _poller->addEvent(listen_fd, READ|ERROR, 回调)
    • 回调里调用 Socket::onAccept(...)

7.3 Timer:每 2 秒管理 Session

  1. 创建 _timer 定期执行 onManagerSession()(遍历 session 做维护)

7.4 clone:为每个 poller 创建一个 TcpServer(多线程监听/accept)

  1. EventPollerPool::Instance().for_each(executor)
  • 对每个 poller(除当前 _poller):

    1. 若不存在:serverRef = std::make_shared<TcpServer>(poller)
    2. serverRef->cloneFrom(*this)
  1. cloneFrom(that)
  • setupEvent():在这个 poller 上创建自己的 _socket 并注册回调

  • _socket->cloneFromListenSocket(*(that._socket))

    • 复制 listen SockFD,并在本 poller 上执行 listen(sock)addEvent(listen_fd, ...)
  • 设置 timer + 复制配置 + 记录 parent

效果:每个 poller 线程都对同一个 listen fd 做 epoll 监听,accept 事件可分摊到多线程。


8) 客户端连入时:accept → 分配 poller → 创建 RtspSession → 读数据进 onRecv

8.1 监听 fd 可读:触发 Socket::onAccept()

  1. 某个 poller 的 epoll_wait 返回 listen_fd READ
  2. 执行 Socket::onAccept(listenSockFD, event)

8.2 accept() 拿到客户端 fd

  1. fd = accept(listen_fd, ...)
  2. 配置 fd(nonblock、nodelay、buf等)

8.3 创建 peer Socket(优先负载均衡)

  1. 优先走 _on_before_accept(_poller)
  • 实际执行 TcpServer::onBeforeAcceptConnection(...)
  • return createSocket(EventPollerPool::Instance().getPoller(false));
  • getPoller(false):按负载挑最空闲 poller(不优先当前线程)
  1. 如果失败才保底:
  • peer_sock = Socket::createSocket(_poller, false)(继承父 poller,无负载均衡)

8.4 peer_sock 绑定 fd + 回调顺序保证

  1. peer_sock_fd = peer_sock->setPeerSock(fd)(同时拿到对端 ip/port)

  2. 构造 completed(析构时会执行 peer_sock->attachEvent(peer_sock_fd)

  3. 调用 _on_accept(peer_sock, completed)

    • 注意:先 onAccept 再 attachEvent,避免没设 onRead 就收数据

9) TcpServer 的 _on_accept:把连接派发到 peer_sock 所属 poller 线程

  1. _on_accept(在 TcpServer::setupEvent() 注册):
  • auto ptr = peer_sock->getPoller().get()
  • auto server = strong_self->getServer(ptr)(找到对应 poller 的 clone server)
  • ptr->async([server]{ server->onAcceptConnection(peer_sock); })

10) onAcceptConnection:创建 Session + 注册 onRead/onErr

  1. helper = _session_alloc(server, peer_sock)
  • 创建 RtspSession(peer_sock)
  • session 继承 SocketHelper,因此继承/设置 _on_create_socket
  • 创建 SessionHelper(server, session),加入全局 SessionMap
  1. server->_session_map.emplace(helper.get(), helper)(server 侧 map 管理)
  2. peer_sock->setOnRead(...)
  • 回调里 session->onRecv(buf)RtspSession::onRecv(buf)
  1. peer_sock->setOnErr(...)
  • 出错时调用 session->onError(err) 并从 _session_map 移除

11) attachEvent:把 peer fd 注册到 poller epoll,开始收发数据

  1. _on_accept 返回后,completed 析构触发:
  • peer_sock->attachEvent(peer_sock_fd)
  1. attachEvent
  • _read_buffer = _poller->getSharedBuffer()
  • _poller->addEvent(peer_fd, READ|WRITE|ERROR, ...)
  1. 之后 peer_fd READ:
  • Socket::onRead() -> recvfrom() -> _on_read(buffer, ...)
  1. _on_read 就是你在 onAcceptConnection 里 setOnRead 注册的回调:
  • 最终进入 RtspSession::onRecv()

你要记住的“一句话版本”

启动阶段: main -> TcpServer -> EventPollerPool(N个poller线程) -> start 注册 session 工厂 -> setupEvent 创建监听socket并挂回调 -> listen 加入 epoll -> clone 到每个 poller
连接阶段: epoll触发 listen_fd -> accept -> onBeforeAccept 按负载选 poller 创建 peer_sock -> onAccept 把连接派发到 peer poller -> onAcceptConnection 创建 RtspSession 并 setOnRead -> attachEvent 注册 peer_fd -> onRead -> RtspSession::onRecv