高并发后端服务开发指南:Sogou C++ Workflow网络与计算任务融合实践

高并发后端服务开发指南:Sogou C++ Workflow网络与计算任务融合实践

【免费下载链接】workflow C++ Parallel Computing and Asynchronous Networking Framework 【免费下载链接】workflow 项目地址: https://gitcode.com/gh_mirrors/workflow12/workflow

痛点直击:传统后端服务的性能瓶颈与解决方案

你是否还在为高并发场景下的服务响应延迟而困扰?是否正在寻找一种能同时处理网络通信与复杂计算的高效框架?Sogou C++ Workflow(以下简称Workflow)框架为你提供一站式解决方案。作为支撑搜狗每日数百亿请求的后端引擎,Workflow以其独特的"协议+算法+任务流"设计理念,完美融合了异步网络通信与并行计算能力,让开发者能够轻松构建高性能、高可靠性的后端服务。

读完本文,你将获得:

  • Workflow框架的核心设计思想与优势
  • 快速搭建高并发HTTP服务器的方法
  • 构建复杂任务依赖关系的DAG图任务实现
  • 网络通信与计算任务融合的实战案例
  • 项目部署与扩展的最佳实践指南

Workflow框架简介:重新定义后端服务开发模式

Workflow是一个轻量级高性能的C++并行计算与异步网络框架,它支持多种协议客户端(HTTP、Redis、MySQL、Kafka等)和服务器开发,同时提供强大的任务流调度能力。其核心优势在于将网络通信、计算任务、文件IO等统一抽象为任务,并通过灵活的任务流编排实现复杂业务逻辑。

核心设计理念

Workflow基于"结构化并发"和"任务隐藏"机制,将复杂的异步操作封装为简单的任务接口。框架中包含五种基础任务类型:

  • 通讯任务:处理各类网络协议通信
  • 计算任务:执行CPU密集型计算
  • 文件IO任务:异步文件读写操作
  • 定时器任务:基于时间的触发机制
  • 计数器任务:多任务协同同步

这些任务可以通过串联、并联或DAG(有向无环图)等方式组合,形成强大的业务处理流程。

框架架构概览

mermaid

快速上手:5分钟构建高性能HTTP服务器

使用Workflow构建HTTP服务器异常简单,以下是一个完整的HTTP回显服务器示例,它能显示客户端发送的所有请求头信息。

服务器实现代码

#include <stdio.h>
#include "workflow/WFHttpServer.h"
#include "workflow/HttpUtil.h"

void process(WFHttpTask *server_task)
{
    protocol::HttpRequest *req = server_task->get_req();
    protocol::HttpResponse *resp = server_task->get_resp();
    long seq = server_task->get_task_seq();
    protocol::HttpHeaderCursor cursor(req);
    std::string name, value;
    char buf[8192];
    int len;

    // 设置响应内容
    resp->append_output_body_nocopy("<html><head><title>Workflow Echo Server</title></head><body>");
    len = snprintf(buf, sizeof(buf), "<h1>Request #%ld</h1>", seq);
    resp->append_output_body(buf, len);
    
    // 添加请求方法、URI和版本信息
    len = snprintf(buf, sizeof(buf), "<p>%s %s %s</p>", 
                  req->get_method(), req->get_request_uri(), req->get_http_version());
    resp->append_output_body(buf, len);
    
    // 添加所有请求头信息
    resp->append_output_body_nocopy("<h3>Request Headers:</h3><ul>");
    while (cursor.next(name, value))
    {
        len = snprintf(buf, sizeof(buf), "<li>%s: %s</li>", name.c_str(), value.c_str());
        resp->append_output_body(buf, len);
    }
    resp->append_output_body_nocopy("</ul></body></html>");
    
    // 设置响应状态和头部
    resp->set_status_code("200");
    resp->set_reason_phrase("OK");
    resp->add_header_pair("Content-Type", "text/html");
    resp->add_header_pair("Server", "Sogou WFHttpServer");
    
    // 每处理10个请求后关闭连接
    if (seq == 9)
        resp->add_header_pair("Connection", "close");
}

