尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Fedora 40 源码编译与实战:Sogou C++ Workflow 异步网络框架指南

Fedora 40 源码编译与实战:Sogou C++ Workflow 异步网络框架指南
📅 发布时间:2026/7/12 8:52:41

1. 项目概述

最近在折腾一个需要处理高并发网络请求和并行计算的后端服务,选型时把目光投向了搜狗开源的C++ Workflow框架。这玩意儿在搜狗内部支撑着搜索、输入法、广告这些核心业务,每天处理上百亿请求,性能和稳定性是经过实战考验的。不过,官方文档虽然详尽,但更多是API手册的风格,对于一个想在Fedora系统上从零开始,把源码编译、环境配置、到跑通第一个示例程序全流程走一遍的新手来说,信息还是有些分散。网上关于Ubuntu的教程不少,但Fedora毕竟有些自己的“脾气”,比如包管理器是dnf,一些依赖库的名字也可能不同。所以,我决定把这次在Fedora 40上从源码构建Sogou C++ Workflow,并完成一个简单HTTP服务器和客户端实战的完整过程记录下来,希望能帮到同样在Fedora上探索这个框架的朋友。

这个指南会覆盖从系统准备、依赖安装、源码编译、库文件配置,到编写、编译并运行你自己的第一个Workflow程序的全过程。我们不仅会“跑起来”,还会拆解其中几个关键步骤背后的原理,比如为什么需要OpenSSL,CMakeLists.txt里到底干了啥,以及如何避免一些常见的编译和链接坑。无论你是刚接触Linux C++服务端开发,还是想寻找一个轻量级但强大的异步网络框架,这篇实战记录应该都能提供一条清晰的路径。

2. 环境准备与依赖解析

在Fedora上从源码构建任何C++项目,第一步永远是确保你的系统环境是干净且完备的。这不仅仅是执行几条命令,理解每个依赖项的作用,能在出问题时快速定位。

2.1 系统更新与基础开发工具

首先,确保你的Fedora系统是最新的。打开终端,执行:

sudo dnf update -y

这个操作会更新所有已安装的软件包到最新版本,包括内核、库文件和开发工具链。对于开发环境来说,保持更新可以避免很多因库版本过旧导致的诡异编译错误。

接下来,安装必不可少的开发工具集。在Fedora上,这通常通过@development-tools这个软件包组来完成:

sudo dnf groupinstall "Development Tools" -y

这个命令会安装一整套工具,包括GCC编译器(gcc-c++)、GNU Make、GDB调试器、autoconf、automake等。它们是编译绝大多数C++项目的基石。你可以通过gcc --version和make --version来验证安装是否成功。

注意:有些精简安装的Fedora可能默认没有安装这些工具组,直接编译会报“g++: command not found”的错误。所以这一步是必须的。

2.2 核心依赖:OpenSSL与CMake

Sogou C++ Workflow的核心功能,特别是HTTP/HTTPS、Redis SSL连接等,依赖于OpenSSL库。根据官方说明,主分支需要OpenSSL 1.1或以上版本(也兼容BoringSSL)。Fedora 40默认仓库中的OpenSSL版本通常是符合要求的,我们安装其开发包:

sudo dnf install openssl-devel -y

这里安装的是openssl-devel,它包含了编译时需要的头文件(.h)和链接库(.so)。如果只安装openssl,只能运行时使用,编译时会找不到头文件。

另一个关键工具是CMake。虽然Workflow源码也提供了GNUmakefile,但使用CMake是更现代、更通用的跨平台构建方式,也便于我们后续集成到自己的项目中。安装CMake:

sudo dnf install cmake -y

验证安装:cmake --version。

2.3 可选依赖:压缩库与Git

如果你计划使用Workflow的Kafka客户端功能,由于Kafka消息可能使用lz4、zstd或snappy进行压缩,你需要安装对应的开发库:

sudo dnf install lz4-devel zstd-devel snappy-devel -y

即使暂时不用Kafka,先安装上也无妨,避免未来需要时重新编译整个Workflow。

最后,我们需要Git来克隆源码:

sudo dnf install git -y

