第3篇 WFNetworkTask 源码解析如果说SeriesWork解决的是“任务怎么串起来”那么WFNetworkTask解决的就是“一个网络任务到底长什么样”。HTTP、Redis、MySQL、DNS 这些看起来完全不同的协议任务在 Workflow 里最后都被统一成了同一个模板基类templateclassREQ,classRESPclassWFNetworkTask:publicCommRequest这一篇我们就沿着这条线往下看理解 Workflow 是怎样把“请求/响应对象、连接、超时、回调、客户端/服务端差异”统一塞进一个网络任务里的。一、WFNetworkTask 的定位面向协议站在通信框架之上先看它的继承关系WFNetworkTaskREQ,RESP-CommRequest-SubTaskCommSession这条继承链很重要SubTask说明它能被 Workflow 的工作流调度CommSession说明它能被通信框架Communicator驱动CommRequest说明它具备“请求一个通信目标”的能力这意味着WFNetworkTask正好卡在两个世界中间对上暴露给业务的是协议化请求/响应对下接入的是连接、socket、poller、超时控制所以可以把它理解成Workflow 的网络任务适配层。二、REQ / RESP 模板参数到底解决了什么以 HTTP 为例usingWFHttpTaskWFNetworkTaskprotocol::HttpRequest,protocol::HttpResponse;以 MySQL 为例usingWFMySQLTaskWFNetworkTaskprotocol::MySQLRequest,protocol::MySQLResponse;这样设计有两个直接好处1. 协议对象天然强类型用户在 callback 里拿到的get_req()/get_resp()是明确的协议对象不需要自己做类型转换。2. 调度逻辑和协议逻辑解耦网络任务的调度模型可以完全复用而协议差异只体现在message_out()message_in()具体的REQ/RESP编解码这就是 Workflow “协议丰富但内核统一”的一个典型体现。三、WFNetworkTask 持有了哪些语义WFNetworkTask自身并不大但语义非常全REQ req;RESP resp;std::functionvoid(WFNetworkTaskREQ,RESP*)prepare;std::functionvoid(WFNetworkTaskREQ,RESP*)callback;intsend_timeo;intreceive_timeo;intkeep_alive_timeo;intwatch_timeo;void*user_data;intstate;interror;inttimeout_reason;从这些字段能看出它同时承担了协议数据载体req/resp用户扩展入口prepare/callback/user_data传输策略发送超时、接收超时、keep-alive、watch timeout结果状态state/error/timeout_reason也就是说一个WFNetworkTask本身就足以描述“一次网络交互”的完整语义。四、为什么它的 start() 也要先进 Series客户端任务的start()依旧是voidstart(){assert(!series_of(this));Workflow::start_series_work(this,nullptr);}这说明网络任务在 Workflow 里没有特殊地位。它和计算任务、定时器任务一样依然只是工作流中的一个节点。这种统一性非常重要因为它意味着一个 HTTP 请求后面可以无缝接一个定时器一个 MySQL 请求后面可以无缝接一个线程计算任务所有任务共享同样的串并联语义这就是 Workflow 名字里 “work flow” 的真正含义。五、CommRequest把网络任务变成可投递请求WFNetworkTask的直接父类CommRequest非常关键virtualvoiddispatch(){if(this-scheduler-request(this,this-object,this-wait_timeout,this-target)0){this-handle(CS_STATE_ERROR,errno);}}这里做的事其实很纯粹向CommScheduler申请一个目标CommTarget让Communicator去建立/复用连接并驱动收发如果投递失败立刻进入错误回调路径这层设计非常巧妙因为WFNetworkTask不直接操作 socket它也不关心连接是否复用它只知道“我需要一个可通信目标把我送进去”这让网络任务保持了很高的抽象层级。六、prepare 和 callback 分别是什么角色很多人第一次看 Workflow 会注意到 callback但忽略了prepare。callback这个大家熟悉表示任务结束后执行业务逻辑if(this-callback)this-callback(this);prepare它在真正发送前调用if(this-prepare)this-prepare(this);returnthis-req;这意味着你可以在连接真正建立之后再最后一次修正请求。这在实际工程里非常有价值比如根据最终连接信息补充 header做 trace 注入做签名做按连接态决定的请求修正所以prepare不是 callback 的替代品而是网络任务发送前的最后一个可编程钩子。七、客户端任务和服务端任务其实是两种不同生命形态Workflow 在WFTask.inl里把网络任务拆成了两个分支WFClientTaskWFServerTask它们都基于WFNetworkTask但生命周期差异非常大。1. 客户端任务一次请求一次回调一次销毁客户端done()非常直接if(this-callback)this-callback(this);deletethis;returnseries-pop();这是典型的一次性交互模型。2. 服务端任务先处理请求再决定是否回包服务端任务更复杂它内部甚至有一个Processor子任务classProcessor:publicSubTask{virtualvoiddispatch(){this-process(this-task);this-taskNULL;this-subtask_done();}};服务端的处理流程不是“收到包立刻 done”而是收到请求构造一个专属 series执行用户 process再决定 reply / shutdown / noreply这让服务端任务也能自然接入 Workflow 的任务流模型而不是特判成另一套框架。八、超时为什么分成这么多种WFNetworkTask提供四类时间控制set_send_timeout(inttimeout);set_receive_timeout(inttimeout);set_keep_alive(inttimeout);set_watch_timeout(inttimeout);这背后反映的是 Workflow 对网络过程的细粒度建模。send timeout发送阶段超时。receive timeout接收阶段超时。keep alive timeout连接在空闲池里可复用多久。watch timeout首次等待阶段超时常用于更复杂的场景。再配合timeout_reasonTOR_WAIT_TIMEOUT TOR_CONNECT_TIMEOUT TOR_TRANSMIT_TIMEOUT你会发现 Workflow 不满足于“超时了”这一级别而是想告诉你它究竟是等资源超时、建连超时还是传输超时。这对于线上诊断非常有价值。九、为什么 get_connection() 这么重要WFNetworkTask提供virtualWFConnection*get_connection()const0;这不是装饰性接口它非常重要因为它把“请求语义”延伸到了“连接语义”。有了它用户或任务实现可以取连接上下文做长连接事务按连接状态做特殊逻辑以 MySQL 为例事务型任务就会利用固定连接语义来保证会话一致性。所以Workflow 里的任务不是只面对消息包它也能感知连接这一层。十、复杂客户端任务真正的威力在WFComplexClientTask真正有意思的不是WFClientTask而是它上面的WFComplexClientTask。这是 Workflow 客户端协议任务的通用骨架它额外处理了URI 初始化默认端口补全RouteManager 路由DNS 解析重定向重试固定地址 / 固定连接比如这里的init_with_uri()就已经把 URI 校验、端口补全和初始化逻辑串起来了。更重要的是复杂客户端任务通过覆写下面几个函数就能拼出不同协议的行为virtualboolinit_success();virtualvoidinit_failed();virtualboolcheck_request();virtualWFRouterTask*route();virtualboolfinish_once();这相当于给不同协议提供了一套统一的“扩展点模板”。所以 HTTP、MySQL、Redis 等任务虽然行为差异很大但它们的骨架极其相似。十一、网络任务的完成不只是“收到响应”在 Workflow 里一个网络任务完成并不只是意味着“socket 收到数据”。它至少包括这些阶段选择目标建立或复用连接发送请求接收并解析响应处理错误/重试/重定向进入 callback回到 series 继续工作流这也是为什么 Workflow 的网络任务值得被当成“一等公民”去设计而不是一个简单的 future 或 callback wrapper。十二、从 WFNetworkTask 里能看出的设计美感我个人觉得WFNetworkTask最漂亮的地方在于它完成了三件事的统一1. 协议统一HTTP、MySQL、Redis、DNS 统一成REQ/RESP模板。2. 调度统一所有网络任务统一走CommRequest - CommScheduler - Communicator。3. 工作流统一所有网络任务最终还是SubTask可以进入SeriesWork。这让整个框架没有“协议库”和“工作流库”的断层而是天然一体化。十三、小结读完WFNetworkTask之后你应该建立这样一个心智模型它不是一个简单的“请求对象”它是一个可被工作流调度的通信会话它站在协议层与通信层之间它把请求/响应、连接、超时、回调统一打包成了一个任务对象用一句更凝练的话说WFNetworkTask是 Workflow 里“协议化异步通信”的统一载体。