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