1、简介

Workflow 是新一代基于任务流模型的C++异步调度编程范式,性能优异、生态丰富、通讯计算融为一体,解决了C++下异步开发的多个难点,目前已经是搜狗公司级C++标准,应用于搜狗大多数C++后端服务。
搜狗公司 C++ 服务器引擎,支撑搜狗几乎所有后端 C++ 在线服务,包括所有搜索服务,云输入法,在线广告等,每日处理超百亿请求。这是一个设计轻盈优雅的企业级程序引擎,可以满足大多数 C++ 后端开发需求。
主要特点:
简单易上手,无依赖(适合初学者)
性能和稳定性优异
丰富的通用协议实现:HTTP、Redis、MySQL、Kafka、WebSocket等
统一计算、网络、文件IO等异步资源
任务流管理(串行、并行、DAG)
一致的解决方案形成一套完备的编程范式
大纲
workflow - 图1

2、功能

  • 作为万能异步客户端。目前支持http,redis,mysql和kafka协议。
    • 轻松构建效率极高的spider。
  • 实现自定义协议client/server,构建自己的RPC系统。
    • srpc就是以它为基础,作为独立项目开源。支持srpc,brpc,trpc和thrift等协议。
  • 构建异步任务流,支持常用的串并联,也支持更加复杂的DAG结构。
  • 作为并行计算工具使用。除了网络任务,我们也包含计算任务的调度。所有类型的任务都可以放入同一个流中。
  • 在Linux系统下作为文件异步IO工具使用,性能超过任何标准调用。磁盘IO也是一种任务。
  • 实现任何计算与通讯关系非常复杂的高性能高并发的后端服务。
  • 构建微服务系统。
    • 项目内置服务治理与负载均衡等功能。
    • 使用workflow-k8s插件,可将服务治理与kubernetes的自动部署融合。
  • PaaS 架构图

workflow_paas_en.png

3、编译和运行环境

sudo apt-get install libworkflow-dev
或部署运行环境:
sudo apt-get install workflow1
注意Ubuntu Linux只有最新22.04版自带workflow。更推荐用git直接下载最新源代码编译

4、编程范式

程序 = 协议 + 算法 + 任务流

  • 协议
    • 大多数情况下,用户使用的是内置的通用网络协议,例如http,redis或各种rpc。
    • 用户可以方便的自定义网络协议,只需提供序列化和反序列化函数,就可以定义出自己的client/server。
  • 算法
    • 在我们的设计里,算法是与协议对称的概念。
      • 如果说协议的调用是rpc,算法的调用就是一次apc(Async Procedure Call)。
    • 我们提供了一些通用算法,例如sort,merge,psort,reduce,可以直接使用。
    • 与自定义协议相比,自定义算法的使用要常见得多。任何一次边界清晰的复杂计算,都应该包装成算法。
  • 任务流
    • 任务流就是实际的业务逻辑,就是把开发好的协议与算法放在流程图里使用起来。
    • 典型的任务流是一个闭合的串并联图。复杂的业务逻辑,可能是一个非闭合的DAG。
    • 任务流图可以直接构建,也可以根据每一步的结果动态生成。所有任务都是异步执行的。

结构化并发与任务隐藏

  • 我们系统中包含五种基础任务:通讯,计算,文件IO,定时器,计数器。
  • 一切任务都由任务工厂产生,用户通过调用接口组织并发结构。例如串联并联,DAG等。
  • 大多数情况下,用户通过任务工厂产生的任务,都隐藏了多个异步过程,但用户并不感知。
    • 例如,一次http请求,可能包含许多次异步过程(DNS,重定向),但对用户来讲,就是一次通信任务。
    • 文件排序,看起来就是一个算法,但其实包括复杂的文件IO与CPU计算的交互过程。
    • 如果把业务逻辑想象成用设计好的电子元件搭建电路,那么每个电子元件内部可能又是一个复杂电路。
    • 任务隐藏机制大幅减少了用户需要创建的任务数量和回调深度。
  • 任何任务都运行在某个串行流(series)里,共享series上下文,让异步任务之间数据传递变得简单。

