秋雨De blog

  • 首页
  • 留言板
  • 关于
  • rss
秋雨De blog
一个技术小白的个人博客
  1. 首页
  2. 未分类
  3. 正文

手写 C++ Web 服务器:模板化中间件链设计

2026年3月14日 134点热度 0人点赞 0条评论

在前几篇文章中,我们已经实现了一个基础的 HTTP 服务器:

  1. 通过 Boost.Asio 处理 TCP 连接
  2. 解析 HTTP 请求头和请求体
  3. 构造 http_request_struct
  4. 执行业务逻辑并返回 http_response_struct

随着功能越来越多,我开始思考一个问题:

服务器应该如何组织请求处理逻辑?

在很多 Web 框架中,一个请求往往需要经过多个处理步骤,例如:

请求参数解析
↓
Content-Type 处理
↓
日志记录
↓
CORS 处理
↓
Cookie 解析
↓
Session 管理
↓
业务逻辑

如果这些逻辑全部写在一个函数里,代码会迅速变得非常混乱。

因此,大多数 Web 框架都会引入一种机制:

Middleware(中间件)

每个中间件负责一件事情,然后按顺序执行。

本篇是 手写 C++ Web 服务器系列的第五篇。
在这一篇中,我们将详细讲解 C++ 模板展开(Template Expansion)与模板特化(Template Specialization) 在框架中的实现方式,并分析其在路由处理与类型适配中的作用。源码地址:https://github.com/Fall-Rain/asio_web_service


一、最初的中间件实现

在最开始,我使用的是一种比较常见的设计:

std::vector<std::shared_ptr<middleware>> middlewares;

每个 middleware 继承自一个基类:

class middleware {
public:
    virtual void handle(Session&, std::function<void()> next) = 0;
};

执行流程:

middleware1
↓
middleware2
↓
middleware3
↓
business logic

这种设计其实就是经典的:

责任链模式(Chain of Responsibility)

但这种实现方式存在几个问题:

1 虚函数调用

每个中间件调用:

virtual dispatch

这会带来额外开销。


2 动态内存分配

每个 middleware 都需要:

new middleware
shared_ptr

3 运行时调度

所有中间件是在 运行时 决定顺序。

但在我的 Web 框架中:

中间件顺序其实是固定的。

既然如此,我们可以把它提前到 编译期。


二、模板中间件链

于是我尝试用 模板参数包(Template Parameter Pack) 来实现 middleware。

先定义一个模板类:

template<typename... middlewares>
class middleware_chain;

这里的:

middlewares...

就是 模板参数包。

例如:

middleware_chain<
    process_params_middleware,
    process_content_type_middleware,
    log_middleware,
    cros_middleware,
    websocket_middlesare,
    cookie_middleware,
    session_middleware
>

编译器会把这些类型打包成:

middlewares...

三、模板递归展开

真正的关键在这里:

template<typename First, typename... Rest>
class middleware_chain<First, Rest...>
{
public:
    template<typename Session>
    static void run(Session session)
    {
        First middleware;        
        middleware.handle(session, [session](){
            middleware_chain<Rest...>::run(session);
        });
    }};

这里使用了 模板偏特化:

middleware_chain<First, Rest...>

意思是:

第一个类型
+
剩余类型

例如:

middleware_chain<A,B,C>

会被解析为:

First = A
Rest = B,C

执行流程变成:

A
↓
middleware_chain<B,C>

然后继续展开:

B
↓
middleware_chain<C>

最终:

C
↓
middleware_chain<>

这就是 模板递归展开(Template Recursion)。


四、模板特化(递归终止)

模板递归必须有一个 终止条件。

因此我写了一个 完全特化:

template<>
class middleware_chain<>
{
public:    
static void run(std::shared_ptr<Session> session)
    {
        try
        {
            session->response =
                business_logic::process_request(session->request);
        }
        catch(const std::exception& e)
        {
            session->response.body = e.what();
        }
    }};

这里:

middleware_chain<>