int main(int argc, char *argv[])
{
    unsigned short port;
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s <port>\n", argv[0]);
        return 1;
    }

    port = atoi(argv[1]);
    WFHttpServer server(process);
    
    // 启动服务器
    if (server.start(port) == 0)
    {
        getchar(); // 按Enter键停止服务器
        server.stop();
    }
    else
    {
        perror("Server start failed");
        return 1;
    }

    return 0;
}

代码解析

上述代码实现了一个功能完整的HTTP服务器,核心步骤包括:

  1. 创建服务器对象WFHttpServer server(process),其中process是请求处理函数
  2. 启动服务器server.start(port)在指定端口启动服务
  3. 处理请求:在process函数中,通过WFHttpTask获取请求和响应对象
  4. 构建响应:使用append_output_body系列方法构建HTML响应内容
  5. 设置连接管理:通过Connection: close头控制连接生命周期

服务器启动后会在指定端口监听HTTP请求,并将客户端发送的请求信息以HTML格式返回。完整代码可参考tutorial/tutorial-04-http_echo_server.cc

编译与运行

使用以下命令编译并运行服务器:

# 克隆仓库
git clone https://gitcode.com/gh_mirrors/workflow12/workflow
cd workflow

# 编译示例
make tutorial

# 运行HTTP回显服务器
./tutorial/tutorial-04-http_echo_server 8888

现在,你可以通过浏览器访问http://localhost:8888来测试这个服务器,它会显示你的请求信息。

任务流编排:构建复杂依赖关系的DAG图任务

Workflow的强大之处在于其灵活的任务流编排能力。除了简单的串并联任务,它还支持复杂的DAG(有向无环图)任务,允许你构建任意复杂的任务依赖关系。

DAG图任务示例

以下示例展示了如何创建一个包含定时器、HTTP请求和计算任务的DAG图,实现复杂的任务依赖关系:

#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <iostream>
#include <string>

using namespace std;

static WFFacilities::WaitGroup wait_group(1);

// HTTP请求回调函数
void http_callback(WFHttpTask *task)
{
    const void *body;
    size_t body_size;
    
    if (task->get_state() == WFT_STATE_SUCCESS)
    {
        task->get_resp()->get_parsed_body(&body, &body_size);
        cout << "HTTP request success. Response size: " << body_size << endl;
    }
    else
    {
        cout << "HTTP request failed. State: " << task->get_state() << endl;
    }
}

// 计算任务回调函数
void go_callback(WFGoTask *task)
{
    cout << "Computation task completed" << endl;
}

// 图任务完成回调
void graph_callback(WFGraphTask *graph)
{
    cout << "DAG graph task completed. All tasks finished." << endl;
    wait_group.done();
}

int main()
{
    // 创建图任务
    WFGraphTask *graph = WFTaskFactory::create_graph_task(graph_callback);
    
    // 创建定时器任务
    WFTimerTask *timer = WFTaskFactory::create_timer_task(1, 0, [](WFTimerTask *task) {
        cout << "Timer task triggered after 1 second" << endl;
    });
    
    // 创建两个HTTP任务
    WFHttpTask *http1 = WFTaskFactory::create_http_task(
        "http://www.sogou.com", 0, 0, http_callback);
    WFHttpTask *http2 = WFTaskFactory::create_http_task(
        "http://www.baidu.com", 0, 0, http_callback);
    
    // 创建计算任务
    WFGoTask *go_task = WFTaskFactory::create_go_task(
        "compute_queue", [](){
            // 模拟耗时计算
            int sum = 0;
            for (int i = 0; i < 100000000; i++) {
                sum += i;
            }
            cout << "Computation result: " << sum << endl;
        }, go_callback);
    
    // 创建图节点
    auto &a = graph->create_graph_node(timer);   // 定时器节点
    auto &b = graph->create_graph_node(http1);   // HTTP任务1节点
    auto &c = graph->create_graph_node(http2);   // HTTP任务2节点
    auto &d = graph->create_graph_node(go_task); // 计算任务节点
    
    // 建立DAG依赖关系: timer -> http1 -> go_task
    //               timer -> http2 -> go_task
    a --> b --> d;
    a --> c --> d;
    
    // 启动图任务
    graph->start();
    
    // 等待所有任务完成
    wait_group.wait();
    cout << "All tasks done." << endl;
    return 0;
}

