Swoole 源码分析之 Http Server 模块

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 想要了解到 `Http Server` 的全貌,其实只要把那张整体的实现图看懂就足以了。但是,如果想要有足够的深度,那么就还需要深入 `Swoole` 的源代码中,就着源码自行分析一遍。同时,也希望这一次的分析,能够给大家带来对 `Swoole` 更多的一些了解。并不要求要深刻的掌握,因为,很多的事情都不可能一蹴而就。从自己的实力出发,勿忘初心。

大家好,我是码农先森。

Http 模块的注册初始化

这次我们分析的就是 Swoole 官网的这段代码,看似简单,实则不简单。

请在此添加图片描述

Swoole 源码文件 swoole_http_server.c 中有这样一个函数 php_swoole_http_server_minit

这个函数是专门用来注册及初始化 Http Server 模块的,如果不预先注册,那么在 PHP 编程 中无法使用的。

// swoole-src/ext-src/swoole_http_server.c:172
void php_swoole_http_server_minit(int module_number) {
   
   
    // 定义 Swoole\Http\Server 为 PHP 中的类名
    // 并且 swoole_http_serve 继承了 swoole_server 即可以使用 `swoole_server` 的所有方法
    SW_INIT_CLASS_ENTRY_EX(swoole_http_server, "Swoole\\Http\\Server", nullptr, nullptr, swoole_server);
    // 这里设置为不可序列化,也就是说这个类能被序列化
    SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server);
    // 这里设置为不可克隆,也就是说这个类的对象不能被复制
    SW_SET_CLASS_CLONEABLE(swoole_http_server, sw_zend_class_clone_deny);
    // 这里设置为不可删除属性,也就是这个类的属性不能被删除
    SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_server, sw_zend_class_unset_property_deny);
}

从上面的这段初始化代码可以看出, swoole_http_server 继承了 swoole_server。因此,可以使用 swoole_server 的所有方法。

$http = new Swoole\Http\Server('127.0.0.1', 9501);
$http->on('start', function ($server) {
   
   });
$http->on('request', function ($request, $response) {
   
   });