表示:

模板参数为空

也就是:

没有 middleware 了

此时执行最终逻辑:

business_logic::process_request()

整个调用链变成:

middleware1
↓
middleware2
↓
middleware3
↓
business_logic

五、在 Session 中执行

现在在 Session 中只需要一行代码:

void Session::run_middlewares()
{
    middleware_chain<
        process_params_middleware,
        process_content_type_middleware,
        log_middleware,
        cros_middleware,
        websocket_middlesare,
        cookie_middleware,
        session_middleware
    >::run(shared_from_this());
}

执行顺序:

process_params_middleware
↓
process_content_type_middleware
↓
log_middleware
↓
cros_middleware
↓
websocket_middlesare
↓
cookie_middleware
↓
session_middleware
↓
business_logic

六、编译期调用链

和传统 middleware 最大的不同在于:

调用链已经在 编译期确定。

编译器看到代码后会展开成类似:

process_params_middleware.handle()

process_content_type_middleware.handle()

log_middleware.handle()

cros_middleware.handle()

websocket_middleware.handle()

cookie_middleware.handle()

session_middleware.handle()

business_logic()

甚至在 O2 / O3 优化下:

很多函数会 直接 inline。

这意味着:

几乎没有额外调用开销

七、这种设计的优势

1 没有虚函数

避免:

virtual dispatch

2 没有动态内存

不需要:

shared_ptr<middleware>

3 编译期优化

整个调用链可以被:

inline
loop unroll
constant propagation

4 类型安全

如果 middleware 类型写错:

编译期报错

而不是运行时错误。


八、完整请求流程

现在整个 Web 框架的请求流程是:

Client
│
▼
TCP Accept
│
▼
HTTP Header 解析
│
▼
HTTP Body 读取
│
▼
Middleware Chain
│
├ 参数解析
├ Content-Type解析
├ 日志
├ CORS
├ WebSocket检测
├ Cookie
└ Session
│
▼
Business Logic
│
▼
HTTP Response

九、总结

通过模板递归和模板特化,我们实现了一套 编译期中间件系统。

核心技术包括:

  • 模板参数包
  • 模板递归展开
  • 模板特化
  • 编译期调用链

本版中间件无运行时开销,符合 C++ zero-cost abstraction

在保证结构清晰的同时,也能获得接近手写代码的性能。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2026年3月14日

fallrain

种一棵树最好的时间是十年前,其次是现在。

点赞
< 上一篇

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

fallrain

种一棵树最好的时间是十年前,其次是现在。

文章目录
  • 一、最初的中间件实现
    • 1 虚函数调用
    • 2 动态内存分配
    • 3 运行时调度
  • 二、模板中间件链
  • 三、模板递归展开
  • 四、模板特化(递归终止)
  • 五、在 Session 中执行
  • 六、编译期调用链
  • 七、这种设计的优势
    • 1 没有虚函数
    • 2 没有动态内存
    • 3 编译期优化
    • 4 类型安全
  • 八、完整请求流程
  • 九、总结
友情连接
猫饭范文泉博客迎風别葉CODING手艺人ScarSu博友圈
归档
  • 2026 年 3 月
  • 2025 年 11 月
  • 2025 年 5 月
  • 2025 年 4 月
  • 2025 年 3 月
  • 2024 年 12 月
  • 2024 年 10 月
  • 2024 年 5 月
  • 2023 年 2 月
  • 2022 年 11 月
  • 2022 年 3 月
  • 2021 年 12 月
  • 2021 年 8 月
  • 2021 年 5 月
  • 2021 年 4 月
  • 2021 年 3 月
  • 2020 年 12 月
  • 2020 年 11 月
  • 2020 年 8 月
  • 2020 年 5 月
  • 2019 年 12 月
  • 2019 年 3 月

吉ICP备18007356号

吉公网安备22020302000184号

Theme Kratos Made By Seaton Jiang

COPYRIGHT © 2026 秋雨De blog ALL RIGHTS RESERVED