DAG任务解析

上述代码创建了一个包含四个任务的DAG图,任务依赖关系如下:

             +-------+          
       +---->| HTTP1 |-----+   
       |     +-------+     |
+-------+              +-v--+ 
| Timer |              | Go | 
+-------+              +-^--+ 
       |     +-------+     |    
       +---->| HTTP2 |-----+    
             +-------+          
  1. 创建图任务:通过WFTaskFactory::create_graph_task创建空的图任务
  2. 创建节点:使用create_graph_node将普通任务转换为图节点
  3. 建立依赖:使用直观的-->运算符定义节点间依赖关系
  4. 启动任务:调用graph->start()启动整个DAG图任务

当定时器任务完成后,两个HTTP任务并行执行,只有当两个HTTP任务都完成后,计算任务才会执行。这种灵活的依赖管理机制使得处理复杂业务逻辑变得简单直观。

完整代码可参考tutorial/tutorial-11-graph_task.cc

网络与计算融合:构建高性能数据处理服务

Workflow的独特之处在于能无缝融合网络通信与计算任务,以下是一个实际应用场景:从多个数据源并行获取数据,进行实时处理后存储到数据库。

应用场景:实时数据聚合服务

mermaid

核心实现代码

#include <workflow/WFTaskFactory.h>
#include <workflow/WFFacilities.h>
#include <workflow/RedisMessage.h>
#include <nlohmann/json.hpp>
#include <vector>
#include <string>

using json = nlohmann::json;
using namespace std;

static WFFacilities::WaitGroup wg(1);

// 数据聚合结构
struct AggregationData {
    vector<json> results;
    size_t completed;
};

// HTTP请求回调
void http_callback(WFHttpTask *task) {
    // 获取聚合数据指针
    AggregationData *agg_data = static_cast<AggregationData *>(task->user_data);
    
    if (task->get_state() == WFT_STATE_SUCCESS) {
        const void *body;
        size_t size;
        task->get_resp()->get_parsed_body(&body, &size);
        
        try {
            // 解析JSON数据
            json j = json::parse(string((const char *)body, size));
            agg_data->results.push_back(j);
        } catch (const exception &e) {
            cerr << "JSON parse error: " << e.what() << endl;
        }
    } else {
        cerr << "HTTP request failed: " << task->get_state() << endl;
    }
    
    // 检查所有请求是否完成
    if (++agg_data->completed == 3) {
        // 创建计算任务处理聚合数据
        WFGoTask *compute_task = WFTaskFactory::create_go_task(
            "compute",
            [agg_data]() {
                // 聚合计算逻辑
                json result;
                result["total"] = agg_data->results.size();
                result["data"] = agg_data->results;
                
                // 创建Redis任务存储结果
                WFRedisTask *redis_task = WFTaskFactory::create_redis_task(
                    "redis://localhost:6379",
                    1,  // 重试次数
                    agg_data {
                        if (task->get_state() == WFT_STATE_SUCCESS) {
                            cout << "Data stored to Redis successfully" << endl;
                        } else {
                            cerr << "Redis task failed: " << task->get_state() << endl;
                        }
                        delete agg_data;  // 释放聚合数据内存
                        wg.done();
                    }
                );
                
                // 设置Redis命令
                protocol::RedisRequest *req = redis_task->get_req();
                req->set_request("SET", {"agg_result", result.dump()});
                
                // 启动Redis任务
                redis_task->start();
            },
            nullptr  // 计算任务回调
        );
        
        // 启动计算任务
        compute_task->start();
    }
}