至此,基础环境就绪。你可以通过dnf list installed | grep -E \"(gcc-c++|openssl-devel|cmake|git)\"来快速检查关键包是否都已安装。

3. 源码获取与编译构建

环境准备好后,我们就可以把Workflow的源码“请”到本地,并把它编译成我们可以使用的库。

3.1 克隆源码仓库

找一个你喜欢的目录,比如~/Projects,然后克隆官方仓库:

cd ~/Projects git clone https://github.com/sogou/workflow.git cd workflow

这个过程会下载主分支(master)的最新代码。网络通畅的话,很快就能完成。完成后,ls一下,你会看到README.md、src、tutorial等关键目录。src里面是框架的所有源代码,tutorial则是一系列极佳的学习示例。

3.2 编译决策:Make vs CMake

进入源码根目录,你会发现有GNUmakefile和CMakeLists.txt两个构建配置文件。用哪个?

  • 直接Make:最简单。只需运行make。它会使用默认的编译选项,在目录下生成静态库(libworkflow.a)和动态库(libworkflow.so),以及头文件。对于快速体验和编译教程示例,这非常方便。
  • 使用CMake:更灵活、更标准。它允许你指定安装前缀(-DCMAKE_INSTALL_PREFIX)、构建类型(-DCMAKE_BUILD_TYPE=Release)、是否编译测试等。这对于想要将Workflow作为自己项目的一个组件,并希望规范安装到系统目录或特定位置的情况,是更好的选择。

考虑到这是一篇“全流程”指南,我们将采用CMake方式,因为它更贴近实际项目中的集成场景。

3.3 使用CMake进行构建与安装

我们采用“外部构建”的最佳实践,即在源码目录外创建一个独立的构建目录。

  1. 创建并进入构建目录:

    mkdir build && cd build

    这样做的好处是,编译产生的所有中间文件(.o, .so等)都集中在build目录里,源码目录保持干净。如果你想推倒重来,直接删除build目录即可。

  2. 运行CMake配置:

    cmake .. -DCMAKE_BUILD_TYPE=Release
    • ..表示CMakeLists.txt在上一级目录(即源码根目录)。
    • -DCMAKE_BUILD_TYPE=Release指定构建类型为发布模式。这会开启编译器优化(如-O2),去掉调试信息,生成性能最优的二进制文件。如果是调试,可以设为Debug。

    CMake会检查系统环境,定位OpenSSL等依赖,并生成适用于你系统的Makefile。如果看到类似-- Configuring done、-- Generating done的输出,且没有报错,就说明配置成功。

  3. 编译源码:

    make -j$(nproc)

    -j$(nproc)参数会让make使用你CPU的所有核心进行并行编译,大幅加快速度。nproc命令会返回你系统的逻辑CPU核心数。

    编译过程需要一些时间,请耐心等待。如果一切顺利,最终会在build目录下生成libworkflow.a(静态库)和libworkflow.so(动态库)。

  4. 安装到系统(可选但推荐):

    sudo make install

    默认情况下,这会将库文件安装到/usr/local/lib,头文件安装到/usr/local/include。这样,你在系统任何地方编译自己的程序时,编译器都能自动找到它们。

    实操心得:对于个人开发机,安装到系统目录很方便。但在生产环境或需要多版本共存时,更推荐通过-DCMAKE_INSTALL_PREFIX=/path/to/your/install参数指定一个自定义路径,然后将该路径添加到你的项目配置中。

3.4 验证编译结果

编译安装后,快速验证一下:

ls /usr/local/lib/libworkflow*

应该能看到libworkflow.so(动态库)和libworkflow.a(静态库)等文件。

ls /usr/local/include/workflow/

应该能看到一系列.h头文件,如WFHttpServer.h、WFTaskFactory.h等。

至此,Sogou C++ Workflow框架本身已经成功部署到你的Fedora系统上了。

4. 第一个Workflow程序:HTTP服务器实战

理论说再多,不如跑个例子。我们来创建一个最简单的HTTP Echo服务器,它接收客户端的请求,并将请求的一些信息原样返回。

4.1 编写服务器代码