这里的 new Swoole\Http\Server('127.0.0.1', 9501)$http->on('start', function ($server) {}$http->on('request', function ($request, $response) {});

在实际的调用中都是在 swoole_server 完成的,因此这里不会再过多的介绍了。可以看我之前的文章 Swoole 源码分析之 TCP Server 模块 这里都介绍了关于 构造函数、回调函数的 实现方式。

下面我们会着重介绍 $http->start() 这个函数,针对 swoole_http_server 做了一些特殊的实现。

$http->start() 的实现

话不多说,先上一张整体的实现图。$http->start() 这个方法的实现,在 Swoole 源码中最直接对应的就是 static PHP_METHOD(swoole_server, start) 这个函数。

请在此添加图片描述

那么刚刚说过,针对 swoole_http_server 做了一些特殊的实现。那么在哪里做的特殊处理呢?

我们来分析这个方法 on_before_start(),它是在真正的 start 服务启动之前做了一些预先工作。

// swoole-src/ext-src/swoole_server.cc:779
void ServerObject::on_before_start() {
   
   
   ...
    bool find_http_port = false;
    // 检查是否是 redis 服务
    if (is_redis_server()) {
   
   
        ...
        serv->onReceive = php_swoole_redis_server_onReceive;
    // 检查是否是 http 服务
    } else if (is_http_server()) {
   
   
        // 检查是否是 websocket 服务
        if (is_websocket_server()) {
   
   
            if (!isset_callback(primary_port, SW_SERVER_CB_onMessage)) {
   
   
                php_swoole_fatal_error(E_ERROR, "require onMessage callback");
                return;
            }
        } else if (!isset_callback(primary_port, SW_SERVER_CB_onRequest)) {
   
   
            php_swoole_fatal_error(E_ERROR, "require onRequest callback");
            return;
        }
        ....
        primary_port->open_http_protocol = 1;
        primary_port->open_http2_protocol = !!(protocol_flag & SW_HTTP2_PROTOCOL);
        primary_port->open_websocket_protocol = !!(protocol_flag & SW_WEBSOCKET_PROTOCOL);
        find_http_port = true;
        // 设置 Swoole Server 真正的 onReceive 回调是 php_swoole_http_server_onReceive
        serv->onReceive = php_swoole_http_server_onReceive;
    } else {
   
   
        ...
        // 否则,就是默认回调到 Swoole Server onReceive 的方法 php_swoole_server_onReceive
        serv->onReceive = php_swoole_server_onReceive;
    }
    ...
    if (find_http_port) {
   
   
        serv->onReceive = php_swoole_http_server_onReceive;
    }
    ...
}

on_before_start 这个方法中,可以看到不仅是对 http_server 做了处理,针对 redis_server 也是如此。

我们最开始提到的 $http->on('request', function ($request, $response) {}); 其中针对被实现的方法是在 php_swoole_http_server_onReceive 中。

接下来,我们揭开 php_swoole_http_server_onReceive 函数的神秘面纱。

php_swoole_http_server_onReceive 函数的实现

这个函数里面会对 http serverwebsocket server 进行分别的处理,即回调函数的设置。

并且,最后会真正的执行到 用户自定义对回调函数的 实现。

// swoole-src/ext-src/swoole_http_server.cc:51
int php_swoole_http_server_onReceive(Server *serv, RecvData *req) {
   
   
    // 获取到对于的连接对象
    Connection *conn = serv->get_connection_verify_no_ssl(session_id);
    ...
    // 如果是 websocket 连接,则进行对应的处理
    if (conn->websocket_status == WebSocket::STATUS_ACTIVE) {
   
   
        return swoole_websocket_onMessage(serv, req);
    }

    // 如果是 http2 连接,则进行对应的处理
    if (conn->http2_stream) {
   
   
        return swoole_http2_server_onReceive(serv, conn, req);
    }

    ...

    // 开始注册对应的回调函数
    do {
   
   
        zend_fcall_info_cache *fci_cache = nullptr;
        // 如果是 websocket 连接,则这是对应的回调函数 SW_SERVER_CB_onHandshake
        if (conn->websocket_status == WebSocket::STATUS_CONNECTION) {
   
   
            fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onHandshake);
            if (fci_cache == nullptr) {
   
   
                swoole_websocket_onHandshake(serv, port, ctx);
                goto _dtor_and_return;
            } else {
   
   
                conn->websocket_status = WebSocket::STATUS_HANDSHAKE;
                ctx->upgrade = 1;
            }
        // 否则是 Http 连接,则这是对应的回调函数 SW_SERVER_CB_onRequest
        } else {
   
   
            fci_cache = php_swoole_server_get_fci_cache(serv, server_fd, SW_SERVER_CB_onRequest);
            if (fci_cache == nullptr) {
   
   
                swoole_websocket_onRequest(ctx);
                goto _dtor_and_return;
            }
        }
        ctx->private_data_2 = fci_cache;
        if (ctx->onBeforeRequest && !ctx->onBeforeRequest(ctx)) {
   
   
            return SW_OK;
        }
        // 对 request 请求进行回调处理
        http_server_process_request(serv, fci_cache, ctx);
    } while (0);

    ...

    return SW_OK;
}

// swoole-src/ext-src/swoole_http_server.cc:51
static void http_server_process_request(Server *serv, zend_fcall_info_cache *fci_cache, HttpContext *ctx) {
   
   
    zval args[2];
    // request 回调函数中的 request 参数
    args[0] = *ctx->request.zobject;
    // request 回调函数中的 response 参数
    args[1] = *ctx->response.zobject;
    // 执行真正的调用,这里将会直接执行 用户自定义对回调函数的 实现
    if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, nullptr, serv->is_enable_coroutine()))) {
   
   
       ...
    }
}

其中 fci_cache 这个变量代表的就是 function ($request, $response) {} 这个函数,args[0] 代表的是 $requestargs[1] 代表的是 $response

对于 Http Server 模块来说,最重要的就是这个回调方法了,因为所有的业务逻辑都是在这里进行实现的。

总结

