资讯专栏INFORMATION COLUMN

Swoole源码学习-一个server的创建

macg0406 / 696人阅读

摘要:若参数和字符串相等则返回。大于则返回大于的值,小于则返回小于的值。下面的中是增加一个这里我们显然设置了主机和端口暂且跳过在这里我们看到调用了中的方法并且将初始化好的等传了进去,我们追进去线程数

创建一个server

今天我们来研究一下swoole中server相关的源码(版本是4.3.1),首先我们先从一段简单代码开始

$http = new SwooleHttpServer("127.0.0.1",9501);

$http->on("request", function ($request, $response) {
    $response->header("Content-Type", "text/html; charset=utf-8");
    $response->end("

Hello Swoole. #".rand(1000, 9999)."

"); }); $http->start();

上面的代码在本地起了一个服务监听9501端口,下图是浏览器请求的接口

好了我们开始,首先分析一下上面的代码其实是调用了一下SwooleHttpServer这个类,然后分别调用了下面三个方法:

__construct(初始化)

on(注册事件)

start(启动server)

这三个方法都在./swoole_server.cc中,初始化是在MINT阶段加载swoole时执行的./swoole_http_server.cc中的swoole_http_server_init方法

void swoole_http_server_init(int module_number)
{
    SWOOLE_INIT_CLASS_ENTRY_EX(swoole_http_server, "SwooleHttpServer", "swoole_http_server", NULL, NULL, swoole_server);
    ...
}

上面可以看到一些命名,注意最后的参数,这里继承了swoole_server,所以上面几个方法的注册实际在swoole_server.c中

void swoole_server_init(int module_number)
{
    SWOOLE_INIT_CLASS_ENTRY(swoole_server, "SwooleServer", "swoole_server", NULL, swoole_server_methods);
    ......
}
static zend_function_entry swoole_server_methods[] = {
    PHP_ME(swoole_server, __construct, arginfo_swoole_server__construct, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_server, on, arginfo_swoole_server_on, ZEND_ACC_PUBLIC)
    PHP_ME(swoole_server, start, arginfo_swoole_void, ZEND_ACC_PUBLIC)
    ......
}

其他的暂且略过,上面可以看到swoole_server_methods中定义的这三个方法,我们首先来看下__construct这个方法,首先在arginfo_swoole_server__construct中定义了可以接收的参数

ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_server__construct, 0, 0, 1)
    ZEND_ARG_INFO(0, host)//主机
    ZEND_ARG_INFO(0, port)//端口
    ZEND_ARG_INFO(0, mode)//模式
    ZEND_ARG_INFO(0, sock_type)//sock类型
ZEND_END_ARG_INFO()

好了下面我们正式进入__construct方法

__construct(server的构造)
static PHP_METHOD(swoole_server, __construct)
{
    size_t host_len = 0;
    char *serv_host;
    zend_long sock_type = SW_SOCK_TCP;//定义默认socket类型SW_SOCK_TCP
    zend_long serv_port = 0;
    zend_long serv_mode = SW_MODE_PROCESS;//定义默认模式SW_MODE_PROCESS

    //only cli env
    if (!SWOOLE_G(cli))
    {
        swoole_php_fatal_error(E_ERROR, "swoole_server only can be used in PHP CLI mode.");
        RETURN_FALSE;
    }

    if (SwooleG.main_reactor)//初始化main_reactor
    {
        SwooleG.origin_main_reactor = SwooleG.main_reactor;
        SwooleG.main_reactor = NULL;
    }

    if (SwooleG.serv != NULL)//判断server状态
    {
        swoole_php_fatal_error(E_ERROR, "server is running. unable to create swoole_server.");
        RETURN_FALSE;
    }
   
    swServer *serv = (swServer *) sw_malloc(sizeof (swServer));

上面是分配一块swServer结构的内存,往下走

    swServer_init(serv);

初始化 server,我们跟进去,代码在./src/server/master.cc中,在这里对swServer中的一些配置做了初始化

void swServer_init(swServer *serv)
{
    swoole_init();

上面的swoole_init重点对SwooleG进行初始化存放swoole一些全局信息,包括

running (启动状态,这里设置为1)

enable_coroutine(协程启用状态,这里设置为1)

log_fd (日志fd)

write_log

fatal_error

cpu_num

uname

pid

memory_pool

max_sockets (最大sockets数量 这里是1024)

...

往下走

    //对serv清零
    bzero(serv, sizeof(swServer));
    //设置默认模式为SW_MODE_BASE
    serv->factory_mode = SW_MODE_BASE;
    //设置reactor数量,这里是cpu核数 和 SW_REACTOR_MAX_THREAD(8) 取小的
    serv->reactor_num = SW_REACTOR_NUM > SW_REACTOR_MAX_THREAD ? SW_REACTOR_MAX_THREAD : SW_REACTOR_NUM;
    
    serv->dispatch_mode = SW_DISPATCH_FDMOD;
    //设置默认worker_num跟cpu核数相同
    serv->worker_num = SW_CPU_NUM;
    //设置最大链接数,这里SW_MAX_CONNECTION是100000 和 上面设置的1024 取小显然 1024
    serv->max_connection = SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets);
    //设置最大等待时间,这里SW_WORKER_MAX_WAIT_TIME为30
    serv->max_wait_time = SW_WORKER_MAX_WAIT_TIME;

    //http server
    serv->http_parse_post = 1;
    serv->http_compression = 1;
    serv->http_compression_level = 1; // Z_BEST_SPEED
    serv->upload_tmp_dir = sw_strdup("/tmp");

    serv->buffer_input_size = SW_BUFFER_INPUT_SIZE;
    serv->buffer_output_size = SW_BUFFER_OUTPUT_SIZE;

    serv->task_ipc_mode = SW_TASK_IPC_UNIXSOCK;

    serv->enable_coroutine = 1;

    /**
     * alloc shared memory(分配共享内存)
     */
    serv->stats = (swServerStats *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swServerStats));
    if (serv->stats == NULL)
    {
        swError("[Master] Fatal Error: failed to allocate memory for swServer->stats.");
    }
    serv->gs = (swServerGS *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swServerGS));
    if (serv->gs == NULL)
    {
        swError("[Master] Fatal Error: failed to allocate memory for swServer->gs.");
    }
    将server放入SwooleG中
    SwooleG.serv = serv;
}