在你喜欢的工作目录,创建一个新文件http_echo_server.cpp:

#include <stdio.h> #include <string.h> #include "workflow/WFHttpServer.h" // 引入HTTP服务器头文件 int main() { // 创建一个HTTP服务器。 // 构造函数的参数是一个std::function,定义了如何处理每一个到达的HTTP任务。 WFHttpServer server([](WFHttpTask *task) { // task->get_req() 获取请求对象 // task->get_resp() 获取响应对象 auto *req = task->get_req(); auto *resp = task->get_resp(); // 设置响应状态码为200 OK resp->set_status_code("200"); resp->set_http_version("HTTP/1.1"); resp->add_header_pair("Content-Type", "text/plain; charset=utf-8"); // 构建响应体:简单返回一些请求信息 char buf[4096]; int len = snprintf(buf, sizeof(buf), "Hello from Sogou C++ Workflow!\n" "Method: %s\n" "Request URI: %s\n" "Query String: %s\n", req->get_method(), req->get_request_uri(), req->get_query_string()); resp->append_output_body(buf, len); // 打印日志到服务器控制台 fprintf(stderr, "[Server] Handled a request for: %s\n", req->get_request_uri()); }); // 启动服务器,监听8888端口。 // server.start() 是一个非阻塞调用,会立即返回。 if (server.start(8888) == 0) { fprintf(stderr, "[Server] Started successfully on port 8888. Press Enter to stop.\n"); // 等待用户输入,以保持服务器运行。 getchar(); // 停止服务器。 server.stop(); } else { fprintf(stderr, "[Server] Cannot start server on port 8888.\n"); return 1; } return 0; }

代码解读:

  1. 核心是回调函数:WFHttpServer的构造函数接受一个函数(这里用了Lambda表达式)。每当有新的HTTP连接到来,框架就会创建一个WFHttpTask并调用这个函数。这种“异步回调”模式是Workflow高性能的基石——它不会阻塞线程去等待IO。
  2. 任务(Task)是核心抽象:WFHttpTask代表了一个完整的HTTP事务(请求+响应)。通过它,你可以访问到请求(req)和响应(resp)对象,进行读写操作。
  3. 非阻塞启动:server.start()启动后,主线程并不会被阻塞在监听循环里。它只是完成了端口的绑定和监听,然后立即返回。实际的连接接受、数据读写、回调执行,都由框架内部的线程池调度。这就是为什么我们需要getchar()来防止主程序立刻退出。

4.2 编译并运行服务器

现在编译这个服务器程序。你需要告诉编译器头文件在哪,以及链接哪个库。

g++ -std=c++11 -o http_echo_server http_echo_server.cpp \ -I/usr/local/include \ # 指定Workflow头文件路径,如果安装到了默认位置 -L/usr/local/lib \ # 指定Workflow库文件路径 -lworkflow \ # 链接动态库 libworkflow.so -lssl -lcrypto # 链接OpenSSL库,因为HTTP可能用到SSL
  • -std=c++11:指定使用C++11标准,Workflow框架需要。
  • -I:添加头文件搜索路径。
  • -L:添加库文件搜索路径。
  • -lworkflow:链接名为workflow的库(即libworkflow.so)。
  • -lssl -lcrypto:链接OpenSSL的库。

如果编译成功,会生成一个名为http_echo_server的可执行文件。

运行它:

./http_echo_server

终端会输出[Server] Started successfully on port 8888. Press Enter to stop.。现在服务器已经在后台运行了。

4.3 测试服务器

打开另一个终端窗口,使用curl命令测试我们的服务器:

curl -v http://localhost:8888/hello curl -v "http://localhost:8888/test?name=foo&age=20"

你会在客户端看到返回的“Hello from Sogou C++ Workflow!”信息以及请求的方法、URI等。同时,在服务器端的终端,你会看到[Server] Handled a request for: ...的日志。

按回车键可以优雅地停止服务器。

注意事项:如果编译时提示“找不到 -lworkflow”,可能是动态链接器缓存没有更新。执行sudo ldconfig刷新一下,或者运行时指定库路径:LD_LIBRARY_PATH=/usr/local/lib ./http_echo_server。

