首发原文链接: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_onReceiveserv->onReceive = php_swoole_http_server_onReceive;} else {...// 否则,就是默认回调到 Swoole Server onReceive 的方法 php_swoole_server_onReceiveserv->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 server
和 websocket 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_onHandshakeif (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]
代表的是 $request
,args[1]
代表的是 $response
。
对于 Http Server
模块来说,最重要的就是这个回调方法了,因为所有的业务逻辑都是在这里进行实现的。
总结
想要了解到 Http Server
的全貌,其实只要把那张整体的实现图看懂就足以了。但是,如果想要有足够的深度,那么就还需要深入 Swoole
的源代码中,就着源码自行分析一遍。同时,也希望这一次的分析,能够给大家带来对 Swoole
更多的一些了解。并不要求要深刻的掌握,因为,很多的事情都不可能一蹴而就。从自己的实力出发,勿忘初心。