回调与内存回收机制

  • 一切调用都是异步执行,几乎不存在占着线程等待的操作。
  • 显式的回调机制。用户清楚自己在写异步程序。
  • 通过一套对象生命周期机制,大幅简化异步程序的内存管理
    • 任何框架创建的任务,生命周期都是从创建到callback函数运行结束为止。没有泄漏风险。
      • 如果创建了任务之后不想运行,则需要通过dismiss()接口删除。
    • 任务中的数据,例如网络请求的resp,也会随着任务被回收。此时用户可通过std::move()把需要的数据移走。
    • 项目中不使用任何智能指针来管理内存。代码观感清新。
  • 尽量避免用户级别派生,以std::function封装用户行为,包括:
    • 任何任务的callback。
    • 任何server的process。符合FaaS(Function as a Service)思想。
    • 一个算法的实现,简单来讲也是一个std::function。
    • 如果深入使用,又会发现一切皆可派生

5、任务流

1、task(任务)和series(任务流):

  • task由工厂创建、自行销毁;
  • series可以手动从工厂创建或者自动创建,也是自行销毁。

    workflow的标准是C++11
    而任务和任务流的概念在理解易用性上简直甩开事件机制和用循环来做多次进入的开发模式十条街(后面这种模式我也不是想得很明白,但发现OpenSSL的异步和grpc的异步server都是这种方式)

    2、有三个原则:

  • 串行是由任务组成的

  • 并行是由串行组成的
  • 并行是一种任务

于是乎,workflow是一套完备的、可以收敛一切循环和控制的任务流模型。
以下是我的一些个人解读:

  1. 由于可以串行,我们就可以动态建流图,并且无限执行下去;
  2. 由于可以并行,并行的是各个串行流,我们可以对多个并发执行的流在完成时做收敛
  3. 并行本身是一种任务,因此可以加到串行流图里,即每个任务都可以是一个复合任务组装而成,组装后提供给其他使用者使用,而使用者不需要关心复合任务内部细节,进一步组装。

    6、异步调度

    workflow认为一切资源都是可以异步调度的,因此对于目前支持对接的资源如下
资源 调度单位 任务名 底层操作资源的父类 调度者 具体系统资源
网络 网络连接(socket fd) WFNetworkTask CommRequest 通信器与连接管理 epoll/kqueue/iocp
CPU 线程 WFThreadTask ExecRequest 执行器与执行队列 pthread
磁盘 文件fd WFFileTask IORequest IOServer linux aio
GPU cudaStream_t WFCudaTask 目前用WFThreadTask去操作cuda stream 用线程执行cuda
sync/async模式
cuda
计时器 时间 WFTimerTask SleepRequest 用一个timer_fd管理 linux timer_fd
计数器 一片用以做计数的内存 WFCounterTask WFGenericTask 内存

6.1、调度器

  • 包含与管理资源池
  • 实现如何对一批连接尽可能高性能地响应其读写、如何尽可能快且尽可能通用地给出一个足够灵活的机制去让各线程执行各种计算
  • 提供请求接口给上层使用

    6.2、封装调度的基本单位

    对应每种可以调度对象的系统接口,我们必须封装自己的结构,作为每次与系统资源交互的基本单位,通过调度器提供的请求接口,扔到调度器里被调度。
    具体来说,这显然是一次网络交互、或者一次线程需要执行的计算任务。然后每个基本单位上,可以有上下文、供子类做具体实现的接口/函数指针等等。

    10、其他

    1、wfrest

    1. wfrest基于 Workflow开发. 是一个设计轻盈优雅的企业级程序引擎.<br /> C++ Web Framework REST API<br /> 快速🚀, 高效⌛️, 简单易用的💥 c++ 异步web框架.