5. 深入核心:异步客户端与任务流

仅仅一个服务器还不足以展现Workflow的威力。它的精髓在于“工作流”,即如何将多个异步任务(网络请求、计算、文件IO等)以串行、并行或更复杂的DAG(有向无环图)方式组织起来。我们通过一个经典的“先抓取网页,再把结果存入Redis”的串联任务来体验。

5.1 串联任务示例:Wget到Redis

这个示例模拟一个常见场景:从网络获取数据,然后进行持久化。我们创建一个客户端程序,它先异步执行一个HTTP GET(类似wget),成功后,再将获取到的内容异步写入Redis。

创建文件wget_to_redis.cpp:

#include <iostream> #include <string> #include "workflow/WFTaskFactory.h" #include "workflow/WFHttpTask.h" #include "workflow/WFRedisTask.h" #include "workflow/WFFacilities.h" using namespace std; int main() { // 1. 创建第一个任务:HTTP GET string url = "http://httpbin.org/get"; WFHttpTask *http_task = WFTaskFactory::create_http_task(url, 4, 2, [](WFHttpTask *task) { // 这是HTTP任务的回调 if (task->get_state() == WFT_STATE_SUCCESS) { const void *body; size_t len; task->get_resp()->get_parsed_body(&body, &len); fprintf(stderr, "[HTTP] Fetch success, body length: %zu\n", len); // 将body数据存储到task的user_data中,传递给下一个任务 task->user_data = (void *)new string((const char *)body, len); } else { fprintf(stderr, "[HTTP] Fetch failed. error: %d\n", task->get_error()); task->user_data = nullptr; } }); // 2. 创建第二个任务:Redis SET // 注意:这里需要你本地或远程有一个Redis服务器,默认端口6379 string redis_url = "redis://127.0.0.1:6379"; WFRedisTask *redis_task = WFTaskFactory::create_redis_task(redis_url, 1, [](WFRedisTask *task) { // 这是Redis任务的回调 if (task->get_state() == WFT_STATE_SUCCESS) { fprintf(stderr, "[Redis] Set operation success.\n"); } else { fprintf(stderr, "[Redis] Set operation failed. error: %d\n", task->get_error()); } // 清理在http_task中申请的内存 if (task->user_data) { delete (string *)task->user_data; } }); // 设置Redis命令:将键"workflow_test"的值设为HTTP响应的body redis_task->get_req()->set_request("SET", {"workflow_test", *(string *)http_task->user_data}); // 3. 创建串行工作流(SeriesWork),将两个任务串联起来 // 工厂函数 series_of(task) 创建一个以给定任务开始的序列 // push_back(task) 将下一个任务追加到序列中 WFFacilities::WaitGroup wait_group(1); // 用于等待整个工作流结束的同步工具 SeriesWork *series = Workflow::create_series_work(http_task, [&wait_group](const SeriesWork *series) { // 这是整个系列结束时的回调 fprintf(stderr, "[Series] All tasks finished.\n"); wait_group.done(); // 通知主线程可以结束了 }); series->push_back(redis_task); // 将Redis任务加入序列 // 4. 启动工作流 series->start(); // 5. 等待工作流完成(非阻塞等待) fprintf(stderr, "[Main] Workflow started, waiting...\n"); wait_group.wait(); fprintf(stderr, "[Main] Program exit.\n"); return 0; }

5.2 代码结构与原理解析