int main() {
    // 创建聚合数据结构
    AggregationData *agg_data = new AggregationData();
    agg_data->completed = 0;
    
    // 要请求的数据源URL列表
    vector<string> urls = {
        "https://api.example.com/data1",
        "https://api.example.com/data2",
        "https://api.example.com/data3"
    };
    
    // 创建并行HTTP请求
    for (const string &url : urls) {
        WFHttpTask *task = WFTaskFactory::create_http_task(
            url,
            3,  // 最大重定向次数
            1,  // 最大重试次数
            http_callback
        );
        task->user_data = agg_data;  // 设置用户数据指针
        task->start();  // 立即启动任务
    }
    
    wg.wait();
    return 0;
}

代码解析

这个示例展示了Workflow如何轻松融合网络通信和计算任务:

  1. 并行HTTP请求:同时向三个数据源发送HTTP请求获取数据
  2. 数据聚合:所有请求完成后,启动计算任务处理聚合数据
  3. 存储结果:计算完成后,将结果存储到Redis数据库

关键技术点:

  • 使用user_data在任务间传递共享数据
  • 通过计数器跟踪并行任务完成状态
  • 嵌套创建任务实现流程控制
  • 结合网络任务与计算任务处理业务逻辑

这种模式非常适合构建高性能的数据聚合服务、实时分析系统或微服务架构中的数据处理节点。

部署与最佳实践

编译与安装

Workflow支持多种编译方式,包括Makefile、CMake和XMake。推荐使用XMake进行构建,它提供了更灵活的配置选项。

# 使用XMake编译
xmake

# 安装库文件
sudo xmake install -o /usr/local

详细的编译选项可参考docs/xmake.md

性能优化建议

  1. 合理设置线程数:根据CPU核心数调整工作线程数,默认情况下框架会自动适配
  2. 连接池管理:对于数据库等长连接服务,使用连接池提高性能
  3. 任务队列配置:为不同类型的任务配置专用队列,避免相互干扰
  4. 超时设置:为网络任务设置合理的超时时间,避免资源长时间占用
  5. 批量操作:对于数据库操作,尽量使用批量接口减少网络往返

监控与调试

Workflow提供了完善的日志系统和状态监控机制:

// 设置全局日志级别
WFGlobal::set_log_level(LOG_LEVEL_DEBUG);

// 监控任务状态
WFHttpTask *task = WFTaskFactory::create_http_task(
    url, 3, 1,
    [](WFHttpTask *task) {
        // 获取任务状态信息
        int state = task->get_state();
        int error = task->get_error();
        
        if (state == WFT_STATE_SUCCESS) {
            // 任务成功处理
        } else {
            // 错误处理
            cerr << "Task failed: state=" << state << ", error=" << error << endl;
        }
    }
);

总结与展望

Sogou C++ Workflow框架通过创新的任务抽象和流编排机制,极大简化了高并发后端服务的开发复杂度。它将网络通信、并行计算和文件IO等操作统一为任务抽象,使开发者能够专注于业务逻辑而非底层实现细节。

无论是构建简单的HTTP服务器,还是实现复杂的分布式计算系统,Workflow都能提供卓越的性能和开发效率。随着微服务和云原生架构的普及,Workflow将成为后端开发的理想选择。

下一步学习资源

参与贡献

Workflow是一个活跃的开源项目,欢迎通过以下方式参与贡献:

  • 提交Issue报告bug或提出功能建议
  • 提交Pull Request改进代码
  • 在社区分享使用经验和最佳实践

项目地址:https://gitcode.com/gh_mirrors/workflow12/workflow

希望本文能帮助你快速掌握Workflow框架的核心功能。现在就开始构建你的高性能后端服务吧!

【免费下载链接】workflow C++ Parallel Computing and Asynchronous Networking Framework 【免费下载链接】workflow 项目地址: https://gitcode.com/gh_mirrors/workflow12/workflow

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值