首页 > 编程语言 >Swoole 源码分析之 Http Server 模块

Swoole 源码分析之 Http Server 模块

时间:2024-02-18 16:55:44浏览次数:35  
标签:http swoole Swoole server 源码 onReceive Http Server

首发原文链接:Swoole 源码分析之 Http Server 模块

Swoole 源码分析之 Http Server 模块

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 更多的一些了解。并不要求要深刻的掌握,因为,很多的事情都不可能一蹴而就。从自己的实力出发,勿忘初心。

标签:http,swoole,Swoole,server,源码,onReceive,Http,Server
From: https://www.cnblogs.com/yxhblogs/p/17968843

相关文章

  • 用python脚本自动发送钉钉消息出现服务器异常的报错: HTTPSConnectionPool(host='oapi.
    一、问题描述执行python脚本发送钉钉消息,出现报错:HTTPSConnectionPool(host='oapi.dingtalk.com',port=443):Maxretriesexceededwithurl:/robot/send?access_token=43df999582e899dc6815c9d6346c9d253060259625c92e4f166e25ea58e5bdb5&timestamp=1708242748918&sign......
  • Http和Tcp的区别
    Http和Tcp的区别1.HTTP协议是HyperTextTransferProtocol(超文本传输协议)的缩写,是用于从万维网(WWW:WorldWideWeb)服务器传输超文本到本地浏览器的传送协议。2.HTTP是一个基于TCP/IP通信协议来传递数据(HTML文件,图片文件,查询结果等)1、简单快速:客户向服务器请求服务时,只需......
  • C# 通过socket 实现HTTP
    http是最常用的互联网协议。http协议是基于tcp协议的,今天我打算使用C#语言,基于tcp编程自己实现http。适用于linux及mac系统。windows的tcp编程我没使用,但原理都是一样的。如果对网络编程不熟悉的,可以先熟悉一下网络编程。通过此例子,一定会对http协议的理解更上一层楼。首先得有......
  • 25个常见的python系统设计源码(python+mysql+vue)
    收集整理了25个常见的python系统设计源码。可以用于课程作业或者毕业设计。所有系统都带源码和文档。1.网上商城系统这是一个基于python+vue开发的商城网站,平台采用B/S结构,后端采用主流的Python语言进行开发,前端采用主流的Vue.js进行开发。整个平台包括前台和后台两个部分。......
  • https://www.luogu.com.cn/problem/P8762
    引言题目链接:https://www.luogu.com.cn/problem/P8762思路首先可以发现到第i个数列末尾时,其前面总共有\(i*(i+1)/2\)个数所以可以用二分判断l和r处于第n1和n2个数列中,则前面完整的序列个数即为n1-1和n2-1。假设完整的序列为n个,则这n个序列的和为n......
  • 100 行代码实现用户登录注册与 RESTful 接口 - 手把手教程附 Python 源码
    在开发大多数应用时,用户系统都是必不可少的部分,而我们总是需要开发围绕用户的登录,注册,获取,更新等接口。在这篇文章将带你用一百多行代码简洁地实现一套这样的用户鉴权与RESTful接口,并使用Session来处理用户的登录登出我们将使用UtilMeta框架完成接口开发,这是一个开源的Py......
  • IDEA 2024.1:Spring支持增强、GitHub Action支持增强、更新HTTP Client等
    有段时间没有更新IDEA了,早上看到IntelliJIDEA2024.1EAP5发布的邮件提示,瞄了一眼,发现真的是越来越强了,其中不少功能对我来说还是非常有用的。也许这些能力对关注DD的小伙伴也有帮助,所以搞篇博客介绍和推荐一下。Spring、Quarkus等主流框架的支持增强SearchEverywhere功能......
  • NumPyML 源码解析(四)
    numpy-ml\numpy_ml\neural_nets\utils\__init__.py"""神经网络特定的常见辅助函数。``neural_nets.utils`模块包含神经网络特定的辅助函数,主要用于处理CNNs。"""#从当前目录下的utils模块中导入所有内容from.utilsimport*WrappersThewrappers.pymoduleimple......
  • NumPyML 源码解析(七)
    numpy-ml\numpy_ml\trees\gbdt.py#导入numpy库并重命名为npimportnumpyasnp#从当前目录下的dt模块中导入DecisionTree类#从当前目录下的losses模块中导入MSELoss和CrossEntropyLoss类from.dtimportDecisionTreefrom.lossesimportMSELoss,CrossEn......
  • NumPyML 源码解析(二)
    ActivationFunctionsTheactivationsmoduleimplementsseveralcommonactivationfunctions:Rectifiedlinearunits(ReLU)(Hahnloseretal.,2000)Leakyrectifiedlinearunits(Maas,Hannun,&Ng,2013)Exponentiallinearunits(Clevert,Unterthiner,......