这个程序虽然不长,但包含了Workflow几个最核心的概念:

  1. 任务工厂(WFTaskFactory):所有任务(HTTP、Redis、MySQL等)都通过这个工厂创建。你需要指定任务参数(如URL、重试次数、重试间隔)和回调函数。
  2. 回调函数:每个任务都有一个回调。关键点在于:这个回调是在任务完成(成功、失败、超时)时被框架调用的,而不是在create_*_task的那一行之后立即执行。这就是“异步”。
  3. 任务状态:在回调里,通过task->get_state()判断任务结果。WFT_STATE_SUCCESS表示成功。
  4. 数据传递(user_data):如何将HTTP任务的结果传给Redis任务?这里演示了一种简单方式:利用task->user_data这个通用指针。在HTTP回调里,将body数据存入一个std::string,并将其地址赋给http_task->user_data。由于两个任务在同一个系列中,它们共享上下文,Redis任务可以通过http_task->user_data拿到这个数据。注意内存管理:我们在Redis回调里负责释放这个string。
  5. 系列工作(SeriesWork):Workflow::create_series_work创建一个系列。系列中的任务会按顺序执行(一个接一个)。系列本身也有一个最终回调。
  6. 等待组(WaitGroup):因为整个流程是异步的,主函数main在调用series->start()后会立刻继续执行。如果不加控制,程序可能在任务完成前就退出了。WFFacilities::WaitGroup提供了一个简单的同步机制,wait_group.wait()会阻塞,直到wait_group.done()被调用(在系列最终回调里)。

5.3 编译与运行客户端

编译这个客户端需要额外链接crypto和pthread(因为Redis协议可能用到):

g++ -std=c++11 -o wget_to_redis wget_to_redis.cpp \ -I/usr/local/include \ -L/usr/local/lib \ -lworkflow -lssl -lcrypto -lpthread

运行前,请确保有一个Redis服务器在运行(例如在本机6379端口)。你可以通过sudo dnf install redis安装并启动Redis服务。

然后运行:

./wget_to_redis

如果网络通畅且Redis服务正常,你会看到类似以下的输出,清晰地展示了任务的执行顺序:

[Main] Workflow started, waiting... [HTTP] Fetch success, body length: 267 [Redis] Set operation success. [Series] All tasks finished. [Main] Program exit.

你可以用redis-cli GET workflow_test命令验证数据是否真的存入了Redis。

6. 常见问题排查与性能调优心得

在实际使用中,你肯定会遇到各种问题。下面是我踩过的一些坑和总结的经验。

6.1 编译与链接问题