好了,server初始化完成,下面再回到__construct方法中

    //解析参数校验参数格式
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lll", &serv_host, &host_len, &serv_port, &serv_mode, &sock_type) == FAILURE)
    {
        swoole_php_fatal_error(E_ERROR, "invalid swoole_server parameters.");
        RETURN_FALSE;
    }
    //如果不是SW_MODE_BASE或者SW_MODE_PROCESS模式报错,这里可以看到目前server支持这两种模式
    if (serv_mode != SW_MODE_BASE && serv_mode != SW_MODE_PROCESS)
    {
        swoole_php_fatal_error(E_ERROR, "invalid $mode parameters %d.", (int) serv_mode);
        RETURN_FALSE;
    }
    //SW_MODE_BASE模式重置reactor_num和worker_num为1
    if (serv_mode == SW_MODE_BASE)
    {
        serv->reactor_num = 1;
        serv->worker_num = 1;
    }
    //将serv_mode赋给factory_mode
    serv->factory_mode = serv_mode;
    //php_sw_server_callbacks清零
    bzero(php_sw_server_callbacks, sizeof(zval*) * PHP_SWOOLE_SERVER_CALLBACK_NUM);

函数 strcasecmp()用来比较参数s1和s2字符串,比较时会自动忽略大小写的差异。若参数s1和s2字符串相等则返回0。s1大于s2则返回大于0 的值,s1 小于s2 则返回小于0的值。下面的if中是增加一个systemd socket.这里我们显然设置了主机和端口暂且跳过

    if (serv_port == 0 && strcasecmp(serv_host, "SYSTEMD") == 0)
    {
        if (swServer_add_systemd_socket(serv) <= 0)
        {
            swoole_php_fatal_error(E_ERROR, "failed to add systemd socket.");
            RETURN_FALSE;
        }
    }
    else
    {

        swListenPort *port = swServer_add_port(serv, sock_type, serv_host, serv_port);

在这里我们看到调用了master.cc中的swServer_add_port方法并且将初始化好的server,sock_type,serv_host,serv_port等传了进去,我们追进去

        if (!port)
        {
            zend_throw_exception_ex(
                swoole_exception_ce_ptr, errno,
                "failed to listen server port[%s:" ZEND_LONG_FMT "]. Error: %s[%d].",
                serv_host, serv_port, strerror(errno), errno
            );
            RETURN_FALSE;
        }
    }

    zval *server_object = getThis();

    zval connection_iterator_object;
    object_init_ex(&connection_iterator_object, swoole_connection_iterator_ce_ptr);
    zend_update_property(swoole_server_ce_ptr, server_object, ZEND_STRL("connections"), &connection_iterator_object);

    swConnectionIterator *i = (swConnectionIterator *) emalloc(sizeof(swConnectionIterator));
    bzero(i, sizeof(swConnectionIterator));
    i->serv = serv;
    swoole_set_object(&connection_iterator_object, i);

    zend_update_property_stringl(swoole_server_ce_ptr, server_object, ZEND_STRL("host"), serv_host, host_len);
    zend_update_property_long(swoole_server_ce_ptr, server_object, ZEND_STRL("port"), (long) serv->listen_list->port);
    zend_update_property_long(swoole_server_ce_ptr, server_object, ZEND_STRL("mode"), serv->factory_mode);
    zend_update_property_long(swoole_server_ce_ptr, server_object, ZEND_STRL("type"), sock_type);
    swoole_set_object(server_object, serv);

    zval *ports = sw_malloc_zval();
    array_init(ports);
    server_port_list.zports = ports;

