创建一个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 *);
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。