问题现象可能原因解决方案
fatal error: workflow/WFHttpServer.h: No such file or directory编译器找不到头文件。确保-I参数指向了正确的头文件路径。如果通过make install安装到默认位置,通常是-I/usr/local/include。
undefined reference toWFHttpServer::WFHttpServer(...)`链接器找不到库文件。确保-L参数指向正确的库路径(如-L/usr/local/lib),并且-lworkflow写在源文件之后。链接顺序很重要。
error while loading shared libraries: libworkflow.so: cannot open shared object file运行时找不到动态库。执行sudo ldconfig更新系统库缓存。或者运行程序时指定库路径:LD_LIBRARY_PATH=/usr/local/lib ./your_program。
链接时出现大量OpenSSL相关未定义引用缺少OpenSSL链接。在链接命令末尾加上-lssl -lcrypto。
CMake配置时提示找不到OpenSSL系统未安装OpenSSL开发包。确保已执行sudo dnf install openssl-devel。

实操心得:对于复杂的项目,强烈建议使用CMake来管理你的代码,而不是手动写g++命令。你可以用find_package(workflow REQUIRED)和target_link_libraries(your_target PRIVATE workflow::workflow)来优雅地链接Workflow。这需要Workflow安装时提供了CMake配置文件(从源码安装的通常会有)。

6.2 运行时问题

  1. 服务器启动失败(端口占用):

    [Server] Cannot start server on port 8888.

    检查端口是否被其他程序占用:sudo ss -tulnp | grep :8888。如果被占用,要么停止那个程序,要么在代码中换一个端口。

  2. 任务卡住或无响应:

    • DNS问题:如果你的任务涉及域名(如http://httpbin.org),而DNS解析失败,任务会卡在DNS查询阶段。Workflow有全局的DNS配置(WFGlobal::get_dns_client()),可以设置DNS服务器地址。
    • 网络超时:所有网络任务都有默认的超时时间。对于不稳定的网络或慢速服务,你可能需要自定义超时。在创建任务时,可以通过WFTaskFactory::create_*_task的参数设置,或者通过task->set_send_timeout()、task->set_receive_timeout()等方法针对单个任务设置。
    • 回调函数异常:确保你的回调函数不会抛出异常。Workflow内部可能无法正确处理C++异常,导致行为未定义。尽量在回调内部做好错误处理。
  3. 内存泄漏怀疑: Workflow框架自身对任务内存的管理非常严谨。一个任务的生命周期是从创建开始,到它的回调函数执行结束为止。之后框架会自动回收该任务对象。如果你创建了任务但不想运行(比如某些条件判断下),必须调用task->dismiss()来显式释放,否则会导致内存泄漏。我们之前在wget_to_redis示例中手动管理user_data指向的string内存,这是用户数据,框架不负责。

6.3 性能调优初步

  1. 计算与IO的分离: Workflow的强大之处在于能轻松混合网络任务和计算任务。对于CPU密集型的计算,不要放在网络任务的回调里长时间执行,这会阻塞当前处理线程。应该将其封装成一个计算任务(WFComputeTask),然后放入工作流中。这样,计算会被调度到独立的计算队列,不会影响网络IO的吞吐。

  2. 合理使用并行(ParallelWork): 如果有多个独立的任务(比如同时请求10个不同的API),不要创建10个SeriesWork,而是应该创建一个ParallelWork,把这些任务作为并行分支加入,然后启动这个并行工作。框架会帮你管理并发和同步。

  3. 连接复用与上游管理: 对于需要频繁访问同一个后端服务(如某个数据库或微服务)的场景,不要每次都创建新的连接。Workflow内置了**上游管理(UpstreamManager)**和负载均衡功能。你可以创建一个“上游”,给它添加多个服务器地址和权重,然后通过上游名来创建任务。框架会自动管理连接池、健康检查、负载均衡(支持一致性哈希等策略),这是构建高可用微服务系统的利器。

  4. 资源限制: 通过WFGlobal::get_global_settings()可以修改全局设置,例如:

    • endpoint_params.max_connections:限制到同一目标端的最大并发连接数,防止把对方服务打挂。
    • poller_threads、handler_threads:调整IO线程和计算线程的数量,根据你的机器CPU核心数和服务类型(IO密集型或CPU密集型)进行优化。

从源码克隆到成功运行第一个串联异步任务,我们在Fedora上完成了一次完整的Sogou C++ Workflow初体验。这个框架的设计哲学非常清晰:用任务(Task)抽象一切异步操作,用系列(Series)和并行(Parallel)组织流程,用回调(Callback)处理结果。它没有采用复杂的协程或者让人头晕的Promise/Future链式调用,而是用一种更直接、对C++开发者更友好的方式来实现高性能异步编程。

我个人的体会是,Workflow的学习曲线前期稍微陡峭,尤其是要理解其“一切皆任务”和“显式回调”的思维模式。但一旦熟悉,用它来构建清晰、高效且易于维护的异步程序会非常顺手。它的代码质量很高,文档(尤其是GitHub Wiki)也相当全面,社区响应也比较及时。对于需要在Linux下用C++编写高性能网络服务的开发者来说,这绝对是一个值得投入时间学习的优秀工具。接下来,你可以继续探索它的更多功能,比如异步MySQL客户端、文件IO任务、定时器、计数器,以及如何构建复杂的DAG工作流,相信会有更多收获。

相关新闻

  • 寄大件怕被坑?这份收费标准明细让你少花一半冤枉钱 - 快递物流资讯
  • Python爬虫实战:爬取BOSS直聘岗位数据,按薪资排序+去重,附可视化分析
  • ADP5350与MK60DN512VLQ10的嵌入式电源管理方案

最新新闻

  • 3步轻松解密:网易云音乐NCM文件转换完全指南
  • 如何在电视上实现18ms低延迟游戏串流:Moonlight TV终极指南
  • TLA2518与TM4C129XNCZAD硬件协同设计与优化
  • 2026西安钻石回收实测|易奢福为本地测评榜首,78家门店仪器定价透明护航 - ys韩
  • EasySoftware国际化实现:多语言支持与本地化最佳实践
  • 工业级负载控制:TPD2015FN与STM32F746VG解决方案

日新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号