#ifdef HT_ALLOW_COW_VIOLATION
    HT_ALLOW_COW_VIOLATION(Z_ARRVAL_P(ports));
#endif

    swListenPort *ls;
    LL_FOREACH(serv->listen_list, ls)
    {
        php_swoole_server_add_port(serv, ls);
    }

    server_port_list.primary_port = (swoole_server_port_property *) serv->listen_list->ptr;

    zend_update_property(swoole_server_ce_ptr, server_object, ZEND_STRL("ports"), ports);
}
struct _swServer
{
    /**
线程数
     * reactor thread/process num
     */
    uint16_t reactor_num;
    /**
     * worker process num
     */
    uint16_t worker_num;
    /**
     * The number of pipe per reactor maintenance
     */
    uint16_t reactor_pipe_num;

    uint8_t factory_mode;

    uint8_t dgram_port_num;

    /**
     * package dispatch mode
     */
    uint8_t dispatch_mode;

    /**
     * No idle work process is available.
     */
    uint8_t scheduler_warning;

    int worker_uid;
    int worker_groupid;

    /**
     * max connection num
     */
    uint32_t max_connection;

    /**
     * worker process max request
     */
    uint32_t max_request;

    int udp_socket_ipv4;
    int udp_socket_ipv6;

    uint32_t max_wait_time;

    /*----------------------------Reactor schedule--------------------------------*/
    uint16_t reactor_round_i;
    uint16_t reactor_next_i;
    uint16_t reactor_schedule_count;

    sw_atomic_t worker_round_id;

