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

Swoole 源码分析之 Http Server 模块的更多相关文章

  1. jQuery1.9.1源码分析--数据缓存Data模块

    jQuery1.9.1源码分析--数据缓存Data模块 阅读目录 jQuery API中Data的基本使用方法介绍 jQuery.acceptData(elem)源码分析 jQuery.data(el ...

  2. jQuery 源码分析(十) 数据缓存模块 data详解

    jQuery的数据缓存模块以一种安全的方式为DOM元素附加任意类型的数据,避免了在JavaScript对象和DOM元素之间出现循环引用,以及由此而导致的内存泄漏. 数据缓存模块为DOM元素和JavaS ...

  3. Hadoop2源码分析-HDFS核心模块分析

    1.概述 这篇博客接着<Hadoop2源码分析-RPC机制初识>来讲述,前面我们对MapReduce.序列化.RPC进行了分析和探索,对Hadoop V2的这些模块都有了大致的了解,通过对 ...

  4. 【Canal源码分析】Canal Server的启动和停止过程

    本文主要解析下canal server的启动过程,希望能有所收获. 一.序列图 1.1 启动 1.2 停止 二.源码分析 整个server启动的过程比较复杂,看图难以理解,需要辅以文字说明. 首先程序 ...

  5. Tornado源码分析 --- 静态文件处理模块

    每个web框架都会有对静态文件的处理支持,下面对于Tornado的静态文件的处理模块的源码进行分析,以加强自己对静态文件处理的理解. 先从Tornado的主要模块 web.py 入手,可以看到在App ...

  6. Python 源码分析:queue 队列模块

    起步 queue 模块提供适用于多线程编程的先进先出(FIFO)数据结构.因为它是线程安全的,所以多个线程很轻松地使用同一个实例. 源码分析 先从初始化的函数来看: 从这初始化函数能得到哪些信息呢?首 ...

  7. jQuery 源码分析(十六) 事件系统模块 底层方法 详解

    jQuery事件系统并没有将事件监听函数直接绑定到DOM元素上,而是基于数据缓存模块来管理监听函数的,事件模块代码有点多,我把它分为了三个部分:分底层方法.实例方法和便捷方法.ready事件来讲,好理 ...

  8. jQuery 源码分析(十三) 数据操作模块 DOM属性 详解

    jQuery的属性操作模块总共有4个部分,本篇说一下第2个部分:DOM属性部分,用于修改DOM元素的属性的(属性和特性是不一样的,一般将property翻译为属性,attribute翻译为特性) DO ...

  9. Eureka 源码分析之 Eureka Server

    文章首发于公众号<程序员果果> 地址 : https://mp.weixin.qq.com/s/FfJrAGQuHyVrsedtbr0Ihw 简介 上一篇文章<Eureka 源码分析 ...

  10. jQuery源码分析(九) 异步队列模块 Deferred 详解

    deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等.(P.s:紧跟上一节:https://www.cnblogs.com/grea ...

随机推荐

  1. 前端vue监听activeMQ消息后端推送消息--实战

    需求 : 工厂员工完成某道工序后,需要将消息推送给 检查人员 也可以使用 WebSockets ,前端更容易实现 思路: 使用activeMQ推送消息,前端实时接收消息 实现 : 1.基于spring ...

  2. stack smashing--canary报错利用

    stack smashing--canary报错利用 一般这种都是考察点比较狭窄,因为这个漏洞在libc2.23以上就被修复了,漏洞产生的原因是因为当覆盖掉canary的时候程序会报错,程序会执行 _ ...

  3. 给picgo上传的图片加个水印

    之前给大家介绍了picgo和免费的图床神器.我们本可以开开心心的进行markdown写作了. 但是总是会有那么一些爬虫网站过来爬你的文章,还把你的文章标明是他们的原著.咋办呢?这里有一个好的办法就是把 ...

  4. BURP保存多个监听器配置

    "感谢您阅读本篇博客!如果您觉得本文对您有所帮助或启发,请不吝点赞和分享给更多的朋友.您的支持是我持续创作的动力,也欢迎留言交流,让我们一起探讨技术,共同成长!谢谢!" 前言 在进 ...

  5. 力扣628(java)-三个数的最大乘积(简单)

    题目: 给你一个整型数组 nums ,在数组中找出由三个数组成的最大乘积,并输出这个乘积. 示例 1: 输入:nums = [1,2,3]输出:6示例 2: 输入:nums = [1,2,3,4]输出 ...

  6. 几种常见的MySQL/PolarDB-MySQL回收表空间方法对比

    简介: 当我们频繁的删除表中的数据后,碎片就会变多,有经验的DBA就会回收表空间,回收表空间有好几种方式,我们要选择哪一种呢? 背景 为什么需要回收表空间?任何一个存储或您购买的实例规格都有容量限制, ...

  7. Alibaba/IOC-golang 正式开源 ——打造服务于go开发者的IOC框架

    简介: IOC(inversion of control)即控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度.IOC-golang 是一款服务于Go语言开发者的依赖注入框架 ...

  8. 阿里巴巴云原生 etcd 服务集群管控优化实践

    简介: 这些年,阿里云原生 etcd 服务发生了翻天覆地的变化,这篇文章主要分享一下 etcd 服务在面对业务量大规模增长下遇到的问题以及我们是如何解决的,希望对读者了解 etcd 的使用和管控运维提 ...

  9. TDA-04D8变送器数据上报阿里云

    ​简介:本文将以TDA-04D8变送器作为采集对象,使用海创微联采集控制系统对TDA-04D8变送器进行采集,然后将设备上的毛重.净重.皮重数据采集上传到阿里云物联网平台,阿里云物联网平台将数据实时可 ...

  10. 顺丰科技 Hudi on Flink 实时数仓实践

    ​简介: 介绍了顺丰科技数仓的架构,趟过的一些问题.使用 Hudi 来优化整个 job 状态的实践细节,以及未来的一些规划. 本文作者为刘杰,介绍了顺丰科技数仓的架构,趟过的一些问题.使用 Hudi ...