创建一个server

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

$http = new Swoole\Http\Server("127.0.0.1",9501);

$http->on('request', function ($request, $response) {
    $response->header("Content-Type", "text/html; charset=utf-8");
    $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>");
});

$http->start();

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

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

  • __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, "Swoole\\Http\\Server", "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, "Swoole\\Server", "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 *);
};

申振
2 声望0 粉丝