    /**
     * run as a daemon process
     */
    uint32_t daemonize :1;
    /**
     * have dgram socket
     */
    uint32_t have_dgram_sock :1;
    /**
     * have stream socket
     */
    uint32_t have_stream_sock :1;
    /**
     * open cpu affinity setting
     */
    uint32_t open_cpu_affinity :1;
    /**
     * disable notice when use SW_DISPATCH_ROUND and SW_DISPATCH_QUEUE
     */
    uint32_t disable_notify :1;
    /**
     * discard the timeout request
     */
    uint32_t discard_timeout_request :1;
    /**
     * parse x-www-form-urlencoded data
     */
    uint32_t http_parse_post :1;
    /**
     * http content compression
     */
    uint32_t http_compression :1;
    /**
     * handle static files
     */
    uint32_t enable_static_handler :1;
    /**
     * enable onConnect/onClose event when use dispatch_mode=1/3
     */
    uint32_t enable_unsafe_event :1;
    /**
     * waiting for worker onConnect callback function to return
     */
    uint32_t enable_delay_receive :1;
    /**
     * asynchronous reloading
     */
    uint32_t reload_async :1;
    /**
     * enable coroutine in task worker
     */
    uint32_t task_enable_coroutine :1;
    /**
     * slowlog
     */
    uint32_t trace_event_worker :1;
    /**
     * yield coroutine when the output buffer is full
     */
    uint32_t send_yield :1;
    /**
     * enable coroutine
     */
    uint32_t enable_coroutine :1;
    /**
     * disable multi-threads
     */
    uint32_t single_thread :1;
    /**
     *  heartbeat check time
     */
    uint16_t heartbeat_idle_time;
    uint16_t heartbeat_check_interval;

    int *cpu_affinity_available;
    int cpu_affinity_available_num;

    double send_timeout;

    uint16_t listen_port_num;
    time_t reload_time;
    time_t warning_time;

    /* buffer output/input setting*/
    uint32_t buffer_output_size;
    uint32_t buffer_input_size;

    void *ptr2;
    void *private_data_3;

    swFactory factory;
    swListenPort *listen_list;
    pthread_t heartbeat_pidt;

    /**
     *  task process
     */
    uint16_t task_worker_num;
    uint8_t task_ipc_mode;
    uint16_t task_max_request;
    swPipe *task_notify;
    swEventData *task_result;

    /**
     * user process
     */
    uint16_t user_worker_num;
    swUserWorker_node *user_worker_list;
    swHashMap *user_worker_map;
    swWorker *user_workers;

    swReactorThread *reactor_threads;
    swWorker *workers;

    swChannel *message_box;

    swServerStats *stats;
    swServerGS *gs;

#ifdef HAVE_PTHREAD_BARRIER
    pthread_barrier_t barrier;
#endif

    swConnection *connection_list;
    swSession *session_list;

    /**
     * temporary directory for HTTP uploaded file.
     */
    char *upload_tmp_dir;
    /**
     * http compression level for gzip/br
     */
    uint8_t http_compression_level;
    /**
     * http static file directory
     */
    char *document_root;
    uint16_t document_root_len;
    /**
     * master process pid
     */
    char *pid_file;
    /**
     * stream
     */
    char *stream_socket;
    int stream_fd;
    swProtocol stream_protocol;
    int last_stream_fd;
    swLinkedList *buffer_pool;

#ifdef SW_BUFFER_RECV_TIME
    double last_receive_usec;
#endif

    int manager_alarm;

    /**
     * message queue key
     */
    uint64_t message_queue_key;
    /**
     * slow request log
     */
    uint8_t request_slowlog_timeout;
    FILE *request_slowlog_file;

    swReactor *reactor_ptr; //Main Reactor
    swFactory *factory_ptr; //Factory

    swLinkedList *hooks[SW_MAX_HOOK_TYPE];