想要了解到 Http Server 的全貌,其实只要把那张整体的实现图看懂就足以了。但是,如果想要有足够的深度,那么就还需要深入 Swoole 的源代码中,就着源码自行分析一遍。同时,也希望这一次的分析,能够给大家带来对 Swoole 更多的一些了解。并不要求要深刻的掌握,因为,很多的事情都不可能一蹴而就。从自己的实力出发,勿忘初心。

欢迎关注、分享、点赞、收藏、在看,我是微信公众号「码农先森」作者。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
缓存 JavaScript 安全
nodejs里面的http模块介绍和使用
综上所述,Node.js的http模块是构建Web服务的基础,其灵活性和强大功能,结合Node.js异步非阻塞的特点,为现代Web应用开发提供了坚实的基础。
101 62
|
2月前
|
监控 网络协议 应用服务中间件
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
本文详细解析了Tomcat架构中复杂的`Connector`组件。作为客户端与服务器间沟通的桥梁,`Connector`负责接收请求、封装为`Request`和`Response`对象,并传递给`Container`处理。文章通过四个关键问题逐步剖析了`Connector`的工作原理,并深入探讨了其构造方法、`init()`与`start()`方法。通过分析`ProtocolHandler`、`Endpoint`等核心组件,揭示了`Connector`初始化及启动的全过程。本文适合希望深入了解Tomcat内部机制的读者。欢迎关注并点赞,持续更新中。如有问题,可搜索【码上遇见你】交流。
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
|
1月前
|
JSON API 开发者
深入解析Python网络编程与Web开发:urllib、requests和http模块的功能、用法及在构建现代网络应用中的关键作用
深入解析Python网络编程与Web开发:urllib、requests和http模块的功能、用法及在构建现代网络应用中的关键作用
16 0
|
1月前
|
移动开发 网络协议 C语言
详解 httptools 模块,一个 HTTP 解析器
详解 httptools 模块,一个 HTTP 解析器
28 0
|
3月前
|
缓存 应用服务中间件 nginx
安装nginx-http-flv-module模块
本文介绍如何为Nginx安装`nginx-http-flv-module`模块。此模块基于`nginx-rtmp-module`二次开发,不仅具备原模块的所有功能,还支持HTTP-FLV播放、GOP缓存、虚拟主机等功能。安装步骤包括:确认Nginx版本、下载相应版本的Nginx与模块源码、重新编译Nginx并加入新模块、验证模块安装成功。特别注意,此模块已包含`nginx-rtmp-module`功能,无需重复编译安装。
163 1
|
3月前
|
JSON API 数据格式
Python网络编程:HTTP请求(requests模块)
在现代编程中,HTTP请求几乎无处不在。无论是数据抓取、API调用还是与远程服务器进行交互,HTTP请求都是不可或缺的一部分。在Python中,requests模块被广泛认为是发送HTTP请求的最简便和强大的工具之一。本文将详细介绍requests模块的功能,并通过一个综合示例展示其应用。
|
3月前
|
缓存 运维 Serverless
函数计算产品使用问题之怎么优化HTTP Server的启动速度
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
3月前
|
Python
【Azure 应用服务】Azure Function HTTP Trigger 遇见奇妙的500 Internal Server Error: Failed to forward request to http://169.254.130.x
【Azure 应用服务】Azure Function HTTP Trigger 遇见奇妙的500 Internal Server Error: Failed to forward request to http://169.254.130.x
|
4月前
|
网络协议 程序员 应用服务中间件
Swoole与Go系列教程之HTTP服务的应用
PHP 曾是Web开发领域佼佼者,随着业务壮大,异步和高并发方面不足显现。Swoole 曾经尝试填补空白,但局限性也比较的明显。Go 语言的崛起,简洁语法和并发优势吸引大厂使用,吸引了大多数程序员的转型。
1004 0
Swoole与Go系列教程之HTTP服务的应用
|
4月前
|
API Python
首先,我们导入了`http.client`模块,它是Python标准库中的一个模块,用于创建和发送HTTP请求。
首先,我们导入了`http.client`模块,它是Python标准库中的一个模块,用于创建和发送HTTP请求。

热门文章

最新文章