    void (*onStart)(swServer *serv);
    void (*onManagerStart)(swServer *serv);
    void (*onManagerStop)(swServer *serv);
    void (*onShutdown)(swServer *serv);
    void (*onPipeMessage)(swServer *, swEventData *);
    void (*onWorkerStart)(swServer *serv, int worker_id);
    void (*onWorkerStop)(swServer *serv, int worker_id);
    void (*onWorkerExit)(swServer *serv, int worker_id);
    void (*onWorkerError)(swServer *serv, int worker_id, pid_t worker_pid, int exit_code, int signo);
    void (*onUserWorkerStart)(swServer *serv, swWorker *worker);
    /**
     * Client
     */
    int (*onReceive)(swServer *, swEventData *);
    int (*onPacket)(swServer *, swEventData *);
    void (*onClose)(swServer *serv, swDataHead *);
    void (*onConnect)(swServer *serv, swDataHead *);
    void (*onBufferFull)(swServer *serv, swDataHead *);
    void (*onBufferEmpty)(swServer *serv, swDataHead *);
    /**
     * Task Worker
     */
    int (*onTask)(swServer *serv, swEventData *data);
    int (*onFinish)(swServer *serv, swEventData *data);
    /**
     * Server method
     */
    int (*send)(swServer *serv, int session_id, void *data, uint32_t length);
    int (*sendfile)(swServer *serv, int session_id, char *file, uint32_t l_file, off_t offset, size_t length);
    int (*sendwait)(swServer *serv, int session_id, void *data, uint32_t length);
    int (*close)(swServer *serv, int session_id, int reset);
    int (*notify)(swServer *serv, swConnection *conn, int event);
    int (*feedback)(swServer *serv, int session_id, int event);

    int (*dispatch_func)(swServer *, swConnection *, swSendData *);
};

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31580.html

相关文章

  • Swoft 源码剖析 - Swoole和Swoft那些事 (Http/Rpc服务篇)

    摘要:和服务关系最密切的进程是中的进程组,绝大部分业务处理都在该进程中进行。随后触发一个事件各组件通过该事件进行配置文件加载路由注册。事件每个请求到来时仅仅会触发事件。服务器生命周期和服务基本一致,详情参考源码剖析功能实现 作者:bromine链接:https://www.jianshu.com/p/4c0...來源:简书著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版。S...

    张汉庆 评论0 收藏0
  • Easyswoole 源码学习和个人解析 目录

    摘要:易用稳定,本次想通过对的学习和个人解析,吸收框架的思想和设计知识,加强自己对的认知和理解。当然,笔者能力水平有限,后续的文章如有错误,还请指出和谅解。目录如下后续添加文章都会记录在此服务启动过程以及主体设计流程源码解析 前言 swoole是什么?官网的原话介绍是这样的: Swoole 使用纯 C 语言编写,提供了 PHP 语言的异步多线程服务器,异步 TCP/UDP 网络客户端,异步 ...

    CoXie 评论0 收藏0
  • swoole——从入门到放弃(一)

    摘要:进程可以使用函数向进程投递新的任务。当前的进程在调用回调函数时会将进程状态切换为忙碌,这时将不再接收新的,当函数返回时会将进程状态切换为空闲然后继续接收新的。当进程投递的任务在中完成时,进程会通过方法将任务处理的结果发送给进程。 swoole——从入门到放弃(一) 一、swoole的源码包安装 下载swoole源码:git clone https://gitee.com/swoole...

    morgan 评论0 收藏0
  • Swoole源码研究】浅析swooleserver实现

    摘要:的部分是基于以及协议的。例如父进程向中写入子进程从中读取子进程向中写入父进程从中读取。默认使用对进程进行分配交给对应的线程进行监听线程收到某个进程的数据后会进行处理值得注意的是这个线程可能并不是发送请求的那个线程。 作者:施洪宝 一. 基础知识 1.1 swoole swoole是面向生产环境的php异步网络通信引擎, php开发人员可以利用swoole开发出高性能的server服务。...

    rainyang 评论0 收藏0
  • Swoole入门到实战(一):PHP7&Swoole源码安装、玩转网络通信引擎、异步非堵塞I

    摘要:服务重点基本概述协议是基于的一种新的网络协议。被调用者通过状态通知机制等来通知调用者,或通过回调函数来处理结果阻塞和非阻塞关注的是调用者等待被调用者返回调用结果时的状态。 一、PHP7源码安装和Swoole源码编译安装 1.1 PHP7源码安装 1.1.1 获取源码与安装     获取PHP7源码:www.php.net tar -xzvf ... # 解压命令 ./configur...

    weakish 评论0 收藏0

发表评论

0条评论

macg0406

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<