From 090f27fb43020b80d46370efdcf478391a8d3aad Mon Sep 17 00:00:00 2001 From: twosee Date: Fri, 27 Apr 2018 21:26:26 +0800 Subject: [PATCH] Initialization. --- .gitignore | 3 + README.md | 685 ++++++++++++++++++ ...45\351\227\250\346\214\207\345\274\225.md" | 49 ++ ...57\345\242\203\344\276\235\350\265\226.md" | 66 ++ ...00\346\272\220\351\241\271\347\233\256.md" | 46 ++ "doc/1.11.1 - \346\241\206\346\236\266.md" | 113 +++ "doc/1.11.2 - \345\267\245\345\205\267.md" | 14 + ...- \345\210\206\345\270\203\345\274\217.md" | 53 ++ ...32\344\277\241\345\215\217\350\256\256.md" | 17 + ...67\344\270\216\346\241\210\344\276\213.md" | 134 ++++ ...24\347\275\221\351\241\271\347\233\256.md" | 23 + ...21\347\273\234\346\270\270\346\210\217.md" | 22 + ...256\257\357\274\210Tencent\357\274\211.md" | 15 + ...2\246\357\274\210Baidu.com\357\274\211.md" | 14 + ...05\346\226\207\351\233\206\345\233\242.md" | 3 + ...51\345\223\224\345\223\251\357\274\211.md" | 3 + ...\224\357\274\210chelun.com\357\274\211.md" | 11 + ...70\346\210\217\347\244\276\345\214\272.md" | 42 ++ ...31\350\257\257\346\212\245\345\221\212.md" | 97 +++ ...70\350\247\201\351\227\256\351\242\230.md" | 15 + ...70\350\247\201\351\227\256\351\242\230.md" | 12 + ... my_global.h: No such file or directory.md | 16 + ... symbol: __sync_bool_compare_and_swap_4.md | 15 + ...72\347\241\200\347\237\245\350\257\206.md" | 31 + ...02\347\224\250\345\234\272\346\231\257.md" | 13 + ...\272\347\216\260zend_mm_heap corrupted.md" | 64 ++ ...15\345\255\227\347\224\261\346\235\245.md" | 27 + ...\210\266swoole\347\211\210\346\234\254.md" | 8 + ...-m\344\270\255\346\262\241\346\234\211.md" | 19 + ...16\344\271\210\345\233\236\344\272\213.md" | 8 + ...- Resource temporarily unavailable [11].md | 12 + ... - Cannot assign requested address [99].md | 21 + ...52\344\272\233\344\274\230\345\212\277.md" | 31 + ...52\344\272\233\344\274\230\345\212\277.md" | 26 + ...4.9 - pcre.h: No such file or directory.md | 23 + ...26\350\257\221\345\256\211\350\243\205.md" | 51 ++ ...26\350\257\221\345\217\202\346\225\260.md" | 66 ++ ...70\350\247\201\351\224\231\350\257\257.md" | 65 ++ ...53\351\200\237\350\265\267\346\255\245.md" | 18 + ...CP\346\234\215\345\212\241\345\231\250.md" | 57 ++ ...45\345\256\242\346\210\267\347\253\257.md" | 61 ++ ...61\344\272\253\346\225\260\346\215\256.md" | 57 ++ ...DP\346\234\215\345\212\241\345\231\250.md" | 37 + ...eb\346\234\215\345\212\241\345\231\250.md" | 32 + ...et\346\234\215\345\212\241\345\231\250.md" | 70 ++ ...56\345\256\232\346\227\266\345\231\250.md" | 22 + ...02\346\255\245\344\273\273\345\212\241.md" | 42 ++ ...CP\345\256\242\346\210\267\347\253\257.md" | 49 ++ ...CP\345\256\242\346\210\267\347\253\257.md" | 41 ++ ...17\350\256\256\350\256\276\350\256\241.md" | 47 ++ ...26\347\250\213\351\241\273\347\237\245.md" | 73 ++ ...57\347\232\204\345\275\261\345\223\215.md" | 22 + ...23\345\255\230\346\270\205\347\220\206.md" | 5 + ...nd\351\232\217\346\234\272\346\225\260.md" | 25 + ...64\346\226\260\350\256\260\345\275\225.md" | 35 + doc/1.5.1 - 2.1.2.md | 7 + doc/1.5.10 - 1.9.23.md | 4 + doc/1.5.11 - 1.9.22.md | 4 + doc/1.5.12 - 1.9.21.md | 9 + doc/1.5.2 - 1.10.3.md | 14 + doc/1.5.3 - 1.10.2.md | 10 + doc/1.5.4 - 2.1.1.md | 5 + doc/1.5.5 - 1.10.1.md | 8 + doc/1.5.6 - 2.0.13.md | 8 + doc/1.5.7 - 2.0.12.md | 3 + doc/1.5.8 - 2.0.11.md | 6 + doc/1.5.9 - 1.10.0.md | 19 + ...71\346\200\247\344\275\277\347\224\250.md" | 2 + ...27\347\232\204\344\275\277\347\224\250.md" | 119 +++ ...\223\345\212\240\345\257\206TCP-Server.md" | 21 + ...22\345\256\232\346\227\266\345\231\250.md" | 19 + ...52\345\212\250\345\210\206\345\214\205.md" | 40 + ...\273\243finish\345\207\275\346\225\260.md" | 14 + ...27\347\232\204\344\275\277\347\224\250.md" | 73 ++ ...10\346\201\257\351\230\237\345\210\227.md" | 98 +++ ...14\201 Stream \346\250\241\345\274\217.md" | 35 + ...43\346\236\220\345\237\237\345\220\215.md" | 55 ++ ...15\345\220\257\347\211\271\346\200\247.md" | 48 ++ ...05\346\227\266\346\234\272\345\210\266.md" | 53 ++ ...45\345\256\242\346\210\267\347\253\257.md" | 82 +++ ...00\346\234\211\350\277\236\346\216\245.md" | 20 + ...55\344\275\277\347\224\250swoole_table.md" | 27 + ...14\201sendfile\346\216\245\345\217\243.md" | 26 + ...56\350\267\257\347\272\277\345\233\276.md" | 31 + .../1.8 - php.ini\351\200\211\351\241\271.md" | 57 ++ ...02\346\225\260\350\260\203\346\225\264.md" | 101 +++ "doc/10 - \345\215\217\347\250\213 Server.md" | 68 ++ ...71\346\263\225\345\210\227\350\241\250.md" | 43 ++ doc/10.1.1 - getDefer.md | 8 + doc/10.1.2 - setDefer.md | 10 + doc/10.1.3 - recv.md | 8 + doc/10.1.4 - Coroutine::create.md | 29 + doc/10.1.5 - Coroutine::getuid.md | 26 + ...66\345\217\221\350\260\203\347\224\250.md" | 57 ++ ...77\347\224\250\345\256\236\344\276\213.md" | 47 ++ ...36\347\216\260\345\216\237\347\220\206.md" | 97 +++ ...13\344\270\216\347\272\277\347\250\213.md" | 7 + ...17\347\250\213\350\260\203\345\272\246.md" | 52 ++ ...70\350\247\201\351\227\256\351\242\230.md" | 16 + ...ing level of '1000' reached, aborting!.md" | 3 + ...13\345\256\242\346\210\267\347\253\257.md" | 7 + ...71\346\263\225\345\210\227\350\241\250.md" | 35 + ...26\347\250\213\351\241\273\347\237\245.md" | 29 + ...13\345\256\242\346\210\267\347\253\257.md" | 92 +++ ...\345\234\272\346\231\257 ( ver < 2.2 ).md" | 49 ++ "doc/11 - \345\215\217\347\250\213 Client.md" | 15 + ...36\346\200\247\345\210\227\350\241\250.md" | 34 + ...71\346\263\225\345\210\227\350\241\250.md" | 22 + ...36\346\200\247\345\210\227\350\241\250.md" | 36 + ...36\346\200\247\345\210\227\350\241\250.md" | 29 + doc/11.6.1 - Coroutine::getUid.md | 16 + doc/11.6.10 - Coroutine::getaddrinfo.md | 19 + doc/11.6.11 - Coroutine::call_user_func.md | 10 + ....6.12 - Coroutine::call_user_func_array.md | 14 + doc/11.6.13 - Coroutine::exec.md | 30 + doc/11.6.14 - Coroutine::readFile.md | 31 + doc/11.6.15 - Coroutine::writeFIle.md | 32 + doc/11.6.2 - Coroutine::create.md | 62 ++ doc/11.6.3 - Coroutine::resume.md | 9 + doc/11.6.4 - Coroutine::suspend.md | 8 + doc/11.6.5 - Coroutine::fread.md | 32 + doc/11.6.6 - Coroutine::fgets.md | 31 + doc/11.6.7 - Coroutine::fwrite.md | 32 + doc/11.6.8 - Coroutine::sleep.md | 26 + doc/11.6.9 - Coroutine::gethostbyname.md | 30 + "doc/12 - \345\215\217\347\250\213 Socket.md" | 15 + "doc/13.1 - \346\226\271\346\263\225.md" | 5 + doc/13.1.1 - setHandler.md | 40 + doc/13.1.2 - format.md | 9 + "doc/13.2 - \345\270\270\351\207\217.md" | 14 + "doc/14 - \351\253\230\347\272\247.md" | 6 + ...le\347\232\204\345\256\236\347\216\260.md" | 38 + ...45\256\210100%\345\217\257\347\224\250.md" | 37 + ...55\347\272\277\351\207\215\350\277\236.md" | 64 ++ ...45\351\230\273\345\241\236\347\232\204.md" | 27 + ...60\346\215\256\347\273\223\346\236\204.md" | 2 + ...5\210\227\357\274\210Queue\357\274\211.md" | 83 +++ ...45\240\206\357\274\210Heap\357\274\211.md" | 84 +++ ...4\357\274\210SplFixedArray\357\274\211.md" | 30 + ...06\351\205\215\346\200\247\350\203\275.md" | 39 + ...\275\225\344\275\277\347\224\250Swoole.md" | 59 ++ ...\275\225\344\275\277\347\224\250Swoole.md" | 164 +++++ ...\220\206swoole\346\234\215\345\212\241.md" | 66 ++ ...55\346\226\255\350\256\276\347\275\256.md" | 27 + ...345\206\205\345\265\214\345\210\260PHP.md" | 27 + ...14.2 - Reactor\347\272\277\347\250\213.md" | 15 + ...05\345\255\230\347\256\241\347\220\206.md" | 81 +++ ...14.3 - Manager\350\277\233\347\250\213.md" | 9 + .../14.4 - Worker\350\277\233\347\250\213.md" | 35 + ...er\347\232\204\345\205\263\347\263\273.md" | 38 + ...\270\255\344\275\277\347\224\250swoole.md" | 14 + ...45\347\232\204\351\200\211\346\213\251.md" | 12 + "doc/15 - \345\205\266\344\273\226.md" | 2 + ...75\346\225\260\345\210\227\350\241\250.md" | 3 + doc/15.1.1 - swoole_set_process_name.md | 27 + doc/15.1.2 - swoole_version.md | 6 + doc/15.1.3 - swoole_strerror.md | 6 + doc/15.1.4 - swoole_errno.md | 7 + doc/15.1.5 - swoole_get_local_ip.md | 12 + doc/15.1.6 - swoole_clear_dns_cache.md | 7 + doc/15.1.7 - swoole_get_local_mac.md | 23 + doc/15.1.8 - swoole_cpu_num.md | 12 + ...67\347\232\204\344\275\277\347\224\250.md" | 58 ++ ...67\347\232\204\344\275\277\347\224\250.md" | 60 ++ ...67\347\232\204\344\275\277\347\224\250.md" | 18 + ...70\345\205\263\345\267\245\345\205\267.md" | 40 + ...06\345\217\262\347\211\271\346\200\247.md" | 3 + doc/15.14.1 - swoole_server->handler.md | 34 + doc/15.14.10 - onMasterConnect.md | 11 + doc/15.14.11 - onMasterClose.md | 10 + doc/15.14.2 - task_worker_max.md | 4 + doc/15.14.3 - swoole_server->addtimer.md | 34 + doc/15.14.4 - swoole_server->deltimer.md | 9 + doc/15.14.5 - onTimer.md | 12 + doc/15.14.6 - swoole_timer_add.md | 19 + doc/15.14.7 - swoole_timer_del.md | 8 + doc/15.14.8 - swoole_get_mysqli_sock.md | 40 + doc/15.14.9 - swoole_mysql_query.md | 66 ++ ...345\275\225\357\274\2101.x\357\274\211.md" | 2 + doc/15.15.1 - 1.9.19.md | 10 + doc/15.15.10 - 1.9.7.md | 14 + doc/15.15.11 - 1.9.6.md | 10 + doc/15.15.12 - 1.9.5.md | 7 + doc/15.15.13 - 1.9.4.md | 5 + doc/15.15.14 - 1.9.3.md | 6 + doc/15.15.15 - 1.9.2.md | 11 + doc/15.15.16 - 1.9.1.md | 29 + doc/15.15.17 - 1.9.0.md | 139 ++++ doc/15.15.18 - 1.8.13.md | 10 + doc/15.15.19 - 1.8.12.md | 17 + doc/15.15.2 - 1.9.18.md | 9 + doc/15.15.20 - 1.8.11.md | 8 + doc/15.15.21 - 1.8.10.md | 6 + doc/15.15.22 - 1.8.9.md | 13 + doc/15.15.23 - 1.8.8.md | 31 + doc/15.15.24 - 1.8.7.md | 15 + doc/15.15.25 - 1.8.6.md | 56 ++ doc/15.15.26 - 1.8.5.md | 16 + doc/15.15.27 - 1.8.4.md | 15 + doc/15.15.28 - 1.8.3.md | 15 + doc/15.15.29 - 1.8.2.md | 10 + doc/15.15.3 - 1.9.17.md | 5 + doc/15.15.30 - 1.8.1.md | 35 + doc/15.15.31 - 1.8.0.md | 81 +++ doc/15.15.32 - 1.7.22.md | 11 + doc/15.15.33 - 1.7.21.md | 12 + doc/15.15.34 - 1.7.20.md | 16 + doc/15.15.35 - 1.7.19.md | 15 + doc/15.15.36 - 1.7.18.md | 26 + doc/15.15.37 - 1.7.17.md | 18 + doc/15.15.38 - 1.7.16.md | 22 + doc/15.15.39 - 1.7.15.md | 16 + doc/15.15.4 - 1.9.16.md | 7 + doc/15.15.40 - 1.7.14.md | 12 + doc/15.15.41 - 1.7.13.md | 15 + doc/15.15.42 - 1.7.12.md | 6 + doc/15.15.43 - 1.7.11.md | 17 + doc/15.15.44 - 1.7.10.md | 21 + doc/15.15.45 - 1.7.9.md | 14 + doc/15.15.46 - 1.7.8.md | 23 + doc/15.15.47 - 1.7.7.md | 11 + doc/15.15.48 - 1.7.6.md | 18 + doc/15.15.49 - 1.7.5.md | 21 + doc/15.15.5 - 1.9.15.md | 11 + doc/15.15.50 - v1.5.md | 24 + doc/15.15.51 - v1.6.md | 100 +++ doc/15.15.52 - v1.7.md | 56 ++ doc/15.15.6 - 1.9.14.md | 10 + doc/15.15.7 - 1.9.12.md | 7 + doc/15.15.8 - 1.9.11.md | 11 + doc/15.15.9 - 1.9.9.md | 9 + ...345\275\225\357\274\2102.x\357\274\211.md" | 2 + doc/15.16.1 - 2.0.1-Alpha.md | 153 ++++ doc/15.16.2 - 2.0.5.md | 110 +++ doc/15.16.3 - 2.0.9.md | 5 + doc/15.16.4 - 2.0.10.md | 5 + .../15.2 - Swoole\347\244\276\345\214\272.md" | 27 + ...\265\240Swoole\351\241\271\347\233\256.md" | 129 ++++ ...le\345\274\200\345\217\221\347\273\204.md" | 11 + ...41\345\217\267\345\210\227\350\241\250.md" | 63 ++ ...201\257(errno)\345\210\227\350\241\250.md" | 248 +++++++ ...45\347\232\204\347\212\266\346\200\201.md" | 13 + ...67\347\232\204\344\275\277\347\224\250.md" | 38 + ...67\347\232\204\344\275\277\347\224\250.md" | 22 + doc/2 - Server.md | 53 ++ ...75\346\225\260\345\210\227\350\241\250.md" | 14 + doc/2.1.1 - swoole_server::__construct.md | 101 +++ doc/2.1.10 - swoole_server->shutdown.md | 12 + doc/2.1.11 - swoole_server->tick.md | 36 + doc/2.1.12 - swoole_server->after.md | 19 + doc/2.1.13 - swoole_server->defer.md | 23 + doc/2.1.14 - swoole_server->clearTimer.md | 12 + doc/2.1.15 - swoole_server->close.md | 12 + doc/2.1.16 - swoole_server->send.md | 31 + doc/2.1.17 - swoole_server->sendfile.md | 16 + doc/2.1.18 - swoole_server->sendto.md | 27 + doc/2.1.19 - swoole_server->sendwait.md | 14 + doc/2.1.2 - swoole_server->set.md | 103 +++ doc/2.1.20 - swoole_server->sendMessage.md | 60 ++ doc/2.1.21 - swoole_server->exist.md | 10 + doc/2.1.22 - swoole_server->pause.md | 12 + doc/2.1.23 - swoole_server->resume.md | 11 + doc/2.1.24 - swoole_server->getClientInfo.md | 49 ++ doc/2.1.25 - swoole_server->getClientList.md | 38 + doc/2.1.26 - swoole_server->bind.md | 21 + doc/2.1.27 - swoole_server->stats.md | 54 ++ doc/2.1.28 - swoole_server->task.md | 54 ++ doc/2.1.29 - swoole_server->taskwait.md | 17 + doc/2.1.3 - swoole_server->on.md | 25 + doc/2.1.30 - swoole_server->taskWaitMulti.md | 37 + doc/2.1.31 - swoole_server->taskCo.md | 51 ++ doc/2.1.32 - swoole_server->finish.md | 13 + doc/2.1.33 - swoole_server->heartbeat.md | 20 + doc/2.1.34 - swoole_server->getLastError.md | 19 + doc/2.1.35 - swoole_server->getSocket.md | 56 ++ doc/2.1.36 - swoole_server->protect.md | 10 + doc/2.1.37 - swoole_server->confirm.md | 10 + doc/2.1.4 - swoole_server->addListener.md | 59 ++ doc/2.1.5 - swoole_server->addProcess.md | 51 ++ doc/2.1.6 - swoole_server->listen.md | 8 + doc/2.1.7 - swoole_server->start.md | 43 ++ doc/2.1.8 - swoole_server->reload.md | 61 ++ doc/2.1.9 - swoole_server->stop.md | 19 + ...36\346\200\247\345\210\227\350\241\250.md" | 2 + doc/2.2.1 - swoole_server::$setting.md | 14 + doc/2.2.2 - swoole_server::$master_pid.md | 5 + doc/2.2.3 - swoole_server::$manager_pid.md | 5 + doc/2.2.4 - swoole_server::$worker_id.md | 14 + doc/2.2.5 - swoole_server::$worker_pid.md | 7 + doc/2.2.6 - swoole_server::$taskworker.md | 8 + doc/2.2.7 - swoole_server::$connections.md | 22 + doc/2.2.8 - swoole_server::$ports.md | 12 + ...15\347\275\256\351\200\211\351\241\271.md" | 13 + doc/2.3.1 - reactor_num.md | 10 + doc/2.3.10 - dispatch_func.md | 62 ++ doc/2.3.11 - message_queue_key.md | 10 + doc/2.3.12 - daemonize.md | 15 + doc/2.3.13 - backlog.md | 16 + doc/2.3.14 - log_file.md | 27 + doc/2.3.15 - log_level.md | 20 + doc/2.3.16 - heartbeat_check_interval.md | 7 + doc/2.3.17 - heartbeat_idle_time.md | 14 + doc/2.3.18 - open_eof_check.md | 16 + doc/2.3.19 - open_eof_split.md | 22 + doc/2.3.2 - worker_num.md | 10 + doc/2.3.20 - package_eof.md | 5 + doc/2.3.21 - open_length_check.md | 44 ++ doc/2.3.22 - package_length_type.md | 14 + doc/2.3.23 - package_length_func.md | 72 ++ doc/2.3.24 - package_max_length.md | 12 + doc/2.3.25 - open_cpu_affinity.md | 17 + doc/2.3.26 - cpu_affinity_ignore.md | 46 ++ doc/2.3.27 - open_tcp_nodelay.md | 3 + doc/2.3.28 - tcp_defer_accept.md | 13 + doc/2.3.29 - ssl_cert_file.md | 30 + doc/2.3.3 - max_request.md | 40 + doc/2.3.30 - ssl_method.md | 14 + doc/2.3.31 - ssl_ciphers.md | 11 + doc/2.3.32 - user.md | 11 + doc/2.3.33 - group.md | 10 + doc/2.3.34 - chroot.md | 9 + doc/2.3.35 - pid_file.md | 13 + doc/2.3.36 - pipe_buffer_size.md | 18 + doc/2.3.37 - buffer_output_size.md | 14 + doc/2.3.38 - socket_buffer_size.md | 31 + doc/2.3.39 - enable_unsafe_event.md | 7 + doc/2.3.4 - max_conn (max_connection).md | 31 + doc/2.3.40 - discard_timeout_request.md | 8 + doc/2.3.41 - enable_reuse_port.md | 9 + doc/2.3.42 - enable_delay_receive.md | 20 + doc/2.3.43 - open_http_protocol.md | 3 + doc/2.3.44 - open_http2_protocol.md | 3 + doc/2.3.45 - open_websocket_protocol.md | 5 + doc/2.3.46 - open_mqtt_protocol.md | 7 + doc/2.3.47 - reload_async.md | 7 + doc/2.3.48 - tcp_fastopen.md | 9 + doc/2.3.49 - request_slowlog_file.md | 35 + doc/2.3.5 - task_worker_num.md | 14 + doc/2.3.6 - task_ipc_mode.md | 18 + doc/2.3.7 - task_max_request.md | 6 + doc/2.3.8 - task_tmpdir.md | 7 + doc/2.3.9 - dispatch_mode.md | 31 + ...21\345\220\254\347\253\257\345\217\243.md" | 101 +++ ...57\351\200\211\345\217\202\346\225\260.md" | 23 + ...57\351\200\211\345\233\236\350\260\203.md" | 39 + ...45\350\277\255\344\273\243\345\231\250.md" | 43 ++ ...32\344\271\211\345\270\270\351\207\217.md" | 47 ++ ...36\350\260\203\345\207\275\346\225\260.md" | 39 + doc/2.6.1 - onStart.md | 34 + doc/2.6.10 - onBufferFull.md | 10 + doc/2.6.11 - onBufferEmpty.md | 10 + doc/2.6.12 - onTask.md | 23 + doc/2.6.13 - onFinish.md | 14 + doc/2.6.14 - onPipeMessage.md | 11 + doc/2.6.15 - onWorkerError.md | 13 + doc/2.6.16 - onManagerStart.md | 9 + doc/2.6.17 - onManagerStop.md | 6 + doc/2.6.2 - onShutdown.md | 20 + doc/2.6.3 - onWorkerStart.md | 35 + doc/2.6.4 - onWorkerStop.md | 10 + doc/2.6.5 - onWorkerExit.md | 14 + doc/2.6.6 - onConnect.md | 22 + doc/2.6.7 - onReceive.md | 54 ++ doc/2.6.8 - onPacket.md | 23 + doc/2.6.9 - onClose.md | 22 + ...30\347\272\247\347\211\271\346\200\247.md" | 7 + ...37\345\221\275\345\221\250\346\234\237.md" | 40 + ...\270\252Server\347\253\257\345\217\243.md" | 22 + ...347\232\204 reactor_id \345\222\214 fd.md" | 31 + ...k \347\232\204\344\275\277\347\224\250.md" | 29 + ...32\344\277\241\346\250\241\345\274\217.md" | 34 + ...36\346\216\245\346\243\200\346\265\213.md" | 17 + ...64\346\214\201\346\226\271\346\241\210.md" | 23 + ...54\347\232\204\344\275\277\347\224\250.md" | 35 + ...64\345\221\275\351\224\231\350\257\257.md" | 57 ++ ...41\345\274\217\344\273\213\347\273\215.md" | 42 ++ ...70\350\247\201\351\227\256\351\242\230.md" | 20 + ...5\220\216\347\253\213\345\215\263close.md" | 23 + ...50\347\232\204\345\217\230\351\207\217.md" | 81 +++ ...41\347\220\206\346\234\272\345\210\266.md" | 87 +++ ...6\210\226mysql\350\277\236\346\216\245.md" | 29 + ...75\346\225\260\351\243\216\346\240\274.md" | 48 ++ ...02\344\275\225\351\200\232\344\277\241.md" | 16 + ...41\346\201\257\357\274\232ERROR (9006).md" | 7 + ...created. unable to create swoole_server.md | 10 + ...13\345\212\233\346\265\213\350\257\225.md" | 40 + ...45\347\232\204\346\265\213\350\257\225.md" | 13 + doc/3 - Client.md | 50 ++ ...71\346\263\225\345\210\227\350\241\250.md" | 13 + doc/3.1.1 - swoole_client::__construct.md | 30 + doc/3.1.10 - swoole_client->send.md | 23 + doc/3.1.11 - swoole_client->sendto.md | 12 + doc/3.1.12 - swoole_client->sendfile.md | 32 + doc/3.1.13 - swoole_client->recv.md | 34 + doc/3.1.14 - swoole_client->close.md | 61 ++ doc/3.1.15 - swoole_client->sleep.md | 23 + doc/3.1.16 - swoole_client->wakeup.md | 9 + doc/3.1.17 - swoole_client->enableSSL.md | 53 ++ doc/3.1.2 - swoole_client->set.md | 9 + doc/3.1.3 - swoole_client->on.md | 51 ++ doc/3.1.4 - swoole_client->connect.md | 62 ++ doc/3.1.5 - swoole_client->isConnected.md | 17 + doc/3.1.6 - swoole_client->getSocket.md | 14 + doc/3.1.7 - swoole_client->getSockName.md | 11 + doc/3.1.8 - swoole_client->getPeerName.md | 11 + doc/3.1.9 - swoole_client->getPeerCert.md | 13 + ...36\350\260\203\345\207\275\346\225\260.md" | 9 + doc/3.2.1 - onConnect.md | 11 + doc/3.2.2 - onError.md | 9 + doc/3.2.3 - onReceive.md | 11 + doc/3.2.4 - onClose.md | 8 + doc/3.2.5 - onBufferFull.md | 10 + doc/3.2.6 - onBufferEmpty.md | 10 + ...36\346\200\247\345\210\227\350\241\250.md" | 2 + doc/3.3.1 - swoole_client->errCode.md | 10 + doc/3.3.2 - swoole_client->sock.md | 13 + doc/3.3.3 - swoole_client->reuse.md | 21 + "doc/3.4 - \345\271\266\350\241\214.md" | 2 + doc/3.4.1 - swoole_client_select.md | 74 ++ ...02\346\255\245\350\277\236\346\216\245.md" | 37 + ...CP\351\225\277\350\277\236\346\216\245.md" | 17 + "doc/3.5 - \345\270\270\351\207\217.md" | 20 + ...15\347\275\256\351\200\211\351\241\271.md" | 105 +++ doc/3.6.1 - ssl_verify_peer.md | 22 + doc/3.6.2 - ssl_host_name.md | 9 + doc/3.6.3 - ssl_cafile.md | 11 + doc/3.6.4 - ssl_capath.md | 11 + doc/3.6.5 - package_length_func.md | 35 + ...70\350\247\201\351\227\256\351\242\230.md" | 4 + doc/4 - Process.md | 111 +++ doc/4.1 - swoole_process::__construct.md | 73 ++ doc/4.10 - swoole_process->statQueue.md | 19 + doc/4.11 - swoole_process->freeQueue.md | 11 + doc/4.12 - swoole_process->push.md | 53 ++ doc/4.13 - swoole_process->pop.md | 11 + doc/4.14 - swoole_process->close.md | 14 + doc/4.15 - swoole_process->exit.md | 17 + doc/4.16 - swoole_process::kill.md | 19 + doc/4.17 - swoole_process::wait.md | 33 + doc/4.18 - swoole_process::daemon.md | 15 + doc/4.19 - swoole_process::signal.md | 21 + doc/4.2 - swoole_process->start.md | 54 ++ doc/4.20 - swoole_process::alarm.md | 31 + doc/4.21 - swoole_process::setAffinity.md | 16 + doc/4.3 - swoole_process->name.md | 12 + doc/4.4 - swoole_process->exec.md | 47 ++ doc/4.5 - swoole_process->write.md | 32 + doc/4.6 - swoole_process->read.md | 41 ++ doc/4.7 - swoole_process->setTimeout.md | 25 + doc/4.8 - swoole_process->setBlocking.md | 30 + doc/4.9 - swoole_process->useQueue.md | 24 + doc/6 - AsyncIO.md | 24 + ...\344\273\266\347\263\273\347\273\237IO.md" | 37 + doc/6.1.1 - swoole_async_readfile.md | 25 + doc/6.1.2 - swoole_async_writefile.md | 21 + doc/6.1.3 - swoole_async_read.md | 26 + doc/6.1.4 - swoole_async_write.md | 11 + doc/6.1.5 - swoole_async_dns_lookup.md | 37 + doc/6.1.6 - swoole_async::exec.md | 28 + doc/6.2 - EventLoop.md | 19 + doc/6.2.1 - swoole_event_add.md | 58 ++ doc/6.2.10 - swoole_event_dispatch.md | 19 + doc/6.2.2 - swoole_event_set.md | 26 + doc/6.2.3 - swoole_event_isset.md | 24 + doc/6.2.4 - swoole_event_write.md | 35 + doc/6.2.5 - swoole_event_del.md | 8 + doc/6.2.6 - swoole_event_exit.md | 6 + doc/6.2.7 - swoole_event_defer.md | 18 + doc/6.2.8 - swoole_event_cycle.md | 32 + doc/6.2.9 - swoole_event_wait.md | 10 + ...22\345\256\232\346\227\266\345\231\250.md" | 13 + doc/6.3.1 - swoole_timer_tick.md | 66 ++ doc/6.3.2 - swoole_timer_after.md | 31 + doc/6.3.3 - swoole_timer_clear.md | 24 + ...QL\345\256\242\346\210\267\347\253\257.md" | 40 + doc/6.4.1 - swoole_mysql->construct.md | 3 + doc/6.4.2 - swoole_mysql->on.md | 16 + doc/6.4.3 - swoole_mysql->connect.md | 38 + doc/6.4.4 - swoole_mysql->escape.md | 28 + doc/6.4.5 - swoole_mysql->query.md | 29 + doc/6.4.6 - swoole_mysql->begin.md | 26 + doc/6.4.7 - swoole_mysql->commit.md | 25 + doc/6.4.8 - swoole_mysql->rollback.md | 10 + doc/6.4.9 - swoole_mysql->close.md | 7 + ...is\345\256\242\346\210\267\347\253\257.md" | 33 + doc/6.5.1 - swoole_redis->__construct.md | 38 + doc/6.5.2 - swoole_redis->on.md | 22 + doc/6.5.3 - swoole_redis->connect.md | 38 + doc/6.5.4 - swoole_redis->__call.md | 59 ++ doc/6.5.5 - swoole_redis->close.md | 7 + ...6.6.1 - swoole_http_client->__construct.md | 31 + doc/6.6.10 - swoole_http_client->upgrade.md | 53 ++ doc/6.6.11 - swoole_http_client->push.md | 27 + doc/6.6.12 - swoole_http_client->execute.md | 7 + doc/6.6.13 - swoole_http_client->download.md | 53 ++ doc/6.6.14 - swoole_http_client->close.md | 9 + doc/6.6.2 - swoole_http_client->set.md | 34 + doc/6.6.3 - swoole_http_client->setMethod.md | 10 + doc/6.6.4 - swoole_http_client->setHeaders.md | 9 + doc/6.6.5 - swoole_http_client->setCookies.md | 10 + doc/6.6.6 - swoole_http_client->setData.md | 11 + doc/6.6.7 - swoole_http_client->addFile.md | 33 + doc/6.6.8 - swoole_http_client->get.md | 29 + doc/6.6.9 - swoole_http_client->post.md | 21 + ....0\345\256\242\346\210\267\347\253\257.md" | 38 + ....7.1 - swoole_http2_client->__construct.md | 11 + doc/6.7.2 - swoole_http2_client->get.md | 30 + doc/6.7.3 - swoole_http2_client->post.md | 20 + ...6.7.4 - swoole_http2_client->setHeaders.md | 9 + ...6.7.5 - swoole_http2_client->setCookies.md | 11 + doc/7 - Memory.md | 7 + doc/7.1 - Lock.md | 36 + doc/7.1.1 - swoole_lock->__construct.md | 14 + doc/7.1.2 - swoole_lock->lock.md | 9 + doc/7.1.3 - swoole_lock->trylock.md | 13 + doc/7.1.4 - swoole_lock->unlock.md | 8 + doc/7.1.5 - swoole_lock->lock_read.md | 12 + doc/7.1.6 - swoole_lock->trylock_read.md | 7 + doc/7.1.7 - swoole_lock->lockwait.md | 15 + doc/7.2 - Buffer.md | 12 + doc/7.2.1 - swoole_buffer->__construct.md | 7 + doc/7.2.2 - swoole_buffer->append.md | 10 + doc/7.2.3 - swoole_buffer->substr.md | 13 + doc/7.2.4 - swoole_buffer->clear.md | 10 + doc/7.2.5 - swoole_buffer->expand.md | 7 + doc/7.2.6 - swoole_buffer->write.md | 13 + doc/7.2.7 - swoole_buffer->read.md | 14 + doc/7.2.8 - swoole_buffer->recycle.md | 12 + doc/7.3 - Table.md | 29 + doc/7.3.1 - swoole_table->__construct.md | 20 + ...70\351\207\217\345\210\227\350\241\250.md" | 6 + doc/7.3.2 - swoole_table->column.md | 66 ++ doc/7.3.3 - swoole_table->create.md | 37 + doc/7.3.4 - swoole_table->set.md | 31 + doc/7.3.5 - swoole_table->incr.md | 14 + doc/7.3.6 - swoole_table->decr.md | 14 + doc/7.3.7 - swoole_table->get.md | 13 + doc/7.3.8 - swoole_table->exist.md | 11 + doc/7.3.9 - swoole_table->del.md | 10 + doc/7.4 - Atomic.md | 30 + doc/7.4.1 - swoole_atomic->__construct.md | 15 + doc/7.4.2 - swoole_atomic->add.md | 16 + doc/7.4.3 - swoole_atomic->sub.md | 13 + doc/7.4.4 - swoole_atomic->get.md | 9 + doc/7.4.5 - swoole_atomic->set.md | 9 + doc/7.4.6 - swoole_atomic->cmpset.md | 10 + doc/7.4.7 - swoole_atomic->wait.md | 29 + doc/7.4.8 - swoole_atomic->wakeup.md | 12 + doc/7.5 - mmap.md | 6 + doc/7.5.1 - swoole_mmap::open.md | 36 + doc/7.6 - Channel.md | 54 ++ doc/7.6.1 - Channel->__construct.md | 9 + doc/7.6.2 - Channel->push.md | 12 + doc/7.6.3 - Channel->pop.md | 10 + doc/7.6.4 - Channel->stats.md | 16 + doc/7.7 - Serialize.md | 25 + doc/7.7.1 - swoole_serialize::pack.md | 10 + doc/7.7.2 - swoole_serialize::unpack.md | 9 + doc/8 - HttpServer.md | 58 ++ doc/8.1 - swoole_http_server.md | 27 + doc/8.1.1 - swoole_http_server->on.md | 24 + doc/8.1.2 - swoole_http_server->start.md | 9 + doc/8.2 - swoole_http_request.md | 6 + doc/8.2.1 - swoole_http_request->$header.md | 9 + doc/8.2.2 - swoole_http_request->$server.md | 15 + doc/8.2.3 - swoole_http_request->$get.md | 12 + doc/8.2.4 - swoole_http_request->$post.md | 9 + doc/8.2.5 - swoole_http_request->$cookie.md | 8 + doc/8.2.6 - swoole_http_request->$files.md | 23 + ...8.2.7 - swoole_http_request->rawContent.md | 12 + doc/8.2.8 - swoole_http_request->getData.md | 9 + doc/8.3 - swoole_http_response.md | 6 + doc/8.3.1 - swoole_http_response->header.md | 36 + doc/8.3.10 - swoole_http_response::create.md | 24 + doc/8.3.2 - swoole_http_response->cookie.md | 14 + doc/8.3.3 - swoole_http_response->status.md | 10 + doc/8.3.4 - swoole_http_response->gzip.md | 22 + doc/8.3.5 - swoole_http_response->redirect.md | 26 + doc/8.3.6 - swoole_http_response->write.md | 12 + doc/8.3.7 - swoole_http_response->sendfile.md | 23 + doc/8.3.8 - swoole_http_response->end.md | 12 + doc/8.3.9 - swoole_http_response->detach.md | 58 ++ ...15\347\275\256\351\200\211\351\241\271.md" | 13 + doc/8.4.1 - upload_tmp_dir.md | 11 + doc/8.4.2 - http_parse_post.md | 9 + doc/8.4.3 - document_root.md | 14 + ...70\350\247\201\351\227\256\351\242\230.md" | 2 + ...50\347\253\257\350\266\205\346\227\266.md" | 27 + ...72\346\254\241\350\257\267\346\261\202.md" | 3 + doc/9 - WebSocket.md | 90 +++ ...36\350\260\203\345\207\275\346\225\260.md" | 6 + doc/9.1.1 - onHandShake.md | 63 ++ doc/9.1.2 - onOpen.md | 11 + doc/9.1.3 - onMessage.md | 29 + ...75\346\225\260\345\210\227\350\241\250.md" | 3 + doc/9.2.1 - swoole_websocket_server->push.md | 14 + doc/9.2.2 - swoole_websocket_server->exist.md | 10 + doc/9.2.3 - swoole_websocket_server::pack.md | 13 + ...9.2.4 - swoole_websocket_server::unpack.md | 8 + ...32\344\271\211\345\270\270\351\207\217.md" | 17 + ...70\350\247\201\351\227\256\351\242\230.md" | 9 + ...15\347\275\256\351\200\211\351\241\271.md" | 13 + 602 files changed, 16738 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 "doc/1 - \345\205\245\351\227\250\346\214\207\345\274\225.md" create mode 100644 "doc/1.1 - \347\216\257\345\242\203\344\276\235\350\265\226.md" create mode 100644 "doc/1.11 - \350\241\215\347\224\237\345\274\200\346\272\220\351\241\271\347\233\256.md" create mode 100644 "doc/1.11.1 - \346\241\206\346\236\266.md" create mode 100644 "doc/1.11.2 - \345\267\245\345\205\267.md" create mode 100644 "doc/1.11.3 - \345\210\206\345\270\203\345\274\217.md" create mode 100644 "doc/1.11.4 - \351\200\232\344\277\241\345\215\217\350\256\256.md" create mode 100644 "doc/1.12 - \347\224\250\346\210\267\344\270\216\346\241\210\344\276\213.md" create mode 100644 "doc/1.12.1 - \347\211\251\350\201\224\347\275\221\351\241\271\347\233\256.md" create mode 100644 "doc/1.12.2 - \347\275\221\347\273\234\346\270\270\346\210\217.md" create mode 100644 "doc/1.12.3 - \350\205\276\350\256\257\357\274\210Tencent\357\274\211.md" create mode 100644 "doc/1.12.4 - \347\231\276\345\272\246\357\274\210Baidu.com\357\274\211.md" create mode 100644 "doc/1.12.5 - \351\230\205\346\226\207\351\233\206\345\233\242.md" create mode 100644 "doc/1.12.6 - BiliBili\357\274\210\345\223\224\345\223\251\345\223\224\345\223\251\357\274\211.md" create mode 100644 "doc/1.12.7 - \350\275\246\350\275\256\344\272\222\350\201\224\357\274\210chelun.com\357\274\211.md" create mode 100644 "doc/1.12.8 - (\346\215\236\346\234\210\347\213\227) \346\270\270\346\210\217\347\244\276\345\214\272.md" create mode 100644 "doc/1.13 - \346\217\220\344\272\244\351\224\231\350\257\257\346\212\245\345\221\212.md" create mode 100644 "doc/1.14 - \345\270\270\350\247\201\351\227\256\351\242\230.md" create mode 100644 "doc/1.14.1 - \345\215\207\347\272\247swoole\347\211\210\346\234\254\347\232\204\345\270\270\350\247\201\351\227\256\351\242\230.md" create mode 100644 doc/1.14.10 - my_global.h: No such file or directory.md create mode 100644 doc/1.14.11 - undefined symbol: __sync_bool_compare_and_swap_4.md create mode 100644 "doc/1.14.12 - \345\255\246\344\271\240Swoole\351\234\200\350\246\201\346\216\214\346\217\241\345\223\252\344\272\233\345\237\272\347\241\200\347\237\245\350\257\206.md" create mode 100644 "doc/1.14.13 - \345\220\214\346\255\245\351\230\273\345\241\236\344\270\216\345\274\202\346\255\245\351\235\236\351\230\273\345\241\236\351\200\202\347\224\250\345\234\272\346\231\257.md" create mode 100644 "doc/1.14.14 - PHP7\347\216\257\345\242\203\344\270\213\345\207\272\347\216\260zend_mm_heap corrupted.md" create mode 100644 "doc/1.14.15 - swoole\351\241\271\347\233\256\350\265\267\346\272\220\345\222\214\345\220\215\345\255\227\347\224\261\346\235\245.md" create mode 100644 "doc/1.14.2 - \347\224\237\346\210\220\345\217\257\345\210\206\345\217\221\347\232\204\344\272\214\350\277\233\345\210\266swoole\347\211\210\346\234\254.md" create mode 100644 "doc/1.14.3 - \345\234\250phpinfo\344\270\255\346\234\211\345\234\250php-m\344\270\255\346\262\241\346\234\211.md" create mode 100644 "doc/1.14.4 - Connection refused\346\230\257\346\200\216\344\271\210\345\233\236\344\272\213.md" create mode 100644 doc/1.14.5 - Resource temporarily unavailable [11].md create mode 100644 doc/1.14.6 - Cannot assign requested address [99].md create mode 100644 "doc/1.14.7 - swoole\344\270\216node.js\347\233\270\346\257\224\346\234\211\345\223\252\344\272\233\344\274\230\345\212\277.md" create mode 100644 "doc/1.14.8 - swoole\344\270\216golang\347\233\270\346\257\224\346\234\211\345\223\252\344\272\233\344\274\230\345\212\277.md" create mode 100644 doc/1.14.9 - pcre.h: No such file or directory.md create mode 100644 "doc/1.2 - \347\274\226\350\257\221\345\256\211\350\243\205.md" create mode 100644 "doc/1.2.1 - \347\274\226\350\257\221\345\217\202\346\225\260.md" create mode 100644 "doc/1.2.2 - \345\270\270\350\247\201\351\224\231\350\257\257.md" create mode 100644 "doc/1.3 - \345\277\253\351\200\237\350\265\267\346\255\245.md" create mode 100644 "doc/1.3.1 - \345\210\233\345\273\272TCP\346\234\215\345\212\241\345\231\250.md" create mode 100644 "doc/1.3.10 - \344\275\277\347\224\250\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257.md" create mode 100644 "doc/1.3.11 - \345\244\232\350\277\233\347\250\213\345\205\261\344\272\253\346\225\260\346\215\256.md" create mode 100644 "doc/1.3.2 - \345\210\233\345\273\272UDP\346\234\215\345\212\241\345\231\250.md" create mode 100644 "doc/1.3.3 - \345\210\233\345\273\272Web\346\234\215\345\212\241\345\231\250.md" create mode 100644 "doc/1.3.4 - \345\210\233\345\273\272WebSocket\346\234\215\345\212\241\345\231\250.md" create mode 100644 "doc/1.3.5 - \350\256\276\347\275\256\345\256\232\346\227\266\345\231\250.md" create mode 100644 "doc/1.3.6 - \346\211\247\350\241\214\345\274\202\346\255\245\344\273\273\345\212\241.md" create mode 100644 "doc/1.3.7 - \345\210\233\345\273\272\345\220\214\346\255\245TCP\345\256\242\346\210\267\347\253\257.md" create mode 100644 "doc/1.3.8 - \345\210\233\345\273\272\345\274\202\346\255\245TCP\345\256\242\346\210\267\347\253\257.md" create mode 100644 "doc/1.3.9 - \347\275\221\347\273\234\351\200\232\344\277\241\345\215\217\350\256\256\350\256\276\350\256\241.md" create mode 100644 "doc/1.4 - \347\274\226\347\250\213\351\241\273\347\237\245.md" create mode 100644 "doc/1.4.3 - while\345\276\252\347\216\257\347\232\204\345\275\261\345\223\215.md" create mode 100644 "doc/1.4.4 - stat\347\274\223\345\255\230\346\270\205\347\220\206.md" create mode 100644 "doc/1.4.5 - mt_rand\351\232\217\346\234\272\346\225\260.md" create mode 100644 "doc/1.5 - \347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225.md" create mode 100644 doc/1.5.1 - 2.1.2.md create mode 100644 doc/1.5.10 - 1.9.23.md create mode 100644 doc/1.5.11 - 1.9.22.md create mode 100644 doc/1.5.12 - 1.9.21.md create mode 100644 doc/1.5.2 - 1.10.3.md create mode 100644 doc/1.5.3 - 1.10.2.md create mode 100644 doc/1.5.4 - 2.1.1.md create mode 100644 doc/1.5.5 - 1.10.1.md create mode 100644 doc/1.5.6 - 2.0.13.md create mode 100644 doc/1.5.7 - 2.0.12.md create mode 100644 doc/1.5.8 - 2.0.11.md create mode 100644 doc/1.5.9 - 1.10.0.md create mode 100644 "doc/1.6 - \346\226\260\347\211\271\346\200\247\344\275\277\347\224\250.md" create mode 100644 "doc/1.6.1 - 2.1.2 \350\277\233\347\250\213\346\261\240\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" create mode 100644 "doc/1.6.10 - 1.7.4 SSL\351\232\247\351\201\223\345\212\240\345\257\206TCP-Server.md" create mode 100644 "doc/1.6.11 - 1.7.4 task\350\277\233\347\250\213\344\270\255\344\275\277\347\224\250\346\257\253\347\247\222\345\256\232\346\227\266\345\231\250.md" create mode 100644 "doc/1.6.12 - 1.7.3 \345\233\272\345\256\232\345\214\205\345\244\264+\345\214\205\344\275\223\345\215\217\350\256\256\350\207\252\345\212\250\345\210\206\345\214\205.md" create mode 100644 "doc/1.6.13 - 1.7.3 onTask\347\233\264\346\216\245return\345\217\226\344\273\243finish\345\207\275\346\225\260.md" create mode 100644 "doc/1.6.14 - 1.7.2 swoole_process\345\244\232\350\277\233\347\250\213\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" create mode 100644 "doc/1.6.15 - 1.7.2 task\350\277\233\347\250\213\344\275\277\347\224\250\346\266\210\346\201\257\351\230\237\345\210\227.md" create mode 100644 "doc/1.6.2 - 1.9.24 \350\260\203\345\272\246\346\224\257\346\214\201 Stream \346\250\241\345\274\217.md" create mode 100644 "doc/1.6.3 - 1.9.24 \345\274\202\346\255\245\345\256\242\346\210\267\347\253\257\350\207\252\345\212\250\350\247\243\346\236\220\345\237\237\345\220\215.md" create mode 100644 "doc/1.6.4 - 1.9.17 \346\224\257\346\214\201\345\274\202\346\255\245\345\256\211\345\205\250\351\207\215\345\220\257\347\211\271\346\200\247.md" create mode 100644 "doc/1.6.5 - 1.9.14 \344\275\277\347\224\250\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257\350\266\205\346\227\266\346\234\272\345\210\266.md" create mode 100644 "doc/1.6.6 - 1.8.0 \344\275\277\347\224\250\345\206\205\347\275\256Http\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257.md" create mode 100644 "doc/1.6.7 - 1.7.16 \344\275\277\347\224\250\350\277\255\344\273\243\345\231\250\351\201\215\345\216\206Server\346\211\200\346\234\211\350\277\236\346\216\245.md" create mode 100644 "doc/1.6.8 - 1.7.5 \345\234\250Server\344\270\255\344\275\277\347\224\250swoole_table.md" create mode 100644 "doc/1.6.9 - 1.7.5 swoole_client\346\224\257\346\214\201sendfile\346\216\245\345\217\243.md" create mode 100644 "doc/1.7 - \351\241\271\347\233\256\350\267\257\347\272\277\345\233\276.md" create mode 100644 "doc/1.8 - php.ini\351\200\211\351\241\271.md" create mode 100644 "doc/1.9 - \345\206\205\346\240\270\345\217\202\346\225\260\350\260\203\346\225\264.md" create mode 100644 "doc/10 - \345\215\217\347\250\213 Server.md" create mode 100644 "doc/10.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" create mode 100644 doc/10.1.1 - getDefer.md create mode 100644 doc/10.1.2 - setDefer.md create mode 100644 doc/10.1.3 - recv.md create mode 100644 doc/10.1.4 - Coroutine::create.md create mode 100644 doc/10.1.5 - Coroutine::getuid.md create mode 100644 "doc/10.2 - \345\271\266\345\217\221\350\260\203\347\224\250.md" create mode 100644 "doc/10.2.1 - \344\275\277\347\224\250\345\256\236\344\276\213.md" create mode 100644 "doc/10.3 - \345\256\236\347\216\260\345\216\237\347\220\206.md" create mode 100644 "doc/10.3.1 - \345\215\217\347\250\213\344\270\216\347\272\277\347\250\213.md" create mode 100644 "doc/10.3.2 - \345\217\221\351\200\201\346\225\260\346\215\256\345\215\217\347\250\213\350\260\203\345\272\246.md" create mode 100644 "doc/10.4 - \345\270\270\350\247\201\351\227\256\351\242\230.md" create mode 100644 "doc/10.4.1 - \350\277\220\350\241\214\344\270\255\345\207\272\347\216\260 Fatal error: Maximum function nesting level of '1000' reached, aborting!.md" create mode 100644 "doc/10.4.2 - \344\270\272\344\273\200\344\271\210\345\217\252\350\203\275\345\234\250\345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\215\217\347\250\213\345\256\242\346\210\267\347\253\257.md" create mode 100644 "doc/10.4.3 - \346\224\257\346\214\201\345\215\217\347\250\213\347\232\204\345\233\236\350\260\203\346\226\271\346\263\225\345\210\227\350\241\250.md" create mode 100644 "doc/10.5 - \347\274\226\347\250\213\351\241\273\347\237\245.md" create mode 100644 "doc/10.5.1 - \345\234\250\345\244\232\344\270\252\345\215\217\347\250\213\351\227\264\345\205\261\347\224\250\345\220\214\344\270\200\344\270\252\345\215\217\347\250\213\345\256\242\346\210\267\347\253\257.md" create mode 100644 "doc/10.5.2 - \347\246\201\346\255\242\344\275\277\347\224\250\345\215\217\347\250\213 API \347\232\204\345\234\272\346\231\257 ( ver < 2.2 ).md" create mode 100644 "doc/11 - \345\215\217\347\250\213 Client.md" create mode 100644 "doc/11.2.1 - \345\261\236\346\200\247\345\210\227\350\241\250.md" create mode 100644 "doc/11.4.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" create mode 100644 "doc/11.4.2 - \345\261\236\346\200\247\345\210\227\350\241\250.md" create mode 100644 "doc/11.5.1 - \345\261\236\346\200\247\345\210\227\350\241\250.md" create mode 100644 doc/11.6.1 - Coroutine::getUid.md create mode 100644 doc/11.6.10 - Coroutine::getaddrinfo.md create mode 100644 doc/11.6.11 - Coroutine::call_user_func.md create mode 100644 doc/11.6.12 - Coroutine::call_user_func_array.md create mode 100644 doc/11.6.13 - Coroutine::exec.md create mode 100644 doc/11.6.14 - Coroutine::readFile.md create mode 100644 doc/11.6.15 - Coroutine::writeFIle.md create mode 100644 doc/11.6.2 - Coroutine::create.md create mode 100644 doc/11.6.3 - Coroutine::resume.md create mode 100644 doc/11.6.4 - Coroutine::suspend.md create mode 100644 doc/11.6.5 - Coroutine::fread.md create mode 100644 doc/11.6.6 - Coroutine::fgets.md create mode 100644 doc/11.6.7 - Coroutine::fwrite.md create mode 100644 doc/11.6.8 - Coroutine::sleep.md create mode 100644 doc/11.6.9 - Coroutine::gethostbyname.md create mode 100644 "doc/12 - \345\215\217\347\250\213 Socket.md" create mode 100644 "doc/13.1 - \346\226\271\346\263\225.md" create mode 100644 doc/13.1.1 - setHandler.md create mode 100644 doc/13.1.2 - format.md create mode 100644 "doc/13.2 - \345\270\270\351\207\217.md" create mode 100644 "doc/14 - \351\253\230\347\272\247.md" create mode 100644 "doc/14.1 - Swoole\347\232\204\345\256\236\347\216\260.md" create mode 100644 "doc/14.10 - swoole\346\234\215\345\212\241\345\231\250\345\246\202\344\275\225\345\201\232\345\210\260\346\227\240\344\272\272\345\200\274\345\256\210100%\345\217\257\347\224\250.md" create mode 100644 "doc/14.11 - MySQL\347\232\204\350\277\236\346\216\245\346\261\240\343\200\201\345\274\202\346\255\245\343\200\201\346\226\255\347\272\277\351\207\215\350\277\236.md" create mode 100644 "doc/14.12 - PHP\344\270\255\345\223\252\344\272\233\345\207\275\346\225\260\346\230\257\345\220\214\346\255\245\351\230\273\345\241\236\347\232\204.md" create mode 100644 "doc/14.13 - \345\256\210\346\212\244\350\277\233\347\250\213\347\250\213\345\272\217\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" create mode 100644 "doc/14.13.1 - \351\230\237\345\210\227\357\274\210Queue\357\274\211.md" create mode 100644 "doc/14.13.2 - \345\240\206\357\274\210Heap\357\274\211.md" create mode 100644 "doc/14.13.3 - \345\256\232\351\225\277\346\225\260\347\273\204\357\274\210SplFixedArray\357\274\211.md" create mode 100644 "doc/14.14 - \344\275\277\347\224\250jemalloc\344\274\230\345\214\226swoole\345\206\205\345\255\230\345\210\206\351\205\215\346\200\247\350\203\275.md" create mode 100644 "doc/14.15 - C\345\274\200\345\217\221\350\200\205\345\246\202\344\275\225\344\275\277\347\224\250Swoole.md" create mode 100644 "doc/14.16 - C++\345\274\200\345\217\221\350\200\205\345\246\202\344\275\225\344\275\277\347\224\250Swoole.md" create mode 100644 "doc/14.17 - \344\275\277\347\224\250systemd\347\256\241\347\220\206swoole\346\234\215\345\212\241.md" create mode 100644 "doc/14.18 - \347\275\221\345\215\241\344\270\255\346\226\255\350\256\276\347\275\256.md" create mode 100644 "doc/14.19 - \345\260\206Swoole\351\235\231\346\200\201\347\274\226\350\257\221\345\206\205\345\265\214\345\210\260PHP.md" create mode 100644 "doc/14.2 - Reactor\347\272\277\347\250\213.md" create mode 100644 "doc/14.20 - \345\274\202\346\255\245\345\233\236\350\260\203\347\250\213\345\272\217\345\206\205\345\255\230\347\256\241\347\220\206.md" create mode 100644 "doc/14.3 - Manager\350\277\233\347\250\213.md" create mode 100644 "doc/14.4 - Worker\350\277\233\347\250\213.md" create mode 100644 "doc/14.5 - Reactor\343\200\201Worker\343\200\201TaskWorker\347\232\204\345\205\263\347\263\273.md" create mode 100644 "doc/14.7 - \345\234\250php-fpm\346\210\226apache\344\270\255\344\275\277\347\224\250swoole.md" create mode 100644 "doc/14.8 - Swoole\345\274\202\346\255\245\344\270\216\345\220\214\346\255\245\347\232\204\351\200\211\346\213\251.md" create mode 100644 "doc/15 - \345\205\266\344\273\226.md" create mode 100644 "doc/15.1 - \345\207\275\346\225\260\345\210\227\350\241\250.md" create mode 100644 doc/15.1.1 - swoole_set_process_name.md create mode 100644 doc/15.1.2 - swoole_version.md create mode 100644 doc/15.1.3 - swoole_strerror.md create mode 100644 doc/15.1.4 - swoole_errno.md create mode 100644 doc/15.1.5 - swoole_get_local_ip.md create mode 100644 doc/15.1.6 - swoole_clear_dns_cache.md create mode 100644 doc/15.1.7 - swoole_get_local_mac.md create mode 100644 doc/15.1.8 - swoole_cpu_num.md create mode 100644 "doc/15.10 - \351\231\204\345\275\225\357\274\232gdb\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" create mode 100644 "doc/15.11 - \351\231\204\345\275\225\357\274\232lsof\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" create mode 100644 "doc/15.12 - \351\231\204\345\275\225\357\274\232perf\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" create mode 100644 "doc/15.13 - \351\231\204\345\275\225\357\274\232\347\274\226\350\257\221PHP\346\211\251\345\261\225\347\232\204\347\233\270\345\205\263\345\267\245\345\205\267.md" create mode 100644 "doc/15.14 - \345\244\207\347\224\250\357\274\232\345\267\262\347\247\273\351\231\244\347\232\204\345\216\206\345\217\262\347\211\271\346\200\247.md" create mode 100644 doc/15.14.1 - swoole_server->handler.md create mode 100644 doc/15.14.10 - onMasterConnect.md create mode 100644 doc/15.14.11 - onMasterClose.md create mode 100644 doc/15.14.2 - task_worker_max.md create mode 100644 doc/15.14.3 - swoole_server->addtimer.md create mode 100644 doc/15.14.4 - swoole_server->deltimer.md create mode 100644 doc/15.14.5 - onTimer.md create mode 100644 doc/15.14.6 - swoole_timer_add.md create mode 100644 doc/15.14.7 - swoole_timer_del.md create mode 100644 doc/15.14.8 - swoole_get_mysqli_sock.md create mode 100644 doc/15.14.9 - swoole_mysql_query.md create mode 100644 "doc/15.15 - \345\216\206\345\217\262\357\274\232\347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225\357\274\2101.x\357\274\211.md" create mode 100644 doc/15.15.1 - 1.9.19.md create mode 100644 doc/15.15.10 - 1.9.7.md create mode 100644 doc/15.15.11 - 1.9.6.md create mode 100644 doc/15.15.12 - 1.9.5.md create mode 100644 doc/15.15.13 - 1.9.4.md create mode 100644 doc/15.15.14 - 1.9.3.md create mode 100644 doc/15.15.15 - 1.9.2.md create mode 100644 doc/15.15.16 - 1.9.1.md create mode 100644 doc/15.15.17 - 1.9.0.md create mode 100644 doc/15.15.18 - 1.8.13.md create mode 100644 doc/15.15.19 - 1.8.12.md create mode 100644 doc/15.15.2 - 1.9.18.md create mode 100644 doc/15.15.20 - 1.8.11.md create mode 100644 doc/15.15.21 - 1.8.10.md create mode 100644 doc/15.15.22 - 1.8.9.md create mode 100644 doc/15.15.23 - 1.8.8.md create mode 100644 doc/15.15.24 - 1.8.7.md create mode 100644 doc/15.15.25 - 1.8.6.md create mode 100644 doc/15.15.26 - 1.8.5.md create mode 100644 doc/15.15.27 - 1.8.4.md create mode 100644 doc/15.15.28 - 1.8.3.md create mode 100644 doc/15.15.29 - 1.8.2.md create mode 100644 doc/15.15.3 - 1.9.17.md create mode 100644 doc/15.15.30 - 1.8.1.md create mode 100644 doc/15.15.31 - 1.8.0.md create mode 100644 doc/15.15.32 - 1.7.22.md create mode 100644 doc/15.15.33 - 1.7.21.md create mode 100644 doc/15.15.34 - 1.7.20.md create mode 100644 doc/15.15.35 - 1.7.19.md create mode 100644 doc/15.15.36 - 1.7.18.md create mode 100644 doc/15.15.37 - 1.7.17.md create mode 100644 doc/15.15.38 - 1.7.16.md create mode 100644 doc/15.15.39 - 1.7.15.md create mode 100644 doc/15.15.4 - 1.9.16.md create mode 100644 doc/15.15.40 - 1.7.14.md create mode 100644 doc/15.15.41 - 1.7.13.md create mode 100644 doc/15.15.42 - 1.7.12.md create mode 100644 doc/15.15.43 - 1.7.11.md create mode 100644 doc/15.15.44 - 1.7.10.md create mode 100644 doc/15.15.45 - 1.7.9.md create mode 100644 doc/15.15.46 - 1.7.8.md create mode 100644 doc/15.15.47 - 1.7.7.md create mode 100644 doc/15.15.48 - 1.7.6.md create mode 100644 doc/15.15.49 - 1.7.5.md create mode 100644 doc/15.15.5 - 1.9.15.md create mode 100644 doc/15.15.50 - v1.5.md create mode 100644 doc/15.15.51 - v1.6.md create mode 100644 doc/15.15.52 - v1.7.md create mode 100644 doc/15.15.6 - 1.9.14.md create mode 100644 doc/15.15.7 - 1.9.12.md create mode 100644 doc/15.15.8 - 1.9.11.md create mode 100644 doc/15.15.9 - 1.9.9.md create mode 100644 "doc/15.16 - \345\216\206\345\217\262\357\274\232\347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225\357\274\2102.x\357\274\211.md" create mode 100644 doc/15.16.1 - 2.0.1-Alpha.md create mode 100644 doc/15.16.2 - 2.0.5.md create mode 100644 doc/15.16.3 - 2.0.9.md create mode 100644 doc/15.16.4 - 2.0.10.md create mode 100644 "doc/15.2 - Swoole\347\244\276\345\214\272.md" create mode 100644 "doc/15.3 - \346\215\220\350\265\240Swoole\351\241\271\347\233\256.md" create mode 100644 "doc/15.4 - \345\212\240\345\205\245Swoole\345\274\200\345\217\221\347\273\204.md" create mode 100644 "doc/15.5 - \351\231\204\345\275\225\357\274\232Linux\344\277\241\345\217\267\345\210\227\350\241\250.md" create mode 100644 "doc/15.6 - \351\231\204\345\275\225\357\274\232Linux\351\224\231\350\257\257\344\277\241\346\201\257(errno)\345\210\227\350\241\250.md" create mode 100644 "doc/15.7 - \351\231\204\345\275\225\357\274\232TCP\350\277\236\346\216\245\347\232\204\347\212\266\346\200\201.md" create mode 100644 "doc/15.8 - \351\231\204\345\275\225\357\274\232tcpdump\346\212\223\345\214\205\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" create mode 100644 "doc/15.9 - \351\231\204\345\275\225\357\274\232strace\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" create mode 100644 doc/2 - Server.md create mode 100644 "doc/2.1 - \345\207\275\346\225\260\345\210\227\350\241\250.md" create mode 100644 doc/2.1.1 - swoole_server::__construct.md create mode 100644 doc/2.1.10 - swoole_server->shutdown.md create mode 100644 doc/2.1.11 - swoole_server->tick.md create mode 100644 doc/2.1.12 - swoole_server->after.md create mode 100644 doc/2.1.13 - swoole_server->defer.md create mode 100644 doc/2.1.14 - swoole_server->clearTimer.md create mode 100644 doc/2.1.15 - swoole_server->close.md create mode 100644 doc/2.1.16 - swoole_server->send.md create mode 100644 doc/2.1.17 - swoole_server->sendfile.md create mode 100644 doc/2.1.18 - swoole_server->sendto.md create mode 100644 doc/2.1.19 - swoole_server->sendwait.md create mode 100644 doc/2.1.2 - swoole_server->set.md create mode 100644 doc/2.1.20 - swoole_server->sendMessage.md create mode 100644 doc/2.1.21 - swoole_server->exist.md create mode 100644 doc/2.1.22 - swoole_server->pause.md create mode 100644 doc/2.1.23 - swoole_server->resume.md create mode 100644 doc/2.1.24 - swoole_server->getClientInfo.md create mode 100644 doc/2.1.25 - swoole_server->getClientList.md create mode 100644 doc/2.1.26 - swoole_server->bind.md create mode 100644 doc/2.1.27 - swoole_server->stats.md create mode 100644 doc/2.1.28 - swoole_server->task.md create mode 100644 doc/2.1.29 - swoole_server->taskwait.md create mode 100644 doc/2.1.3 - swoole_server->on.md create mode 100644 doc/2.1.30 - swoole_server->taskWaitMulti.md create mode 100644 doc/2.1.31 - swoole_server->taskCo.md create mode 100644 doc/2.1.32 - swoole_server->finish.md create mode 100644 doc/2.1.33 - swoole_server->heartbeat.md create mode 100644 doc/2.1.34 - swoole_server->getLastError.md create mode 100644 doc/2.1.35 - swoole_server->getSocket.md create mode 100644 doc/2.1.36 - swoole_server->protect.md create mode 100644 doc/2.1.37 - swoole_server->confirm.md create mode 100644 doc/2.1.4 - swoole_server->addListener.md create mode 100644 doc/2.1.5 - swoole_server->addProcess.md create mode 100644 doc/2.1.6 - swoole_server->listen.md create mode 100644 doc/2.1.7 - swoole_server->start.md create mode 100644 doc/2.1.8 - swoole_server->reload.md create mode 100644 doc/2.1.9 - swoole_server->stop.md create mode 100644 "doc/2.2 - \345\261\236\346\200\247\345\210\227\350\241\250.md" create mode 100644 doc/2.2.1 - swoole_server::$setting.md create mode 100644 doc/2.2.2 - swoole_server::$master_pid.md create mode 100644 doc/2.2.3 - swoole_server::$manager_pid.md create mode 100644 doc/2.2.4 - swoole_server::$worker_id.md create mode 100644 doc/2.2.5 - swoole_server::$worker_pid.md create mode 100644 doc/2.2.6 - swoole_server::$taskworker.md create mode 100644 doc/2.2.7 - swoole_server::$connections.md create mode 100644 doc/2.2.8 - swoole_server::$ports.md create mode 100644 "doc/2.3 - \351\205\215\347\275\256\351\200\211\351\241\271.md" create mode 100644 doc/2.3.1 - reactor_num.md create mode 100644 doc/2.3.10 - dispatch_func.md create mode 100644 doc/2.3.11 - message_queue_key.md create mode 100644 doc/2.3.12 - daemonize.md create mode 100644 doc/2.3.13 - backlog.md create mode 100644 doc/2.3.14 - log_file.md create mode 100644 doc/2.3.15 - log_level.md create mode 100644 doc/2.3.16 - heartbeat_check_interval.md create mode 100644 doc/2.3.17 - heartbeat_idle_time.md create mode 100644 doc/2.3.18 - open_eof_check.md create mode 100644 doc/2.3.19 - open_eof_split.md create mode 100644 doc/2.3.2 - worker_num.md create mode 100644 doc/2.3.20 - package_eof.md create mode 100644 doc/2.3.21 - open_length_check.md create mode 100644 doc/2.3.22 - package_length_type.md create mode 100644 doc/2.3.23 - package_length_func.md create mode 100644 doc/2.3.24 - package_max_length.md create mode 100644 doc/2.3.25 - open_cpu_affinity.md create mode 100644 doc/2.3.26 - cpu_affinity_ignore.md create mode 100644 doc/2.3.27 - open_tcp_nodelay.md create mode 100644 doc/2.3.28 - tcp_defer_accept.md create mode 100644 doc/2.3.29 - ssl_cert_file.md create mode 100644 doc/2.3.3 - max_request.md create mode 100644 doc/2.3.30 - ssl_method.md create mode 100644 doc/2.3.31 - ssl_ciphers.md create mode 100644 doc/2.3.32 - user.md create mode 100644 doc/2.3.33 - group.md create mode 100644 doc/2.3.34 - chroot.md create mode 100644 doc/2.3.35 - pid_file.md create mode 100644 doc/2.3.36 - pipe_buffer_size.md create mode 100644 doc/2.3.37 - buffer_output_size.md create mode 100644 doc/2.3.38 - socket_buffer_size.md create mode 100644 doc/2.3.39 - enable_unsafe_event.md create mode 100644 doc/2.3.4 - max_conn (max_connection).md create mode 100644 doc/2.3.40 - discard_timeout_request.md create mode 100644 doc/2.3.41 - enable_reuse_port.md create mode 100644 doc/2.3.42 - enable_delay_receive.md create mode 100644 doc/2.3.43 - open_http_protocol.md create mode 100644 doc/2.3.44 - open_http2_protocol.md create mode 100644 doc/2.3.45 - open_websocket_protocol.md create mode 100644 doc/2.3.46 - open_mqtt_protocol.md create mode 100644 doc/2.3.47 - reload_async.md create mode 100644 doc/2.3.48 - tcp_fastopen.md create mode 100644 doc/2.3.49 - request_slowlog_file.md create mode 100644 doc/2.3.5 - task_worker_num.md create mode 100644 doc/2.3.6 - task_ipc_mode.md create mode 100644 doc/2.3.7 - task_max_request.md create mode 100644 doc/2.3.8 - task_tmpdir.md create mode 100644 doc/2.3.9 - dispatch_mode.md create mode 100644 "doc/2.4 - \347\233\221\345\220\254\347\253\257\345\217\243.md" create mode 100644 "doc/2.4.1 - \345\217\257\351\200\211\345\217\202\346\225\260.md" create mode 100644 "doc/2.4.2 - \345\217\257\351\200\211\345\233\236\350\260\203.md" create mode 100644 "doc/2.4.3 - \350\277\236\346\216\245\350\277\255\344\273\243\345\231\250.md" create mode 100644 "doc/2.5 - \351\242\204\345\256\232\344\271\211\345\270\270\351\207\217.md" create mode 100644 "doc/2.6 - \344\272\213\344\273\266\345\233\236\350\260\203\345\207\275\346\225\260.md" create mode 100644 doc/2.6.1 - onStart.md create mode 100644 doc/2.6.10 - onBufferFull.md create mode 100644 doc/2.6.11 - onBufferEmpty.md create mode 100644 doc/2.6.12 - onTask.md create mode 100644 doc/2.6.13 - onFinish.md create mode 100644 doc/2.6.14 - onPipeMessage.md create mode 100644 doc/2.6.15 - onWorkerError.md create mode 100644 doc/2.6.16 - onManagerStart.md create mode 100644 doc/2.6.17 - onManagerStop.md create mode 100644 doc/2.6.2 - onShutdown.md create mode 100644 doc/2.6.3 - onWorkerStart.md create mode 100644 doc/2.6.4 - onWorkerStop.md create mode 100644 doc/2.6.5 - onWorkerExit.md create mode 100644 doc/2.6.6 - onConnect.md create mode 100644 doc/2.6.7 - onReceive.md create mode 100644 doc/2.6.8 - onPacket.md create mode 100644 doc/2.6.9 - onClose.md create mode 100644 "doc/2.7 - \351\253\230\347\272\247\347\211\271\346\200\247.md" create mode 100644 "doc/2.7.10 - swoole_server\344\270\255\345\257\271\350\261\241\347\232\2044\345\261\202\347\224\237\345\221\275\345\221\250\346\234\237.md" create mode 100644 "doc/2.7.11 - \345\234\250worker\350\277\233\347\250\213\345\206\205\347\233\221\345\220\254\344\270\200\344\270\252Server\347\253\257\345\217\243.md" create mode 100644 "doc/2.7.2 - \345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\347\232\204 reactor_id \345\222\214 fd.md" create mode 100644 "doc/2.7.3 - Length_Check \345\222\214 EOF_Check \347\232\204\344\275\277\347\224\250.md" create mode 100644 "doc/2.7.4 - Worker\344\270\216Reactor\351\200\232\344\277\241\346\250\241\345\274\217.md" create mode 100644 "doc/2.7.5 - TCP-Keepalive\346\255\273\350\277\236\346\216\245\346\243\200\346\265\213.md" create mode 100644 "doc/2.7.6 - TCP\346\234\215\345\212\241\345\231\250\345\277\203\350\267\263\347\273\264\346\214\201\346\226\271\346\241\210.md" create mode 100644 "doc/2.7.7 - \345\244\232\347\253\257\345\217\243\347\233\221\345\220\254\347\232\204\344\275\277\347\224\250.md" create mode 100644 "doc/2.7.8 - \346\215\225\350\216\267Server\350\277\220\350\241\214\346\234\237\350\207\264\345\221\275\351\224\231\350\257\257.md" create mode 100644 "doc/2.7.9 - swoole_server\347\232\204\344\270\244\347\247\215\350\277\220\350\241\214\346\250\241\345\274\217\344\273\213\347\273\215.md" create mode 100644 "doc/2.8 - \345\270\270\350\247\201\351\227\256\351\242\230.md" create mode 100644 "doc/2.8.1 - \344\270\272\344\273\200\344\271\210\344\270\215\350\246\201send\345\256\214\345\220\216\347\253\213\345\215\263close.md" create mode 100644 "doc/2.8.2 - \345\246\202\344\275\225\345\234\250\345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\350\256\277\351\227\256\345\244\226\351\203\250\347\232\204\345\217\230\351\207\217.md" create mode 100644 "doc/2.8.3 - swoole_server\344\270\255\345\206\205\345\255\230\347\256\241\347\220\206\346\234\272\345\210\266.md" create mode 100644 "doc/2.8.4 - \346\230\257\345\220\246\345\217\257\344\273\245\345\205\261\347\224\2501\344\270\252redis\346\210\226mysql\350\277\236\346\216\245.md" create mode 100644 "doc/2.8.6 - 4\347\247\215PHP\345\233\236\350\260\203\345\207\275\346\225\260\351\243\216\346\240\274.md" create mode 100644 "doc/2.8.7 - \344\270\215\345\220\214\347\232\204Server\347\250\213\345\272\217\345\256\236\344\276\213\351\227\264\345\246\202\344\275\225\351\200\232\344\277\241.md" create mode 100644 "doc/2.8.8 - \351\224\231\350\257\257\344\277\241\346\201\257\357\274\232ERROR (9006).md" create mode 100644 doc/2.8.9 - eventLoop has already been created. unable to create swoole_server.md create mode 100644 "doc/2.9 - \345\216\213\345\212\233\346\265\213\350\257\225.md" create mode 100644 "doc/2.9.1 - \345\271\266\345\217\22110\344\270\207TCP\350\277\236\346\216\245\347\232\204\346\265\213\350\257\225.md" create mode 100644 doc/3 - Client.md create mode 100644 "doc/3.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" create mode 100644 doc/3.1.1 - swoole_client::__construct.md create mode 100644 doc/3.1.10 - swoole_client->send.md create mode 100644 doc/3.1.11 - swoole_client->sendto.md create mode 100644 doc/3.1.12 - swoole_client->sendfile.md create mode 100644 doc/3.1.13 - swoole_client->recv.md create mode 100644 doc/3.1.14 - swoole_client->close.md create mode 100644 doc/3.1.15 - swoole_client->sleep.md create mode 100644 doc/3.1.16 - swoole_client->wakeup.md create mode 100644 doc/3.1.17 - swoole_client->enableSSL.md create mode 100644 doc/3.1.2 - swoole_client->set.md create mode 100644 doc/3.1.3 - swoole_client->on.md create mode 100644 doc/3.1.4 - swoole_client->connect.md create mode 100644 doc/3.1.5 - swoole_client->isConnected.md create mode 100644 doc/3.1.6 - swoole_client->getSocket.md create mode 100644 doc/3.1.7 - swoole_client->getSockName.md create mode 100644 doc/3.1.8 - swoole_client->getPeerName.md create mode 100644 doc/3.1.9 - swoole_client->getPeerCert.md create mode 100644 "doc/3.2 - \345\233\236\350\260\203\345\207\275\346\225\260.md" create mode 100644 doc/3.2.1 - onConnect.md create mode 100644 doc/3.2.2 - onError.md create mode 100644 doc/3.2.3 - onReceive.md create mode 100644 doc/3.2.4 - onClose.md create mode 100644 doc/3.2.5 - onBufferFull.md create mode 100644 doc/3.2.6 - onBufferEmpty.md create mode 100644 "doc/3.3 - \345\261\236\346\200\247\345\210\227\350\241\250.md" create mode 100644 doc/3.3.1 - swoole_client->errCode.md create mode 100644 doc/3.3.2 - swoole_client->sock.md create mode 100644 doc/3.3.3 - swoole_client->reuse.md create mode 100644 "doc/3.4 - \345\271\266\350\241\214.md" create mode 100644 doc/3.4.1 - swoole_client_select.md create mode 100644 "doc/3.4.2 - TCP\345\256\242\346\210\267\347\253\257\345\274\202\346\255\245\350\277\236\346\216\245.md" create mode 100644 "doc/3.4.3 - SWOOLE_KEEP\345\273\272\347\253\213TCP\351\225\277\350\277\236\346\216\245.md" create mode 100644 "doc/3.5 - \345\270\270\351\207\217.md" create mode 100644 "doc/3.6 - \351\205\215\347\275\256\351\200\211\351\241\271.md" create mode 100644 doc/3.6.1 - ssl_verify_peer.md create mode 100644 doc/3.6.2 - ssl_host_name.md create mode 100644 doc/3.6.3 - ssl_cafile.md create mode 100644 doc/3.6.4 - ssl_capath.md create mode 100644 doc/3.6.5 - package_length_func.md create mode 100644 "doc/3.7 - \345\270\270\350\247\201\351\227\256\351\242\230.md" create mode 100644 doc/4 - Process.md create mode 100644 doc/4.1 - swoole_process::__construct.md create mode 100644 doc/4.10 - swoole_process->statQueue.md create mode 100644 doc/4.11 - swoole_process->freeQueue.md create mode 100644 doc/4.12 - swoole_process->push.md create mode 100644 doc/4.13 - swoole_process->pop.md create mode 100644 doc/4.14 - swoole_process->close.md create mode 100644 doc/4.15 - swoole_process->exit.md create mode 100644 doc/4.16 - swoole_process::kill.md create mode 100644 doc/4.17 - swoole_process::wait.md create mode 100644 doc/4.18 - swoole_process::daemon.md create mode 100644 doc/4.19 - swoole_process::signal.md create mode 100644 doc/4.2 - swoole_process->start.md create mode 100644 doc/4.20 - swoole_process::alarm.md create mode 100644 doc/4.21 - swoole_process::setAffinity.md create mode 100644 doc/4.3 - swoole_process->name.md create mode 100644 doc/4.4 - swoole_process->exec.md create mode 100644 doc/4.5 - swoole_process->write.md create mode 100644 doc/4.6 - swoole_process->read.md create mode 100644 doc/4.7 - swoole_process->setTimeout.md create mode 100644 doc/4.8 - swoole_process->setBlocking.md create mode 100644 doc/4.9 - swoole_process->useQueue.md create mode 100644 doc/6 - AsyncIO.md create mode 100644 "doc/6.1 - \345\274\202\346\255\245\346\226\207\344\273\266\347\263\273\347\273\237IO.md" create mode 100644 doc/6.1.1 - swoole_async_readfile.md create mode 100644 doc/6.1.2 - swoole_async_writefile.md create mode 100644 doc/6.1.3 - swoole_async_read.md create mode 100644 doc/6.1.4 - swoole_async_write.md create mode 100644 doc/6.1.5 - swoole_async_dns_lookup.md create mode 100644 doc/6.1.6 - swoole_async::exec.md create mode 100644 doc/6.2 - EventLoop.md create mode 100644 doc/6.2.1 - swoole_event_add.md create mode 100644 doc/6.2.10 - swoole_event_dispatch.md create mode 100644 doc/6.2.2 - swoole_event_set.md create mode 100644 doc/6.2.3 - swoole_event_isset.md create mode 100644 doc/6.2.4 - swoole_event_write.md create mode 100644 doc/6.2.5 - swoole_event_del.md create mode 100644 doc/6.2.6 - swoole_event_exit.md create mode 100644 doc/6.2.7 - swoole_event_defer.md create mode 100644 doc/6.2.8 - swoole_event_cycle.md create mode 100644 doc/6.2.9 - swoole_event_wait.md create mode 100644 "doc/6.3 - \345\274\202\346\255\245\346\257\253\347\247\222\345\256\232\346\227\266\345\231\250.md" create mode 100644 doc/6.3.1 - swoole_timer_tick.md create mode 100644 doc/6.3.2 - swoole_timer_after.md create mode 100644 doc/6.3.3 - swoole_timer_clear.md create mode 100644 "doc/6.4 - \345\274\202\346\255\245MySQL\345\256\242\346\210\267\347\253\257.md" create mode 100644 doc/6.4.1 - swoole_mysql->construct.md create mode 100644 doc/6.4.2 - swoole_mysql->on.md create mode 100644 doc/6.4.3 - swoole_mysql->connect.md create mode 100644 doc/6.4.4 - swoole_mysql->escape.md create mode 100644 doc/6.4.5 - swoole_mysql->query.md create mode 100644 doc/6.4.6 - swoole_mysql->begin.md create mode 100644 doc/6.4.7 - swoole_mysql->commit.md create mode 100644 doc/6.4.8 - swoole_mysql->rollback.md create mode 100644 doc/6.4.9 - swoole_mysql->close.md create mode 100644 "doc/6.5 - \345\274\202\346\255\245Redis\345\256\242\346\210\267\347\253\257.md" create mode 100644 doc/6.5.1 - swoole_redis->__construct.md create mode 100644 doc/6.5.2 - swoole_redis->on.md create mode 100644 doc/6.5.3 - swoole_redis->connect.md create mode 100644 doc/6.5.4 - swoole_redis->__call.md create mode 100644 doc/6.5.5 - swoole_redis->close.md create mode 100644 doc/6.6.1 - swoole_http_client->__construct.md create mode 100644 doc/6.6.10 - swoole_http_client->upgrade.md create mode 100644 doc/6.6.11 - swoole_http_client->push.md create mode 100644 doc/6.6.12 - swoole_http_client->execute.md create mode 100644 doc/6.6.13 - swoole_http_client->download.md create mode 100644 doc/6.6.14 - swoole_http_client->close.md create mode 100644 doc/6.6.2 - swoole_http_client->set.md create mode 100644 doc/6.6.3 - swoole_http_client->setMethod.md create mode 100644 doc/6.6.4 - swoole_http_client->setHeaders.md create mode 100644 doc/6.6.5 - swoole_http_client->setCookies.md create mode 100644 doc/6.6.6 - swoole_http_client->setData.md create mode 100644 doc/6.6.7 - swoole_http_client->addFile.md create mode 100644 doc/6.6.8 - swoole_http_client->get.md create mode 100644 doc/6.6.9 - swoole_http_client->post.md create mode 100644 "doc/6.7 - \345\274\202\346\255\245Http2.0\345\256\242\346\210\267\347\253\257.md" create mode 100644 doc/6.7.1 - swoole_http2_client->__construct.md create mode 100644 doc/6.7.2 - swoole_http2_client->get.md create mode 100644 doc/6.7.3 - swoole_http2_client->post.md create mode 100644 doc/6.7.4 - swoole_http2_client->setHeaders.md create mode 100644 doc/6.7.5 - swoole_http2_client->setCookies.md create mode 100644 doc/7 - Memory.md create mode 100644 doc/7.1 - Lock.md create mode 100644 doc/7.1.1 - swoole_lock->__construct.md create mode 100644 doc/7.1.2 - swoole_lock->lock.md create mode 100644 doc/7.1.3 - swoole_lock->trylock.md create mode 100644 doc/7.1.4 - swoole_lock->unlock.md create mode 100644 doc/7.1.5 - swoole_lock->lock_read.md create mode 100644 doc/7.1.6 - swoole_lock->trylock_read.md create mode 100644 doc/7.1.7 - swoole_lock->lockwait.md create mode 100644 doc/7.2 - Buffer.md create mode 100644 doc/7.2.1 - swoole_buffer->__construct.md create mode 100644 doc/7.2.2 - swoole_buffer->append.md create mode 100644 doc/7.2.3 - swoole_buffer->substr.md create mode 100644 doc/7.2.4 - swoole_buffer->clear.md create mode 100644 doc/7.2.5 - swoole_buffer->expand.md create mode 100644 doc/7.2.6 - swoole_buffer->write.md create mode 100644 doc/7.2.7 - swoole_buffer->read.md create mode 100644 doc/7.2.8 - swoole_buffer->recycle.md create mode 100644 doc/7.3 - Table.md create mode 100644 doc/7.3.1 - swoole_table->__construct.md create mode 100644 "doc/7.3.10 - \345\270\270\351\207\217\345\210\227\350\241\250.md" create mode 100644 doc/7.3.2 - swoole_table->column.md create mode 100644 doc/7.3.3 - swoole_table->create.md create mode 100644 doc/7.3.4 - swoole_table->set.md create mode 100644 doc/7.3.5 - swoole_table->incr.md create mode 100644 doc/7.3.6 - swoole_table->decr.md create mode 100644 doc/7.3.7 - swoole_table->get.md create mode 100644 doc/7.3.8 - swoole_table->exist.md create mode 100644 doc/7.3.9 - swoole_table->del.md create mode 100644 doc/7.4 - Atomic.md create mode 100644 doc/7.4.1 - swoole_atomic->__construct.md create mode 100644 doc/7.4.2 - swoole_atomic->add.md create mode 100644 doc/7.4.3 - swoole_atomic->sub.md create mode 100644 doc/7.4.4 - swoole_atomic->get.md create mode 100644 doc/7.4.5 - swoole_atomic->set.md create mode 100644 doc/7.4.6 - swoole_atomic->cmpset.md create mode 100644 doc/7.4.7 - swoole_atomic->wait.md create mode 100644 doc/7.4.8 - swoole_atomic->wakeup.md create mode 100644 doc/7.5 - mmap.md create mode 100644 doc/7.5.1 - swoole_mmap::open.md create mode 100644 doc/7.6 - Channel.md create mode 100644 doc/7.6.1 - Channel->__construct.md create mode 100644 doc/7.6.2 - Channel->push.md create mode 100644 doc/7.6.3 - Channel->pop.md create mode 100644 doc/7.6.4 - Channel->stats.md create mode 100644 doc/7.7 - Serialize.md create mode 100644 doc/7.7.1 - swoole_serialize::pack.md create mode 100644 doc/7.7.2 - swoole_serialize::unpack.md create mode 100644 doc/8 - HttpServer.md create mode 100644 doc/8.1 - swoole_http_server.md create mode 100644 doc/8.1.1 - swoole_http_server->on.md create mode 100644 doc/8.1.2 - swoole_http_server->start.md create mode 100644 doc/8.2 - swoole_http_request.md create mode 100644 doc/8.2.1 - swoole_http_request->$header.md create mode 100644 doc/8.2.2 - swoole_http_request->$server.md create mode 100644 doc/8.2.3 - swoole_http_request->$get.md create mode 100644 doc/8.2.4 - swoole_http_request->$post.md create mode 100644 doc/8.2.5 - swoole_http_request->$cookie.md create mode 100644 doc/8.2.6 - swoole_http_request->$files.md create mode 100644 doc/8.2.7 - swoole_http_request->rawContent.md create mode 100644 doc/8.2.8 - swoole_http_request->getData.md create mode 100644 doc/8.3 - swoole_http_response.md create mode 100644 doc/8.3.1 - swoole_http_response->header.md create mode 100644 doc/8.3.10 - swoole_http_response::create.md create mode 100644 doc/8.3.2 - swoole_http_response->cookie.md create mode 100644 doc/8.3.3 - swoole_http_response->status.md create mode 100644 doc/8.3.4 - swoole_http_response->gzip.md create mode 100644 doc/8.3.5 - swoole_http_response->redirect.md create mode 100644 doc/8.3.6 - swoole_http_response->write.md create mode 100644 doc/8.3.7 - swoole_http_response->sendfile.md create mode 100644 doc/8.3.8 - swoole_http_response->end.md create mode 100644 doc/8.3.9 - swoole_http_response->detach.md create mode 100644 "doc/8.4 - \351\205\215\347\275\256\351\200\211\351\241\271.md" create mode 100644 doc/8.4.1 - upload_tmp_dir.md create mode 100644 doc/8.4.2 - http_parse_post.md create mode 100644 doc/8.4.3 - document_root.md create mode 100644 "doc/8.5 - \345\270\270\350\247\201\351\227\256\351\242\230.md" create mode 100644 "doc/8.5.1 - CURL\345\217\221\351\200\201POST\350\257\267\346\261\202\346\234\215\345\212\241\345\231\250\347\253\257\350\266\205\346\227\266.md" create mode 100644 "doc/8.5.2 - \344\275\277\347\224\250Chrome\350\256\277\351\227\256\346\234\215\345\212\241\345\231\250\344\274\232\344\272\247\347\224\2372\346\254\241\350\257\267\346\261\202.md" create mode 100644 doc/9 - WebSocket.md create mode 100644 "doc/9.1 - \345\233\236\350\260\203\345\207\275\346\225\260.md" create mode 100644 doc/9.1.1 - onHandShake.md create mode 100644 doc/9.1.2 - onOpen.md create mode 100644 doc/9.1.3 - onMessage.md create mode 100644 "doc/9.2 - \345\207\275\346\225\260\345\210\227\350\241\250.md" create mode 100644 doc/9.2.1 - swoole_websocket_server->push.md create mode 100644 doc/9.2.2 - swoole_websocket_server->exist.md create mode 100644 doc/9.2.3 - swoole_websocket_server::pack.md create mode 100644 doc/9.2.4 - swoole_websocket_server::unpack.md create mode 100644 "doc/9.3 - \351\242\204\345\256\232\344\271\211\345\270\270\351\207\217.md" create mode 100644 "doc/9.4 - \345\270\270\350\247\201\351\227\256\351\242\230.md" create mode 100644 "doc/9.5 - \351\205\215\347\275\256\351\200\211\351\241\271.md" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5dcbe0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +/*.json +README_RAW.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6b02ab1 --- /dev/null +++ b/README.md @@ -0,0 +1,685 @@ +# Swoole中文文档 + +全量MarkDown版本 + +## 目录 + +- ### **1** [入门指引](./doc/1 - 入门指引.md) + - #### **1.1** [环境依赖](./doc/1.1 - 环境依赖.md) + - #### **1.2** [编译安装](./doc/1.2 - 编译安装.md) + - **`1.2.1`** [编译参数](./doc/1.2.1 - 编译参数.md) + - **`1.2.2`** [常见错误](./doc/1.2.2 - 常见错误.md) + - #### **1.3** [快速起步](./doc/1.3 - 快速起步.md) + - **`1.3.1`** [创建TCP服务器](./doc/1.3.1 - 创建TCP服务器.md) + - **`1.3.2`** [创建UDP服务器](./doc/1.3.2 - 创建UDP服务器.md) + - **`1.3.3`** [创建Web服务器](./doc/1.3.3 - 创建Web服务器.md) + - **`1.3.4`** [创建WebSocket服务器](./doc/1.3.4 - 创建WebSocket服务器.md) + - **`1.3.5`** [设置定时器](./doc/1.3.5 - 设置定时器.md) + - **`1.3.6`** [执行异步任务](./doc/1.3.6 - 执行异步任务.md) + - **`1.3.7`** [创建同步TCP客户端](./doc/1.3.7 - 创建同步TCP客户端.md) + - **`1.3.8`** [创建异步TCP客户端](./doc/1.3.8 - 创建异步TCP客户端.md) + - **`1.3.9`** [网络通信协议设计](./doc/1.3.9 - 网络通信协议设计.md) + - **`1.3.10`** [使用异步客户端](./doc/1.3.10 - 使用异步客户端.md) + - **`1.3.11`** [多进程共享数据](./doc/1.3.11 - 多进程共享数据.md) + - #### **1.4** [编程须知](./doc/1.4 - 编程须知.md) + - **`1.4.1`** [sleep\usleep的影响](./doc/1.4.1 - sleep\usleep的影响.md) + - **`1.4.2`** [exit\die函数的影响](./doc/1.4.2 - exit\die函数的影响.md) + - **`1.4.3`** [while循环的影响](./doc/1.4.3 - while循环的影响.md) + - **`1.4.4`** [stat缓存清理](./doc/1.4.4 - stat缓存清理.md) + - **`1.4.5`** [mt_rand随机数](./doc/1.4.5 - mt_rand随机数.md) + - #### **1.5** [版本更新记录](./doc/1.5 - 版本更新记录.md) + - **`1.5.1`** [2.1.2](./doc/1.5.1 - 2.1.2.md) + - **`1.5.2`** [1.10.3](./doc/1.5.2 - 1.10.3.md) + - **`1.5.3`** [1.10.2](./doc/1.5.3 - 1.10.2.md) + - **`1.5.4`** [2.1.1](./doc/1.5.4 - 2.1.1.md) + - **`1.5.5`** [1.10.1](./doc/1.5.5 - 1.10.1.md) + - **`1.5.6`** [2.0.13](./doc/1.5.6 - 2.0.13.md) + - **`1.5.7`** [2.0.12](./doc/1.5.7 - 2.0.12.md) + - **`1.5.8`** [2.0.11](./doc/1.5.8 - 2.0.11.md) + - **`1.5.9`** [1.10.0](./doc/1.5.9 - 1.10.0.md) + - **`1.5.10`** [1.9.23](./doc/1.5.10 - 1.9.23.md) + - **`1.5.11`** [1.9.22](./doc/1.5.11 - 1.9.22.md) + - **`1.5.12`** [1.9.21](./doc/1.5.12 - 1.9.21.md) + - #### **1.6** [新特性使用](./doc/1.6 - 新特性使用.md) + - **`1.6.1`** [2.1.2 进程池模块的使用](./doc/1.6.1 - 2.1.2 进程池模块的使用.md) + - **`1.6.2`** [1.9.24 调度支持 Stream 模式](./doc/1.6.2 - 1.9.24 调度支持 Stream 模式.md) + - **`1.6.3`** [1.9.24 异步客户端自动解析域名](./doc/1.6.3 - 1.9.24 异步客户端自动解析域名.md) + - **`1.6.4`** [1.9.17 支持异步安全重启特性](./doc/1.6.4 - 1.9.17 支持异步安全重启特性.md) + - **`1.6.5`** [1.9.14 使用异步客户端超时机制](./doc/1.6.5 - 1.9.14 使用异步客户端超时机制.md) + - **`1.6.6`** [1.8.0 使用内置Http异步客户端](./doc/1.6.6 - 1.8.0 使用内置Http异步客户端.md) + - **`1.6.7`** [1.7.16 使用迭代器遍历Server所有连接](./doc/1.6.7 - 1.7.16 使用迭代器遍历Server所有连接.md) + - **`1.6.8`** [1.7.5 在Server中使用swoole_table](./doc/1.6.8 - 1.7.5 在Server中使用swoole_table.md) + - **`1.6.9`** [1.7.5 swoole_client支持sendfile接口](./doc/1.6.9 - 1.7.5 swoole_client支持sendfile接口.md) + - **`1.6.10`** [1.7.4 SSL隧道加密TCP-Server](./doc/1.6.10 - 1.7.4 SSL隧道加密TCP-Server.md) + - **`1.6.11`** [1.7.4 task进程中使用毫秒定时器](./doc/1.6.11 - 1.7.4 task进程中使用毫秒定时器.md) + - **`1.6.12`** [1.7.3 固定包头+包体协议自动分包](./doc/1.6.12 - 1.7.3 固定包头+包体协议自动分包.md) + - **`1.6.13`** [1.7.3 onTask直接return取代finish函数](./doc/1.6.13 - 1.7.3 onTask直接return取代finish函数.md) + - **`1.6.14`** [1.7.2 swoole_process多进程模块的使用](./doc/1.6.14 - 1.7.2 swoole_process多进程模块的使用.md) + - **`1.6.15`** [1.7.2 task进程使用消息队列](./doc/1.6.15 - 1.7.2 task进程使用消息队列.md) + - #### **1.7** [项目路线图](./doc/1.7 - 项目路线图.md) + - #### **1.8** [php.ini选项](./doc/1.8 - php.ini选项.md) + - #### **1.9** [内核参数调整](./doc/1.9 - 内核参数调整.md) + - #### **1.11** [衍生开源项目](./doc/1.11 - 衍生开源项目.md) + - **`1.11.1`** [框架](./doc/1.11.1 - 框架.md) + - **`1.11.2`** [工具](./doc/1.11.2 - 工具.md) + - **`1.11.3`** [分布式](./doc/1.11.3 - 分布式.md) + - **`1.11.4`** [通信协议](./doc/1.11.4 - 通信协议.md) + - #### **1.12** [用户与案例](./doc/1.12 - 用户与案例.md) + - **`1.12.1`** [物联网项目](./doc/1.12.1 - 物联网项目.md) + - **`1.12.2`** [网络游戏](./doc/1.12.2 - 网络游戏.md) + - **`1.12.3`** [腾讯(Tencent)](./doc/1.12.3 - 腾讯(Tencent).md) + - **`1.12.4`** [百度(Baidu.com)](./doc/1.12.4 - 百度(Baidu.com).md) + - **`1.12.5`** [阅文集团](./doc/1.12.5 - 阅文集团.md) + - **`1.12.6`** [BiliBili(哔哩哔哩)](./doc/1.12.6 - BiliBili(哔哩哔哩).md) + - **`1.12.7`** [车轮互联(chelun.com)](./doc/1.12.7 - 车轮互联(chelun.com).md) + - **`1.12.8`** [(捞月狗) 游戏社区](./doc/1.12.8 - (捞月狗) 游戏社区.md) + - #### **1.13** [提交错误报告](./doc/1.13 - 提交错误报告.md) + - #### **1.14** [常见问题](./doc/1.14 - 常见问题.md) + - **`1.14.1`** [升级swoole版本的常见问题](./doc/1.14.1 - 升级swoole版本的常见问题.md) + - **`1.14.2`** [生成可分发的二进制swoole版本](./doc/1.14.2 - 生成可分发的二进制swoole版本.md) + - **`1.14.3`** [在phpinfo中有在php-m中没有](./doc/1.14.3 - 在phpinfo中有在php-m中没有.md) + - **`1.14.4`** [Connection refused是怎么回事](./doc/1.14.4 - Connection refused是怎么回事.md) + - **`1.14.5`** [Resource temporarily unavailable [11]](./doc/1.14.5 - Resource temporarily unavailable [11].md) + - **`1.14.6`** [Cannot assign requested address [99]](./doc/1.14.6 - Cannot assign requested address [99].md) + - **`1.14.7`** [swoole与node.js相比有哪些优势](./doc/1.14.7 - swoole与node.js相比有哪些优势.md) + - **`1.14.8`** [swoole与golang相比有哪些优势](./doc/1.14.8 - swoole与golang相比有哪些优势.md) + - **`1.14.9`** [pcre.h: No such file or directory](./doc/1.14.9 - pcre.h: No such file or directory.md) + - **`1.14.10`** [my_global.h: No such file or directory](./doc/1.14.10 - my_global.h: No such file or directory.md) + - **`1.14.11`** [undefined symbol: __sync_bool_compare_and_swap_4](./doc/1.14.11 - undefined symbol: __sync_bool_compare_and_swap_4.md) + - **`1.14.12`** [学习Swoole需要掌握哪些基础知识](./doc/1.14.12 - 学习Swoole需要掌握哪些基础知识.md) + - **`1.14.13`** [同步阻塞与异步非阻塞适用场景](./doc/1.14.13 - 同步阻塞与异步非阻塞适用场景.md) + - **`1.14.14`** [PHP7环境下出现zend_mm_heap corrupted](./doc/1.14.14 - PHP7环境下出现zend_mm_heap corrupted.md) + - **`1.14.15`** [swoole项目起源和名字由来](./doc/1.14.15 - swoole项目起源和名字由来.md) +- ### **2** [Server](./doc/2 - Server.md) + - #### **2.1** [函数列表](./doc/2.1 - 函数列表.md) + - **`2.1.1`** [swoole_server::__construct](./doc/2.1.1 - swoole_server::__construct.md) + - **`2.1.2`** [swoole_server->set](./doc/2.1.2 - swoole_server->set.md) + - **`2.1.3`** [swoole_server->on](./doc/2.1.3 - swoole_server->on.md) + - **`2.1.4`** [swoole_server->addListener](./doc/2.1.4 - swoole_server->addListener.md) + - **`2.1.5`** [swoole_server->addProcess](./doc/2.1.5 - swoole_server->addProcess.md) + - **`2.1.6`** [swoole_server->listen](./doc/2.1.6 - swoole_server->listen.md) + - **`2.1.7`** [swoole_server->start](./doc/2.1.7 - swoole_server->start.md) + - **`2.1.8`** [swoole_server->reload](./doc/2.1.8 - swoole_server->reload.md) + - **`2.1.9`** [swoole_server->stop](./doc/2.1.9 - swoole_server->stop.md) + - **`2.1.10`** [swoole_server->shutdown](./doc/2.1.10 - swoole_server->shutdown.md) + - **`2.1.11`** [swoole_server->tick](./doc/2.1.11 - swoole_server->tick.md) + - **`2.1.12`** [swoole_server->after](./doc/2.1.12 - swoole_server->after.md) + - **`2.1.13`** [swoole_server->defer](./doc/2.1.13 - swoole_server->defer.md) + - **`2.1.14`** [swoole_server->clearTimer](./doc/2.1.14 - swoole_server->clearTimer.md) + - **`2.1.15`** [swoole_server->close](./doc/2.1.15 - swoole_server->close.md) + - **`2.1.16`** [swoole_server->send](./doc/2.1.16 - swoole_server->send.md) + - **`2.1.17`** [swoole_server->sendfile](./doc/2.1.17 - swoole_server->sendfile.md) + - **`2.1.18`** [swoole_server->sendto](./doc/2.1.18 - swoole_server->sendto.md) + - **`2.1.19`** [swoole_server->sendwait](./doc/2.1.19 - swoole_server->sendwait.md) + - **`2.1.20`** [swoole_server->sendMessage](./doc/2.1.20 - swoole_server->sendMessage.md) + - **`2.1.21`** [swoole_server->exist](./doc/2.1.21 - swoole_server->exist.md) + - **`2.1.22`** [swoole_server->pause](./doc/2.1.22 - swoole_server->pause.md) + - **`2.1.23`** [swoole_server->resume](./doc/2.1.23 - swoole_server->resume.md) + - **`2.1.24`** [swoole_server->getClientInfo](./doc/2.1.24 - swoole_server->getClientInfo.md) + - **`2.1.25`** [swoole_server->getClientList](./doc/2.1.25 - swoole_server->getClientList.md) + - **`2.1.26`** [swoole_server->bind](./doc/2.1.26 - swoole_server->bind.md) + - **`2.1.27`** [swoole_server->stats](./doc/2.1.27 - swoole_server->stats.md) + - **`2.1.28`** [swoole_server->task](./doc/2.1.28 - swoole_server->task.md) + - **`2.1.29`** [swoole_server->taskwait](./doc/2.1.29 - swoole_server->taskwait.md) + - **`2.1.30`** [swoole_server->taskWaitMulti](./doc/2.1.30 - swoole_server->taskWaitMulti.md) + - **`2.1.31`** [swoole_server->taskCo](./doc/2.1.31 - swoole_server->taskCo.md) + - **`2.1.32`** [swoole_server->finish](./doc/2.1.32 - swoole_server->finish.md) + - **`2.1.33`** [swoole_server->heartbeat](./doc/2.1.33 - swoole_server->heartbeat.md) + - **`2.1.34`** [swoole_server->getLastError](./doc/2.1.34 - swoole_server->getLastError.md) + - **`2.1.35`** [swoole_server->getSocket](./doc/2.1.35 - swoole_server->getSocket.md) + - **`2.1.36`** [swoole_server->protect](./doc/2.1.36 - swoole_server->protect.md) + - **`2.1.37`** [swoole_server->confirm](./doc/2.1.37 - swoole_server->confirm.md) + - #### **2.2** [属性列表](./doc/2.2 - 属性列表.md) + - **`2.2.1`** [swoole_server::$setting](./doc/2.2.1 - swoole_server::$setting.md) + - **`2.2.2`** [swoole_server::$master_pid](./doc/2.2.2 - swoole_server::$master_pid.md) + - **`2.2.3`** [swoole_server::$manager_pid](./doc/2.2.3 - swoole_server::$manager_pid.md) + - **`2.2.4`** [swoole_server::$worker_id](./doc/2.2.4 - swoole_server::$worker_id.md) + - **`2.2.5`** [swoole_server::$worker_pid](./doc/2.2.5 - swoole_server::$worker_pid.md) + - **`2.2.6`** [swoole_server::$taskworker](./doc/2.2.6 - swoole_server::$taskworker.md) + - **`2.2.7`** [swoole_server::$connections](./doc/2.2.7 - swoole_server::$connections.md) + - **`2.2.8`** [swoole_server::$ports](./doc/2.2.8 - swoole_server::$ports.md) + - #### **2.3** [配置选项](./doc/2.3 - 配置选项.md) + - **`2.3.1`** [reactor_num](./doc/2.3.1 - reactor_num.md) + - **`2.3.2`** [worker_num](./doc/2.3.2 - worker_num.md) + - **`2.3.3`** [max_request](./doc/2.3.3 - max_request.md) + - **`2.3.4`** [max_conn (max_connection)](./doc/2.3.4 - max_conn (max_connection).md) + - **`2.3.5`** [task_worker_num](./doc/2.3.5 - task_worker_num.md) + - **`2.3.6`** [task_ipc_mode](./doc/2.3.6 - task_ipc_mode.md) + - **`2.3.7`** [task_max_request](./doc/2.3.7 - task_max_request.md) + - **`2.3.8`** [task_tmpdir](./doc/2.3.8 - task_tmpdir.md) + - **`2.3.9`** [dispatch_mode](./doc/2.3.9 - dispatch_mode.md) + - **`2.3.10`** [dispatch_func](./doc/2.3.10 - dispatch_func.md) + - **`2.3.11`** [message_queue_key](./doc/2.3.11 - message_queue_key.md) + - **`2.3.12`** [daemonize](./doc/2.3.12 - daemonize.md) + - **`2.3.13`** [backlog](./doc/2.3.13 - backlog.md) + - **`2.3.14`** [log_file](./doc/2.3.14 - log_file.md) + - **`2.3.15`** [log_level](./doc/2.3.15 - log_level.md) + - **`2.3.16`** [heartbeat_check_interval](./doc/2.3.16 - heartbeat_check_interval.md) + - **`2.3.17`** [heartbeat_idle_time](./doc/2.3.17 - heartbeat_idle_time.md) + - **`2.3.18`** [open_eof_check](./doc/2.3.18 - open_eof_check.md) + - **`2.3.19`** [open_eof_split](./doc/2.3.19 - open_eof_split.md) + - **`2.3.20`** [package_eof](./doc/2.3.20 - package_eof.md) + - **`2.3.21`** [open_length_check](./doc/2.3.21 - open_length_check.md) + - **`2.3.22`** [package_length_type](./doc/2.3.22 - package_length_type.md) + - **`2.3.23`** [package_length_func](./doc/2.3.23 - package_length_func.md) + - **`2.3.24`** [package_max_length](./doc/2.3.24 - package_max_length.md) + - **`2.3.25`** [open_cpu_affinity](./doc/2.3.25 - open_cpu_affinity.md) + - **`2.3.26`** [cpu_affinity_ignore](./doc/2.3.26 - cpu_affinity_ignore.md) + - **`2.3.27`** [open_tcp_nodelay](./doc/2.3.27 - open_tcp_nodelay.md) + - **`2.3.28`** [tcp_defer_accept](./doc/2.3.28 - tcp_defer_accept.md) + - **`2.3.29`** [ssl_cert_file](./doc/2.3.29 - ssl_cert_file.md) + - **`2.3.30`** [ssl_method](./doc/2.3.30 - ssl_method.md) + - **`2.3.31`** [ssl_ciphers](./doc/2.3.31 - ssl_ciphers.md) + - **`2.3.32`** [user](./doc/2.3.32 - user.md) + - **`2.3.33`** [group](./doc/2.3.33 - group.md) + - **`2.3.34`** [chroot](./doc/2.3.34 - chroot.md) + - **`2.3.35`** [pid_file](./doc/2.3.35 - pid_file.md) + - **`2.3.36`** [pipe_buffer_size](./doc/2.3.36 - pipe_buffer_size.md) + - **`2.3.37`** [buffer_output_size](./doc/2.3.37 - buffer_output_size.md) + - **`2.3.38`** [socket_buffer_size](./doc/2.3.38 - socket_buffer_size.md) + - **`2.3.39`** [enable_unsafe_event](./doc/2.3.39 - enable_unsafe_event.md) + - **`2.3.40`** [discard_timeout_request](./doc/2.3.40 - discard_timeout_request.md) + - **`2.3.41`** [enable_reuse_port](./doc/2.3.41 - enable_reuse_port.md) + - **`2.3.42`** [enable_delay_receive](./doc/2.3.42 - enable_delay_receive.md) + - **`2.3.43`** [open_http_protocol](./doc/2.3.43 - open_http_protocol.md) + - **`2.3.44`** [open_http2_protocol](./doc/2.3.44 - open_http2_protocol.md) + - **`2.3.45`** [open_websocket_protocol](./doc/2.3.45 - open_websocket_protocol.md) + - **`2.3.46`** [open_mqtt_protocol](./doc/2.3.46 - open_mqtt_protocol.md) + - **`2.3.47`** [reload_async](./doc/2.3.47 - reload_async.md) + - **`2.3.48`** [tcp_fastopen](./doc/2.3.48 - tcp_fastopen.md) + - **`2.3.49`** [request_slowlog_file](./doc/2.3.49 - request_slowlog_file.md) + - #### **2.4** [监听端口](./doc/2.4 - 监听端口.md) + - **`2.4.1`** [可选参数](./doc/2.4.1 - 可选参数.md) + - **`2.4.2`** [可选回调](./doc/2.4.2 - 可选回调.md) + - **`2.4.3`** [连接迭代器](./doc/2.4.3 - 连接迭代器.md) + - #### **2.5** [预定义常量](./doc/2.5 - 预定义常量.md) + - #### **2.6** [事件回调函数](./doc/2.6 - 事件回调函数.md) + - **`2.6.1`** [onStart](./doc/2.6.1 - onStart.md) + - **`2.6.2`** [onShutdown](./doc/2.6.2 - onShutdown.md) + - **`2.6.3`** [onWorkerStart](./doc/2.6.3 - onWorkerStart.md) + - **`2.6.4`** [onWorkerStop](./doc/2.6.4 - onWorkerStop.md) + - **`2.6.5`** [onWorkerExit](./doc/2.6.5 - onWorkerExit.md) + - **`2.6.6`** [onConnect](./doc/2.6.6 - onConnect.md) + - **`2.6.7`** [onReceive](./doc/2.6.7 - onReceive.md) + - **`2.6.8`** [onPacket](./doc/2.6.8 - onPacket.md) + - **`2.6.9`** [onClose](./doc/2.6.9 - onClose.md) + - **`2.6.10`** [onBufferFull](./doc/2.6.10 - onBufferFull.md) + - **`2.6.11`** [onBufferEmpty](./doc/2.6.11 - onBufferEmpty.md) + - **`2.6.12`** [onTask](./doc/2.6.12 - onTask.md) + - **`2.6.13`** [onFinish](./doc/2.6.13 - onFinish.md) + - **`2.6.14`** [onPipeMessage](./doc/2.6.14 - onPipeMessage.md) + - **`2.6.15`** [onWorkerError](./doc/2.6.15 - onWorkerError.md) + - **`2.6.16`** [onManagerStart](./doc/2.6.16 - onManagerStart.md) + - **`2.6.17`** [onManagerStop](./doc/2.6.17 - onManagerStop.md) + - #### **2.7** [高级特性](./doc/2.7 - 高级特性.md) + - **`2.7.1`** [改变Worker进程的用户\组](./doc/2.7.1 - 改变Worker进程的用户\组.md) + - **`2.7.2`** [回调函数中的 reactor_id 和 fd](./doc/2.7.2 - 回调函数中的 reactor_id 和 fd.md) + - **`2.7.3`** [Length_Check 和 EOF_Check 的使用](./doc/2.7.3 - Length_Check 和 EOF_Check 的使用.md) + - **`2.7.4`** [Worker与Reactor通信模式](./doc/2.7.4 - Worker与Reactor通信模式.md) + - **`2.7.5`** [TCP-Keepalive死连接检测](./doc/2.7.5 - TCP-Keepalive死连接检测.md) + - **`2.7.6`** [TCP服务器心跳维持方案](./doc/2.7.6 - TCP服务器心跳维持方案.md) + - **`2.7.7`** [多端口监听的使用](./doc/2.7.7 - 多端口监听的使用.md) + - **`2.7.8`** [捕获Server运行期致命错误](./doc/2.7.8 - 捕获Server运行期致命错误.md) + - **`2.7.9`** [swoole_server的两种运行模式介绍](./doc/2.7.9 - swoole_server的两种运行模式介绍.md) + - **`2.7.10`** [swoole_server中对象的4层生命周期](./doc/2.7.10 - swoole_server中对象的4层生命周期.md) + - **`2.7.11`** [在worker进程内监听一个Server端口](./doc/2.7.11 - 在worker进程内监听一个Server端口.md) + - **`2.7.12`** [在php-fpm\apache中使用task功能](./doc/2.7.12 - 在php-fpm\apache中使用task功能.md) + - #### **2.8** [常见问题](./doc/2.8 - 常见问题.md) + - **`2.8.1`** [为什么不要send完后立即close](./doc/2.8.1 - 为什么不要send完后立即close.md) + - **`2.8.2`** [如何在回调函数中访问外部的变量](./doc/2.8.2 - 如何在回调函数中访问外部的变量.md) + - **`2.8.3`** [swoole_server中内存管理机制](./doc/2.8.3 - swoole_server中内存管理机制.md) + - **`2.8.4`** [是否可以共用1个redis或mysql连接](./doc/2.8.4 - 是否可以共用1个redis或mysql连接.md) + - **`2.8.5`** [关于onConnect\onReceive\onClose顺序](./doc/2.8.5 - 关于onConnect\onReceive\onClose顺序.md) + - **`2.8.6`** [4种PHP回调函数风格](./doc/2.8.6 - 4种PHP回调函数风格.md) + - **`2.8.7`** [不同的Server程序实例间如何通信](./doc/2.8.7 - 不同的Server程序实例间如何通信.md) + - **`2.8.8`** [错误信息:ERROR (9006)](./doc/2.8.8 - 错误信息:ERROR (9006).md) + - **`2.8.9`** [eventLoop has already been created. unable to create swoole_server](./doc/2.8.9 - eventLoop has already been created. unable to create swoole_server.md) + - #### **2.9** [压力测试](./doc/2.9 - 压力测试.md) + - **`2.9.1`** [并发10万TCP连接的测试](./doc/2.9.1 - 并发10万TCP连接的测试.md) + - **`2.9.2`** [PHP7+Swoole\Nginx\Golang性能对比](./doc/2.9.2 - PHP7+Swoole\Nginx\Golang性能对比.md) +- ### **3** [Client](./doc/3 - Client.md) + - #### **3.1** [方法列表](./doc/3.1 - 方法列表.md) + - **`3.1.1`** [swoole_client::__construct](./doc/3.1.1 - swoole_client::__construct.md) + - **`3.1.2`** [swoole_client->set](./doc/3.1.2 - swoole_client->set.md) + - **`3.1.3`** [swoole_client->on](./doc/3.1.3 - swoole_client->on.md) + - **`3.1.4`** [swoole_client->connect](./doc/3.1.4 - swoole_client->connect.md) + - **`3.1.5`** [swoole_client->isConnected](./doc/3.1.5 - swoole_client->isConnected.md) + - **`3.1.6`** [swoole_client->getSocket](./doc/3.1.6 - swoole_client->getSocket.md) + - **`3.1.7`** [swoole_client->getSockName](./doc/3.1.7 - swoole_client->getSockName.md) + - **`3.1.8`** [swoole_client->getPeerName](./doc/3.1.8 - swoole_client->getPeerName.md) + - **`3.1.9`** [swoole_client->getPeerCert](./doc/3.1.9 - swoole_client->getPeerCert.md) + - **`3.1.10`** [swoole_client->send](./doc/3.1.10 - swoole_client->send.md) + - **`3.1.11`** [swoole_client->sendto](./doc/3.1.11 - swoole_client->sendto.md) + - **`3.1.12`** [swoole_client->sendfile](./doc/3.1.12 - swoole_client->sendfile.md) + - **`3.1.13`** [swoole_client->recv](./doc/3.1.13 - swoole_client->recv.md) + - **`3.1.14`** [swoole_client->close](./doc/3.1.14 - swoole_client->close.md) + - **`3.1.15`** [swoole_client->sleep](./doc/3.1.15 - swoole_client->sleep.md) + - **`3.1.16`** [swoole_client->wakeup](./doc/3.1.16 - swoole_client->wakeup.md) + - **`3.1.17`** [swoole_client->enableSSL](./doc/3.1.17 - swoole_client->enableSSL.md) + - #### **3.2** [回调函数](./doc/3.2 - 回调函数.md) + - **`3.2.1`** [onConnect](./doc/3.2.1 - onConnect.md) + - **`3.2.2`** [onError](./doc/3.2.2 - onError.md) + - **`3.2.3`** [onReceive](./doc/3.2.3 - onReceive.md) + - **`3.2.4`** [onClose](./doc/3.2.4 - onClose.md) + - **`3.2.5`** [onBufferFull](./doc/3.2.5 - onBufferFull.md) + - **`3.2.6`** [onBufferEmpty](./doc/3.2.6 - onBufferEmpty.md) + - #### **3.3** [属性列表](./doc/3.3 - 属性列表.md) + - **`3.3.1`** [swoole_client->errCode](./doc/3.3.1 - swoole_client->errCode.md) + - **`3.3.2`** [swoole_client->sock](./doc/3.3.2 - swoole_client->sock.md) + - **`3.3.3`** [swoole_client->reuse](./doc/3.3.3 - swoole_client->reuse.md) + - #### **3.4** [并行](./doc/3.4 - 并行.md) + - **`3.4.1`** [swoole_client_select](./doc/3.4.1 - swoole_client_select.md) + - **`3.4.2`** [TCP客户端异步连接](./doc/3.4.2 - TCP客户端异步连接.md) + - **`3.4.3`** [SWOOLE_KEEP建立TCP长连接](./doc/3.4.3 - SWOOLE_KEEP建立TCP长连接.md) + - #### **3.5** [常量](./doc/3.5 - 常量.md) + - #### **3.6** [配置选项](./doc/3.6 - 配置选项.md) + - **`3.6.1`** [ssl_verify_peer](./doc/3.6.1 - ssl_verify_peer.md) + - **`3.6.2`** [ssl_host_name](./doc/3.6.2 - ssl_host_name.md) + - **`3.6.3`** [ssl_cafile](./doc/3.6.3 - ssl_cafile.md) + - **`3.6.4`** [ssl_capath](./doc/3.6.4 - ssl_capath.md) + - **`3.6.5`** [package_length_func](./doc/3.6.5 - package_length_func.md) + - #### **3.7** [常见问题](./doc/3.7 - 常见问题.md) +- ### **4** [Process](./doc/4 - Process.md) + - #### **4.1** [swoole_process::__construct](./doc/4.1 - swoole_process::__construct.md) + - #### **4.2** [swoole_process->start](./doc/4.2 - swoole_process->start.md) + - #### **4.3** [swoole_process->name](./doc/4.3 - swoole_process->name.md) + - #### **4.4** [swoole_process->exec](./doc/4.4 - swoole_process->exec.md) + - #### **4.5** [swoole_process->write](./doc/4.5 - swoole_process->write.md) + - #### **4.6** [swoole_process->read](./doc/4.6 - swoole_process->read.md) + - #### **4.7** [swoole_process->setTimeout](./doc/4.7 - swoole_process->setTimeout.md) + - #### **4.8** [swoole_process->setBlocking](./doc/4.8 - swoole_process->setBlocking.md) + - #### **4.9** [swoole_process->useQueue](./doc/4.9 - swoole_process->useQueue.md) + - #### **4.10** [swoole_process->statQueue](./doc/4.10 - swoole_process->statQueue.md) + - #### **4.11** [swoole_process->freeQueue](./doc/4.11 - swoole_process->freeQueue.md) + - #### **4.12** [swoole_process->push](./doc/4.12 - swoole_process->push.md) + - #### **4.13** [swoole_process->pop](./doc/4.13 - swoole_process->pop.md) + - #### **4.14** [swoole_process->close](./doc/4.14 - swoole_process->close.md) + - #### **4.15** [swoole_process->exit](./doc/4.15 - swoole_process->exit.md) + - #### **4.16** [swoole_process::kill](./doc/4.16 - swoole_process::kill.md) + - #### **4.17** [swoole_process::wait](./doc/4.17 - swoole_process::wait.md) + - #### **4.18** [swoole_process::daemon](./doc/4.18 - swoole_process::daemon.md) + - #### **4.19** [swoole_process::signal](./doc/4.19 - swoole_process::signal.md) + - #### **4.20** [swoole_process::alarm](./doc/4.20 - swoole_process::alarm.md) + - #### **4.21** [swoole_process::setAffinity](./doc/4.21 - swoole_process::setAffinity.md) +- ### **5** [Process\Pool](./doc/5 - Process\Pool.md) + - #### **5.1** [Process\Pool::__construct](./doc/5.1 - Process\Pool::__construct.md) + - #### **5.2** [Process\Pool->on](./doc/5.2 - Process\Pool->on.md) + - #### **5.3** [Process\Pool->listen](./doc/5.3 - Process\Pool->listen.md) + - #### **5.4** [Process\Pool->write](./doc/5.4 - Process\Pool->write.md) + - #### **5.5** [Process\Pool->start](./doc/5.5 - Process\Pool->start.md) +- ### **6** [AsyncIO](./doc/6 - AsyncIO.md) + - #### **6.1** [异步文件系统IO](./doc/6.1 - 异步文件系统IO.md) + - **`6.1.1`** [swoole_async_readfile](./doc/6.1.1 - swoole_async_readfile.md) + - **`6.1.2`** [swoole_async_writefile](./doc/6.1.2 - swoole_async_writefile.md) + - **`6.1.3`** [swoole_async_read](./doc/6.1.3 - swoole_async_read.md) + - **`6.1.4`** [swoole_async_write](./doc/6.1.4 - swoole_async_write.md) + - **`6.1.5`** [swoole_async_dns_lookup](./doc/6.1.5 - swoole_async_dns_lookup.md) + - **`6.1.6`** [swoole_async::exec](./doc/6.1.6 - swoole_async::exec.md) + - #### **6.2** [EventLoop](./doc/6.2 - EventLoop.md) + - **`6.2.1`** [swoole_event_add](./doc/6.2.1 - swoole_event_add.md) + - **`6.2.2`** [swoole_event_set](./doc/6.2.2 - swoole_event_set.md) + - **`6.2.3`** [swoole_event_isset](./doc/6.2.3 - swoole_event_isset.md) + - **`6.2.4`** [swoole_event_write](./doc/6.2.4 - swoole_event_write.md) + - **`6.2.5`** [swoole_event_del](./doc/6.2.5 - swoole_event_del.md) + - **`6.2.6`** [swoole_event_exit](./doc/6.2.6 - swoole_event_exit.md) + - **`6.2.7`** [swoole_event_defer](./doc/6.2.7 - swoole_event_defer.md) + - **`6.2.8`** [swoole_event_cycle](./doc/6.2.8 - swoole_event_cycle.md) + - **`6.2.9`** [swoole_event_wait](./doc/6.2.9 - swoole_event_wait.md) + - **`6.2.10`** [swoole_event_dispatch](./doc/6.2.10 - swoole_event_dispatch.md) + - #### **6.3** [异步毫秒定时器](./doc/6.3 - 异步毫秒定时器.md) + - **`6.3.1`** [swoole_timer_tick](./doc/6.3.1 - swoole_timer_tick.md) + - **`6.3.2`** [swoole_timer_after](./doc/6.3.2 - swoole_timer_after.md) + - **`6.3.3`** [swoole_timer_clear](./doc/6.3.3 - swoole_timer_clear.md) + - #### **6.4** [异步MySQL客户端](./doc/6.4 - 异步MySQL客户端.md) + - **`6.4.1`** [swoole_mysql->construct](./doc/6.4.1 - swoole_mysql->construct.md) + - **`6.4.2`** [swoole_mysql->on](./doc/6.4.2 - swoole_mysql->on.md) + - **`6.4.3`** [swoole_mysql->connect](./doc/6.4.3 - swoole_mysql->connect.md) + - **`6.4.4`** [swoole_mysql->escape](./doc/6.4.4 - swoole_mysql->escape.md) + - **`6.4.5`** [swoole_mysql->query](./doc/6.4.5 - swoole_mysql->query.md) + - **`6.4.6`** [swoole_mysql->begin](./doc/6.4.6 - swoole_mysql->begin.md) + - **`6.4.7`** [swoole_mysql->commit](./doc/6.4.7 - swoole_mysql->commit.md) + - **`6.4.8`** [swoole_mysql->rollback](./doc/6.4.8 - swoole_mysql->rollback.md) + - **`6.4.9`** [swoole_mysql->close](./doc/6.4.9 - swoole_mysql->close.md) + - #### **6.5** [异步Redis客户端](./doc/6.5 - 异步Redis客户端.md) + - **`6.5.1`** [swoole_redis->__construct](./doc/6.5.1 - swoole_redis->__construct.md) + - **`6.5.2`** [swoole_redis->on](./doc/6.5.2 - swoole_redis->on.md) + - **`6.5.3`** [swoole_redis->connect](./doc/6.5.3 - swoole_redis->connect.md) + - **`6.5.4`** [swoole_redis->__call](./doc/6.5.4 - swoole_redis->__call.md) + - **`6.5.5`** [swoole_redis->close](./doc/6.5.5 - swoole_redis->close.md) + - #### **6.6** [异步Http\WebSocket客户端](./doc/6.6 - 异步Http\WebSocket客户端.md) + - **`6.6.1`** [swoole_http_client->__construct](./doc/6.6.1 - swoole_http_client->__construct.md) + - **`6.6.2`** [swoole_http_client->set](./doc/6.6.2 - swoole_http_client->set.md) + - **`6.6.3`** [swoole_http_client->setMethod](./doc/6.6.3 - swoole_http_client->setMethod.md) + - **`6.6.4`** [swoole_http_client->setHeaders](./doc/6.6.4 - swoole_http_client->setHeaders.md) + - **`6.6.5`** [swoole_http_client->setCookies](./doc/6.6.5 - swoole_http_client->setCookies.md) + - **`6.6.6`** [swoole_http_client->setData](./doc/6.6.6 - swoole_http_client->setData.md) + - **`6.6.7`** [swoole_http_client->addFile](./doc/6.6.7 - swoole_http_client->addFile.md) + - **`6.6.8`** [swoole_http_client->get](./doc/6.6.8 - swoole_http_client->get.md) + - **`6.6.9`** [swoole_http_client->post](./doc/6.6.9 - swoole_http_client->post.md) + - **`6.6.10`** [swoole_http_client->upgrade](./doc/6.6.10 - swoole_http_client->upgrade.md) + - **`6.6.11`** [swoole_http_client->push](./doc/6.6.11 - swoole_http_client->push.md) + - **`6.6.12`** [swoole_http_client->execute](./doc/6.6.12 - swoole_http_client->execute.md) + - **`6.6.13`** [swoole_http_client->download](./doc/6.6.13 - swoole_http_client->download.md) + - **`6.6.14`** [swoole_http_client->close](./doc/6.6.14 - swoole_http_client->close.md) + - #### **6.7** [异步Http2.0客户端](./doc/6.7 - 异步Http2.0客户端.md) + - **`6.7.1`** [swoole_http2_client->__construct](./doc/6.7.1 - swoole_http2_client->__construct.md) + - **`6.7.2`** [swoole_http2_client->get](./doc/6.7.2 - swoole_http2_client->get.md) + - **`6.7.3`** [swoole_http2_client->post](./doc/6.7.3 - swoole_http2_client->post.md) + - **`6.7.4`** [swoole_http2_client->setHeaders](./doc/6.7.4 - swoole_http2_client->setHeaders.md) + - **`6.7.5`** [swoole_http2_client->setCookies](./doc/6.7.5 - swoole_http2_client->setCookies.md) +- ### **7** [Memory](./doc/7 - Memory.md) + - #### **7.1** [Lock](./doc/7.1 - Lock.md) + - **`7.1.1`** [swoole_lock->__construct](./doc/7.1.1 - swoole_lock->__construct.md) + - **`7.1.2`** [swoole_lock->lock](./doc/7.1.2 - swoole_lock->lock.md) + - **`7.1.3`** [swoole_lock->trylock](./doc/7.1.3 - swoole_lock->trylock.md) + - **`7.1.4`** [swoole_lock->unlock](./doc/7.1.4 - swoole_lock->unlock.md) + - **`7.1.5`** [swoole_lock->lock_read](./doc/7.1.5 - swoole_lock->lock_read.md) + - **`7.1.6`** [swoole_lock->trylock_read](./doc/7.1.6 - swoole_lock->trylock_read.md) + - **`7.1.7`** [swoole_lock->lockwait](./doc/7.1.7 - swoole_lock->lockwait.md) + - #### **7.2** [Buffer](./doc/7.2 - Buffer.md) + - **`7.2.1`** [swoole_buffer->__construct](./doc/7.2.1 - swoole_buffer->__construct.md) + - **`7.2.2`** [swoole_buffer->append](./doc/7.2.2 - swoole_buffer->append.md) + - **`7.2.3`** [swoole_buffer->substr](./doc/7.2.3 - swoole_buffer->substr.md) + - **`7.2.4`** [swoole_buffer->clear](./doc/7.2.4 - swoole_buffer->clear.md) + - **`7.2.5`** [swoole_buffer->expand](./doc/7.2.5 - swoole_buffer->expand.md) + - **`7.2.6`** [swoole_buffer->write](./doc/7.2.6 - swoole_buffer->write.md) + - **`7.2.7`** [swoole_buffer->read](./doc/7.2.7 - swoole_buffer->read.md) + - **`7.2.8`** [swoole_buffer->recycle](./doc/7.2.8 - swoole_buffer->recycle.md) + - #### **7.3** [Table](./doc/7.3 - Table.md) + - **`7.3.1`** [swoole_table->__construct](./doc/7.3.1 - swoole_table->__construct.md) + - **`7.3.2`** [swoole_table->column](./doc/7.3.2 - swoole_table->column.md) + - **`7.3.3`** [swoole_table->create](./doc/7.3.3 - swoole_table->create.md) + - **`7.3.4`** [swoole_table->set](./doc/7.3.4 - swoole_table->set.md) + - **`7.3.5`** [swoole_table->incr](./doc/7.3.5 - swoole_table->incr.md) + - **`7.3.6`** [swoole_table->decr](./doc/7.3.6 - swoole_table->decr.md) + - **`7.3.7`** [swoole_table->get](./doc/7.3.7 - swoole_table->get.md) + - **`7.3.8`** [swoole_table->exist](./doc/7.3.8 - swoole_table->exist.md) + - **`7.3.9`** [swoole_table->del](./doc/7.3.9 - swoole_table->del.md) + - **`7.3.10`** [常量列表](./doc/7.3.10 - 常量列表.md) + - #### **7.4** [Atomic](./doc/7.4 - Atomic.md) + - **`7.4.1`** [swoole_atomic->__construct](./doc/7.4.1 - swoole_atomic->__construct.md) + - **`7.4.2`** [swoole_atomic->add](./doc/7.4.2 - swoole_atomic->add.md) + - **`7.4.3`** [swoole_atomic->sub](./doc/7.4.3 - swoole_atomic->sub.md) + - **`7.4.4`** [swoole_atomic->get](./doc/7.4.4 - swoole_atomic->get.md) + - **`7.4.5`** [swoole_atomic->set](./doc/7.4.5 - swoole_atomic->set.md) + - **`7.4.6`** [swoole_atomic->cmpset](./doc/7.4.6 - swoole_atomic->cmpset.md) + - **`7.4.7`** [swoole_atomic->wait](./doc/7.4.7 - swoole_atomic->wait.md) + - **`7.4.8`** [swoole_atomic->wakeup](./doc/7.4.8 - swoole_atomic->wakeup.md) + - #### **7.5** [mmap](./doc/7.5 - mmap.md) + - **`7.5.1`** [swoole_mmap::open](./doc/7.5.1 - swoole_mmap::open.md) + - #### **7.6** [Channel](./doc/7.6 - Channel.md) + - **`7.6.1`** [Channel->__construct](./doc/7.6.1 - Channel->__construct.md) + - **`7.6.2`** [Channel->push](./doc/7.6.2 - Channel->push.md) + - **`7.6.3`** [Channel->pop](./doc/7.6.3 - Channel->pop.md) + - **`7.6.4`** [Channel->stats](./doc/7.6.4 - Channel->stats.md) + - #### **7.7** [Serialize](./doc/7.7 - Serialize.md) + - **`7.7.1`** [swoole_serialize::pack](./doc/7.7.1 - swoole_serialize::pack.md) + - **`7.7.2`** [swoole_serialize::unpack](./doc/7.7.2 - swoole_serialize::unpack.md) +- ### **8** [HttpServer](./doc/8 - HttpServer.md) + - #### **8.1** [swoole_http_server](./doc/8.1 - swoole_http_server.md) + - **`8.1.1`** [swoole_http_server->on](./doc/8.1.1 - swoole_http_server->on.md) + - **`8.1.2`** [swoole_http_server->start](./doc/8.1.2 - swoole_http_server->start.md) + - #### **8.2** [swoole_http_request](./doc/8.2 - swoole_http_request.md) + - **`8.2.1`** [swoole_http_request->$header](./doc/8.2.1 - swoole_http_request->$header.md) + - **`8.2.2`** [swoole_http_request->$server](./doc/8.2.2 - swoole_http_request->$server.md) + - **`8.2.3`** [swoole_http_request->$get](./doc/8.2.3 - swoole_http_request->$get.md) + - **`8.2.4`** [swoole_http_request->$post](./doc/8.2.4 - swoole_http_request->$post.md) + - **`8.2.5`** [swoole_http_request->$cookie](./doc/8.2.5 - swoole_http_request->$cookie.md) + - **`8.2.6`** [swoole_http_request->$files](./doc/8.2.6 - swoole_http_request->$files.md) + - **`8.2.7`** [swoole_http_request->rawContent](./doc/8.2.7 - swoole_http_request->rawContent.md) + - **`8.2.8`** [swoole_http_request->getData](./doc/8.2.8 - swoole_http_request->getData.md) + - #### **8.3** [swoole_http_response](./doc/8.3 - swoole_http_response.md) + - **`8.3.1`** [swoole_http_response->header](./doc/8.3.1 - swoole_http_response->header.md) + - **`8.3.2`** [swoole_http_response->cookie](./doc/8.3.2 - swoole_http_response->cookie.md) + - **`8.3.3`** [swoole_http_response->status](./doc/8.3.3 - swoole_http_response->status.md) + - **`8.3.4`** [swoole_http_response->gzip](./doc/8.3.4 - swoole_http_response->gzip.md) + - **`8.3.5`** [swoole_http_response->redirect](./doc/8.3.5 - swoole_http_response->redirect.md) + - **`8.3.6`** [swoole_http_response->write](./doc/8.3.6 - swoole_http_response->write.md) + - **`8.3.7`** [swoole_http_response->sendfile](./doc/8.3.7 - swoole_http_response->sendfile.md) + - **`8.3.8`** [swoole_http_response->end](./doc/8.3.8 - swoole_http_response->end.md) + - **`8.3.9`** [swoole_http_response->detach](./doc/8.3.9 - swoole_http_response->detach.md) + - **`8.3.10`** [swoole_http_response::create](./doc/8.3.10 - swoole_http_response::create.md) + - #### **8.4** [配置选项](./doc/8.4 - 配置选项.md) + - **`8.4.1`** [upload_tmp_dir](./doc/8.4.1 - upload_tmp_dir.md) + - **`8.4.2`** [http_parse_post](./doc/8.4.2 - http_parse_post.md) + - **`8.4.3`** [document_root](./doc/8.4.3 - document_root.md) + - #### **8.5** [常见问题](./doc/8.5 - 常见问题.md) + - **`8.5.1`** [CURL发送POST请求服务器端超时](./doc/8.5.1 - CURL发送POST请求服务器端超时.md) + - **`8.5.2`** [使用Chrome访问服务器会产生2次请求](./doc/8.5.2 - 使用Chrome访问服务器会产生2次请求.md) + - **`8.5.3`** [GET\POST请求的最大尺寸](./doc/8.5.3 - GET\POST请求的最大尺寸.md) +- ### **9** [WebSocket](./doc/9 - WebSocket.md) + - #### **9.1** [回调函数](./doc/9.1 - 回调函数.md) + - **`9.1.1`** [onHandShake](./doc/9.1.1 - onHandShake.md) + - **`9.1.2`** [onOpen](./doc/9.1.2 - onOpen.md) + - **`9.1.3`** [onMessage](./doc/9.1.3 - onMessage.md) + - #### **9.2** [函数列表](./doc/9.2 - 函数列表.md) + - **`9.2.1`** [swoole_websocket_server->push](./doc/9.2.1 - swoole_websocket_server->push.md) + - **`9.2.2`** [swoole_websocket_server->exist](./doc/9.2.2 - swoole_websocket_server->exist.md) + - **`9.2.3`** [swoole_websocket_server::pack](./doc/9.2.3 - swoole_websocket_server::pack.md) + - **`9.2.4`** [swoole_websocket_server::unpack](./doc/9.2.4 - swoole_websocket_server::unpack.md) + - #### **9.3** [预定义常量](./doc/9.3 - 预定义常量.md) + - #### **9.4** [常见问题](./doc/9.4 - 常见问题.md) + - #### **9.5** [配置选项](./doc/9.5 - 配置选项.md) +- ### **10** [协程 Server](./doc/10 - 协程 Server.md) + - #### **10.1** [方法列表](./doc/10.1 - 方法列表.md) + - **`10.1.1`** [getDefer](./doc/10.1.1 - getDefer.md) + - **`10.1.2`** [setDefer](./doc/10.1.2 - setDefer.md) + - **`10.1.3`** [recv](./doc/10.1.3 - recv.md) + - **`10.1.4`** [Coroutine::create](./doc/10.1.4 - Coroutine::create.md) + - **`10.1.5`** [Coroutine::getuid](./doc/10.1.5 - Coroutine::getuid.md) + - #### **10.2** [并发调用](./doc/10.2 - 并发调用.md) + - **`10.2.1`** [使用实例](./doc/10.2.1 - 使用实例.md) + - #### **10.3** [实现原理](./doc/10.3 - 实现原理.md) + - **`10.3.1`** [协程与线程](./doc/10.3.1 - 协程与线程.md) + - **`10.3.2`** [发送数据协程调度](./doc/10.3.2 - 发送数据协程调度.md) + - #### **10.4** [常见问题](./doc/10.4 - 常见问题.md) + - **`10.4.1`** [运行中出现 Fatal error: Maximum function nesting level of '1000' reached, aborting!](./doc/10.4.1 - 运行中出现 Fatal error: Maximum function nesting level of '1000' reached, aborting!.md) + - **`10.4.2`** [为什么只能在回调函数中使用协程客户端](./doc/10.4.2 - 为什么只能在回调函数中使用协程客户端.md) + - **`10.4.3`** [支持协程的回调方法列表](./doc/10.4.3 - 支持协程的回调方法列表.md) + - #### **10.5** [编程须知](./doc/10.5 - 编程须知.md) + - **`10.5.1`** [在多个协程间共用同一个协程客户端](./doc/10.5.1 - 在多个协程间共用同一个协程客户端.md) + - **`10.5.2`** [禁止使用协程 API 的场景 ( ver < 2.2 )](./doc/10.5.2 - 禁止使用协程 API 的场景 ( ver < 2.2 ).md) + - **`10.5.3`** [使用类静态变量\全局变量保存上下文](./doc/10.5.3 - 使用类静态变量\全局变量保存上下文.md) +- ### **11** [协程 Client](./doc/11 - 协程 Client.md) + - #### **11.1** [Coroutine\Client](./doc/11.1 - Coroutine\Client.md) + - **`11.1.1`** [Coroutine\Client->connect](./doc/11.1.1 - Coroutine\Client->connect.md) + - **`11.1.2`** [Coroutine\Client->send](./doc/11.1.2 - Coroutine\Client->send.md) + - **`11.1.3`** [Coroutine\Client->recv](./doc/11.1.3 - Coroutine\Client->recv.md) + - **`11.1.4`** [Coroutine\Client->close](./doc/11.1.4 - Coroutine\Client->close.md) + - **`11.1.5`** [Coroutine\Client->peek](./doc/11.1.5 - Coroutine\Client->peek.md) + - #### **11.2** [Coroutine\Http\Client](./doc/11.2 - Coroutine\Http\Client.md) + - **`11.2.1`** [属性列表](./doc/11.2.1 - 属性列表.md) + - **`11.2.2`** [Coroutine\Http\Client->get](./doc/11.2.2 - Coroutine\Http\Client->get.md) + - **`11.2.3`** [Coroutine\Http\Client->post](./doc/11.2.3 - Coroutine\Http\Client->post.md) + - **`11.2.4`** [Coroutine\Http\Client->upgrade](./doc/11.2.4 - Coroutine\Http\Client->upgrade.md) + - **`11.2.5`** [Coroutine\Http\Client->push](./doc/11.2.5 - Coroutine\Http\Client->push.md) + - **`11.2.6`** [Coroutine\Http\Client->recv](./doc/11.2.6 - Coroutine\Http\Client->recv.md) + - **`11.2.7`** [Coroutine\Http\Client->addFile](./doc/11.2.7 - Coroutine\Http\Client->addFile.md) + - #### **11.3** [Coroutine\Http2\Client](./doc/11.3 - Coroutine\Http2\Client.md) + - **`11.3.1`** [Coroutine\Http2\Client->__construct](./doc/11.3.1 - Coroutine\Http2\Client->__construct.md) + - **`11.3.2`** [Coroutine\Http2\Client->set](./doc/11.3.2 - Coroutine\Http2\Client->set.md) + - **`11.3.3`** [Coroutine\Http2\Client->connect](./doc/11.3.3 - Coroutine\Http2\Client->connect.md) + - **`11.3.4`** [Coroutine\Http2\Client->send](./doc/11.3.4 - Coroutine\Http2\Client->send.md) + - **`11.3.5`** [Coroutine\Http2\Client->write](./doc/11.3.5 - Coroutine\Http2\Client->write.md) + - **`11.3.6`** [Coroutine\Http2\Client->recv](./doc/11.3.6 - Coroutine\Http2\Client->recv.md) + - **`11.3.7`** [Coroutine\Http2\Client->close](./doc/11.3.7 - Coroutine\Http2\Client->close.md) + - #### **11.4** [Coroutine\Redis](./doc/11.4 - Coroutine\Redis.md) + - **`11.4.1`** [方法列表](./doc/11.4.1 - 方法列表.md) + - **`11.4.2`** [属性列表](./doc/11.4.2 - 属性列表.md) + - #### **11.5** [Coroutine\MySQL](./doc/11.5 - Coroutine\MySQL.md) + - **`11.5.1`** [属性列表](./doc/11.5.1 - 属性列表.md) + - **`11.5.2`** [Coroutine\MySQL->connect](./doc/11.5.2 - Coroutine\MySQL->connect.md) + - **`11.5.3`** [Coroutine\MySQL->query](./doc/11.5.3 - Coroutine\MySQL->query.md) + - **`11.5.4`** [Coroutine\MySQL->prepare](./doc/11.5.4 - Coroutine\MySQL->prepare.md) + - **`11.5.5`** [Coroutine\MySQL\Statement->execute](./doc/11.5.5 - Coroutine\MySQL\Statement->execute.md) + - #### **11.6** [Coroutine\Util](./doc/11.6 - Coroutine\Util.md) + - **`11.6.1`** [Coroutine::getUid](./doc/11.6.1 - Coroutine::getUid.md) + - **`11.6.2`** [Coroutine::create](./doc/11.6.2 - Coroutine::create.md) + - **`11.6.3`** [Coroutine::resume](./doc/11.6.3 - Coroutine::resume.md) + - **`11.6.4`** [Coroutine::suspend](./doc/11.6.4 - Coroutine::suspend.md) + - **`11.6.5`** [Coroutine::fread](./doc/11.6.5 - Coroutine::fread.md) + - **`11.6.6`** [Coroutine::fgets](./doc/11.6.6 - Coroutine::fgets.md) + - **`11.6.7`** [Coroutine::fwrite](./doc/11.6.7 - Coroutine::fwrite.md) + - **`11.6.8`** [Coroutine::sleep](./doc/11.6.8 - Coroutine::sleep.md) + - **`11.6.9`** [Coroutine::gethostbyname](./doc/11.6.9 - Coroutine::gethostbyname.md) + - **`11.6.10`** [Coroutine::getaddrinfo](./doc/11.6.10 - Coroutine::getaddrinfo.md) + - **`11.6.11`** [Coroutine::call_user_func](./doc/11.6.11 - Coroutine::call_user_func.md) + - **`11.6.12`** [Coroutine::call_user_func_array](./doc/11.6.12 - Coroutine::call_user_func_array.md) + - **`11.6.13`** [Coroutine::exec](./doc/11.6.13 - Coroutine::exec.md) + - **`11.6.14`** [Coroutine::readFile](./doc/11.6.14 - Coroutine::readFile.md) + - **`11.6.15`** [Coroutine::writeFIle](./doc/11.6.15 - Coroutine::writeFIle.md) + - #### **11.7** [Coroutine\Channel](./doc/11.7 - Coroutine\Channel.md) + - **`11.7.1`** [Coroutine\Channel->__construct](./doc/11.7.1 - Coroutine\Channel->__construct.md) + - **`11.7.2`** [Coroutine\Channel->push](./doc/11.7.2 - Coroutine\Channel->push.md) + - **`11.7.3`** [Coroutine\Channel->pop](./doc/11.7.3 - Coroutine\Channel->pop.md) + - **`11.7.4`** [Coroutine\Channel->stats](./doc/11.7.4 - Coroutine\Channel->stats.md) + - **`11.7.5`** [Coroutine\Channel->close](./doc/11.7.5 - Coroutine\Channel->close.md) + - **`11.7.6`** [Coroutine\Channel::select](./doc/11.7.6 - Coroutine\Channel::select.md) + - #### **11.8** [Coroutine\PostgreSQL](./doc/11.8 - Coroutine\PostgreSQL.md) + - **`11.8.1`** [Coroutine\PostgreSQL->connect](./doc/11.8.1 - Coroutine\PostgreSQL->connect.md) + - **`11.8.2`** [Coroutine\PostgreSQL->query](./doc/11.8.2 - Coroutine\PostgreSQL->query.md) + - **`11.8.3`** [Coroutine\PostgreSQL->fetchAll](./doc/11.8.3 - Coroutine\PostgreSQL->fetchAll.md) + - **`11.8.4`** [Coroutine\PostgreSQL->affectedRows](./doc/11.8.4 - Coroutine\PostgreSQL->affectedRows.md) + - **`11.8.5`** [Coroutine\PostgreSQL->numRows](./doc/11.8.5 - Coroutine\PostgreSQL->numRows.md) + - **`11.8.6`** [Coroutine\PostgreSQL->fetchObject](./doc/11.8.6 - Coroutine\PostgreSQL->fetchObject.md) + - **`11.8.7`** [Coroutine\PostgreSQL->fetchAssoc](./doc/11.8.7 - Coroutine\PostgreSQL->fetchAssoc.md) + - **`11.8.8`** [Coroutine\PostgreSQL->fetchArray](./doc/11.8.8 - Coroutine\PostgreSQL->fetchArray.md) + - **`11.8.9`** [Coroutine\PostgreSQL->fetchRow](./doc/11.8.9 - Coroutine\PostgreSQL->fetchRow.md) + - **`11.8.10`** [Coroutine\PostgreSQL->metaData](./doc/11.8.10 - Coroutine\PostgreSQL->metaData.md) +- ### **12** [协程 Socket](./doc/12 - 协程 Socket.md) + - #### **12.1** [Coroutine\Socket::__construct](./doc/12.1 - Coroutine\Socket::__construct.md) + - #### **12.2** [Coroutine\Socket->bind](./doc/12.2 - Coroutine\Socket->bind.md) + - #### **12.3** [Coroutine\Socket->listen](./doc/12.3 - Coroutine\Socket->listen.md) + - #### **12.4** [Coroutine\Socket->accept](./doc/12.4 - Coroutine\Socket->accept.md) + - #### **12.5** [Coroutine\Socket->connect](./doc/12.5 - Coroutine\Socket->connect.md) + - #### **12.6** [Coroutine\Socket->send](./doc/12.6 - Coroutine\Socket->send.md) + - #### **12.7** [Coroutine\Socket->recv](./doc/12.7 - Coroutine\Socket->recv.md) + - #### **12.8** [Coroutine\Socket->sendto](./doc/12.8 - Coroutine\Socket->sendto.md) + - #### **12.9** [Coroutine\Socket->recvfrom](./doc/12.9 - Coroutine\Socket->recvfrom.md) + - #### **12.10** [Coroutine\Socket->getsockname](./doc/12.10 - Coroutine\Socket->getsockname.md) + - #### **12.11** [Coroutine\Socket->getpeername](./doc/12.11 - Coroutine\Socket->getpeername.md) + - #### **12.12** [Coroutine\Socket->close](./doc/12.12 - Coroutine\Socket->close.md) +- ### **13** [Redis\Server](./doc/13 - Redis\Server.md) + - #### **13.1** [方法](./doc/13.1 - 方法.md) + - **`13.1.1`** [setHandler](./doc/13.1.1 - setHandler.md) + - **`13.1.2`** [format](./doc/13.1.2 - format.md) + - #### **13.2** [常量](./doc/13.2 - 常量.md) +- ### **14** [高级](./doc/14 - 高级.md) + - #### **14.1** [Swoole的实现](./doc/14.1 - Swoole的实现.md) + - #### **14.2** [Reactor线程](./doc/14.2 - Reactor线程.md) + - #### **14.3** [Manager进程](./doc/14.3 - Manager进程.md) + - #### **14.4** [Worker进程](./doc/14.4 - Worker进程.md) + - #### **14.5** [Reactor、Worker、TaskWorker的关系](./doc/14.5 - Reactor、Worker、TaskWorker的关系.md) + - #### **14.6** [Task\Finish特性的用途](./doc/14.6 - Task\Finish特性的用途.md) + - #### **14.7** [在php-fpm或apache中使用swoole](./doc/14.7 - 在php-fpm或apache中使用swoole.md) + - #### **14.8** [Swoole异步与同步的选择](./doc/14.8 - Swoole异步与同步的选择.md) + - #### **14.9** [TCP\UDP压测工具](./doc/14.9 - TCP\UDP压测工具.md) + - #### **14.10** [swoole服务器如何做到无人值守100%可用](./doc/14.10 - swoole服务器如何做到无人值守100%可用.md) + - #### **14.11** [MySQL的连接池、异步、断线重连](./doc/14.11 - MySQL的连接池、异步、断线重连.md) + - #### **14.12** [PHP中哪些函数是同步阻塞的](./doc/14.12 - PHP中哪些函数是同步阻塞的.md) + - #### **14.13** [守护进程程序常用数据结构](./doc/14.13 - 守护进程程序常用数据结构.md) + - **`14.13.1`** [队列(Queue)](./doc/14.13.1 - 队列(Queue).md) + - **`14.13.2`** [堆(Heap)](./doc/14.13.2 - 堆(Heap).md) + - **`14.13.3`** [定长数组(SplFixedArray)](./doc/14.13.3 - 定长数组(SplFixedArray).md) + - #### **14.14** [使用jemalloc优化swoole内存分配性能](./doc/14.14 - 使用jemalloc优化swoole内存分配性能.md) + - #### **14.15** [C开发者如何使用Swoole](./doc/14.15 - C开发者如何使用Swoole.md) + - #### **14.16** [C++开发者如何使用Swoole](./doc/14.16 - C++开发者如何使用Swoole.md) + - #### **14.17** [使用systemd管理swoole服务](./doc/14.17 - 使用systemd管理swoole服务.md) + - #### **14.18** [网卡中断设置](./doc/14.18 - 网卡中断设置.md) + - #### **14.19** [将Swoole静态编译内嵌到PHP](./doc/14.19 - 将Swoole静态编译内嵌到PHP.md) + - #### **14.20** [异步回调程序内存管理](./doc/14.20 - 异步回调程序内存管理.md) +- ### **15** [其他](./doc/15 - 其他.md) + - #### **15.1** [函数列表](./doc/15.1 - 函数列表.md) + - **`15.1.1`** [swoole_set_process_name](./doc/15.1.1 - swoole_set_process_name.md) + - **`15.1.2`** [swoole_version](./doc/15.1.2 - swoole_version.md) + - **`15.1.3`** [swoole_strerror](./doc/15.1.3 - swoole_strerror.md) + - **`15.1.4`** [swoole_errno](./doc/15.1.4 - swoole_errno.md) + - **`15.1.5`** [swoole_get_local_ip](./doc/15.1.5 - swoole_get_local_ip.md) + - **`15.1.6`** [swoole_clear_dns_cache](./doc/15.1.6 - swoole_clear_dns_cache.md) + - **`15.1.7`** [swoole_get_local_mac](./doc/15.1.7 - swoole_get_local_mac.md) + - **`15.1.8`** [swoole_cpu_num](./doc/15.1.8 - swoole_cpu_num.md) + - #### **15.2** [Swoole社区](./doc/15.2 - Swoole社区.md) + - #### **15.3** [捐赠Swoole项目](./doc/15.3 - 捐赠Swoole项目.md) + - #### **15.4** [加入Swoole开发组](./doc/15.4 - 加入Swoole开发组.md) + - #### **15.5** [附录:Linux信号列表](./doc/15.5 - 附录:Linux信号列表.md) + - #### **15.6** [附录:Linux错误信息(errno)列表](./doc/15.6 - 附录:Linux错误信息(errno)列表.md) + - #### **15.7** [附录:TCP连接的状态](./doc/15.7 - 附录:TCP连接的状态.md) + - #### **15.8** [附录:tcpdump抓包工具的使用](./doc/15.8 - 附录:tcpdump抓包工具的使用.md) + - #### **15.9** [附录:strace工具的使用](./doc/15.9 - 附录:strace工具的使用.md) + - #### **15.10** [附录:gdb工具的使用](./doc/15.10 - 附录:gdb工具的使用.md) + - #### **15.11** [附录:lsof工具的使用](./doc/15.11 - 附录:lsof工具的使用.md) + - #### **15.12** [附录:perf工具的使用](./doc/15.12 - 附录:perf工具的使用.md) + - #### **15.13** [附录:编译PHP扩展的相关工具](./doc/15.13 - 附录:编译PHP扩展的相关工具.md) + - #### **15.14** [备用:已移除的历史特性](./doc/15.14 - 备用:已移除的历史特性.md) + - **`15.14.1`** [swoole_server->handler](./doc/15.14.1 - swoole_server->handler.md) + - **`15.14.2`** [task_worker_max](./doc/15.14.2 - task_worker_max.md) + - **`15.14.3`** [swoole_server->addtimer](./doc/15.14.3 - swoole_server->addtimer.md) + - **`15.14.4`** [swoole_server->deltimer](./doc/15.14.4 - swoole_server->deltimer.md) + - **`15.14.5`** [onTimer](./doc/15.14.5 - onTimer.md) + - **`15.14.6`** [swoole_timer_add](./doc/15.14.6 - swoole_timer_add.md) + - **`15.14.7`** [swoole_timer_del](./doc/15.14.7 - swoole_timer_del.md) + - **`15.14.8`** [swoole_get_mysqli_sock](./doc/15.14.8 - swoole_get_mysqli_sock.md) + - **`15.14.9`** [swoole_mysql_query](./doc/15.14.9 - swoole_mysql_query.md) + - **`15.14.10`** [onMasterConnect](./doc/15.14.10 - onMasterConnect.md) + - **`15.14.11`** [onMasterClose](./doc/15.14.11 - onMasterClose.md) + - **`15.14.12`** [Nginx\Golang\Swoole\Node.js的性能对比](./doc/15.14.12 - Nginx\Golang\Swoole\Node.js的性能对比.md) + - #### **15.15** [历史:版本更新记录(1.x)](./doc/15.15 - 历史:版本更新记录(1.x).md) + - **`15.15.1`** [1.9.19](./doc/15.15.1 - 1.9.19.md) + - **`15.15.2`** [1.9.18](./doc/15.15.2 - 1.9.18.md) + - **`15.15.3`** [1.9.17](./doc/15.15.3 - 1.9.17.md) + - **`15.15.4`** [1.9.16](./doc/15.15.4 - 1.9.16.md) + - **`15.15.5`** [1.9.15](./doc/15.15.5 - 1.9.15.md) + - **`15.15.6`** [1.9.14](./doc/15.15.6 - 1.9.14.md) + - **`15.15.7`** [1.9.12](./doc/15.15.7 - 1.9.12.md) + - **`15.15.8`** [1.9.11](./doc/15.15.8 - 1.9.11.md) + - **`15.15.9`** [1.9.9](./doc/15.15.9 - 1.9.9.md) + - **`15.15.10`** [1.9.7](./doc/15.15.10 - 1.9.7.md) + - **`15.15.11`** [1.9.6](./doc/15.15.11 - 1.9.6.md) + - **`15.15.12`** [1.9.5](./doc/15.15.12 - 1.9.5.md) + - **`15.15.13`** [1.9.4](./doc/15.15.13 - 1.9.4.md) + - **`15.15.14`** [1.9.3](./doc/15.15.14 - 1.9.3.md) + - **`15.15.15`** [1.9.2](./doc/15.15.15 - 1.9.2.md) + - **`15.15.16`** [1.9.1](./doc/15.15.16 - 1.9.1.md) + - **`15.15.17`** [1.9.0](./doc/15.15.17 - 1.9.0.md) + - **`15.15.18`** [1.8.13](./doc/15.15.18 - 1.8.13.md) + - **`15.15.19`** [1.8.12](./doc/15.15.19 - 1.8.12.md) + - **`15.15.20`** [1.8.11](./doc/15.15.20 - 1.8.11.md) + - **`15.15.21`** [1.8.10](./doc/15.15.21 - 1.8.10.md) + - **`15.15.22`** [1.8.9](./doc/15.15.22 - 1.8.9.md) + - **`15.15.23`** [1.8.8](./doc/15.15.23 - 1.8.8.md) + - **`15.15.24`** [1.8.7](./doc/15.15.24 - 1.8.7.md) + - **`15.15.25`** [1.8.6](./doc/15.15.25 - 1.8.6.md) + - **`15.15.26`** [1.8.5](./doc/15.15.26 - 1.8.5.md) + - **`15.15.27`** [1.8.4](./doc/15.15.27 - 1.8.4.md) + - **`15.15.28`** [1.8.3](./doc/15.15.28 - 1.8.3.md) + - **`15.15.29`** [1.8.2](./doc/15.15.29 - 1.8.2.md) + - **`15.15.30`** [1.8.1](./doc/15.15.30 - 1.8.1.md) + - **`15.15.31`** [1.8.0](./doc/15.15.31 - 1.8.0.md) + - **`15.15.32`** [1.7.22](./doc/15.15.32 - 1.7.22.md) + - **`15.15.33`** [1.7.21](./doc/15.15.33 - 1.7.21.md) + - **`15.15.34`** [1.7.20](./doc/15.15.34 - 1.7.20.md) + - **`15.15.35`** [1.7.19](./doc/15.15.35 - 1.7.19.md) + - **`15.15.36`** [1.7.18](./doc/15.15.36 - 1.7.18.md) + - **`15.15.37`** [1.7.17](./doc/15.15.37 - 1.7.17.md) + - **`15.15.38`** [1.7.16](./doc/15.15.38 - 1.7.16.md) + - **`15.15.39`** [1.7.15](./doc/15.15.39 - 1.7.15.md) + - **`15.15.40`** [1.7.14](./doc/15.15.40 - 1.7.14.md) + - **`15.15.41`** [1.7.13](./doc/15.15.41 - 1.7.13.md) + - **`15.15.42`** [1.7.12](./doc/15.15.42 - 1.7.12.md) + - **`15.15.43`** [1.7.11](./doc/15.15.43 - 1.7.11.md) + - **`15.15.44`** [1.7.10](./doc/15.15.44 - 1.7.10.md) + - **`15.15.45`** [1.7.9](./doc/15.15.45 - 1.7.9.md) + - **`15.15.46`** [1.7.8](./doc/15.15.46 - 1.7.8.md) + - **`15.15.47`** [1.7.7](./doc/15.15.47 - 1.7.7.md) + - **`15.15.48`** [1.7.6](./doc/15.15.48 - 1.7.6.md) + - **`15.15.49`** [1.7.5](./doc/15.15.49 - 1.7.5.md) + - **`15.15.50`** [v1.5](./doc/15.15.50 - v1.5.md) + - **`15.15.51`** [v1.6](./doc/15.15.51 - v1.6.md) + - **`15.15.52`** [v1.7](./doc/15.15.52 - v1.7.md) + - #### **15.16** [历史:版本更新记录(2.x)](./doc/15.16 - 历史:版本更新记录(2.x).md) + - **`15.16.1`** [2.0.1-Alpha](./doc/15.16.1 - 2.0.1-Alpha.md) + - **`15.16.2`** [2.0.5](./doc/15.16.2 - 2.0.5.md) + - **`15.16.3`** [2.0.9](./doc/15.16.3 - 2.0.9.md) + - **`15.16.4`** [2.0.10](./doc/15.16.4 - 2.0.10.md) + + diff --git "a/doc/1 - \345\205\245\351\227\250\346\214\207\345\274\225.md" "b/doc/1 - \345\205\245\351\227\250\346\214\207\345\274\225.md" new file mode 100644 index 0000000..3e79905 --- /dev/null +++ "b/doc/1 - \345\205\245\351\227\250\346\214\207\345\274\225.md" @@ -0,0 +1,49 @@ +#入门指引 + +Swoole虽然是标准的PHP扩展,实际上与普通的扩展不同。普通的扩展只是提供一个库函数。而swoole扩展在运行后会接管PHP的控制权,进入事件循环。当IO事件发生后,swoole会自动回调指定的PHP函数。 + +* 新手入门教程:[https://github.com/LinkedDestiny/swoole-doc](https://github.com/LinkedDestiny/swoole-doc) + +Swoole要求使用者必须具备一定的Linux/Unix环境编程基础,[《学习Swoole需要掌握哪些基础知识》](/wiki/page/487.html) 本文列出了基础知识清单。 + +swoole_server +---- +强大的TCP/UDP Server框架,多线程,EventLoop,事件驱动,异步,Worker进程组,Task异步任务,毫秒定时器,SSL/TLS隧道加密。 + +* `swoole_http_server`是`swoole_server`的子类,内置了Http的支持 +* `swoole_websocket_server`是`swoole_http_server`的子类,内置了WebSocket的支持 +* `swoole_redis_server`是`swoole_server`的子类,内置了Redis服务器端协议的支持 + +> 子类可以调用父类的所有方法和属性 + +swoole_client +----- +`TCP/UDP/UnixSocket`客户端,支持`IPv4/IPv6`,支持`SSL/TLS`隧道加密,支持`SSL`双向证书,支持同步并发调用,支持异步事件驱动编程。 + +swoole_event +---- +EventLoop API,让用户可以直接操作底层的事件循环,将socket,stream,管道等Linux文件加入到事件循环中。 + +> eventloop接口仅可用于socket类型的文件描述符,不能用于磁盘文件读写 + +swoole_async +---- +异步IO接口,提供了 异步文件系统IO,定时器,异步DNS查询,异步MySQL等API,异步Http客户端,异步Redis客户端。 + +* swoole_timer 异步毫秒定时器,可以实现间隔时间或一次性的定时任务 +* swoole_async_read/swoole_async_write 文件系统操作的异步接口 + +swoole_process +---- +进程管理模块,可以方便的创建子进程,进程间通信,进程管理。 + +swoole_buffer +---- +强大的内存区管理工具,像C一样进行指针计算,又无需关心内存的申请和释放,而且不用担心内存越界,底层全部做好了。 + +swoole_table +----- +基于共享内存和自旋锁实现的超高性能内存表。彻底解决线程,进程间数据共享,加锁同步等问题。 + +> swoole_table的性能可以达到单线程每秒读写100W次 + diff --git "a/doc/1.1 - \347\216\257\345\242\203\344\276\235\350\265\226.md" "b/doc/1.1 - \347\216\257\345\242\203\344\276\235\350\265\226.md" new file mode 100644 index 0000000..54e2acd --- /dev/null +++ "b/doc/1.1 - \347\216\257\345\242\203\344\276\235\350\265\226.md" @@ -0,0 +1,66 @@ +#环境依赖 + +* 仅支持 `Linux`、`FreeBSD`、`MacOS` 三种操作系统 +* 在`Windows`平台,可使用`CygWin`或`WSL(Windows Subsystem for Linux)` +* `Linux` 内核版本 `2.3.32` 以上 +* `gcc4.4` 以上版本或者`clang` +* 编译为 `libswoole.so` 作为 `C/C++` 库时需要使用 `cmake-2.4` 或更高版本 + +> 建议使用 `Ubuntu14`、`CentOS7` 或更高版本的操作系统 + +PHP版本依赖 +---------- +* `Swoole-1.x`需要 `PHP-5.3.10` 或更高版本 +* `Swoole-2.x`需要 `PHP-7.0.0` 或更高版本 +* 不依赖 `PHP` 的 `stream`、`sockets`、`pcntl`、`posix`、`sysvmsg` 等扩展。`PHP` 只需安装最基本的扩展即可 + + +ARM平台(树莓派Raspberry PI) +-------------------- +* 请使用 `1.7.10` 或更高版本 +* 使用 `GCC` 交叉编译 +* 在编译 `Swoole` 时,需要手工修改 `Makefile` 去掉 `-O2` 编译参数 + +MIPS平台(OpenWrt路由器) +------ +* 请使用 swoole-1.7.21 或更高版本 +* 使用 GCC 交叉编译 + +CygWin环境支持(Windows系统) +------------ +swoole-1.7.7 增加了对 cygwin 环境的支持,在 Windows 环境下,可以直接使用 cygwin + php 来跑 swoole 程序。 + +* 安装 cygwin,并安装 gcc、make、autoconf、php 4个包 +* 下载swoole源码,在 cygwin-shell 中进行 phpize/configure/make/make install +* 修改 php.ini,加入 swoole.so + +> cygwin 模式下需要对 PHP 进行简化,去掉不使用的扩展,避免进程占用内存过大,导致 Fork 操作失败 + +BashOnWindows +----------- +Windows 10 系统增加了 Linux 子系统支持,BashOnWindows 环境下也可以使用 swoole。安装命令 + +``` +apt-get install php7.0 php7.0-curl php7.0-gd php7.0-gmp php7.0-json php7.0-mysql php7.0-opcache php7.0-readline php7.0-sqlite3 php7.0-tidy php7.0-xml php7.0-bcmath php7.0-bz2 php7.0-intl php7.0-mbstring php7.0-mcrypt php7.0-soap php7.0-xsl php7.0-zip +pecl install swoole +echo 'extension=swoole.so' >> /etc/php/7.0/mods-available/swoole.ini +cd /etc/php/7.0/cli/conf.d/ && ln -s ../../mods-available/swoole.ini 20-swoole.ini +cd /etc/php/7.0/fpm/conf.d/ && ln -s ../../mods-available/swoole.ini 20-swoole.ini +``` + +* BashOnWindows 环境下必须关闭 `daemonize` 选项 +* 需要修改 `config.h` 关闭 `HAVE_SIGNALFD` + +DockerOnWindows +--------------- +在 `Windows` 下开发可以使用 `Hyper-V+Docker` 来方便的开发 `Swoole` 应用,安装好 `Docker` 后再 `Settings` 里的 `Shared Droves` 选项里共享代码所在磁盘。然后使用如下命令来快速启动 `Docker` 容器。 + +``` +docker run --rm -t -i --name myapp -p 9501:9501 -v e:/path/to:/app:rw xutongle/php:7.1-fpm /bin/bash +``` + +* `e:/path/to` 为源码所在路径 +* `/app` 为容器内路径 +* 在 `bash` 里执行 `cd /app && php server.php` + + diff --git "a/doc/1.11 - \350\241\215\347\224\237\345\274\200\346\272\220\351\241\271\347\233\256.md" "b/doc/1.11 - \350\241\215\347\224\237\345\274\200\346\272\220\351\241\271\347\233\256.md" new file mode 100644 index 0000000..7a77074 --- /dev/null +++ "b/doc/1.11 - \350\241\215\347\224\237\345\274\200\346\272\220\351\241\271\347\233\256.md" @@ -0,0 +1,46 @@ +#衍生开源项目 + +开发框架 +---- +* [Swoft](https://www.swoft.org) 首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield, 有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 +* [EasySwoole](http://www.easyswoole.com/) EasySwoole 是一款基于Swoole Server 开发的常驻内存型PHP框架,专为API而生,摆脱传统PHP运行模式在进程唤起和文件加载上带来的性能损失。EasySwoole 高度封装了Swoole Server 而依旧维持Swoole Server 原有特性,支持同时混合监听HTTP、自定义TCP、UDP协议,让开发者以最低的学习成本和精力编写出多进程,可异步,高可用的应用服务。 +* [SwooleDistributed](https://github.com/SwooleDistributed/SwooleDistributed) SD框架全称SwooleDistributed,从名称上看一个是Swoole一个是Distributed,他是基于Swoole扩展的可以分布式部署的应用服务器框架。 借助于PHP的高效开发环境,Swoole的高性能异步网络通信引擎,以及其他的高可用的扩展和工具,SD框架提供给广大开发者一个稳定的高效的而且功能强大的应用服务器框架。 +* [MixPHP](http://www.mixphp.cn/) 是一个基于 Swoole 的常驻内存型 PHP 高性能框架,围绕常驻内存的方式而设计,提供了 Web / Console 开发所需的众多开箱即用的组件,MixPHP 追求简单、实用主义,开发文档完善,试图让更多开发者以更低的学习成本享受到 Swoole 带来的高性能与全新的编程体验。 +* [Swoolefy](https://github.com/bingcool/swoolefy) 基于swoole扩展实现的轻量级高性能的API和Web应用服务框架,高度封装了http,websocket,udp服务器,以及基于tcp实现可扩展,自定义协议的rpc服务器,同时支持composer包方式快速部署项目。基于易用,swoolefy抽象Event事件处理类,实现与底层的回调的解耦,专注逻辑业务,支持同步|异步调用,内置view、Log、session、mysql、redis、memcached、mongodb,mailer等常用组件。 + +服务器 +------ +* [MyQEE-Server](https://github.com/myqee/server) 将swoole服务和功能对象抽象化,为每个 Worker、Task、多端口分配一个对象,带来全新的编程体验让代码清晰有条理,适合多端口以及Http、WebSocket、Tcp混合的应用服务器开发,支持创建大文件、断点、分片上传的Http服务器 +* [EPServer](https://github.com/ewenlaz/epserver) 高性能TCP服务器框架,底层基于swoole扩展 +* [WebSocket & WebIM](https://github.com/matyhtf/PHPWebIM) +* [Upload-Server](https://github.com/matyhtf/swoole/blob/master/examples/server/upload_server.php) 基于swoole扩展开发的,高性能TCP文件上传服务器,是全异步非阻塞多进程的。可同时支持数万个TCP客户端连接,上传文件。 +* [php-queue](https://github.com/matyhtf/php-queue) PHP开发的磁盘存储消息队列服务,基于leveldb和swoole,在4核机器上处理能力可以达到2.5W/s +* [PtWebserver](https://git.oschina.net/pantian/PtWebserver) PtWebserver 基于php swoole 扩展的高性能web 服务器。应用对象常驻内存,不用重复创建对象,提高响应时间与性能 +* [swoole-jobs](https://github.com/kcloze/swoole-jobs) swoole-jobs,基于swoole的job调度组件,支持composer,可以跟任意框架集成 + +应用项目 +---- +* [zchat](https://github.com/shenzhe/zchat) 基于zphp框架和swoole扩展开发的PHP网页即时聊天室系统。 +* [PHPWebIM](https://github.com/matyhtf/phpwebim) 基于swoole扩展开发的websocket网页聊天系统 +* [swoole_flash_game](https://github.com/matyhtf/swoole_flash_game) 基于swoole扩展开发的flash游戏,可与服务器实时交互 +* [statistics](https://github.com/smalleyes/statistics) 一个运用php与swoole实现的统计监控系统 +* [swoole-bot](https://github.com/kcloze/swoole-bot)基于swoole实现的微信机器人,依赖vbot和微信网页版的功能,帮助管理微信群/聊天/踢人等 +* [vbot](https://github.com/hanson/vbot) 基于web api打造的微信机器人,可以通过配置开启 swoole,便可打造自己的个性化微信客户端 + +微服务框架 +--- +* [SwooleDistributed](http://sd.youwoxing.net) 2.0版本为微服务框架,具有服务注册中心,可以发布服务,监测服务状态,进程内的负载均衡,同时具有熔断,降级等保护服务的高级功能。服务健康状态,上下线服务自动感知,可以通过RPC或者HTTP与其他服务器进行交互。如果服务中断框架会自动将请求迁移到可用的服务上,尽量保证高可用性,性能更是优秀。通过版本管理还可以支持灰度发布。 +* [Group-Co](https://github.com/fucongcong/Group-Co) 优雅的异步协程框架,并内置分布式服务化体系,可以根据自身架构需求自定义实现服务的上下线,监控,发布等等。 + +HTTP 应用框架 +----- +* [zhttp](https://github.com/keaixiaou/zhttp) 基于swoole+generator的异步非阻塞轻量级web框架(api和web皆可),内置mysql、redis、memcached、mongodb全套异步客户端的连接池,内置http异步客户端,近乎同步的写法,却是异步的调用,性能强悍 + +* [FastD](https://github.com/JanHuang/fastD) 适用于对性能有要求的 API 场景,并且灵活的扩展性可以让开发者们更容易地建造自己的服务。支持HTTP、TCP、UDP、WebSocket,简单,易用。 + +* [LaravelS](https://github.com/hhxsv5/laravel-s) 基于Swoole加速Laravel/Lumen,常驻内存,内置HTTP/Websocket服务器,异步的事件监听,异步任务队列,毫秒级定时任务,平滑Reload,与Nginx配合搭建高可用分布式服务器群,开箱即用。 + +* [Yii2-Swoole](https://github.com/tsingsun/yii2-swoole) 支持基于Yii2框架运行于Swoole中,同时可以很简单的支持Swool 1.0与2.0协程,自带mysql,redis连接池,可以使用Yii2的全栈框架来开发HTTP,WebSocket等网络服务。 +``` +如果您有基于swoole开发新的开源项目,可以联系我们。将你的开源项目加入swoole官方推荐列表中。 +``` \ No newline at end of file diff --git "a/doc/1.11.1 - \346\241\206\346\236\266.md" "b/doc/1.11.1 - \346\241\206\346\236\266.md" new file mode 100644 index 0000000..b355d57 --- /dev/null +++ "b/doc/1.11.1 - \346\241\206\346\236\266.md" @@ -0,0 +1,113 @@ +#框架 + +* [Tencent-TSF](https://github.com/tencent-php/tsf) 腾讯公司推出的PHP协程框架,基于Swoole+PHP Generator实现Coroutine,可以像Golang一样用协程实现高并发服务器。 +* [swoole_framework](https://github.com/matyhtf/swoole_framework)基于swoole扩展开发的通用后端服务框架,包含了内置PHP应用服务器、FastCGI、WebSocket、Web框架等丰富的功能特性 +* [MixPHP](http://www.mixphp.cn/) 是一个基于 Swoole 的常驻内存型 PHP 高性能框架,围绕常驻内存的方式而设计,提供了 Web / Console 开发所需的众多开箱即用的组件,MixPHP 追求简单、实用主义,开发文档完善,试图让更多开发者以更低的学习成本享受到 Swoole 带来的高性能与全新的编程体验。 +* [LaravelS](https://github.com/hhxsv5/laravel-s) 基于Swoole加速Laravel/Lumen,常驻内存,内置HTTP/Websocket服务器,异步的事件监听,异步任务队列,毫秒级定时任务,平滑Reload,与Nginx配合搭建高可用分布式服务器群,开箱即用。 +* [zphp](https://github.com/shenzhe/zphp)一个极轻的的,专用于游戏(社交,网页,移动)的服务器端开发框架.提供高性能实时通信方案。zphp使用swoole作为底层网络通信的框架。 +* [zapi](https://github.com/keaixiaou/zapi) 基于swoole+generator的http api异步非阻塞轻量级框架,内置mysql、redis、memcached、mongodb全套异步客户端的连接池,内置http异步客户端,近乎同步的写法,却是异步的调用,性能强悍 +* [zhttp](https://github.com/keaixiaou/zhttp) 基于swoole+generator的异步非阻塞轻量级web框架,内置mysql、redis、memcached、mongodb全套异步客户端的连接池,内置http异步客户端,近乎同步的写法,却是异步的调用,性能强悍 +* [swoole-yaf](https://github.com/LinkedDestiny/swoole-yaf) 结合PHP的Yaf框架和Swoole扩展的高性能PHP Web框架 +* [Swoole-Yaf](https://github.com/wenjun1055/swoole-yaf) 将Yaf框架和Swoole扩展提供的HttpServer结合在一起,server和框架高度结合形成超高性能的组合 +* [ciswoole](https://github.com/smalleyes/ciswoole) CodeIgniter 2.2 with Swoole_Http_Server +* [owl-mvc](https://github.com/yeaha/owl-mvc) 基于 swoole_http_server 的一套PHP MVC框架 +* [hprose/hprose-php](https://github.com/hprose/hprose-php) 高性能远程对象调用服务,PHP 版本底层使用 swoole 实现了 http,https,tcp,tcp6,websocket, unix socket 服务器和 tcp,tcp6,unix socket 客户端。 +* [yiiSwoole](https://github.com/kcloze/yiiSwoole) Yii 1.1.16 with Swoole Http_Server,In high-concurrency situations,will be better than php-fpm +* [Dora-RPC](https://github.com/xcl3721/Dora-RPC) 是基础swoole实现的轻量级高性能RPC框架,支持同步/异步调用,拥有有多任务并发及长链接维持特性 +* [Blink](https://github.com/bixuehujin/blink) 是一个为构建 “long running” 服务而生的 Web 微型高性能框架,它为构建 Web 应用程序提供简洁优雅的API,尽量的减轻我们的常规开发工作 +* [swPromise](https://github.com/coooold/swPromise) 基于swoole的PHP promise框架 +* [Aurora](https://github.com/zxz054321/aurora) 是一个建立在 Lightning 之上的高性能高并发框架,为追求极限性能而打造,底层由Phalcon + Swoole组合驱动,适用于需要支持高并发的场景,如API 接口、微服务等。 +* [Group](https://github.com/fucongcong/Group) 轻量级框架。基于swoole实现了定时任务,分布式任务队列,异步多进程服务(模拟map-reduce),结合hprose的rpc服务。 +* [Group-co](https://github.com/fucongcong/Group-Co) 优雅的异步协程框架,支持服务化搭建高并发httpserver,支持分布式使用,详情请戳链接。 +* [FastD](https://github.com/JanHuang/fastD) FastD 是一个支持 Swoole 的轻量级 Web 开发框架,可适用于对性能有要求的 API 场景,并且灵活的扩展性可以让开发者们更容易地建造自己的服务 (基于Swoole) +* [Yii2-Swoole](https://github.com/tsingsun/yii2-swoole) 支持基于Yii2框架运行于Swoole中,同时可以很简单的支持Swool 1.0与2.0协程,自带mysql,redis连接池,可以使用Yii2的全栈框架来开发HTTP,WebSocket等网络服务。 + +* [ultraman](https://github.com/zoooozz/ultraman) 结合PHP的Yaf框架和Swoole扩展的高性能PHP 封装成composer 及其容易上手 + + +Swoft:基于2.0原生协程的高性能PHP微服务框架 +---- +[https://github.com/swoft-cloud/swoft](https://github.com/swoft-cloud/swoft) + +首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield,有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 + +- 基于 Swoole 扩展 +- 内置协程 HTTP, TCP, WebSocket 网络服务器 +- 强大的 AOP (面向切面编程) +- 灵活完善的注解功能 +- 全局的依赖注入容器 +- 基于 PSR-7 的 HTTP 消息实现 +- 基于 PSR-14 的事件管理器 +- 基于 PSR-15 的中间件 +- 基于 PSR-16 的缓存设计 +- 可扩展的高性能 RPC +- 完善的服务治理,熔断,降级,负载,注册与发现 +- 数据库 ORM +- 通用连接池 +- 协程 Mysql, Redis, RPC, HTTP 客户端 +- 协程和同步阻塞客户端无缝自动切换 +- 协程、异步任务投递 +- 自定义用户进程 +- RESTful 支持 +- 国际化(i18n)支持 +- 高性能路由 +- 快速灵活的参数验证器 +- 别名机制 +- 强大的日志系统 +- 跨平台热更新自动 Reload + + +MyQEE 服务器类库 +----- + +[https://github.com/myqee/server](https://github.com/myqee/server) + +MyQEE 服务器类库是一套基础服务器类库,让你可以摒弃 Swoole 传统的 On 回调写法,在不损失性能和功能的前提下实现功能和服务的对象抽象化,实现全新的编程体验,让代码清晰有条理。特别适合复杂的应用服务器,不管是你要在一起集成 Http 还是 Tcp 还是 WebSocket 服务,解决了使用 Swoole 开发复杂服务器的痛点。另外,通过本类库使得php新手使用 swoole 会变得更轻松不再那么迷茫(比如多端口绑定、任务进程和工作进程的关系和功能)。 + +MyQEE服务器类库特性: + +* 对象抽象化:为每个 Worker、TaskWorker、以及端口监听分配一个对象,业务层自己实现相应功能即可,让开发代码清晰有条理; +* 填补了 Swoole 服务器开发中的很多坑; +* 支持大文件、断点、分片上传功能并完美融合服务(`swoole_http_server` 不支持大文件上传,会有内存问题,也存在一些细节上的bug); +* 易于使用的多重混合服务器端口监听方案; +* 解决服务器选型痛点; +* 解决代码混乱的痛点; +* 解决新手搞不清 Worker、TaskWorker 和多端口之间的功能、关系、使用特性; +* 更加简单易用的热更新方案; +* 连接池、资源池; +* 更多的周边功能特性; + + + + +zys高性能服务框架 +----- + 基于Yaf和Swoole的i高性能Service框架,核心特性: + +1. 基于swoole提供分布式服务器通讯服务 +2. 基于thrift提供rpc远程调用服务 +3. 基于HTML5提供在线网络直播平台服务 +4. 基于swoole提供同步异步数据库连接池服务 +5. 基于swoole提供异步任务服务器投递任务服务 +6. 基于vmstat提供服务器硬件实时监控服务 +7. 基于yac、yaconf提供共享数据、配置服务 +8. 基于zqf提供高并发计数器、红包、二维码服务 +9. 很好的支持网页版console的shell服务 +10. 基于hprose提供rpc远程调用、推送等服务 + +WebWorker-swoole高性能http服务框架 +----- + 基于Swoole2.0的协程特性写的框架,核心特性: + +1. 实现了简单路由功能的小巧框架,便于开发者使用和扩展,非常具有灵活性 +2. 相比php-fpm或mod_php的方式性能有几十倍左右的提升 +3. 可设置自动加载目录加载目录下的所有php文件(仅一级不支持递归) +4. 自定义404响应 +5. 支持中间件 +6. redis支持原生同步和协程版本,只需要一个配置参数即可 +7. mysql支持原生同步和协程版本,只需要一个配置参数即可 + +easyPHP-Swoole 高性能HTTP框架 +----- +easyPHP-Swoole 专为API而生,是一款常驻内存化的PHP开发框架,摆脱传统PHP运行模式在进程唤起和文件加载上带来的性能损失,自带服务器功能,无需依赖Apache或Nginx运行。在web服务器模式下,支持多层级(组模式)控制器访问与多种事件回调,高度封装了Swoole Server 而依旧维持Swoole Server原有特性,支持在 Server 中监听自定义的TCP、UDP协议,让开发者可以最低的学习成本和精力,编写出多进程,可定时,可异步,高可用的应用服务。 +项目地址 : [https://github.com/kiss291323003/easyPHP-Swoole](https://github.com/kiss291323003/easyPHP-Swoole) \ No newline at end of file diff --git "a/doc/1.11.2 - \345\267\245\345\205\267.md" "b/doc/1.11.2 - \345\267\245\345\205\267.md" new file mode 100644 index 0000000..67da0f0 --- /dev/null +++ "b/doc/1.11.2 - \345\267\245\345\205\267.md" @@ -0,0 +1,14 @@ +#工具 + +* [swoole-ide-helper](https://github.com/EagleWu/swoole-ide-helper) 在IDE下自动识别swoole 扩展的类、函数、宏,自动补全函数名 +* [redis-async](https://github.com/swoole/redis-async) 基于swoole开发的异步Redis+连接池,性能非常强劲。使用redis-async开发的Web应用,QPS可以高达3.5万QPS,超过php-fpm+php-redis扩展性能的10倍。 +* [mysql-async](https://github.com/swoole/mysql-async) 基于swoole扩展开发的异步MySQL类库,内置连接池和SQL任务排队机制 +* [swoole-crontab](https://github.com/osgochina/swoole-crontab) 基于swoole的定时器程序,支持秒级处理. +异步多进程处理。完全兼容crontab语法,且支持秒的配置 +* [swoole-vmstat](https://github.com/smalleyes/swoole-vmstat) 运用swoole在浏览器更友好的实现vmstat +* [DHT](https://github.com/ylqjgm/DHT) 使用swoole编写的DHT爬虫程序,可正常获取infohash +* [swoole-linux-dash](https://github.com/smalleyes/swoole-linux-dash) 运用swoole友好的实现Linux dash性能监控工具集合(uptime,free等) +* [Plumber](https://github.com/Footstones/Plumber) 消息队列beanstalk的Worker守护进程 +* [GitHook](https://github.com/wenjun1055/githook) Git钩子程序,Github/Bitbucket repo 在某个分支发生 push 行为的时候,自动触发一段脚本重启swoole server +* [swoole-server-manager](https://github.com/df007df/swoole-server-manager) 常驻服务管理框架 +* [php-reflection-code](https://github.com/flyhope/php-reflection-code) PHP反射IDE自动提示生成器,外链自己的GIT项目或把代码放进自己的项目可实现IDE(Zend/Eclipse/Netbeans等)中代码提示Swoole内置类与函数。 \ No newline at end of file diff --git "a/doc/1.11.3 - \345\210\206\345\270\203\345\274\217.md" "b/doc/1.11.3 - \345\210\206\345\270\203\345\274\217.md" new file mode 100644 index 0000000..a9d6c57 --- /dev/null +++ "b/doc/1.11.3 - \345\210\206\345\270\203\345\274\217.md" @@ -0,0 +1,53 @@ +#分布式 + +SwooleDistributed(推荐使用) +------- +[SwooleDistributed](http://sd.youwoxing.net) swoole 分布式全栈框架框架,它的特点: + +- 优秀的框架(MVC)设计,丰富的支持极大加快开发速度 +- 通过开启不同端口同时支持TCP和HTTP,WebSocket,同一逻辑处理不同协议 +- 全异步支持,无需手动处理连接池,异步redis,异步mysql,mysql语法构建器,支持异步mysql事务,异步httpclient,效率出众 +- 协程模式全支持,异步redis,异步mysql,异步httpclient,异步task,全部都提供了协程模式,业务代码摆脱处处回调的困扰(不是swoole2.0同样支持) +- 支持协程嵌套,支持协程内异常处理(和正常逻辑用法一样) +- 额外提供了protobuf完整RPC实例,轻松使用protobuf +- 天然分布式的支持,一台机器不够零配置,零代码修改完成高效分布式系统的搭建 +- [完善详细的文档](http://docs.youwoxing.net),还有实例代码,轻松掌握 +- 线上项目打造维护,不断优化与改进 +* [SwooleDistributed](http://sd.youwoxing.net) 2.0版本为微服务框架,拥有1.x版本全部功能,核心代码重构,协程效率更加优秀,具有服务注册中心,可以发布服务,监测服务状态,进程内的负载均衡,同时具有熔断,降级等保护服务的高级功能。服务健康状态,上下线服务自动感知,可以通过RPC或者HTTP与其他服务器进行交互。如果服务中断框架会自动将请求迁移到可用的服务上,尽量保证高可用性。通过版本管理还可以支持灰度发布。 + +swoole-task +-------- +[swoole-task](https://github.com/luxixing/swoole-task) 是基于PHP swoole扩展开发的一个异步多进程任务处理框架,服务端和客户端通过http协议进行交互。 + +它适用于任务需要花费较长时间处理,而客户端不必关注任务执行结果的场景.比如数据清洗统计类的工作,报表生成类任务。 + +swoole-jobs +---- +[swoole-jobs](https://github.com/kcloze/swoole-jobs) 基于swoole的job调度组件,特性: + +- redis/rabbitmq/zeromq等任何一种做队列消息存储(目前只实现redis/rabbitmq) +- 利用swoole的process实现多进程管理,进程个数可配置,worker进程退出后会自动拉起 +- 子进程循环次数可配置,防止业务代码内存泄漏 +- 支持topic特性,不同的job绑定不同的topic +- 支持composer,可以跟任意框架集成 +- 日志文件自动切割,默认最大100M,最多5个日志文件,防止日志刷满磁盘 + +DFS +--- +[DFS](https://github.com/qieangel2013/dfs) 分布式文件服务器,核心特性: + +1. 基于swoole和inotify实现分布式文件服务 +2. 采用协议包来实时同步文件、性能很高,采用sendfile传送文件,内存、cpu占有率很少 +3. 文件实时监控及监控子目录服务 +4. 自动断线重连服务 +5. 自动扫描本地已存在的文件目录实时同步服务 + +multiprocess +--- +[multiprocess](https://github.com/kcloze/multiprocess) 基于swoole的进程管理组件,可轻松让普通PHP脚本变守护进程和多进程执行: + +1. 基于swoole的脚本管理,用于多进程和守护进程管理 +2. 进程个数可配置,可以根据配置一次性执行多条命令 +3. 子进程异常退出时,自动重启 +4. 主进程异常退出时,子进程在干完手头活后退出(平滑退出) + diff --git "a/doc/1.11.4 - \351\200\232\344\277\241\345\215\217\350\256\256.md" "b/doc/1.11.4 - \351\200\232\344\277\241\345\215\217\350\256\256.md" new file mode 100644 index 0000000..15e7905 --- /dev/null +++ "b/doc/1.11.4 - \351\200\232\344\277\241\345\215\217\350\256\256.md" @@ -0,0 +1,17 @@ +#通信协议 + +MQTT +---- +[swoole_mqtt_php](https://github.com/xavier-chen/swoole_mqtt_php) 基于swoole实现的mqtt协议消息通信服务器 + +Yar服务器 +----- + 基于swoole实现的yar服务, php7 + swoole的性能超强。 + +其他 +---- + +* [FastCGI协议](https://github.com/swoole/framework/blob/master/libs/Swoole/Protocol/FastCGI.php) 基于swoole的FastCGI服务器实现 +* [Comet协议](https://github.com/swoole/framework/blob/master/libs/Swoole/Protocol/CometServer.php) 基于swoole的Http长轮循,Comet服务器实现 +* [RPC/SOA服务器](https://github.com/matyhtf/swoole_framework/blob/master/examples/soa_server.php) +* [PHP-ftp-server](https://github.com/matyhtf/swoole_framework/blob/master/examples/ftp_server.php) 基于swoole扩展开发的高性能FTP服务器,支持主动被动模式、虚拟目录。性能非常好,可用于生产环境。 diff --git "a/doc/1.12 - \347\224\250\346\210\267\344\270\216\346\241\210\344\276\213.md" "b/doc/1.12 - \347\224\250\346\210\267\344\270\216\346\241\210\344\276\213.md" new file mode 100644 index 0000000..a34331c --- /dev/null +++ "b/doc/1.12 - \347\224\250\346\210\267\344\270\216\346\241\210\344\276\213.md" @@ -0,0 +1,134 @@ +#用户与案例 + +农博创新 +------ +智慧农业物联网+大数据 +农博创新: + +![](http://www.swoole.com/static/uploads//wiki/201709/19/151640612443.jpg) +------ + +川海开源商城 +----- +川海开源商城:(商城+webim+cs端im) + +ChuanHaiShop 采用php的yaf内存框架开发,运行速度极快,自身集成orm,使用灵活,sql语句的执行采用sql预处理方式,从根源上避免了sql注入,业务逻辑大量采用行锁,事务,运行稳定。 + +im服务端采用swoole做websocket集群,川海即时通讯插件助您整合咨询系统,商家可使用桌面版与用户的web版在线交流,提高订单成交。(websocket通讯,并做了ie低版本兼容) + +产品特征: + +多规格可以设置多价格,规格自定义 + +运费支持设置省级运费 + +产品价格由 金额和积分2部分,积分用来抵扣现金,价格设置支持2位小数 + +![](http://www.swoole.com/static/uploads//wiki/201710/12/779060290888.png) +![](http://www.swoole.com/static/uploads//wiki/201710/12/779270695390.png) +![](http://www.swoole.com/static/uploads//wiki/201711/20/391130410650.jpg) + +半次元 (bcy.net) +------ +半次元: +半次元是国内第一中文COS绘画小说社区,汇聚了包括Coser、绘师、写手等创作者在内的众多二次元同好,提供cosplay、绘画和文字作品创作发表、二次元同好交流等社群服务。 + +核心程序基于swoole+redis,使用swoole模拟map-reduce方案,在4核8G云主机单机查找/拼接/筛选千万级别用户时间轴动态,平均单次响应时间在60ms内。 + +![半次元](/static/uploads//wiki/201608/18/204860827822.jpg) + +im.classba.com.cn +----- +基于swoole开发的WebIM, + +![截图1](http://www.swoole.com/static/uploads//wiki/201609/28/531100857223.jpg) +![截图2](http://wiki.swoole.com/static/uploads//wiki/201609/28/531710822812.png "截图2") + +积目 +----- +积目:开启目的社交第一户,首创目的语言系统,让你省略长编大论的开场白,用目的图标证明自己。改变目前交友过程单一枯燥的局面,用照片弹幕,让你每天打开积目,都有新鲜事件发生! + +[![](http://ww1.sinaimg.cn/large/0060lm7Tgy1fdl7lqhysbj307i0dc77c.jpg)](http://ww1.sinaimg.cn/large/0060lm7Tgy1fdl7lqhysbj307i0dc77c.jpg) +[![](http://ww1.sinaimg.cn/large/0060lm7Tgy1fdl7lqhen3j307i0dcwhz.jpg)](http://ww1.sinaimg.cn/large/0060lm7Tgy1fdl7lqhen3j307i0dcwhz.jpg) +[![](http://ww2.sinaimg.cn/large/0060lm7Tgy1fdl7lqdz91j307i0dcjtk.jpg)](http://ww2.sinaimg.cn/large/0060lm7Tgy1fdl7lqdz91j307i0dcjtk.jpg) +[![](http://ww4.sinaimg.cn/large/0060lm7Tgy1fdl7lr3p3qj307i0dcwia.jpg)](http://ww4.sinaimg.cn/large/0060lm7Tgy1fdl7lr3p3qj307i0dcwia.jpg) + +https://itunes.apple.com/us/app/%E7%A7%AF%E7%9B%AE-%E7%9B%AE%E7%9A%84%E7%85%A7%E7%89%87%E5%BC%B9%E5%B9%95%E7%A4%BE%E4%BA%A4/id1094615747?mt=8 + +边锋网络 +---- + +战旗直播:[http://www.zhanqi.tv](http://www.zhanqi.tv) + +战旗直播从单个laravel应用在高并发下经常502,后来用swoole + phalcon异步服务化架构重构,实现高可扩展及可用性,每天几亿级Hits。 + + +![战旗直播架构图](http://wiki.swoole.com/static/image/zhanqi/1.png) + + +![图片1](http://wiki.swoole.com/static/image/zhanqi/2.jpg) + +虎牙直播 +---- +虎牙直播:[http://www.huya.com](http://www.huya.com) + +虎牙直播APP基于Swoole实现了TCP长连接PUSH服务,日均活跃200万用户。TCP并发连接数超过40万,收发消息数峰值超过10万条每秒。 + +![图片1](http://wiki.swoole.com/static/image/huya.jpg) + +YY语音 +---- +部门内多款移动APP使用swoole作为底层框架,实现了手机客户端与服务器段长连接,直接通信的模式。大大提升了移动网络下应用程序的用户体验。 + +![游戏刷子](/static/image/shuazi.png) + +另外部门内部的数据统计监控平台,实时日志上报和实时推送,爬虫引擎都是基于swoole的。 + +学而思教育 +---- +暂无 + +九正建材网 +----- +[http://www.jc001.cn/](http://www.jc001.cn/) + +我们线上使用swoole跑了两个服务,上线一个月左右,非常稳定, +一个用于写入访客访问日志系统,节约了宝贵的mysql连接资源; +一个用于内部系统的数据处理,任务操作很耗时,从队列 + 定时(crontab)转换为使用swoole服务,处理变得更可控更即时。 + +集运宝 +----- +[http://www.jiyunbao.com.cn/](http://www.jiyunbao.com.cn/) + +“集运宝”是一款为了减少物流环节,降低物流成本,提高物流效益,可以给物流行业降低30%的运输成本的一款软件。 +集装箱运输行业的现状是每天有上万辆集装箱车辆,都是放空去浙江、江苏、安徽和别的省去拉货回来,所以就形成了很大的资源浪费,集装箱车辆又找不到合适的货源,不得不放空支外地,物流公司的利润也就越来越低,当然也希望找到案例可靠价格低廉的车源。所以“集运宝”这款软件呢,就是为双方搭建的一个信息对接平台,降低物流行业的成本,减少集装箱车辆的空驰率,可以配到合适的散货,全面有效地提高上海公路物流发展,提高物流信息的快速运转,降低物流运输成本,整合了上海集装箱行业和物流行业的关系的一款软件。 + +目前集运宝已有2000多用户正在使用,项目建成后目标市场20万用户。现在使用的用户反馈效果较好,操作简单、使用方便,为集装箱行业降低了运输成本,也提高了物流行业的发展。 + +* 后台:CentOS6,swoole扩展V1.7.1, swoole框架 +* 数据库:mssql server 2005 +* 电脑客户端:html+js+websocket +* 安卓手机客户端:java+websocket +* 后台管理系统:PowerBuilder9.0 + +![图片1](/static/image/jiyunbao/1.png) +![图片2](/static/image/jiyunbao/2.jpg) +![图片3](/static/image/jiyunbao/3.jpg) +![图片4](/static/image/jiyunbao/4.png) +![图片5](/static/image/jiyunbao/5.png) +![图片6](/static/image/jiyunbao/6.png) +![图片7](/static/image/jiyunbao/7.png) + +拍够购 +------ + + +「拍够购」 是首家日本免代购的服务平台,目前全面针对日本雅虎代购,提供代拍服务和海外直邮,自动代拍程序在上线初期使用ruby+eventmachine 开发,因性能问题更换swoole websocket 分布式客户端投标,性能问题瞬间2倍提升,也减轻服务器压力,同时基于websocket,管理员可以实时检测投标程序状态。 + +![拍够购](/static/image/paigogo.jpg) + +更多案例 +----- +正在收集中。如果您的线上产品中使用了swoole,可以将您的使用情况和产品截图发给我们。 + + diff --git "a/doc/1.12.1 - \347\211\251\350\201\224\347\275\221\351\241\271\347\233\256.md" "b/doc/1.12.1 - \347\211\251\350\201\224\347\275\221\351\241\271\347\233\256.md" new file mode 100644 index 0000000..eed647f --- /dev/null +++ "b/doc/1.12.1 - \347\211\251\350\201\224\347\275\221\351\241\271\347\233\256.md" @@ -0,0 +1,23 @@ +#物联网项目 + +一 NodeMCU+Swoole +----- +NodeMCU是一款开源快速硬件原型平台,包括固件和开发板,用几行简单的Lua脚本就能开发物联网应用。 +Doit Car的远程控制后台完全基于Swoole开发,Swoole TCP Server负责和小车通讯,Swoole WebScoket Server负责实时和浏览器交互。 +编程技术支持QQ群:453053759。 + +二 DoitCar +------ +DoitCar由深圳四博智联科技有限公司开发。公司于2014年成立,由中科院4名博士联合创立。公司专注于开源硬件、机器人领域。Doit取自公司英文名称Shenzhen Doctors of Intelligence & Technology (DOIT)的缩写。 +初级版本的DoitCar支持AP(热点)、STA(连接路由)两种基本模式,也支持AP+STA共存的模式。 + +![图片1](http://www.swoole.com/static/uploads//user_images/201506/16/504480679084.jpg) +![图片2](http://www.swoole.com/static/uploads//user_images/201506/16/506450744968.jpg) + +小车视频介绍 +---- +AP模式: + + +STA 模式: + diff --git "a/doc/1.12.2 - \347\275\221\347\273\234\346\270\270\346\210\217.md" "b/doc/1.12.2 - \347\275\221\347\273\234\346\270\270\346\210\217.md" new file mode 100644 index 0000000..f9cbee8 --- /dev/null +++ "b/doc/1.12.2 - \347\275\221\347\273\234\346\270\270\346\210\217.md" @@ -0,0 +1,22 @@ +#网络游戏 + +有乐游戏 +---- + +[有乐斗地主](http://www.youjoy.tv/),中国第一款真正的电视棋牌斗地主。采用独家原创的电视操作交互体验,由有乐游戏公司(YouJoy.tv)出品。该游戏基于电视遥控器深度定制。使用遥控器的方向、确定、返回以及菜单键,即可轻松玩转,让你享受窝在沙发里打牌的轻松愉悦! + +程序基于swoole+redis,完全分布式运算,在4核8G云主机单机支撑优质在线量为6000-8000,且因为完全的分布式运算,理论上最高单服在线无上限(受限于redis承载等第三方因素,达到个10万估计没问题) + +![女王大人](/static/image/youle.png) + +希望之光 +--------------- + +![希望之光](/static/image/xiwang.png) + +上海胜游 +---- +公司研发的《女王大人》手机游戏,后台程序全部基于PHP+Swoole+websocket,实现了TCP长连接,服务器推送。取代传统游戏慢速的Http方式。 + +![女王大人](/static/image/nvwang.jpg) + diff --git "a/doc/1.12.3 - \350\205\276\350\256\257\357\274\210Tencent\357\274\211.md" "b/doc/1.12.3 - \350\205\276\350\256\257\357\274\210Tencent\357\274\211.md" new file mode 100644 index 0000000..99ffbba --- /dev/null +++ "b/doc/1.12.3 - \350\205\276\350\256\257\357\274\210Tencent\357\274\211.md" @@ -0,0 +1,15 @@ +#腾讯(Tencent) + +QQ公众号 +---- +腾讯生活服务号(QQ公众号)的业务逻辑层基本都是基于swoole开发完成。 + +![图片1](http://www.swoole.com/static/uploads/user_images/201409/15/588840416079.jpg) +![图片2](http://www.swoole.com/static/uploads/user_images/user_images/201409/15/588840701140.jpg) + +营销QQ +---- +Swoole在营销QQ项目中也得到了大量应用,如:增强版的消息网管server(tcp), 业务逻辑server(udp)。 + +![图片3](http://www.swoole.com/static/uploads/user_images/user_images/user_images/201409/15/588970879339.png) +![图片4](http://www.swoole.com/static/uploads/user_images/user_images/user_images/user_images/201409/15/589000856527.png) diff --git "a/doc/1.12.4 - \347\231\276\345\272\246\357\274\210Baidu.com\357\274\211.md" "b/doc/1.12.4 - \347\231\276\345\272\246\357\274\210Baidu.com\357\274\211.md" new file mode 100644 index 0000000..3bad288 --- /dev/null +++ "b/doc/1.12.4 - \347\231\276\345\272\246\357\274\210Baidu.com\357\274\211.md" @@ -0,0 +1,14 @@ +#百度(Baidu.com) + +百度地图 +----- +百度地图:http://map.baidu.com/ + +采用swoole+redis,基于swoole扩展,根据内部的需求,封装了一套可扩展、高性的PHP server,已应用于多个业务模块,提供高并发的kv数据查询及业务数据的在线计算,每天亿级的PV量 + +百度订单中心 +---- +作为百度统一的订单中心,承担着百度各业务线订单数据的归集、存储、挖掘、分析等工作,旨在向各业务线提供用户完整的订单数据;并以数据产品的形态,向各业务线提供基于用户消费行为的运营工具和相关报告。 + +![百度swoole图片2](http://wiki.swoole.com/static/image/baidu/2.jpg) +![百度swoole图片3](http://wiki.swoole.com/static/image/baidu/3.jpg) diff --git "a/doc/1.12.5 - \351\230\205\346\226\207\351\233\206\345\233\242.md" "b/doc/1.12.5 - \351\230\205\346\226\207\351\233\206\345\233\242.md" new file mode 100644 index 0000000..a9b6c25 --- /dev/null +++ "b/doc/1.12.5 - \351\230\205\346\226\207\351\233\206\345\233\242.md" @@ -0,0 +1,3 @@ +#阅文集团 + +正在上传中。 \ No newline at end of file diff --git "a/doc/1.12.6 - BiliBili\357\274\210\345\223\224\345\223\251\345\223\224\345\223\251\357\274\211.md" "b/doc/1.12.6 - BiliBili\357\274\210\345\223\224\345\223\251\345\223\224\345\223\251\357\274\211.md" new file mode 100644 index 0000000..ec662d2 --- /dev/null +++ "b/doc/1.12.6 - BiliBili\357\274\210\345\223\224\345\223\251\345\223\224\345\223\251\357\274\211.md" @@ -0,0 +1,3 @@ +#BiliBili(哔哩哔哩) + +正在上传中。 \ No newline at end of file diff --git "a/doc/1.12.7 - \350\275\246\350\275\256\344\272\222\350\201\224\357\274\210chelun.com\357\274\211.md" "b/doc/1.12.7 - \350\275\246\350\275\256\344\272\222\350\201\224\357\274\210chelun.com\357\274\211.md" new file mode 100644 index 0000000..547fb51 --- /dev/null +++ "b/doc/1.12.7 - \350\275\246\350\275\256\344\272\222\350\201\224\357\274\210chelun.com\357\274\211.md" @@ -0,0 +1,11 @@ +#车轮互联(chelun.com) + +车轮查违章 +----- + + +车轮考驾照 +---- + +车轮社区 +---- \ No newline at end of file diff --git "a/doc/1.12.8 - (\346\215\236\346\234\210\347\213\227) \346\270\270\346\210\217\347\244\276\345\214\272.md" "b/doc/1.12.8 - (\346\215\236\346\234\210\347\213\227) \346\270\270\346\210\217\347\244\276\345\214\272.md" new file mode 100644 index 0000000..1acdce5 --- /dev/null +++ "b/doc/1.12.8 - (\346\215\236\346\234\210\347\213\227) \346\270\270\346\210\217\347\244\276\345\214\272.md" @@ -0,0 +1,42 @@ +#(捞月狗) 游戏社区 + + +![图片1](https://imgx.lygou.cc/cai/img/public/lyg/zh-cn/logo_black.png) + +捞月狗成立于2012年,由知名魔兽视频作者痞子狼创立。 +超过7000万玩家的社交&内容&数据软件。 +覆盖了海外众多的游戏玩家。 +经过数年发展,捞月狗已经成为全球游戏玩家最大的社区。 +官网: + +## 技术支撑: + +随着业务快速的发展 为了后续的迭代以及代码的解耦 介绍开发成本 我们采用swoole作为我们的技术支撑,将现有的架构慢慢转成微服务 降低复杂性 减少出错。 + + +------------ + +* PHP版本我们采用7.1.0 +* yaf 3.0 +* swoole 1.10 +* hprose 2.0 + + +捞月狗社区采用php的yaf内存框架开发 在yaf上面封装了一层 基本目录结构为mvc 开发不需要在意yaf 内置信息 以及学习yaf框架 + +hprose-swoole 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大 我们使用它来组建我们的TCP协议 + +swoole 作为应用服务器 使用 swoole 创建 http_server 性能也满稳定 +使用swoole 中的task 作为异步调用 耗时接口处理 节省了许多事情 做了很多PHP无法去做的事情 + +附上yaf+swoole封装的地址: https://github.com/zoooozz/ultraman 简称奥特曼 + +效果截图 + +![](https://wiki.swoole.com/static/uploads/wiki/201802/28/143910625661.jpg) + + + + + + diff --git "a/doc/1.13 - \346\217\220\344\272\244\351\224\231\350\257\257\346\212\245\345\221\212.md" "b/doc/1.13 - \346\217\220\344\272\244\351\224\231\350\257\257\346\212\245\345\221\212.md" new file mode 100644 index 0000000..e5354ec --- /dev/null +++ "b/doc/1.13 - \346\217\220\344\272\244\351\224\231\350\257\257\346\212\245\345\221\212.md" @@ -0,0 +1,97 @@ +#提交错误报告 + +当使用`swoole`发生段错误时,请及时向开发组报告。可以使用`gdb`工具来得到一份`bt`信息。使用`gdb`跟踪需要在编译`swoole`时增加`--enable-debug`参数。 + +> 如果不方便`gdb`,也可以提供一份可稳定复现的`demo`程序 + +打开core dump +```shell +ulimit -c unlimited +``` + +使用`gdb`来查看`core dump`信息。`core`文件一般在当前目录,如果操作系统做了处理,将`core dump`文件放置到其他目录,请替换为相应的路径 +``` +gdb php core +gdb php /tmp/core.4596 +``` + +在gdb下输入bt查看调用栈信息 +``` +(gdb)bt +Program terminated with signal 11, Segmentation fault. +#0 0x00007f1cdbe205e0 in swServer_onTimer (reactor=, event=...) + at /usr/local/php/swoole-swoole-1.5.9b/src/network/Server.c:92 +92 serv->onTimer(serv, timer_node->interval); +Missing separate debuginfos, use: debuginfo-install php-cli-5.3.3-22.el6.x86_64 +``` + +在gdb中使用f指令查看代码段 +``` +(gdb)f 1 +(gdb)f 0 +``` + +如果没有函数调用栈信息,可能是编译去除了debug信息。请手工修改swoole源码目录下的`Makefile`文件,修改CFLAGS为 +```shell +CFLAGS = -Wall -pthread -g -O0 +``` + +内存检测 +----- +除了使用`gdb`分析之外可以使用`valgrind`工具检测程序是否正常运行。 + +```shell +USE_ZEND_ALLOC=0 valgrind php your_file.php +``` + +* 程序逻辑覆盖后执行`ctrl+c`中断,将屏幕打印的信息复制到文件中 + +``` +注意:有些情况下在虚拟机下共享目录core文件无法生成,会生成一个0字节的文件,请将core文件生成目录改到/var/log/core下。 +``` +提交问题 +----- +请将上面的得到的信息,连同机器信息,包括`php -v` `gcc -v` `uname -a` 提交到 [Github Issues页面](https://github.com/swoole/swoole-src/issues/new) 或者发送邮件到 。 + +若确定是`Swoole`底层的问题,开发组会快速解决。 + +反馈建议 +---- +为了减少`Swoole`内核开发者与反馈者之间的沟通成本,请认真阅读以下内容,在`GitHub`平台尽可能地按照`Issue`模板提交问题。 + +* 请提供发生问题时使用的`php`、`swoole`、操作系统、`gcc`和`openssl`(可选)版本信息 +* 请描述具体是什么情况下发生,尽可能地给出可稳定重现的**代码**和**测试过程** +* 请使用`valgrind`、`gdb`、`strace`等工具进行初步地问题跟踪,并贴出相关信息和线索 +* 请认真查看`php`错误日志、`swoole`的`log_file`、操作系统的`syslog`等日志信息,找到可能与该问题关联的信息和线索 + +获取版本信息 +---- +#### php +```shell +php -v +``` + +#### swoole +```shell +php --ri swoole +``` + +#### 操作系统类型 +如`Linux`、`MacOS`、`FreeBSD`、`CygWin`、`树莓派`等 + +#### 内核版本 +```shell +uname -a +``` + +#### gcc +```shell +cc -v +``` + +#### openssl +```shell +openssl version +``` + + diff --git "a/doc/1.14 - \345\270\270\350\247\201\351\227\256\351\242\230.md" "b/doc/1.14 - \345\270\270\350\247\201\351\227\256\351\242\230.md" new file mode 100644 index 0000000..5fe58a0 --- /dev/null +++ "b/doc/1.14 - \345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -0,0 +1,15 @@ +#常见问题 + +swoole与phpdaemon/react有何不同 +--- +swoole是完全使用C语言编写,多线程epoll,作为PHP扩展运行的。 +phpdaemon/react都是基于libevent扩展使用php开发,以脚本方式执行。 +swoole中提供的多线程Reactor,异步MySQL,毫秒定时器,异步文件读写、异步DNS查询,在PHP生态圈中是独一无二的。 + +Linux内核问题 +---- +swoole建议使用Linux2.6.32+,低于此版本的系统有很多特性会不支持。swoole会启用兼容的代码来实现特性,性能较差,而且缺少维护。可能会产生问题,仅供开发使用。 + +Swoole的性能如何 +---- +swoole使用C语言开发,性能接近nginx。这里有一个echo server的测试。可以作为参考: \ No newline at end of file diff --git "a/doc/1.14.1 - \345\215\207\347\272\247swoole\347\211\210\346\234\254\347\232\204\345\270\270\350\247\201\351\227\256\351\242\230.md" "b/doc/1.14.1 - \345\215\207\347\272\247swoole\347\211\210\346\234\254\347\232\204\345\270\270\350\247\201\351\227\256\351\242\230.md" new file mode 100644 index 0000000..904da66 --- /dev/null +++ "b/doc/1.14.1 - \345\215\207\347\272\247swoole\347\211\210\346\234\254\347\232\204\345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -0,0 +1,12 @@ +#升级swoole版本的常见问题 + +可以使用pecl进行安装和升级 +```shell +pecl upgrade swoole +``` + +也可以直接从github/pecl下载一个新版本,重新安装编译。 + +* 更新swoole版本,不需要卸载或者删除旧版本swoole,安装过程会覆盖旧版本 +* swoole编译安装后没有额外的文件,仅有一个swoole.so,如果是在其他机器编译好的二进制版本。直接互相覆盖swoole.so,即可实现版本切换 +* git clone拉取的代码,执行git pull更新代码后,务必要再次执行phpize、./configure、make clean、make install \ No newline at end of file diff --git a/doc/1.14.10 - my_global.h: No such file or directory.md b/doc/1.14.10 - my_global.h: No such file or directory.md new file mode 100644 index 0000000..5d96efb --- /dev/null +++ b/doc/1.14.10 - my_global.h: No such file or directory.md @@ -0,0 +1,16 @@ +#my_global.h: No such file or directory + +PHP缺少mysqln,请检查php编译参数。 + +```shell +php -i | grep configure +``` +或者查看phpinfo页面中的configure项 + + +编译PHP时,./configure参数中务必要加入 + +```shell + --enable-mysqlnd --with-mysqli +``` + diff --git a/doc/1.14.11 - undefined symbol: __sync_bool_compare_and_swap_4.md b/doc/1.14.11 - undefined symbol: __sync_bool_compare_and_swap_4.md new file mode 100644 index 0000000..4080dd9 --- /dev/null +++ b/doc/1.14.11 - undefined symbol: __sync_bool_compare_and_swap_4.md @@ -0,0 +1,15 @@ +#undefined symbol: __sync_bool_compare_and_swap_4 + +运行swoole程序时出现此错误,说明操作系统gcc版本过低,请升级gcc至4.4以上版本。 + +``` +/usr/local/php56/bin/php: symbol lookup error: /usr/local/php56/lib/php/extensions/no-debug-non-zts-20131226/swoole.so: undefined symbol: __sync_bool_compare_and_swap_4 +``` + +然后重新编译安装swoole +```shell +phpize +./configure +make clean +make install +``` \ No newline at end of file diff --git "a/doc/1.14.12 - \345\255\246\344\271\240Swoole\351\234\200\350\246\201\346\216\214\346\217\241\345\223\252\344\272\233\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/doc/1.14.12 - \345\255\246\344\271\240Swoole\351\234\200\350\246\201\346\216\214\346\217\241\345\223\252\344\272\233\345\237\272\347\241\200\347\237\245\350\257\206.md" new file mode 100644 index 0000000..be70a9a --- /dev/null +++ "b/doc/1.14.12 - \345\255\246\344\271\240Swoole\351\234\200\350\246\201\346\216\214\346\217\241\345\223\252\344\272\233\345\237\272\347\241\200\347\237\245\350\257\206.md" @@ -0,0 +1,31 @@ +#学习Swoole需要掌握哪些基础知识 + +多进程/多线程 +---- +* 了解Linux操作系统进程和线程的概念 +* 了解Linux进程/线程切换调度的基本知识 +* 了解进程间通信的基本知识,如管道、UnixSocket、消息队列、共享内存 + +SOCKET +----- +* 了解SOCKET的基本操作如accept/connect、send/recv、close、listen、bind +* 了解SOCKET的接收缓存区、发送缓存区、阻塞/非阻塞、超时等概念 + +IO复用 +---- +* 了解select/poll/epoll +* 了解基于select/epoll实现的事件循环,Reactor模型 +* 了解可读事件、可写事件 + +TCP/IP网络协议 +---- +* 了解TCP/IP协议 +* 了解TCP、UDP传输协议 + +调试工具 +---- +* 使用 [gdb](https://wiki.swoole.com/wiki/page/p-gdb.html) 调试`Linux`程序 +* 使用 [strace](https://wiki.swoole.com/wiki/page/p-strace.html) 跟踪进程的系统调用 +* 使用 [tcpdump](https://wiki.swoole.com/wiki/page/p-tcpdump.html) 跟踪网络通信过程 +* 其他`Linux`系统工具,如ps、[lsof](https://wiki.swoole.com/wiki/page/p-lsof.html)、top、vmstat、netstat、sar、ss等 + diff --git "a/doc/1.14.13 - \345\220\214\346\255\245\351\230\273\345\241\236\344\270\216\345\274\202\346\255\245\351\235\236\351\230\273\345\241\236\351\200\202\347\224\250\345\234\272\346\231\257.md" "b/doc/1.14.13 - \345\220\214\346\255\245\351\230\273\345\241\236\344\270\216\345\274\202\346\255\245\351\235\236\351\230\273\345\241\236\351\200\202\347\224\250\345\234\272\346\231\257.md" new file mode 100644 index 0000000..4ca06e4 --- /dev/null +++ "b/doc/1.14.13 - \345\220\214\346\255\245\351\230\273\345\241\236\344\270\216\345\274\202\346\255\245\351\235\236\351\230\273\345\241\236\351\200\202\347\224\250\345\234\272\346\231\257.md" @@ -0,0 +1,13 @@ +#同步阻塞与异步非阻塞适用场景 + +异步的优势 +----- +* 高并发,同步阻塞IO模型的并发能力依赖于进程/线程数量,例如 `php-fpm`开启了200个进程,理论上最大支持的并发能力为200。如果每个请求平均需要100ms,那么应用程序就可以提供2000qps。异步非阻塞的并发能力几乎是无限的,可以发起或维持大量并发TCP连接 +* 无IO等待,同步模型无法解决`IOWait`很高的场景,如上述例子每个请求平均要10s,那么应用程序就只能提供20qps了。而异步程序不存在IO等待,所以无论请求要花费多长时间,对整个程序的处理能力没有任何影响 + +同步的优势 +---- +* 编码简单,同步模式编写/调试程序更轻松 +* 可控性好,同步模式的程序具有良好的过载保护机制,如在下面的情况异步程序就会出问题 +* Accept保护,同步模式下一个TCP服务器最大能接受 `进程数+Backlog` 个TCP连接。一旦超过此数量,Server将无法再接受连接,客户端会连接失败。避免服务器Accept太多连接,导致请求堆积 + diff --git "a/doc/1.14.14 - PHP7\347\216\257\345\242\203\344\270\213\345\207\272\347\216\260zend_mm_heap corrupted.md" "b/doc/1.14.14 - PHP7\347\216\257\345\242\203\344\270\213\345\207\272\347\216\260zend_mm_heap corrupted.md" new file mode 100644 index 0000000..fd86a0d --- /dev/null +++ "b/doc/1.14.14 - PHP7\347\216\257\345\242\203\344\270\213\345\207\272\347\216\260zend_mm_heap corrupted.md" @@ -0,0 +1,64 @@ +#PHP7环境下出现zend_mm_heap corrupted + +PHP7+Swoole开启`opcache`,运行时出现`zend_mm_heap corrupted`。这个问题的主要原因是PHP7增加了一个优化项,如果PHP代码中一个数组只声明一次,并且没有对数据进行修改操作。PHP7会将此数组转为`immutable`类型,此数组仅作为只读。 + +PHP7的解析器只能识别PHP程序中的数组操作行为,但是扩展层对数组的修改无法识别。而Swoole的`Server->set`等方法可能会修改传入的数组,导致出现内存错误。 + +Immutable数组 +--- +```php +$array = array( + 'worker_num' => 1, + 'log_file' => 'swoole.log', +); +``` + +非Immutable数组 +---- +```php +$array = array( + 'worker_num' => 1, + 'log_file' => 'swoole.log', +); +//有修改行为,PHP7不会优化此数组为只读 +$array['daemonize'] = true; +``` + +未启用opcache时,Immutable数组即使被修改了,只要PHP代码中没有再操作此数据则不会出现内存错误。一旦开启opcache,Immutable数组会被转存到`SharedMemory`并进行持久化。这时Swoole修改此数组会使ZendVM发生内存错误,抛出`zend_mm_heap corrupted`错误。 + +```c +zval *zsetting = sw_zend_read_property(swoole_server_class_entry_ptr, getThis(), ZEND_STRL("setting"), 1); +if (zsetting == NULL || ZVAL_IS_NULL(zsetting)) +{ + SW_MAKE_STD_ZVAL(zsetting); + array_init(zsetting); + zend_update_property(swoole_server_class_entry_ptr, getThis(), ZEND_STRL("setting"), zsetting); +} + +add_assoc_bool(zsetting, "open_http_protocol", 1); +add_assoc_bool(zsetting, "open_mqtt_protocol", 0); +add_assoc_bool(zsetting, "open_eof_check", 0); +add_assoc_bool(zsetting, "open_length_check", 0); +``` +底层修复 +---- +1.9.2版本增加了一个`php_swoole_array_separate`的宏,用于将`Immutable数组`分离并重新构建一个`非Immutable数组`,底层就可以修改这个数组的值了。实现代码: +```c +#define php_swoole_array_separate(arr) zval *_new_##arr;\ + SW_MAKE_STD_ZVAL(_new_##arr);\ + array_init(_new_##arr);\ + sw_php_array_merge(Z_ARRVAL_P(_new_##arr), Z_ARRVAL_P(arr));\ + arr = _new_##arr; +``` +在C扩展中如果需要修改PHP代码传入的数组,必须调用`php_swoole_array_separate`将数组分离。 +```c +php_swoole_array_separate(zset); +``` + +解决办法 +---- +升级到最新版本的swoole,或者关闭`opcache`扩展,可修改`php.ini`加入配置项: + +```shell +opcache.enable_cli = off +``` diff --git "a/doc/1.14.15 - swoole\351\241\271\347\233\256\350\265\267\346\272\220\345\222\214\345\220\215\345\255\227\347\224\261\346\235\245.md" "b/doc/1.14.15 - swoole\351\241\271\347\233\256\350\265\267\346\272\220\345\222\214\345\220\215\345\255\227\347\224\261\346\235\245.md" new file mode 100644 index 0000000..1f5ffb9 --- /dev/null +++ "b/doc/1.14.15 - swoole\351\241\271\347\233\256\350\265\267\346\272\220\345\222\214\345\220\215\345\255\227\347\224\261\346\235\245.md" @@ -0,0 +1,27 @@ +#swoole项目起源和名字由来 + +项目起源 +---- +Swoole 项目最初的想法是来自于之前所做的一个企业软件项目。当时大概是2010年底,公司产品有一个需求是用户可以任意生成一个 email 地址,然后其他用户可以向这个email发邮件,后台能实时将邮件内容解析成数据,并主动通知用户。当时项目使用PHP开发的,在实现这个需求时遇到了难题,PHP只能依赖其他的STMP服务器,通过pop3协议定时查收新邮件来完成,这样就不是实时的。如果要实现的实时系统必须自己写一个TCP Socket Server实现SMTP协议接收数据。当时PHP在这个领域几乎是空白,没有一套成熟的网络通信框架。为了实现需求,我从socket学起到TCP/IP、IO复用、libevent、多进程,最后终于实现了这套程序。做完这个项目后我就想把这套程序开源出来,希望能帮助其他PHPer解决在这个领域的难题。如果能有这样一个框架,那么PHP就能从单纯地做一个Web网站延伸到更大的空间。 + +性能问题 +--- +还有一个重要的原因是PHP程序的性能问题,我最早是学Java出身的,工作后才转行成为一名PHP程序员。在使用PHP开发程序的过程中,我一直在思考的问题 PHP 和 Java 比最大的优势是什么?简单高效, PHP 在请求完成之后会释放所有资源和内存,无须担心内存泄漏。代码的质量无论高低一样运行的很流畅。但同时这也是 PHP 致命的缺点。一旦请求数量上升,并发很高的时候,快速创建资源,又马上释放,使得 PHP 程序运行效率急剧下降。另外一旦项目的功能的越来越复杂,代码增多后,对于 PHP 也会是灾难。这也是 PHP 的框架为什么没有被 PHP 程序员广泛接受,而 Java 不存在这个问题。再好的框架也会被这种低效的方式拖累,导致系统变慢。所以想到了使用 PHP 来开发 PHP 的应用服务器,让 PHP 的代码加载到内存后,拥有更长的生命周期,这样建立的数据库连接和其他大的对象,不被释放。每次请求只需要处理很少的代码,而这些代码只在第一次运行时,被 PHP 解析器编译,驻留内存。另外,之前 PHP 不能实现的,对象持久化、数据库连接池,缓存连接池都可以实现。系统的运行效率会大大提高。 + +经过一段时间研究,目前已经初步得到实现。使用 PHP 本身编写出 HTTP 服务器,以独立服务器方式运行,单个程序页面 ( 有对象生成,数据库连接、 smarty 模板操作 ) 的执行时间由原来的 0.0x 秒,下降到 0.00x 秒。使用 Apache AB 并发 100 测试。比传统 LAMP 方式, Request per Second 高出至少 10 倍。在我的测试机上 (Ubuntu10.04 Inter Core E5300 + 2G 内存 ) , Apache 只跑到 83RPS 。 Swoole Server 可以跑到 1150 多 RPS。 + +这个项目就是Swoole的雏形。这个版本一直持续维护了2年多,在这个过程中逐步有了一些经验积累,对这套技术方案的存在问题有了更深入的理解,比如性能差、限制较多无法直接调用操作系统接口、内存管理效率低下。 + +入职腾讯 +---- +2011年底我入职腾讯,负责朋友网的PHP平台开发工作。惊奇地发现朋友网的同事不光这样想了,他们直接做到了。朋友网团队已经在生产环境中使用了这套方案。朋友网有三架马车,第一个是PWS,这是一个纯PHP编写的WebServer,朋友网线上有600多台服务器运行在PWS上,完全没有使用Apache、PHP-FPM之类的程序。第二个是SAPS,这是使用纯PHP开发的一个分布式队列,当时大概由150台服务器的集群在跑,很多图片裁剪、头像处理、消息同时、数据同步等逻辑全部使用了SAPS做逻辑异步化。第三个是PSF,这是一个PHP实现的Server框架,朋友网很多逻辑层的服务器都是基于PSF实现的。大概有300台左右的集群在运行PSF服务器程序。在朋友网的这段时间,我学到了很多Linux底层、网络通信的知识,积累了很多大型集群高并发环境的网络通信跟踪、调试经验,为开发Swoole打下了一个很好的基础。 + +开发Swoole +---- +在这期间也学习了解到了Node.js、Golang这些优秀的技术方案,得到了更多灵感。在2012年的时候就有了新的想法,决定使用C语言重新实现一个性能更强、功能更强大的版本。这就是现在的Swoole扩展。 + +现在Swoole已经被很多PHP技术团队用于实际项目的开发工作,国内国外都有。国内知名的有百度订单中心、百度地图、腾讯QQ公众号和企业QQ、战旗直播、360、当当网、穷游等。另外还有很多物联网、硬件、游戏项目也在使用Swoole 。另外基于Swoole的开源框架也越来越多,比如TSF、Blink、swPromise 等等,在Github上也能找到很多Swoole相关的项目和代码。 + +名字由来 +---- +Swoole这个名字不是一个英文单词,是由我创造的一个音近字。我最早想到的名字是叫做`sword-server`,寓意是为广大PHPer创造一把锋利的剑,后来联想到google也是凭空创造出来的,所以我就给它命名为`swoole`。 diff --git "a/doc/1.14.2 - \347\224\237\346\210\220\345\217\257\345\210\206\345\217\221\347\232\204\344\272\214\350\277\233\345\210\266swoole\347\211\210\346\234\254.md" "b/doc/1.14.2 - \347\224\237\346\210\220\345\217\257\345\210\206\345\217\221\347\232\204\344\272\214\350\277\233\345\210\266swoole\347\211\210\346\234\254.md" new file mode 100644 index 0000000..d0fa3fa --- /dev/null +++ "b/doc/1.14.2 - \347\224\237\346\210\220\345\217\257\345\210\206\345\217\221\347\232\204\344\272\214\350\277\233\345\210\266swoole\347\211\210\346\234\254.md" @@ -0,0 +1,8 @@ +#生成可分发的二进制swoole版本 + +swoole编译后会生成一个`swoole.so`动态连接库。如果服务器的`Linux`内核、`glibc`、`PHP`版本相同,就可以直接使用二进制版本,而不需要在当前机器上重新编译。 + +所以管理一个机器集群的swoole,可以在单独的一台母机上进行编译,生成`swoole.so`。其他的集群机器只需要分发`swoole.so`即可。 + +也可以将`swoole.so`制作成`rpm/deb`等安装包,使用操作系统的包管理工具,如 yum, rpm, apt-get, dpkg 等工具进行swoole软件的分发,安装,卸载。 + diff --git "a/doc/1.14.3 - \345\234\250phpinfo\344\270\255\346\234\211\345\234\250php-m\344\270\255\346\262\241\346\234\211.md" "b/doc/1.14.3 - \345\234\250phpinfo\344\270\255\346\234\211\345\234\250php-m\344\270\255\346\262\241\346\234\211.md" new file mode 100644 index 0000000..844121e --- /dev/null +++ "b/doc/1.14.3 - \345\234\250phpinfo\344\270\255\346\234\211\345\234\250php-m\344\270\255\346\262\241\346\234\211.md" @@ -0,0 +1,19 @@ +#在phpinfo中有在php-m中没有 + +编译安装完swoole后,在php-fpm/apache的phpinfo页面中有,在命令行的php -m中没有。原因可能是cli/php-fpm/apache使用不同的php.ini配置 + +一、确认php.ini的位置 +------ +cli命令行下 +```shell +php -i|grep php.ini +``` + +php-fpm/apache,查看`phpinfo页面`找到php.ini的绝对路径。 + +二、查看对应php.ini是否有extension=swoole.so +------ +```shell +cat /usr/local/lib/php.ini | grep swoole.so +``` + diff --git "a/doc/1.14.4 - Connection refused\346\230\257\346\200\216\344\271\210\345\233\236\344\272\213.md" "b/doc/1.14.4 - Connection refused\346\230\257\346\200\216\344\271\210\345\233\236\344\272\213.md" new file mode 100644 index 0000000..657e4d7 --- /dev/null +++ "b/doc/1.14.4 - Connection refused\346\230\257\346\200\216\344\271\210\345\233\236\344\272\213.md" @@ -0,0 +1,8 @@ +#Connection refused是怎么回事 + +telnet 127.0.0.1 9501 时发生Connection refused,这表示服务器未监听此端口。 + +* 检查程序是否执行成功: ps aux +* 检查端口是否在监听: netstat -lp +* 查看网络通信通信过程是否正常: tcpdump traceroute + diff --git a/doc/1.14.5 - Resource temporarily unavailable [11].md b/doc/1.14.5 - Resource temporarily unavailable [11].md new file mode 100644 index 0000000..b3513ed --- /dev/null +++ b/doc/1.14.5 - Resource temporarily unavailable [11].md @@ -0,0 +1,12 @@ +#Resource temporarily unavailable [11] + +客户端swoole_client在recv时报 +```shell +swoole_client::recv(): recv() failed. Error: Resource temporarily unavailable [11] +``` + +这个错误表示,服务器端在规定的时间内没有返回数据,接收超时了。 + +* 可以通过tcpdump查看网络通信过程,检查服务器是否发送了数据 +* 服务器的$serv->send函数需要检测是否返回了true +* 外网通信时,耗时较多需要调大swoole_client的超时时间 \ No newline at end of file diff --git a/doc/1.14.6 - Cannot assign requested address [99].md b/doc/1.14.6 - Cannot assign requested address [99].md new file mode 100644 index 0000000..daa479e --- /dev/null +++ b/doc/1.14.6 - Cannot assign requested address [99].md @@ -0,0 +1,21 @@ +#Cannot assign requested address [99] + +客户端连接时出现 +``` +Error: Cannot assign requested address [99] +``` + +此错误是指无法分配本地端口。每一个socket客户端,系统都要分配一个本地端口。当一台机器存在大量客户端socket时,本地端口可能会不够用,这时再发起网络请求就会报 #99 错误。 + +相关内核参数是: +``` +net.ipv4.ip_local_port_range = 20000 65000 +``` + +启用快速回收 +----------- +快速回收可以加速local port的回收,在短连接的服务中需要开启此参数 + +``` +net.ipv4.tcp_tw_recycle = 1 +``` \ No newline at end of file diff --git "a/doc/1.14.7 - swoole\344\270\216node.js\347\233\270\346\257\224\346\234\211\345\223\252\344\272\233\344\274\230\345\212\277.md" "b/doc/1.14.7 - swoole\344\270\216node.js\347\233\270\346\257\224\346\234\211\345\223\252\344\272\233\344\274\230\345\212\277.md" new file mode 100644 index 0000000..30c5ad3 --- /dev/null +++ "b/doc/1.14.7 - swoole\344\270\216node.js\347\233\270\346\257\224\346\234\211\345\223\252\344\272\233\344\274\230\345\212\277.md" @@ -0,0 +1,31 @@ +#swoole与node.js相比有哪些优势 + +CPU多核的利用 +---- +* node.js没有内置对多线程/多进程的支持,用户必须使用cluster/child_process等扩展自行实现并行 +* swoole内置对多线程/多进程的支持,用户仅需配置参数即可 + +> 对于熟悉并行编程的程序员使用node.js cluster/child_process可以解决问题。但毕竟不是官方提供的,难免会产生BUG,需要开发者自己负责 +> 对于不熟悉并行编程的程序员,并行会变得困难。很多技术人员采用了启动多个程序实例来解决此问题。 + +同步阻塞的支持 +----- +* swoole同时支持同步/异步2种模式 +* node.js仅支持异步 + +为什么强调同步阻塞模式的支持。多进程同步阻塞模式是Unix世界40多年历史中最成熟的一种编程模式。配套的调试工具非常丰富完善,稳定性、成熟度、调度公平性、开发调试效率都是最佳的。多线程、异步回调、协程等模式编程虽然可以带来一定的性能提升,但复杂度过高,开发调试困难。 + +__业务逻辑很重的程序,最佳的方式仍然是多进程同步阻塞。__ + +> 协程本质上也是一种异步IO,无法利用现有的工具如strace,gdb进行调试 +> swoole中对于复杂业务逻辑,推荐使用同步阻塞 + +自动协议的支持 +---- +* node.js没有内置通用协议处理的支持,需要自行实现代码 +* swoole内置了通用协议处理的支持,可以借助swoole提供的功能轻松实现 + +TCP心跳检测 +--- +* swoole内置了对TCP心跳检测的支持 + diff --git "a/doc/1.14.8 - swoole\344\270\216golang\347\233\270\346\257\224\346\234\211\345\223\252\344\272\233\344\274\230\345\212\277.md" "b/doc/1.14.8 - swoole\344\270\216golang\347\233\270\346\257\224\346\234\211\345\223\252\344\272\233\344\274\230\345\212\277.md" new file mode 100644 index 0000000..815a0f0 --- /dev/null +++ "b/doc/1.14.8 - swoole\344\270\216golang\347\233\270\346\257\224\346\234\211\345\223\252\344\272\233\344\274\230\345\212\277.md" @@ -0,0 +1,26 @@ +#swoole与golang相比有哪些优势 + +开发效率 +---- +* go语言是本质上是静态语言,开发效率不高,更适合底层软件的开发 +* swoole基于php语言,而php是动态脚本语言,开发效率最佳,更适合应用软件的开发 + +IO模型 +---- +* go语言使用单线程eventloop处理IO事件,多线程实现协程调度,执行用户层代码 +* swoole使用多线程eventloop处理IO事件,多进程执行用户层php代码 + +> Go对与IO事件的处理是单线程的,无法利用多核,吞吐量稍弱于swoole +> 在实际的TCP/UDP 密集IO压测中,swoole表现要优于go + +Go协程(goroutine)是运行在多线程上的,线程可以共享堆栈和文件描述符,在实现连接池、并发库方面更有优势。额外的带来的一个问题是,存在数据同步问题,需要用户自行考虑加锁。 + +Swoole的用户代码运行在多进程环境,无需考虑加锁问题。但无法直接访问内存和资源,需要借助task进程实现中转。 + +语言性能 +---- +* go语言是静态编译的,语言本身的性能大大超过php,密集计算更有优势 +* php是动态解释执行的,语言性能较差,不适合密集计算程序 + +> 将密集计算、大量调用的代码使用C扩展实现可以解决性能问题 +> PHP7将增加JIT支持,在密集计算方面会有很大改善 \ No newline at end of file diff --git a/doc/1.14.9 - pcre.h: No such file or directory.md b/doc/1.14.9 - pcre.h: No such file or directory.md new file mode 100644 index 0000000..6bb45e3 --- /dev/null +++ b/doc/1.14.9 - pcre.h: No such file or directory.md @@ -0,0 +1,23 @@ +#pcre.h: No such file or directory + +编译swoole扩展出现 +``` +fatal error: pcre.h: No such file or directory +``` + +原因是缺少pcre,需要安装libpcre + +ubuntu/debian: +----- +```shell +apt-get install libpcre3 libpcre3-dev +``` +centos/redhat: +---- +```shell +yum install pcre-devel +``` + +其他Linux: +---- +到[PCRE官方网站](http://www.pcre.org/)下载源码包,编译安装pcre库。 diff --git "a/doc/1.2 - \347\274\226\350\257\221\345\256\211\350\243\205.md" "b/doc/1.2 - \347\274\226\350\257\221\345\256\211\350\243\205.md" new file mode 100644 index 0000000..cae68c5 --- /dev/null +++ "b/doc/1.2 - \347\274\226\350\257\221\345\256\211\350\243\205.md" @@ -0,0 +1,51 @@ +#编译安装 + + `Swoole`扩展是按照`PHP`标准扩展构建的。使用`phpize`来生成编译检测脚本,`./configure`来做编译配置检测,`make`进行编译,`make install`进行安装。 + +* 请下载`releases`版本的`swoole`,直接从`github`主干上拉取最新代码可能会编译不过 +* 如果当前用户不是`root`,可能没有`PHP`安装目录的写权限,安装时需要`sudo`或者`su` +* 如果是在`git`分支上直接`git pull`更新代码,重新编译前务必要执行`make clean` + +安装准备 +---- +安装swoole前必须保证系统已经安装了下列软件 +``` +php-5.3.10 或更高版本 +gcc-4.4 或更高版本 +make +autoconf +pcre (centos系统可以执行命令:yum install pcre-devel) +``` + +下载地址 +---- +* +* +* + +下载源代码包后,在终端进入源码目录,执行下面的命令进行编译和安装 + +```shell +cd swoole +phpize +./configure +make +sudo make install +``` + +PECL +---- +swoole项目已收录到PHP官方扩展库,除了手工下载编译外,还可以通过PHP官方提供的pecl命令,一键下载安装swoole +```shell +pecl install swoole +``` + +配置php.ini +---- +编译安装成功后,修改php.ini加入 +```shell +extension=swoole.so +``` +通过`php -m`或`phpinfo()`来查看是否成功加载了swoole,如果没有可能是`php.ini`的路径不对,可以使用`php --ini`来定位到`php.ini`的绝对路径。 + + diff --git "a/doc/1.2.1 - \347\274\226\350\257\221\345\217\202\346\225\260.md" "b/doc/1.2.1 - \347\274\226\350\257\221\345\217\202\346\225\260.md" new file mode 100644 index 0000000..2e76793 --- /dev/null +++ "b/doc/1.2.1 - \347\274\226\350\257\221\345\217\202\346\225\260.md" @@ -0,0 +1,66 @@ +#编译参数 + +这里是`./configure`编译配置的额外参数,用于开启某些特性 + +> `1.8.7`或更高版本不再需要设置`--enable-async-mysql`和`--enable-async-httpclient`,`async_mysql`和`async_httpclient`改为内置 + +--enable-swoole-debug +---- +打开调试日志,开启此选项后swoole将打印各类细节的调试日志。**生产环境不要启用**。 + +--enable-sockets +---- +增加对`sockets`资源的支持。开启此参数,`swoole_event_add`就可以添加`sockets`扩展创建的连接到`swoole`的事件循环中。另外`Server`和`Client`的`getSocket()`方法也需要依赖此编译参数。 +>依赖`sockets`扩展 + +--enable-openssl +---- +启用`SSL`支持 +>使用操作系统提供的`libssl.so`动态连接库 + +--with-openssl-dir +---- +指定`openssl`库的路径。`--with-openssl-dir=/opt/openssl/` + +--enable-http2 +---- +增加对HTTP2的支持 +>依赖`nghttp2`库 + +--enable-async-redis +---- +增加异步Redis客户端支持 +>依赖`hiredis`库 + +--enable-timewheel +---- +启用时间轮算法,优化心跳检测性能 +> 此设置为试验性质 + +--enable-mysqlnd +---- +启用`mysqlnd`支持,启用`swoole_mysql::escapse`方法。启用此参数后,`PHP`必须有`mysqlnd`模块,否则会导致`swoole`无法运行。 +>依赖`mysqlnd`模块 + +--enable-ringbuffer +---- +开启`RingBuffer`内存池 +> 此设置为试验性质,主要用于提升性能,生产环境请不要开启 + +--enable-swoole-static +---- +静态编译到`PHP`内核中,非静态编译(`swoole.so`)请勿使用此参数。 + +--enable-coroutine +---- +启用协程 + +--enable-coroutine-postgresql +---- +启用协程Postgresql客户端 +>依赖libpq库 + +--with-libpq-dir +---- +指定`libpq`库的路径. `--with-libpq-dir=/etc/postgresql` + diff --git "a/doc/1.2.2 - \345\270\270\350\247\201\351\224\231\350\257\257.md" "b/doc/1.2.2 - \345\270\270\350\247\201\351\224\231\350\257\257.md" new file mode 100644 index 0000000..844a0b0 --- /dev/null +++ "b/doc/1.2.2 - \345\270\270\350\247\201\351\224\231\350\257\257.md" @@ -0,0 +1,65 @@ +#常见错误 + +make或make install无法执行或编译错误 +---- + +NOTICE: PHP message: PHP Warning: PHP Startup: swoole: Unable to initialize module +Module compiled with module API=20090626 +PHP compiled with module API=20121212 +These options need to match + in Unknown on line 0 + +php版本和编译时使用的phpize和php-config不对应,需要使用绝对路径来进行编译。使用绝对路径执行PHP。 +```shell +/usr/local/php-5.4.17/bin/phpize +./configure --with-php-config=/usr/local/php-5.4.17/bin/php-config +/usr/local/php-5.4.17/bin/php server.php +``` + +缺少mysql头文件 +---- +```shell +php_mysqli_structs.h:64:23: fatal error: my_global.h: No such file or directory +``` +没有找到`mysqlclient`的头文件,需要安装`mysqlclient-dev` + +> 建议自行编译php,不要使用Linux包管理系统自带的php版本 + +缺少pcre.h头文件 +---- +```shell +fatal error: pcre.h: No such file or directory +``` +原因是缺少pcre,[需要安装libpcre](/wiki/page/312.html) + +Cannot find autoconf +---- +phpize命令需要`autoconf`工具,请先安装它。 + +make install失败 +---- +make install需要root权限,如果不是以root用户登录的,请用sudo或su,再进行安装。 + +修改了php.ini后,php -m或phpinfo中没有swoole +---- +```shell +php -i|grep php.ini +``` +查看加载的php.ini路径,确认加载了正确的php.ini。 + +修改php.ini,打开错误显示,查看是否存在启动时错误。 +``` +display_errors => On +display_startup_errors => On +``` +缺少hiredis.h +---- +编译配置时启用`--enable-async-redis`,但没有安装`hiredis`库,编译时会报`fatal error: 'hiredis/hiredis.h' file not found`,请安装`hiredis`库或者去掉`--enable-async-redis`选项。 + +error: too many arguments to function 'zend_exception_error' +---- +你的PHP版本低于PHP-5.3.10,请升级PHP版本。 + +如果还是编译失败了怎么办? +---- +不要气馁,加入我们的 [Swoole官方QQ群](/wiki/page/69.html#entry_h2_1),你的问题会在24小时内被解决。 \ No newline at end of file diff --git "a/doc/1.3 - \345\277\253\351\200\237\350\265\267\346\255\245.md" "b/doc/1.3 - \345\277\253\351\200\237\350\265\267\346\255\245.md" new file mode 100644 index 0000000..05406ed --- /dev/null +++ "b/doc/1.3 - \345\277\253\351\200\237\350\265\267\346\255\245.md" @@ -0,0 +1,18 @@ +#快速起步 + +Swoole的绝大部分功能只能用于cli命令行环境,请首先准备好Linux Shell环境。可使用`vim`、`emacs`、`phpstorm`或其他编辑器编写代码,并在命令行中通过下列指令执行程序。 + +```shell +php /path/to/your_file.php +``` + +成功执行`Swoole`服务器程序后,如果你的代码中没有任何`echo`语句,屏幕不会有任何输出,但实际上底层已经在监听网络端口,等待客户端发起连接。可使用相应的客户端工具和程序连接到服务器,进行测试。 + +进程管理 +--- +* 默认使用`SWOOLE_PROCESS`模式,因此会额外创建`Master`和`Manager`两个进程。在设置`worker_num`之后,实际会出现`2 + worker_num`个进程 +* 服务器启动后,可以通过`kill 主进程ID`来结束所有工作进程 + +PHP环境 +---- +`Swoole`提供的绝大的部分模块只能用于`cli`命令行终端。目前只有`Client`同步客户端可以用于`php-fpm`环境下。请勿在`Web`环境中使用`Server`等模块。 \ No newline at end of file diff --git "a/doc/1.3.1 - \345\210\233\345\273\272TCP\346\234\215\345\212\241\345\231\250.md" "b/doc/1.3.1 - \345\210\233\345\273\272TCP\346\234\215\345\212\241\345\231\250.md" new file mode 100644 index 0000000..aabdc64 --- /dev/null +++ "b/doc/1.3.1 - \345\210\233\345\273\272TCP\346\234\215\345\212\241\345\231\250.md" @@ -0,0 +1,57 @@ +#创建TCP服务器 + +程序代码 +---- +server.php + +```php +//创建Server对象,监听 127.0.0.1:9501端口 +$serv = new swoole_server("127.0.0.1", 9501); + +//监听连接进入事件 +$serv->on('connect', function ($serv, $fd) { + echo "Client: Connect.\n"; +}); + +//监听数据接收事件 +$serv->on('receive', function ($serv, $fd, $from_id, $data) { + $serv->send($fd, "Server: ".$data); +}); + +//监听连接关闭事件 +$serv->on('close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); + +//启动服务器 +$serv->start(); +``` + +这里就创建了一个TCP服务器,监听本机9501端口。它的逻辑很简单,当客户端Socket通过网络发送一个 `hello` 字符串时,服务器会回复一个 `Server: hello` 字符串。 + +swoole_server是异步服务器,所以是通过监听事件的方式来编写程序的。当对应的事件发生时底层会主动回调指定的PHP函数。如当有新的TCP连接进入时会执行onConnect事件回调,当某个连接向服务器发送数据时会回调onReceive函数。 + +* 服务器可以同时被成千上万个客户端连接,$fd就是客户端连接的唯一标识符 +* 调用 `$server->send()` 方法向客户端连接发送数据,参数就是$fd客户端标识符 +* 调用 `$server->close()` 方法可以强制关闭某个客户端连接 +* 客户端可能会主动断开连接,此时会触发onClose事件回调 + + +执行程序 +---- +```shell +php server.php +``` +在命令行下运行server.php程序,启动成功后可以使用 `netstat` 工具看到,已经在监听9501端口。这时就可以使用telnet/netcat工具连接服务器。 + +```shell +telnet 127.0.0.1 9501 +hello +Server: hello +``` + +无法连接到服务器的简单检测手段 +---- +* 在`Linux`下,使用`netstat -an | grep 端口`,查看端口是否已经被打开处于`Listening`状态 +* 上一步确认后,检查防火墙问题 +* 注意服务器所使用的IP地址,如果是`127.0.0.1`回环地址,则客户端只能使用`127.0.0.1`才能连接上 \ No newline at end of file diff --git "a/doc/1.3.10 - \344\275\277\347\224\250\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257.md" "b/doc/1.3.10 - \344\275\277\347\224\250\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 0000000..66be5b8 --- /dev/null +++ "b/doc/1.3.10 - \344\275\277\347\224\250\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,61 @@ +#使用异步客户端 + + `PHP`提供的`MySQL`、`CURL`、`Redis` 等客户端是同步的,会导致服务器程序发生阻塞。`Swoole`提供了常用的异步客户端组件,来解决此问题。编写纯异步服务器程序时,可以使用这些异步客户端。 + +异步客户端可以配合使用`SplQueue`实现连接池,以达到长连接复用的目的。在实际项目中可以使用`PHP`提供的`Yield/Generator`语法实现半协程的异步框架。也可以基于`Promises`简化异步程序的编写。 + +MySQL +---- +```php +$db = new Swoole\MySQL; +$server = array( + 'host' => '127.0.0.1', + 'user' => 'test', + 'password' => 'test', + 'database' => 'test', +); + +$db->connect($server, function ($db, $result) { + $db->query("show tables", function (Swoole\MySQL $db, $result) { + var_dump($result); + $db->close(); + }); +}); +``` + +与`mysqli`和`PDO`等客户端不同,`Swoole\MySQL`是异步非阻塞的,连接服务器、执行SQL时,需要传入一个回调函数。`connect`的结果不在返回值中,而是在回调函数中。`query`的结果也需要在回调函数中进行处理。 + +Redis +---- +```php +$redis = new Swoole\Redis; +$redis->connect('127.0.0.1', 6379, function ($redis, $result) { + $redis->set('test_key', 'value', function ($redis, $result) { + $redis->get('test_key', function ($redis, $result) { + var_dump($result); + }); + }); +}); +``` + +Http +--- +```php +$cli = new Swoole\Http\Client('127.0.0.1', 80); +$cli->setHeaders(array('User-Agent' => 'swoole-http-client')); +$cli->setCookies(array('test' => 'value')); + +$cli->post('/dump.php', array("test" => 'abc'), function ($cli) { + var_dump($cli->body); + $cli->get('/index.php', function ($cli) { + var_dump($cli->cookies); + var_dump($cli->headers); + }); +}); +``` + +`Swoole\Http\Client`的作用与`CURL`完全一致,它完整实现了`Http`客户端的相关功能。具体请参考 [HttpClient文档](/wiki/page/p-http_client.html) + +其他客户端 +---- +`Swoole`底层目前只提供了最常用的`MySQL`、`Redis`、`Http`异步客户端,如果你的应用程序中需要实现其他协议客户端,如`Kafka`、`AMQP`等协议,可以基于`Swoole\Client`异步`TCP`客户端,开发相关协议解析代码,来自行实现。 diff --git "a/doc/1.3.11 - \345\244\232\350\277\233\347\250\213\345\205\261\344\272\253\346\225\260\346\215\256.md" "b/doc/1.3.11 - \345\244\232\350\277\233\347\250\213\345\205\261\344\272\253\346\225\260\346\215\256.md" new file mode 100644 index 0000000..895f272 --- /dev/null +++ "b/doc/1.3.11 - \345\244\232\350\277\233\347\250\213\345\205\261\344\272\253\346\225\260\346\215\256.md" @@ -0,0 +1,57 @@ +#多进程共享数据 + +由于`PHP`语言不支持多线程,因此`Swoole`使用多进程模式。在多进程模式下存在进程内存隔离,在工作进程内修改`global`全局变量和超全局变量时,在其他进程是无效的。 + +进程隔离 +---- +```php +$fds = array(); +$server->on('connect', function ($server, $fd){ + echo "connection open: {$fd}\n"; + global $fds; + $fds[] = $fd; + var_dump($fds); +}); +``` + +`$fds` 虽然是全局变量,但只在当前的进程内有效。`Swoole`服务器底层会创建多个`Worker`进程,在`var_dump($fds)`打印出来的值,只有部分连接的`fd`。 + +对应的解决方案就是使用外部存储服务: + +* 数据库,如:`MySQL`、`MongoDB` +* 缓存服务器,如:`Redis`、`Memcache` +* 磁盘文件,多进程并发读写时需要加锁 + +普通的数据库和磁盘文件操作,存在较多`IO`等待时间。因此推荐使用: + +* `Redis` 内存数据库,读写速度非常快 +* `/dev/shm` 内存文件系统,读写操作全部在内存中完成,无`IO`消耗,性能极高 + +除了使用存储之外,还可以使用共享内存来保存数据 + +共享内存 +---- +`PHP`提供了多套共享内存的扩展,但实际上真正在实际项目中可用的并不多。 + +#### shm 扩展 +提供了`shm_put_var`/`shm_get_var`共享内存读写方法。但其底层实现使用链表结构,在保存大量数值时时间复杂度为`O(N)`,性能非常差。并且读写数据没有加锁,存在数据同步问题,需要使用者自行加锁。 + +> 不推荐使用 + +#### shmop 扩展 +提供了`shmop_read`/`shmop_write`共享内存读写方法。仅提供了基础的共享内存操作指令,并未提供数据结构和封装。不适合普通开发者使用。 + +> 不推荐使用 + +#### apcu 扩展 +提供了`apc_fetch`/`apc_store`可以使用`Key-Value`方式访问。`APC`扩展总体上是可以用于实际项目的,缺点是锁的粒度较粗,在大量并发读写操作时锁的碰撞较为密集。 + +> `yac`扩展,不适合用于保存数据,其设计原理导致存在一定的数据`miss`率,仅作为缓存,不可作为存储 + +#### swoole_table +`Swoole`官方提供的共享内存读写工具,提供了`Key-Value`操作方式,使用非常简单。底层使用自旋锁实现,在大量并发读写操作时性能依然非常强劲。推荐使用。`swoole_table`仍然存在一个两个缺点,使用时需要根据实际情况来选择。 + +* 提前申请内存,`swoole_table`在使用前就需要分配好内存,可能会占用较多内存 +* 无法动态扩容,`swoole_table`内存管理是静态的,不支持动态申请新内存,因此一个`Table`在设置为`N`行之后,不能超过限制 + + diff --git "a/doc/1.3.2 - \345\210\233\345\273\272UDP\346\234\215\345\212\241\345\231\250.md" "b/doc/1.3.2 - \345\210\233\345\273\272UDP\346\234\215\345\212\241\345\231\250.md" new file mode 100644 index 0000000..969cfb3 --- /dev/null +++ "b/doc/1.3.2 - \345\210\233\345\273\272UDP\346\234\215\345\212\241\345\231\250.md" @@ -0,0 +1,37 @@ +#创建UDP服务器 + +程序代码 +----- +udp_server.php +```php +//创建Server对象,监听 127.0.0.1:9502端口,类型为SWOOLE_SOCK_UDP +$serv = new swoole_server("127.0.0.1", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_UDP); + +//监听数据接收事件 +$serv->on('Packet', function ($serv, $data, $clientInfo) { + $serv->sendto($clientInfo['address'], $clientInfo['port'], "Server ".$data); + var_dump($clientInfo); +}); + +//启动服务器 +$serv->start(); +``` + +UDP服务器与TCP服务器不同,UDP没有连接的概念。启动Server后,客户端无需Connect,直接可以向Server监听的9502端口发送数据包。对应的事件为onPacket。 + +* $clientInfo是客户端的相关信息,是一个数组,有客户端的IP和端口等内容 +* 调用 `$server->sendto` 方法向客户端发送数据 + +启动服务 +---- +```shell +php udp_server.php +``` +UDP服务器可以使用`netcat -u` 来连接测试 +```shell +netcat -u 127.0.0.1 9502 +hello +Server: hello +``` + + diff --git "a/doc/1.3.3 - \345\210\233\345\273\272Web\346\234\215\345\212\241\345\231\250.md" "b/doc/1.3.3 - \345\210\233\345\273\272Web\346\234\215\345\212\241\345\231\250.md" new file mode 100644 index 0000000..f4dcf7c --- /dev/null +++ "b/doc/1.3.3 - \345\210\233\345\273\272Web\346\234\215\345\212\241\345\231\250.md" @@ -0,0 +1,32 @@ +#创建Web服务器 + +程序代码 +----- +http_server.php +```php +$http = new swoole_http_server("0.0.0.0", 9501); + +$http->on('request', function ($request, $response) { + var_dump($request->get, $request->post); + $response->header("Content-Type", "text/html; charset=utf-8"); + $response->end("

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

"); +}); + +$http->start(); +``` + +Http服务器只需要关注请求响应即可,所以只需要监听一个`onRequest`事件。当有新的Http请求进入就会触发此事件。事件回调函数有2个参数,一个是$request对象,包含了请求的相关信息,如GET/POST请求的数据。 + +另外一个是response对象,对request的响应可以通过操作response对象来完成。$response->end()方法表示输出一段HTML内容,并结束此请求。 + +* `0.0.0.0` 表示监听所有IP地址,一台服务器可能同时有多个IP,如`127.0.0.1`本地回环IP、`192.168.1.100`局域网IP、`210.127.20.2` 外网IP,这里也可以单独指定监听一个IP +* `9501` 监听的端口,如果被占用程序会抛出致命错误,中断执行。 + +启动服务 +---- +```shell +php http_server.php +``` +* 可以打开浏览器,访问`http://127.0.0.1:9501`查看程序的结果。 +* 也可以使用apache `ab`工具对服务器进行压力测试 + diff --git "a/doc/1.3.4 - \345\210\233\345\273\272WebSocket\346\234\215\345\212\241\345\231\250.md" "b/doc/1.3.4 - \345\210\233\345\273\272WebSocket\346\234\215\345\212\241\345\231\250.md" new file mode 100644 index 0000000..c2313db --- /dev/null +++ "b/doc/1.3.4 - \345\210\233\345\273\272WebSocket\346\234\215\345\212\241\345\231\250.md" @@ -0,0 +1,70 @@ +#创建WebSocket服务器 + +程序代码 +---- +ws_server.php +```php +//创建websocket服务器对象,监听0.0.0.0:9502端口 +$ws = new swoole_websocket_server("0.0.0.0", 9502); + +//监听WebSocket连接打开事件 +$ws->on('open', function ($ws, $request) { + var_dump($request->fd, $request->get, $request->server); + $ws->push($request->fd, "hello, welcome\n"); +}); + +//监听WebSocket消息事件 +$ws->on('message', function ($ws, $frame) { + echo "Message: {$frame->data}\n"; + $ws->push($frame->fd, "server: {$frame->data}"); +}); + +//监听WebSocket连接关闭事件 +$ws->on('close', function ($ws, $fd) { + echo "client-{$fd} is closed\n"; +}); + +$ws->start(); +``` + +WebSocket服务器是建立在Http服务器之上的长连接服务器,客户端首先会发送一个Http的请求与服务器进行握手。握手成功后会触发onOpen事件,表示连接已就绪,onOpen函数中可以得到`$request`对象,包含了Http握手的相关信息,如GET参数、Cookie、Http头信息等。 + +建立连接后客户端与服务器端就可以双向通信了。 + +* 客户端向服务器端发送信息时,服务器端触发`onMessage`事件回调 +* 服务器端可以调用`$server->push()`向某个客户端(使用$fd标识符)发送消息 +* 服务器端可以设置`onHandShake`事件回调来手工处理WebSocket握手 + +运行程序 +---- +```shell +php ws_server.php +``` +可以使用Chrome浏览器进行测试,JS代码为: +```javascript +var wsServer = 'ws://127.0.0.1:9502'; +var websocket = new WebSocket(wsServer); +websocket.onopen = function (evt) { + console.log("Connected to WebSocket server."); +}; + +websocket.onclose = function (evt) { + console.log("Disconnected"); +}; + +websocket.onmessage = function (evt) { + console.log('Retrieved data from server: ' + evt.data); +}; + +websocket.onerror = function (evt, e) { + console.log('Error occured: ' + evt.data); +}; +``` + +* 不能直接使用swoole_client与websocket服务器通信,swoole_client是TCP客户端 +* 必须实现WebSocket协议才能和WebSocket服务器通信,可以使用swoole/framework提供的[PHP WebSocket客户端](https://github.com/swoole/framework/blob/master/libs/Swoole/Client/WebSocket.php) + +Comet +---- +WebSocket服务器除了提供WebSocket功能之外,实际上也可以处理Http长连接。只需要增加`onRequest`事件监听即可实现Comet方案Http长轮询。 + diff --git "a/doc/1.3.5 - \350\256\276\347\275\256\345\256\232\346\227\266\345\231\250.md" "b/doc/1.3.5 - \350\256\276\347\275\256\345\256\232\346\227\266\345\231\250.md" new file mode 100644 index 0000000..4344e2c --- /dev/null +++ "b/doc/1.3.5 - \350\256\276\347\275\256\345\256\232\346\227\266\345\231\250.md" @@ -0,0 +1,22 @@ +#设置定时器 + +swoole提供了类似JavaScript的`setInterval`/`setTimeout`异步高精度定时器,粒度为毫秒级。使用也非常简单。 + +程序代码 +----- +```php +//每隔2000ms触发一次 +swoole_timer_tick(2000, function ($timer_id) { + echo "tick-2000ms\n"; +}); + +//3000ms后执行此函数 +swoole_timer_after(3000, function () { + echo "after 3000ms.\n"; +}); +``` + +* `swoole_timer_tick`函数就相当于setInterval,是持续触发的 +* `swoole_timer_after`函数相当于setTimeout,仅在约定的时间触发一次 +* `swoole_timer_tick`和`swoole_timer_after`函数会返回一个整数,表示定时器的ID +* 可以使用 `swoole_timer_clear` 清除此定时器,参数为定时器ID diff --git "a/doc/1.3.6 - \346\211\247\350\241\214\345\274\202\346\255\245\344\273\273\345\212\241.md" "b/doc/1.3.6 - \346\211\247\350\241\214\345\274\202\346\255\245\344\273\273\345\212\241.md" new file mode 100644 index 0000000..84390d9 --- /dev/null +++ "b/doc/1.3.6 - \346\211\247\350\241\214\345\274\202\346\255\245\344\273\273\345\212\241.md" @@ -0,0 +1,42 @@ +#执行异步任务 + +在Server程序中如果需要执行很耗时的操作,比如一个聊天服务器发送广播,Web服务器中发送邮件。如果直接去执行这些函数就会阻塞当前进程,导致服务器响应变慢。 + +Swoole提供了异步任务处理的功能,可以投递一个异步任务到TaskWorker进程池中执行,不影响当前请求的处理速度。 + +程序代码 +----- +基于第一个TCP服务器,只需要增加`onTask`和`onFinish`2个事件回调函数即可。另外需要设置task进程数量,可以根据任务的耗时和任务量配置适量的task进程。 + +```php +$serv = new swoole_server("127.0.0.1", 9501); + +//设置异步任务的工作进程数量 +$serv->set(array('task_worker_num' => 4)); + +$serv->on('receive', function($serv, $fd, $from_id, $data) { + //投递异步任务 + $task_id = $serv->task($data); + echo "Dispath AsyncTask: id=$task_id\n"; +}); + +//处理异步任务 +$serv->on('task', function ($serv, $task_id, $from_id, $data) { + echo "New AsyncTask[id=$task_id]".PHP_EOL; + //返回任务执行的结果 + $serv->finish("$data -> OK"); +}); + +//处理异步任务的结果 +$serv->on('finish', function ($serv, $task_id, $data) { + echo "AsyncTask[$task_id] Finish: $data".PHP_EOL; +}); + +$serv->start(); +``` + +调用`$serv->task()`后,程序立即返回,继续向下执行代码。onTask回调函数Task进程池内被异步执行。执行完成后调用`$serv->finish()`返回结果。 + +> finish操作是可选的,也可以不返回任何结果 + + diff --git "a/doc/1.3.7 - \345\210\233\345\273\272\345\220\214\346\255\245TCP\345\256\242\346\210\267\347\253\257.md" "b/doc/1.3.7 - \345\210\233\345\273\272\345\220\214\346\255\245TCP\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 0000000..f4237f2 --- /dev/null +++ "b/doc/1.3.7 - \345\210\233\345\273\272\345\220\214\346\255\245TCP\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,49 @@ +#创建同步TCP客户端 + +程序代码 +---- +client.php + +```php +$client = new swoole_client(SWOOLE_SOCK_TCP); + +//连接到服务器 +if (!$client->connect('127.0.0.1', 9501, 0.5)) +{ + die("connect failed."); +} +//向服务器发送数据 +if (!$client->send("hello world")) +{ + die("send failed."); +} +//从服务器接收数据 +$data = $client->recv(); +if (!$data) +{ + die("recv failed."); +} +echo $data; +//关闭连接 +$client->close(); +``` + +创建一个TCP的同步客户端,此客户端可以用于连接到我们第一个示例的TCP服务器。向服务器端发送一个`hello world`字符串,服务器会返回一个 `Server: hello world`字符串。 + +这个客户端是同步阻塞的,connect/send/recv 会等待IO完成后再返回。同步阻塞操作并不消耗CPU资源,IO操作未完成当前进程会自动转入`sleep`模式,当IO完成后操作系统会唤醒当前进程,继续向下执行代码。 + +* `TCP`需要进行`3`次握手,所以`connect`至少需要`3`次网络传输过程 +* 在发送少量数据时`$client->send`都是可以立即返回的。发送大量数据时,`socket`缓存区可能会塞满,`send`操作会阻塞。 +* `recv`操作会阻塞等待服务器返回数据,`recv`耗时等于服务器处理时间+网络传输耗时之合。 + +TCP通信过程 +---- +![TCP通信](https://www.swoole.com/static/image/tcp_syn.png) + +执行程序 +------ +```shell +php client.php +Server: hello world +``` + diff --git "a/doc/1.3.8 - \345\210\233\345\273\272\345\274\202\346\255\245TCP\345\256\242\346\210\267\347\253\257.md" "b/doc/1.3.8 - \345\210\233\345\273\272\345\274\202\346\255\245TCP\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 0000000..b86ebae --- /dev/null +++ "b/doc/1.3.8 - \345\210\233\345\273\272\345\274\202\346\255\245TCP\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,41 @@ +#创建异步TCP客户端 + +程序代码 +---- +async_client.php +```php +$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + +//注册连接成功回调 +$client->on("connect", function($cli) { + $cli->send("hello world\n"); +}); + +//注册数据接收回调 +$client->on("receive", function($cli, $data){ + echo "Received: ".$data."\n"; +}); + +//注册连接失败回调 +$client->on("error", function($cli){ + echo "Connect failed\n"; +}); + +//注册连接关闭回调 +$client->on("close", function($cli){ + echo "Connection close\n"; +}); + +//发起连接 +$client->connect('127.0.0.1', 9501, 0.5); +``` + +异步客户端与上一个同步TCP客户端不同,异步客户端是非阻塞的。可以用于编写高并发的程序。swoole官方提供的`redis-async`、`mysql-async`都是基于异步swoole_client实现的。 + +异步客户端需要设置回调函数,有4个事件回调必须设置`onConnect`、`onError`、`onReceive`、`onClose`。分别在客户端连接成功、连接失败、收到数据、连接关闭时触发。 + +`$client->connect()` 发起连接的操作会立即返回,不存在任何等待。当对应的IO事件完成后,swoole底层会自动调用设置好的回调函数。 + +> 异步客户端只能用于cli环境 + + diff --git "a/doc/1.3.9 - \347\275\221\347\273\234\351\200\232\344\277\241\345\215\217\350\256\256\350\256\276\350\256\241.md" "b/doc/1.3.9 - \347\275\221\347\273\234\351\200\232\344\277\241\345\215\217\350\256\256\350\256\276\350\256\241.md" new file mode 100644 index 0000000..0e0ba7e --- /dev/null +++ "b/doc/1.3.9 - \347\275\221\347\273\234\351\200\232\344\277\241\345\215\217\350\256\256\350\256\276\350\256\241.md" @@ -0,0 +1,47 @@ +#网络通信协议设计 + +为什么需要通信协议 +----- +TCP协议在底层机制上解决了UDP协议的顺序和丢包重传问题。但相比UDP又带来了新的问题,TCP协议是流式的,数据包没有边界。应用程序使用TCP通信就会面临这些难题。 + +因为TCP通信是流式的,在接收1个大数据包时,可能会被拆分成多个数据包发送。多次Send底层也可能会合并成一次进行发送。这里就需要2个操作来解决: + +* 分包:Server收到了多个数据包,需要拆分数据包 +* 合包:Server收到的数据只是包的一部分,需要缓存数据,合并成完整的包 + +所以TCP网络通信时需要设定通信协议。常见的TCP网络通信协议有`HTTP`、`HTTPS`、`FTP`、`SMTP`、`POP3`、`IMAP`、`SSH`、`Redis`、`Memcache`、`MySQL` 。 + +如果要设计一个通用协议的Server,那么就要按照通用协议的标准去处理网络数据。除了通用协议外还可以自定义协议。Swoole支持了2种类型的自定义网络通信协议。 + +EOF结束符协议 +----- +EOF协议处理的原理是每个数据包结尾加一串特殊字符表示包已结束。如`memcache`、`ftp`、`stmp`都使用`\r\n`作为结束符。发送数据时只需要在包末尾增加`\r\n`即可。使用EOF协议处理,一定要确保数据包中间不会出现EOF,否则会造成分包错误。 + +在`swoole_server`和`swoole_client`的代码中只需要设置2个参数就可以使用EOF协议处理。 + +```php +$server->set(array( + 'open_eof_split' => true, + 'package_eof' => "\r\n", +)); +$client->set(array( + 'open_eof_split' => true, + 'package_eof' => "\r\n", +)); +``` + +固定包头+包体协议 +---- +固定包头的协议非常通用,在BAT的服务器程序中经常能看到。这种协议的特点是一个数据包总是由包头+包体2部分组成。包头由一个字段指定了包体或整个包的长度,长度一般是使用2字节/4字节整数来表示。服务器收到包头后,可以根据长度值来精确控制需要再接收多少数据就是完整的数据包。Swoole的配置可以很好的支持这种协议,可以灵活地设置4项参数应对所有情况。 + +Swoole的Server和异步Client都是在`onReceive`回调函数中处理数据包,当设置了协议处理后,只有收到一个完整数据包时才会触发`onReceive`事件。同步客户端在设置了协议处理后,调用 `$client->recv()` 不再需要传入长度,recv函数在收到完整数据包或发生错误后返回。 + +```php +$server->set(array( + 'open_length_check' => true, + 'package_max_length' => 81920, + 'package_length_type' => 'n', //see php pack() + 'package_length_offset' => 0, + 'package_body_offset' => 2, +)); +``` diff --git "a/doc/1.4 - \347\274\226\347\250\213\351\241\273\347\237\245.md" "b/doc/1.4 - \347\274\226\347\250\213\351\241\273\347\237\245.md" new file mode 100644 index 0000000..cce9766 --- /dev/null +++ "b/doc/1.4 - \347\274\226\347\250\213\351\241\273\347\237\245.md" @@ -0,0 +1,73 @@ +#编程须知 + +这个频道内会详细介绍异步编程与同步编程的不同之处以及需要注意的事项。 + +注意事项 +----- +* 不要在代码中执行`sleep`以及其他睡眠函数,这样会导致整个进程阻塞 +* `exit/die`是危险的,会导致`Worker`进程退出 +* 可通过`register_shutdown_function`来捕获致命错误,在进程异常退出时做一些清理工作,具体参考 [/wiki/page/305.html](/wiki/page/305.html) +* `PHP`代码中如果有异常抛出,必须在回调函数中进行`try/catch`捕获异常,否则会导致工作进程退出 +* 不支持`set_exception_handler`,必须使用`try/catch`方式处理异常 +* `Worker`进程不得共用同一个`Redis`或`MySQL`等网络服务客户端,`Redis/MySQL`创建连接的相关代码可以放到`onWorkerStart`回调函数中,具体参考 [/wiki/page/325.html](/wiki/page/325.html) + +异步编程 +----- +* 异步程序要求代码中不得包含任何同步阻塞操作 +* **异步与同步代码不能混用,一旦应用程序使用了任何同步阻塞的代码,程序即退化为同步模式** + +协程编程 +---- +使用`Coroutine`特性,请认真阅读 [协程编程须知](/wiki/page/851.html) + +类/函数重复定义 +---- +新手非常容易犯这个错误,由于`Swoole`是常驻内存的,所以加载类/函数定义的文件后不会释放。因此引入类/函数的php文件时必须要使用`include_once`或`require_once`,否会发生`cannot redeclare function/class` 的致命错误。 + +内存管理 +---- +PHP守护进程与普通Web程序的变量生命周期、内存管理方式完全不同。请参考 [swoole_server内存管理](/wiki/page/p-zend_mm.html) 页面。__编写`swoole_server`或其他常驻进程时需要特别注意。__ + +进程隔离 +---- +进程隔离也是很多新手经常遇到的问题。修改了全局变量的值,为什么不生效,原因就是全局变量在不同的进程,内存空间是隔离的,所以无效。所以使用`Swoole`开发`Server`程序需要了解`进程隔离`问题。 + +* 不同的进程中PHP变量不是共享,即使是全局变量,在A进程内修改了它的值,在B进程内是无效的 +* 如果需要在不同的Worker进程内共享数据,可以用`Redis`、`MySQL`、`文件`、`Swoole\Table`、`APCu`、`shmget`等工具实现 +* 不同进程的文件句柄是隔离的,所以在A进程创建的Socket连接或打开的文件,在B进程内是无效,即使是将它的fd发送到B进程也是不可用的 + +#### 实例: #### + +```php +$server = new Swoole\Http\Server('127.0.0.1', 9500); + +$i = 1; + +$server->on('Request', function ($request, $response) { + global $i; + $response->end($i++); +}); + +$server->start(); +``` + +在多进程的服务器中,`$i`变量虽然是全局变量(`global`),但由于进程隔离的原因。假设有`4`个工作进程,在`进程1`中进行`$i++`,实际上只有`进程1`中的`$i`变成`2`了,其他另外`3`个进程内`$i`变量的值还是`1`。 + +正确的做法是使用`Swoole`提供的`Swoole\Atomic`或`Swoole\Table`数据结构来保存数据。如上述代码可以使用`Swoole\Atomic`实现。 + +```php +$server = new Swoole\Http\Server('127.0.0.1', 9500); + +$atomic = new Swoole\Atomic(1); + +$server->on('Request', function ($request, $response) use ($atomic) { + $response->end($atomic->add(1)); +}); + +$server->start(); +``` + +* `Swoole\Atomic`数据是建立在共享内存之上的,使用`add`方法加`1`时,在其他工作进程内也是有效的 + + + diff --git "a/doc/1.4.3 - while\345\276\252\347\216\257\347\232\204\345\275\261\345\223\215.md" "b/doc/1.4.3 - while\345\276\252\347\216\257\347\232\204\345\275\261\345\223\215.md" new file mode 100644 index 0000000..343ab53 --- /dev/null +++ "b/doc/1.4.3 - while\345\276\252\347\216\257\347\232\204\345\275\261\345\223\215.md" @@ -0,0 +1,22 @@ +#while循环的影响 + +异步程序如果遇到死循环,事件将无法触发。异步IO程序使用`Reactor模型`,运行过程中必须在`reactor->wait`处轮询。如果遇到死循环,那么程序的控制权就在while中了,`reactor`无法得到控制权,无法检测事件,所以IO事件回调函数也将无法触发。 + +> 密集运算的代码没有任何IO操作,所以不能称为阻塞 + +实例程序 +---- +```php +$serv = new swoole_server("127.0.0.1", 9501); +$serv->set(['worker_num' => 1]); +$serv->on('receive', function ($serv, $fd, $reactorId, $data) { + while(1) + { + $i ++; + } + $serv->send($fd, 'Swoole: '.$data); +}); +$serv->start(); +``` +`onReceive`事件中执行了死循环,`server`无法再收到任何客户端请求,必须等待循环结束才能继续处理新的事件。 + diff --git "a/doc/1.4.4 - stat\347\274\223\345\255\230\346\270\205\347\220\206.md" "b/doc/1.4.4 - stat\347\274\223\345\255\230\346\270\205\347\220\206.md" new file mode 100644 index 0000000..1923939 --- /dev/null +++ "b/doc/1.4.4 - stat\347\274\223\345\255\230\346\270\205\347\220\206.md" @@ -0,0 +1,5 @@ +#stat缓存清理 + +PHP底层对`stat`系统调用增加了`Cache`,在使用`stat`、`fstat`、`filemtime`等函数时,底层可能会命中缓存,返回历史数据。 + +可以使用`clearstatcache`函数清理文件`stat`缓存。 \ No newline at end of file diff --git "a/doc/1.4.5 - mt_rand\351\232\217\346\234\272\346\225\260.md" "b/doc/1.4.5 - mt_rand\351\232\217\346\234\272\346\225\260.md" new file mode 100644 index 0000000..50edb14 --- /dev/null +++ "b/doc/1.4.5 - mt_rand\351\232\217\346\234\272\346\225\260.md" @@ -0,0 +1,25 @@ +#mt_rand随机数 + +在`Swoole`中如果在父进程内调用了`mt_rand`,不同的子进程内再调用`mt_rand`返回的结果会是相同的。所以必须在每个子进程内调用`mt_srand`重新播种。 + +> `shuffle`和`array_rand`等依赖随机数的`PHP`函数同样会受到影响 + +```php +mt_rand(0, 1); +//开始 +$worker_num = 16; + +// fork 进程 +for($i = 0; $i < $worker_num; $i++) { + $process = new swoole_process('child_async', false, 2); + $pid = $process -> start(); +} + +//异步执行进程 +function child_async(swoole_process $worker) { + mt_srand(); + echo mt_rand(0, 100).PHP_EOL; + $worker->exit(); +} + +``` \ No newline at end of file diff --git "a/doc/1.5 - \347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225.md" "b/doc/1.5 - \347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225.md" new file mode 100644 index 0000000..f98a8c6 --- /dev/null +++ "b/doc/1.5 - \347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225.md" @@ -0,0 +1,35 @@ +#版本更新记录 + +swoole从1.5版本开始建立起严格的版本更新记录。目前已历经1.5,1.6,1.7共3个大版本,几十个小版本。平均迭代时间为每半年一个大版本,每2-4周一个小版本。 + +> 查看历史版本的更新记录,请[点击这里](/wiki/page/411.html) + +建议使用的版本 +---- + +``` +稳定版:v1.10.4 +预览版:v2.1.3 +``` + +> `1.x`分支已进入特性锁定期,不再开发新功能,仅修复`BUG` +> `2.x`版本可通过增加`--disable-coroutine`关闭协程特性,使其变为非协程版本 + +版本类型 +----- +* alpha 特性预览版本,表示开发计划中的任务已完成,进行开放预览,可能会存在较多BUG +* beta 测试版本,表示已经可以用于开发环境测试,可能存在BUG +* rc[1-n] 候选发布版本,表示进入发布周期,正在做大范围的测试,在此期间仍可能发现BUG +* stable 稳定版,表示此版本已完毕,可正式投入使用 + +单双数版本 +---- +* 单数版本为特性新增版本,主要工作是新增功能特性、代码重构、结构调整。可能会带来一些BUG。 +* 双数版本为问题修复版本,主要工作是修复现有的已知问题、提升性能、完善细节。稳定性更高 + +快速查看当前swoole的版本 +----- + +```shell +php --ri swoole +``` diff --git a/doc/1.5.1 - 2.1.2.md b/doc/1.5.1 - 2.1.2.md new file mode 100644 index 0000000..489beb2 --- /dev/null +++ b/doc/1.5.1 - 2.1.2.md @@ -0,0 +1,7 @@ +#2.1.2 + +- 添加 PostgreSQL coroutine client (depends on libpg) +- 添加 Co::readFile +- 添加 Co::writeFile +- 添加 swoole_process_pool +- 添加 swoole_msgqueue \ No newline at end of file diff --git a/doc/1.5.10 - 1.9.23.md b/doc/1.5.10 - 1.9.23.md new file mode 100644 index 0000000..e4a0103 --- /dev/null +++ b/doc/1.5.10 - 1.9.23.md @@ -0,0 +1,4 @@ +#1.9.23 + +* 修复`SSL`服务器偶然发生崩溃的问题 +* 修复同步客户端超时发生死循环的问题 \ No newline at end of file diff --git a/doc/1.5.11 - 1.9.22.md b/doc/1.5.11 - 1.9.22.md new file mode 100644 index 0000000..6dd408f --- /dev/null +++ b/doc/1.5.11 - 1.9.22.md @@ -0,0 +1,4 @@ +#1.9.22 + +* 增加`tls_host_name`选项 +* 移除`Client`复用连接时清理`Socket`缓存区的逻辑 diff --git a/doc/1.5.12 - 1.9.21.md b/doc/1.5.12 - 1.9.21.md new file mode 100644 index 0000000..02d9af6 --- /dev/null +++ b/doc/1.5.12 - 1.9.21.md @@ -0,0 +1,9 @@ +#1.9.21 + +* 增加`Atomic\Long`,支持`64`位有符号长整型 +* 优化底层`GlobalMemory`实现,支持创建无限个数的`Atomic`、`Lock`、`Table` +* 禁止序列化`Swoole`各模块对象 +* 修复`Http\Client::download`第4个参数无效的问题 +* 修复`FreeBSD`平台下编译报错的问题 +* 修复`MacOS`平台下`sendfile`存在`5`秒延迟的问题 +* 增加`Process::setTimeout` diff --git a/doc/1.5.2 - 1.10.3.md b/doc/1.5.2 - 1.10.3.md new file mode 100644 index 0000000..acfb6be --- /dev/null +++ b/doc/1.5.2 - 1.10.3.md @@ -0,0 +1,14 @@ +#1.10.3 + +主要更新 +---- +* 增加 [swoole_event_dispatch](/wiki/page/p-swoole_event_dispatch.html) +* 增加 [swoole_event_isset](/wiki/page/p-swoole_event_isset.html) +* 优化 `Accept` 连接的性能 +* 增加 `swoole_event_cycle` 第二个参数 +* 增加 [swoole_process::setBlocking](/wiki/page/897.html) +* 增加 [swoole_http_request::getData](/wiki/page/876.html) + +发布日期 +---- +`2018-03-28` \ No newline at end of file diff --git a/doc/1.5.3 - 1.10.2.md b/doc/1.5.3 - 1.10.2.md new file mode 100644 index 0000000..6bdc818 --- /dev/null +++ b/doc/1.5.3 - 1.10.2.md @@ -0,0 +1,10 @@ +#1.10.2 + +* 修复`BASE`模式设置`max_request=1`时发生崩溃的问题 +* 修复`WebSocket`客户端在握手响应与数据帧在同一个传输单元时解包失败的问题 +* 修复`SSL`连接无法使用`sendfile`的问题 +* 修复`BASE`模式下频繁`reload`导致进程丢失的问题 +* 修复`swoole_async_dns_lookup`在启用`jemalloc`时发生崩溃的问题 +* 修复`PHP7.2`版本中开启`opcache.enable_cli=On`时发生崩溃的问题 +* 修改`Client`在域名解析失败时的错误信息 +* 进程在`reload`时标记为繁忙状态,不再接收新请求 \ No newline at end of file diff --git a/doc/1.5.4 - 2.1.1.md b/doc/1.5.4 - 2.1.1.md new file mode 100644 index 0000000..c0de1f4 --- /dev/null +++ b/doc/1.5.4 - 2.1.1.md @@ -0,0 +1,5 @@ +#2.1.1 + +* 增加`Co::fgets`函数,可按行读取文件内容 +* 修复`SSL`连接`sendfile`死循环的问题 +* 修复`BASE`模式下在`onConnect`回调中调用`sendfile`无效的问题 \ No newline at end of file diff --git a/doc/1.5.5 - 1.10.1.md b/doc/1.5.5 - 1.10.1.md new file mode 100644 index 0000000..34008ca --- /dev/null +++ b/doc/1.5.5 - 1.10.1.md @@ -0,0 +1,8 @@ +#1.10.1 + +* 修复`Http2`服务器设置`Header`错误的问题 +* 增加`http_proxy_user`和`http_proxy_password`配置项 +* 增加`Client::shutdown`方法,可关闭`socket`读写 +* 增加`WebSocket\Server::isEstablished`方法 +* 增加`debug_mode`配置项,用于`debug`版本关闭日志输出 +* 兼容`Unity3D`的`HttpClient` diff --git a/doc/1.5.6 - 2.0.13.md b/doc/1.5.6 - 2.0.13.md new file mode 100644 index 0000000..9b1eb7c --- /dev/null +++ b/doc/1.5.6 - 2.0.13.md @@ -0,0 +1,8 @@ +#2.0.13 + +* 增加`SWOOLE_ERROR`错误码常量 +* 增加`Coroutine\Channel`协程通道模块 +* 增加`Coroutine\Http\Client`的`http_proxy`设置支持 +* 增加`onWorkerStart`对协程的支持 +* 增加短命名特性 +* 移除非命名空间风格类 \ No newline at end of file diff --git a/doc/1.5.7 - 2.0.12.md b/doc/1.5.7 - 2.0.12.md new file mode 100644 index 0000000..a0c49bd --- /dev/null +++ b/doc/1.5.7 - 2.0.12.md @@ -0,0 +1,3 @@ +#2.0.12 + +* 不再支持PHP5 \ No newline at end of file diff --git a/doc/1.5.8 - 2.0.11.md b/doc/1.5.8 - 2.0.11.md new file mode 100644 index 0000000..7b748b5 --- /dev/null +++ b/doc/1.5.8 - 2.0.11.md @@ -0,0 +1,6 @@ +#2.0.11 + +* 添加 swoole_mysql::prepare +* 添加 Coroutine::fread +* 添加 Coroutine::fwrite +* 添加 Coroutine::gethostbyname \ No newline at end of file diff --git a/doc/1.5.9 - 1.10.0.md b/doc/1.5.9 - 1.10.0.md new file mode 100644 index 0000000..68ab089 --- /dev/null +++ b/doc/1.5.9 - 1.10.0.md @@ -0,0 +1,19 @@ +#1.10.0 + +* 更新`Table::incr`和`Table::decr`支持有符号整型 +* 兼容`PHP-7.2`版本 +* 增加`Event::cycle`函数 +* 修复`Event::del`函数无法移除标准输入句柄的问题 +* 修复`Task`进程内定时器间隔小于`Client`接收超时时间,引起`Client::recv`死锁的问题 +* 增加自动解析域名功能,异步客户端不再需要添加额外代码实现域名解析 +* 增加`ssl_host_name`配置项,用于验证`SSL/TLS`主机合法性 +* 使用`dispatch_mode = 3`时,当所有`Worker`为忙的状态时打印一条错误日志 +* 增加端口迭代器,可遍历某个监听端口的所有连接 +* 修复`Table`在非`x86`平台存在的内存对齐问题 +* 修复`BASE`模式下`max_request`配置无效的问题 +* 修复`WebSocket`服务器在某些客户端`ping`帧带有`mask`数据时回包错误的问题 +* 修复`HttpClient`使用`HEAD`方法响应内容携带`Content-Length`导致卡死的问题 +* 增加`STREAM`模块,`Reactor`、`Worker`、`Task`通信方式更灵活 +* 增加`request_slowlog_timeout`配置,可记录慢请求日志 +* 增加`MySQL`异步客户端对`JSON`格式的支持 + diff --git "a/doc/1.6 - \346\226\260\347\211\271\346\200\247\344\275\277\347\224\250.md" "b/doc/1.6 - \346\226\260\347\211\271\346\200\247\344\275\277\347\224\250.md" new file mode 100644 index 0000000..02e40db --- /dev/null +++ "b/doc/1.6 - \346\226\260\347\211\271\346\200\247\344\275\277\347\224\250.md" @@ -0,0 +1,2 @@ +#新特性使用 + diff --git "a/doc/1.6.1 - 2.1.2 \350\277\233\347\250\213\346\261\240\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" "b/doc/1.6.1 - 2.1.2 \350\277\233\347\250\213\346\261\240\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..66123da --- /dev/null +++ "b/doc/1.6.1 - 2.1.2 \350\277\233\347\250\213\346\261\240\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,119 @@ +#2.1.2 进程池模块的使用 + +在`Swoole-2.1.2`版本中我们将`Server`的进程管理模块封装成了`PHP`类,现在可以在`PHP`代码中使用`Swoole`的进程管理器了。 + +在实际项目中经常需要写一些长期运行的脚本,如基于`redis`、`kafka`、`rabbitmq`实现的多进程队列消费者,多进程爬虫等等。程序员需要使用`pcntl`和`posix`相关的扩展库实现多进程编程,需要开发者具备深厚的`Linux`系统编程功底,否则很容易出现问题。 + +`Swoole`提供的进程管理器来自于`Swoole\Server`,经过大量生产项目验证,稳定性和健壮性都非常高。可大大简化多进程脚本编程工作。 + +一、 创建进程池 +----- +在`PHP`代码中使用`new Swoole\Process\Pool`即可创建一个进程池,构造方法的第一个参数传入工作进程的数量。使用`on`方法设置`WorkerStart`即可在工作进程启动时执行指定的代码,可以在这里进行`while(true)`循环从`redis`队列中获取任务并处理。使用`start`方法启动所有进程,管理器开始进入`wait`状态。 + +```php +$workerNum = 10; +$pool = new Swoole\Process\Pool($workerNum); + +$pool->on("WorkerStart", function ($pool, $workerId) { + echo "Worker#{$workerId} is started\n"; + $redis = new Redis(); + $redis->pconnect('127.0.0.1', 6379); + $key = "key1"; + while (true) { + $msgs = $redis->brpop($key, 2); + if ( $msgs == null) continue; + var_dump($msgs); + } +}); + +$pool->on("WorkerStop", function ($pool, $workerId) { + echo "Worker#{$workerId} is stopped\n"; +}); + +$pool->start(); +``` + +使用进程管理器,可以保证工作进程的稳定性。 + +* 某个工作进程遇到致命错误、主动退出时管理器会进行回收,避免出现僵尸进程 +* 工作进程退出后,管理器会自动拉起、创建一个新的工作进程 + +二、信号处理 +---- +`Swoole`进程管理器自带了信号处理,向管理器进程发送: + +* `SIGTERM`信号:中止服务,向所有工作进程发送`SIGTERM`关闭进程 +* `SIGUSR1`信号:重启工作进程,管理器会逐个重启工作进程 + +在工作进程中可以配合使用`pcntl_signal`和`pcntl_signal_dispatch`实现信号处理。 +```php +$pool->on("WorkerStart", function ($pool, $workerId) { + $running = true; + pcntl_signal(SIGTERM, function () use (&$running) { + $running = false; + }); + echo "Worker#{$workerId} is started\n"; + $redis = new Redis(); + $redis->pconnect('127.0.0.1', 6379); + $key = "key1"; + while ($running) { + $msgs = $redis->brpop($key, 2); + pcntl_signal_dispatch(); + if ( $msgs == null) continue; + var_dump($msgs); + } +}); +``` + +三、任务投递 +----- +`Swoole`进程管理器自带了消息队列和`TCP-Socket`消息投递的支持。可设置监听系统队列或者`TCP`端口,接收任务数据。此项功能是可选的,要使用任务投递功能,需要对进程池对象设置`onMessage`回调。 + +#### 消息队列 + +```php +$pool = new Swoole\Process\Pool(2, SWOOLE_IPC_MSGQUEUE, 0x7000001); + +$pool->on("WorkerStart", function ($pool, $workerId) { + echo "Worker#{$workerId} is started\n"; +}); + +$pool->on("Message", function ($pool, $message) { + echo "Message: {$message}\n"; +}); + +$pool->start(); + +``` + +需要在构造方法的第二个参数传入`SWOOLE_IPC_MSGQUEUE`,第三个参数设置监听的消息队列`KEY`。其他程序中使用消息队列相关`API`就可以向工作进程投递任务了。 + +#### TCP 端口 + +```php +$pool = new Swoole\Process\Pool(2, SWOOLE_IPC_SOCKET); + +$pool->on("WorkerStart", function ($pool, $workerId) { + echo "Worker#{$workerId} is started\n"; +}); + +$pool->on("Message", function ($pool, $message) { + echo "Message: {$message}\n"; +}); + +$pool->listen('127.0.0.1', 8089); + +$pool->start(); +``` + + +使用`TCP`端口监听,需要设置构造方法的第二个参数为`SWOOLE_IPC_SOCKET`,并使用`listen`方法设置监听的主机和端口。 + +底层使用了`4`字节长度+包体的协议。其他程序中向此端口发送数据时,需要在数据包之前增加一个长度字段。 + +```php +$fp = stream_socket_client("tcp://127.0.0.1:8089", $errno, $errstr) or die("error: $errstr\n"); +$msg = json_encode(['data' => 'hello', 'uid' => 1991]); +fwrite($fp, pack('N', strlen($msg)).$msg); +fclose($fp); +``` \ No newline at end of file diff --git "a/doc/1.6.10 - 1.7.4 SSL\351\232\247\351\201\223\345\212\240\345\257\206TCP-Server.md" "b/doc/1.6.10 - 1.7.4 SSL\351\232\247\351\201\223\345\212\240\345\257\206TCP-Server.md" new file mode 100644 index 0000000..76c6acd --- /dev/null +++ "b/doc/1.6.10 - 1.7.4 SSL\351\232\247\351\201\223\345\212\240\345\257\206TCP-Server.md" @@ -0,0 +1,21 @@ +#1.7.4 SSL隧道加密TCP-Server + +1.7.4后swoole增加了对SSL隧道加密的支持,在swoole_server中可以启用SSL证书加密。使用仅需增加$serv->set的配置即可,并将listener端口的类型,增加SWOOLE_SSL标志。 + +```php +$serv = new swoole_server("0.0.0.0", 443, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$key_dir = dirname(dirname(__DIR__)).'/tests/ssl'; + + // SWOOLE_SOCK_TCP表示此端口不加密 +// SWOOLE_SOCK_TCP | SWOOLE_SSL 表示此端口启用加密 + +$serv->addlistener('0.0.0.0', 80, SWOOLE_SOCK_TCP); + +$serv->set(array( + 'worker_num' => 4, + 'ssl_cert_file' => $key_dir.'/ssl.crt', + 'ssl_key_file' => $key_dir.'/ssl.key', +)); +``` + +配置证书后即可启用SSL隧道加密,onReceive/$serv->send函数中还是继续填写明文,加密工作由底层完成。应用层无需考虑。 diff --git "a/doc/1.6.11 - 1.7.4 task\350\277\233\347\250\213\344\270\255\344\275\277\347\224\250\346\257\253\347\247\222\345\256\232\346\227\266\345\231\250.md" "b/doc/1.6.11 - 1.7.4 task\350\277\233\347\250\213\344\270\255\344\275\277\347\224\250\346\257\253\347\247\222\345\256\232\346\227\266\345\231\250.md" new file mode 100644 index 0000000..03649c0 --- /dev/null +++ "b/doc/1.6.11 - 1.7.4 task\350\277\233\347\250\213\344\270\255\344\275\277\347\224\250\346\257\253\347\247\222\345\256\232\346\227\266\345\231\250.md" @@ -0,0 +1,19 @@ +#1.7.4 task进程中使用毫秒定时器 + +task进程有别于worker进程,worker进程内有EventLoop,所以即可同步阻塞,又可以异步非阻塞。但task进程设计之初就是仅仅支持同步阻塞模式的。 + +而swoole的毫秒定时器是使用timerfd实现的异步定时器。所以在1.7.4之前,task进程中是无法使用定时器的。1.7.4专门对task进程进行了优化,实现了同步的信号触发式定时器。 + +> swoole并不是在信号回调函数中执行定时器代码,所以不存在安全问题。 + +使用方法与普通的worker进程相同。 + +```php +function onWorkerStart($serv, $worker_id) { + if ($worker_id >= $serv->setting['worker_num']) { //超过worker_num,表示这是一个task进程 + $serv->tick(1000); //1s + $serv->after(200); //200ms + } +} +``` + diff --git "a/doc/1.6.12 - 1.7.3 \345\233\272\345\256\232\345\214\205\345\244\264+\345\214\205\344\275\223\345\215\217\350\256\256\350\207\252\345\212\250\345\210\206\345\214\205.md" "b/doc/1.6.12 - 1.7.3 \345\233\272\345\256\232\345\214\205\345\244\264+\345\214\205\344\275\223\345\215\217\350\256\256\350\207\252\345\212\250\345\210\206\345\214\205.md" new file mode 100644 index 0000000..f7c2dd2 --- /dev/null +++ "b/doc/1.6.12 - 1.7.3 \345\233\272\345\256\232\345\214\205\345\244\264+\345\214\205\344\275\223\345\215\217\350\256\256\350\207\252\345\212\250\345\210\206\345\214\205.md" @@ -0,0 +1,40 @@ +#1.7.3 固定包头+包体协议自动分包 + +swoole-1.7.3版本重构了length_check特性的代码,对于固定包头+包体格式的协议可以直接在master进程中进行分包和组包,worker进程中可以一次性收到一个完整的包。配合dispatch_mode = 1或3,swoole提供了一个强大的半异步/半同步服务器模型。带来的好处是: + +* C扩展层进行协议的处理,性能最佳,原PHP代码虽然也可以实现协议处理,但需要耗费较多CPU +* TCP连接与业务逻辑分离,有效利用所有Worker进程,即使只有1个TCP连接,也可以利用所有Worker + +使用方法: +===== +使用也很简单,仅需$serv->set中增加参数即可。 + +open_length_check => true +---- +打开包长检测特性 + +package_length_type => 'N' +---- +长度字段的类型,固定包头中用一个4字节或2字节表示包体长度。类型是一个字符,详情参见php的[pack](http://php.net/manual/zh/function.pack.php)函数文档 +比较常用的类型为: + +* N 4字节网络字节序,最大为2^32 +* n 2字节网络字节序,最大为65536 + +package_length_offset => 10 +----- +从第几个字节开始是长度,比如包头长度为120字节,第10个字节为长度值,这里填入9(从0开始计数) + +package_body_offset => 120 +----- +从第几个字节开始计算长度,比如包头为长度为120字节,第10个字节为长度值,包体长度为1000。如果长度包含包头,这里填入0,如果不包含包头,这里填入120 + +package_max_length => 800000 +----- +最大允许的包长度。因为在一个请求包完整接收前,需要将所有数据保存在内存中,所以需要做保护。避免内存占用过大。 + +配置分发策略 +==== +dispatch_mode = 1 或 3 +1:轮询分配,会逐个分配到所有worker,3:争抢分配,仅分配给空闲状态的worker + diff --git "a/doc/1.6.13 - 1.7.3 onTask\347\233\264\346\216\245return\345\217\226\344\273\243finish\345\207\275\346\225\260.md" "b/doc/1.6.13 - 1.7.3 onTask\347\233\264\346\216\245return\345\217\226\344\273\243finish\345\207\275\346\225\260.md" new file mode 100644 index 0000000..648edf1 --- /dev/null +++ "b/doc/1.6.13 - 1.7.3 onTask\347\233\264\346\216\245return\345\217\226\344\273\243finish\345\207\275\346\225\260.md" @@ -0,0 +1,14 @@ +#1.7.3 onTask直接return取代finish函数 + +1.7.3之后,task/finish可以支持超大包了。发送的数据不再限制为8192。 + +另外swoole_server->finish函数已经废弃,采用直接在onTask回调函数中return字符串的方式。 + +```php +function onTask($serv, $task_id, $from_id, $data) +{ + //your code + //1.7.3之前,是$serv->finish("result"); + return "result."; +} +``` diff --git "a/doc/1.6.14 - 1.7.2 swoole_process\345\244\232\350\277\233\347\250\213\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" "b/doc/1.6.14 - 1.7.2 swoole_process\345\244\232\350\277\233\347\250\213\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..7ca85dd --- /dev/null +++ "b/doc/1.6.14 - 1.7.2 swoole_process\345\244\232\350\277\233\347\250\213\346\250\241\345\235\227\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,73 @@ +#1.7.2 swoole_process多进程模块的使用 + +1.7.2 swoole增加了多进程管理模块来替代PHP的pcntl,它相比pcntl的不同点是: + +* swoole_process提供了pcntl没有的进程间通信 +* swoole_process支持重定向标准输入和输出,在子进程内echo或者读键盘输入可以被重定向为从管道中取数据 +* 子进程可以异步化 + +进程间通信(IPC) +---- +子进程和父进程之间可以通过管道通信,传递数据。IPC在多进程编程中经常用到,PHP的pcntl模块没有提供IPC的功能,所以功能有局限。而swoole_process提供了这些功能,并且封装了接口。只需调用接口即可完成进程间通信。 + +```php +$worker_num = 8; + +for($i = 0; $i < $worker_num; $i++) +{ + $process = new swoole_process('callback_function', $redirect_stdout); + $pid = $process->start(); + $workers[$pid] = $process; +} + +function callback_function(swoole_process $worker) +{ + //echo "Worker: start. PID=".$worker->pid."\n"; + //recv data from master + $recv = $worker->read(); + echo "From Master: $recv\n"; + + //send data to master + $worker->write("hello master\n"); + + sleep(2); + $worker->exit(0); +} +``` + +read/write 2个方法就是向管道内读写数据。主进程内可以通过write/read 向子进程写入,读取数据。 + +标准输入/输出重定向 +---- +swoole_process支持了标准输入输出的重定向,子进程内echo时,会自动写入管道,而不是打印到屏幕。 + +子进程异步 +---- +swoole_process创建的子进程可以是同步的,也可以是异步的。 +```php +function callback_function_async(swoole_process $worker) +{ + //echo "Worker: start. PID=".$worker->pid."\n"; + //recv data from master + $GLOBALS['worker'] = $worker; + swoole_event_add($worker->pipe, function($pipe) { + $worker = $GLOBALS['worker']; + $recv = $worker->read(); + + echo "From Master: $recv\n"; + + //send data to master + $worker->write("hello master\n"); + + sleep(2); + + $worker->exit(0); + }); +} +``` +可以将管道加入到swoole_event中即可实现异步的进程间通信,另外子进程内可以使用swoole_timer/swoole_client/swoole_async这些异步的API。或者使用swoole_event_add直接操作swoole的EventLoop。 + +其他 +---- +swoole_process 1.7.3 还会加入进程CPU亲和设置、守护进程化、使用消息队列/共享内存Channel等特性。 + diff --git "a/doc/1.6.15 - 1.7.2 task\350\277\233\347\250\213\344\275\277\347\224\250\346\266\210\346\201\257\351\230\237\345\210\227.md" "b/doc/1.6.15 - 1.7.2 task\350\277\233\347\250\213\344\275\277\347\224\250\346\266\210\346\201\257\351\230\237\345\210\227.md" new file mode 100644 index 0000000..31a3880 --- /dev/null +++ "b/doc/1.6.15 - 1.7.2 task\350\277\233\347\250\213\344\275\277\347\224\250\346\266\210\346\201\257\351\230\237\345\210\227.md" @@ -0,0 +1,98 @@ +#1.7.2 task进程使用消息队列 + +1.7.2新增特性,可将task进程单独设置为消息队列。带来的好处是: + +任务排队容量增加 +----- +在维持worker进程异步的前提下,task进程可使用消息队列提升任务排队的容量,unix sock受到缓存区尺寸限制,而消息队列不受限制,可以利用到操作系统所有的内存。 +如你的机器有32G内存,如果是unix sock一般缓冲区只有8M。如果你的任务很多,会堆积在socket缓存区中。当超过缓冲区时就会无法再投递新的任务。 +而消息队列,只要操作系统有剩余内存,那一直可以投递新的任务到队列中。 + +支持外部程序投递任务 +---- +当前的`swoole`使用`Unix Socket`,只允许程序内部进行通信。采用消息队列后,拿到消息队列的`key`。其他程序就可以向此队列投递数据了。 + +#### 实例 +```php +class SwooleTask +{ + protected $queueId; + protected $workerId; + protected $taskId = 1; + + const SW_TASK_TMPFILE = 1; //tmp file + const SW_TASK_SERIALIZE = 2; //php serialize + const SW_TASK_NONBLOCK = 4; //task + + const SW_EVENT_TASK = 7; + + /** + * SwooleTask constructor. + * @param $key + * @param int $workerId + * @throws Exception + */ + function __construct($key, $workerId = 0) + { + $this->queueId = msg_get_queue($key); + if ($this->queueId === false) + { + throw new \Exception("msg_get_queue() failed."); + } + $this->workerId = $workerId; + } + + protected static function pack($taskId, $data) + { + $flags = self::SW_TASK_NONBLOCK; + $type = self::SW_EVENT_TASK; + if (!is_string($data)) + { + $data = serialize($data); + $flags |= self::SW_TASK_SERIALIZE; + } + if (strlen($data) >= 8180) + { + $tmpFile = tempnam('/tmp/', 'swoole.task'); + file_put_contents($tmpFile, $data); + $data = pack('l', strlen($data)) . $tmpFile . "\0"; + $flags |= self::SW_TASK_TMPFILE; + $len = 128 + 24; + } + else + { + $len = strlen($data); + } + + return pack('lSsCCS', $taskId, $len, 0, $type, 0, $flags) . $data; + } + + function dispatch($data) + { + $taskId = $this->taskId++; + if (!msg_send($this->queueId, 2, self::pack($taskId, $data), false)) + { + return false; + } + else + { + return $taskId; + } + } +} + +echo "Sending text to msg queue.\n"; +$task = new SwooleTask(0x70001002, 1); +//普通字符串 +$task->dispatch("Hello from PHP!"); +``` + +task进程是可以与swoole_server所有的客户端连接进行通信的,所以外部程序使用消息队列作为IPC,就可以与所有客户端连接进行通信。 + +使用方法 +----- +只需设置swoole_server::set参数即可。新增的参数如下: + +* task_ipc_mode => 1 | 2 | 3,默认为1就是普通的unix socket通信方式,2, 3就是使用消息队列模式。模式2和模式3的不同之处是,模式2支持定向投递,$serv->task($data, $task_worker_id) 这里可以指定投递到哪个task进程。模式3是完全争抢模式,task进程会争抢队列,将无法使用定向投递,即使指定了$task_worker_id,在模式3下也是无效的。 + +* message_queue_key => 0x72000100 ,指定一个消息队列key。如果需要运行多个swoole_server的实例,务必指定。否则会发生数据错乱 diff --git "a/doc/1.6.2 - 1.9.24 \350\260\203\345\272\246\346\224\257\346\214\201 Stream \346\250\241\345\274\217.md" "b/doc/1.6.2 - 1.9.24 \350\260\203\345\272\246\346\224\257\346\214\201 Stream \346\250\241\345\274\217.md" new file mode 100644 index 0000000..77593a5 --- /dev/null +++ "b/doc/1.6.2 - 1.9.24 \350\260\203\345\272\246\346\224\257\346\214\201 Stream \346\250\241\345\274\217.md" @@ -0,0 +1,35 @@ +#1.9.24 调度支持 Stream 模式 + +单连接并发的同步服务器一般使用`dispatch_mode = 3`调度请求分配到`Worker`进程,底层实现使用了忙闲识别方式。 + +* 当`Worker`进程接收到请求回调`onReceive`或`onRequest`时,将`Worker`进程的状态设置为`BUSY`,这时`Reactor`线程将不会再给当前的`Worker`进程分配新的请求 +* 当`Worker`进程处理完当前的请求后,将状态设置为`IDLE`,这时`Reactor`线程将会继续给当前的`Worker`进程分配新请求 + +`dispatch_mode = 3`忙闲分配模式,在极端情况下所有`Worker`均处于`BUSY`时,会退化为`dispatch_mode = 1`轮询模式。无论`Worker`进程处于闲还是忙的状态,都会分配到新请求。这样极端情况下,某些请求可能会无法被最快处理。使用`dispatch_mode = 3`时,需要保证绝大部分时间有充足的空闲`Worker`。 + +在`1.9.24`版本中底层新增加了`Stream`模式。将`dispatch`的过程进行了逆转,`Reactor`线程不再调度决定向哪个`Worker`进程投递新请求,而是发起一个`Stream`的`Connect`到一个`Unix Socket`端口。 + +* 空闲的`Worker`会`Accept`连接,并接收`Reactor`传递的新请求 +* `Worker`进程处理请求期间不再`Accept`,新请求将有其他`Worker`进行处理 +* `Worker`进程完成请求处理后,直接使用`Stream`的通道向对应的`TCP`客户端连接发送结果数据,响应完毕后关闭`Reactor`和`Worker`之间的`Stream`连接 + +新的`Stream`模式使用配置`dispatch_mode = 7`来设置开启。此模式的最大优势是:**无论任何极端情况下,都可以保证请求会被最快被处理**。 + +使用实例 +---- +```php +$serv = new swoole_server("127.0.0.1", 9501); + +$serv->set(array( + 'dispatch_mode' => 7, + 'worker_num' => 2, +)); + +$serv->on('receive', function (swoole_server $serv, $fd, $threadId, $data) +{ + var_dump($data); + echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; +}); + +$serv->start(); +``` \ No newline at end of file diff --git "a/doc/1.6.3 - 1.9.24 \345\274\202\346\255\245\345\256\242\346\210\267\347\253\257\350\207\252\345\212\250\350\247\243\346\236\220\345\237\237\345\220\215.md" "b/doc/1.6.3 - 1.9.24 \345\274\202\346\255\245\345\256\242\346\210\267\347\253\257\350\207\252\345\212\250\350\247\243\346\236\220\345\237\237\345\220\215.md" new file mode 100644 index 0000000..7bae657 --- /dev/null +++ "b/doc/1.6.3 - 1.9.24 \345\274\202\346\255\245\345\256\242\346\210\267\347\253\257\350\207\252\345\212\250\350\247\243\346\236\220\345\237\237\345\220\215.md" @@ -0,0 +1,55 @@ +#1.9.24 异步客户端自动解析域名 + +在`1.9.24`之前的版本,如果`Client`要通过域名连接服务器,需要手工调用`swoole_async_dns_lookup`函数,否则底层会发生阻塞。在最新的`1.9.24`中底层支持了自动异步解析域名,不再需要显式调用`swoole_async_dns_lookup`。 + +有效范围 +----- +* `Swoole\Client` +* `Swoole\Http\Client` +* `Swoole\Coroutine\Client` +* `Swoole\Coroutine\Http\Client` + +旧版本 +---- +```php +swoole_async_dns_lookup("www.baidu.com", function ($domain, $ip) { + $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); + $client->on("connect", function(swoole_client $cli) { + $cli->send("GET / HTTP/1.1\r\n\r\n"); + }); + $client->on("receive", function(swoole_client $cli, $data){ + echo "Receive: $data"; + $cli->send(str_repeat('A', 100)."\n"); + sleep(1); + }); + $client->on("error", function(swoole_client $cli){ + echo "error\n"; + }); + $client->on("close", function(swoole_client $cli){ + echo "Connection close\n"; + }); + $client->connect( $ip, 9501); +}); +``` + +新版本 +--- +```php +$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$client->on("connect", function(swoole_client $cli) { + $cli->send("GET / HTTP/1.1\r\n\r\n"); +}); +$client->on("receive", function(swoole_client $cli, $data){ + echo "Receive: $data"; + $cli->send(str_repeat('A', 100)."\n"); + sleep(1); +}); +$client->on("error", function(swoole_client $cli){ + echo "error\n"; +}); +$client->on("close", function(swoole_client $cli){ + echo "Connection close\n"; +}); +//底层会自动进行异步域名解析 +$client->connect('www.baidu.com', 9501); +``` \ No newline at end of file diff --git "a/doc/1.6.4 - 1.9.17 \346\224\257\346\214\201\345\274\202\346\255\245\345\256\211\345\205\250\351\207\215\345\220\257\347\211\271\346\200\247.md" "b/doc/1.6.4 - 1.9.17 \346\224\257\346\214\201\345\274\202\346\255\245\345\256\211\345\205\250\351\207\215\345\220\257\347\211\271\346\200\247.md" new file mode 100644 index 0000000..5948f10 --- /dev/null +++ "b/doc/1.6.4 - 1.9.17 \346\224\257\346\214\201\345\274\202\346\255\245\345\256\211\345\205\250\351\207\215\345\220\257\347\211\271\346\200\247.md" @@ -0,0 +1,48 @@ +#1.9.17 支持异步安全重启特性 + + `1.9.17`版本重构了底层`WorkerStop`的机制,实现了异步安全重启的特性。包括`stop`、`reload`、`max_request` 3个特性全部复用了一套代码。都支持了异步安全重启。 + +之前的版本 Worker进程收到`SIGTERM`、达到`max_request`时,会立即停止服务,这时`Worker`进程内可能仍然有事件监听,这些异步任务将会被丢弃。新版本中会先创建新的`Worker`,旧的`Worker`在完成所有事件之后自行退出。为了防止某些`Worker`一直不退出,底层还增加了一个`30`秒的定时器,在约定的时间内旧`Worker`没有退出,底层会强行终止。 + +> 可设置`server->max_wait_time`修改`Worker`进程最大等待时间,默认为`30`秒 + +实现原理 +---- +* `Worker`进程收到`SIGTERM`、达到`max_request`时,移除管道监听,立即回调`onWorkerStop`,并通知`Manager`进程。这时当前的`Worker`不会再收到任何客户端请求数据 +* `Worker`进程会设置一个`30`秒的超时定时器,实现退出超时 +* `Manager`进程收到`Worker`进程的消息后,创建新的`Worker` +* 新的`Worker`继续处理客户端请求数据 +* 旧的`Worker`会持续触发`onWorkerExit`事件,`PHP`代码可以此事件回调函数中实现清理逻辑 +* 旧的`Worker`会持续检测`EventLoop`中的`socket`数量,在没有任何事件监听后退出进程 +* 旧的`Worker`在`30`秒内仍然没有完成异步`IO`任务,底层强制终止运行,退出进程 + +进程退出事件 +---- +为了支持异步重启特性,底层新增了一个`onWorkerExit`事件,当旧的`Worker`即将退出时,会持续触发`onWorkerExit`事件,在此事件回调函数中,应用层可以尝试清理某些长连接`Socket` + +```php +$serv->on('WorkerExit', function (swoole_server $serv, $worker_id) { + $redisState = $serv->redis->getState(); + if ($redisState == Swoole\Redis::STATE_READY or $redisState == Swoole\Redis::STATE_SUBSCRIBE) + { + $serv->redis->close(); + } +}); +``` + +设置等待时间 +--- +```php +$serv->set([ + 'max_wait_time' => 60, +]); +``` + +开启方式 +--- + +onWorkerExit 回调功能需要设置 `reload_async = true` 才能开启 + +```php +$serv->set(['reload_async' => true]); +``` \ No newline at end of file diff --git "a/doc/1.6.5 - 1.9.14 \344\275\277\347\224\250\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257\350\266\205\346\227\266\346\234\272\345\210\266.md" "b/doc/1.6.5 - 1.9.14 \344\275\277\347\224\250\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257\350\266\205\346\227\266\346\234\272\345\210\266.md" new file mode 100644 index 0000000..8b731ed --- /dev/null +++ "b/doc/1.6.5 - 1.9.14 \344\275\277\347\224\250\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257\350\266\205\346\227\266\346\234\272\345\210\266.md" @@ -0,0 +1,53 @@ +#1.9.14 使用异步客户端超时机制 + +早期的Swoole客户端不支持连接超时检测,即使在`connect`方法中传入了超时时间在异步客户端中也是无效的。所以需要客户端自行添加定时器来检测连接是否超时。1.9.14版本底层增加了超时机制,应用不再需要添加定时器。 + +TCP客户端 +---- + +```php +$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +//设置事件回调函数 +$client->on("connect", function($cli) { + $cli->send("hello world\n"); +}); +$client->on("receive", function($cli, $data){ + echo "Received: ".$data."\n"; +}); +$client->on("error", function($cli){ + echo "Connect failed\n"; +}); +$client->on("close", function($cli){ + echo "Connection close\n"; +}); +//发起网络连接 +$client->connect('127.0.0.1', 9501, 0.5); +``` + +* `connect`方法的第三个参数就表示设置超时时间,如果在约定的时间内服务器没有响应,底层将自动`close`并回调`onError`事件 +* `onError`回调中可以使用`$client->errCode`获取错误码,连接超时的错误码为`ETIMEOUT` + +Http客户端 +----- +除了连接超时外,某些请求响应式的异步客户端,如HttpClient,还支持了请求超时设置。当`HttpClient`发送了`Request`后服务器未能在规定的时间内返回`Response`,这时底层会自动`close`,并回调。`HttpClient`的状态码将设置为`-2` + +```php +$cli = new Swoole\Http\Client('127.0.0.1', 80); +$cli->set(array( + "timeout" => 3.0, //设置连接和请求的超时时间为3秒 +)); +$cli->setHeaders(array('User-Agent' => 'swoole-http-client')); +$cli->setCookies(array('test' => 'value')); + +$cli->post('/dump.php', array("test" => 'abc'), function ($cli) { + if (empty($cli->body)) { + if ($cli->statusCode == -1) { + echo "连接服务器超时\n"; + } else if ($cli->statusCode == -2) { + echo "服务器响应超时\n"; + } + } else { + echo "请求成功:HTML=".$cli->body; + } +}); +``` \ No newline at end of file diff --git "a/doc/1.6.6 - 1.8.0 \344\275\277\347\224\250\345\206\205\347\275\256Http\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257.md" "b/doc/1.6.6 - 1.8.0 \344\275\277\347\224\250\345\206\205\347\275\256Http\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 0000000..df0aa64 --- /dev/null +++ "b/doc/1.6.6 - 1.8.0 \344\275\277\347\224\250\345\206\205\347\275\256Http\345\274\202\346\255\245\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,82 @@ +#1.8.0 使用内置Http异步客户端 + +Swoole-1.8.0版本内置了`HttpClient`,经过多个版本的迭代,内置HttpClient无论从功能、性能、稳定性上都已经非常出色。 + +使用实例 +--- +```php +$cli = new swoole_http_client('127.0.0.1', 80); +$cli->setHeaders(['User-Agent' => "swoole"]); + +$cli->post('/dump.php', array("test" => 'abc'), function ($cli) { + echo $cli->body; +}); +``` + +并发能力 +---- +相比`curl`和`file_get_contents`这样PHP提供的Http客户端,`swoole_http_client`最大的优势是支持大量并发。 + +`file_get_contents`只能同时请求一个URL,并发只能通过开启多进程实现。`curl`提供了`curl_multi`功能实现并发基于`select`和多线程。并发能力都很差。而`swoole_http_client`是基于`epoll`实现的异步客户端,没有并发限制,可在一个进程内同时并发上万请求。 + +性能问题 +---- +在PHP中也有纯PHP实现的Http客户端,如`Guzzle`,这些类库最大的问题是Http协议解析是由PHP代码实现的,PHP代码在这样场景下进行大量运算性能较差,而且还会占用大量内存。`swoole_http_client`是由C代码实现的,解析Http协议的性能是非常高的,内存占用也很少。 + +在解析`gzip`压缩后的HTML时,`swoole_http_client`的优势更为明显,它可以使用`download`方法,以很小的内存占用即可完成超大文件的下载。由于PHP层面没有提供`zlib`流式分段解压的支持,只能将Http Body全部放置到内存中,调用`gzdecode`一次性解压,而这会占用大量内存。 + +SSL支持 +---- +`swoole_http_client`支持SSL和TLS隧道加密的`https`网址,并且支持配置客户端证书。 + +```php +$cli = new swoole_http_client('127.0.0.1', 80, true); +//如果服务器需要提供SSL证书 +$cli->set(array( + 'ssl_cert_file' => $certFile, + 'ssl_key_file' => $keyFile, +)); + +$cli->setHeaders(['User-Agent' => "swoole"]); + +$cli->get('/index.php', function ($cli) { + file_put_contents(__DIR__.'/t.html', $cli->body); +}); +``` + +Socks5代理 +---- +`swoole_http_client`支持`Socks5`代理,只需要设置几个参数就可以直接使用。 + +```php +$cli = new swoole_http_client('127.0.0.1', 80); + +$cli->set(array( + 'socks5_host' => '192.168.1.100', + 'socks5_port' => 1080, + 'socks5_username' => 'username', //用户名和密码为可选项 + 'socks5_password' => 'password', +)); + +$cli->setHeaders(['User-Agent' => "swoole"]); + +$cli->get('/index.php', function ($cli) { + file_put_contents(__DIR__.'/t.html', $cli->body); +}); +``` + +上传文件 +--- +`swoole_http_client`底层使用了`sendfile`系统调用实现了http上传超大文件,配合底层的`epoll`可以实现非常低的消耗完成超巨大文件的上传。`sendfile`是零拷贝的,占用内存非常少,并且不存在多次内存复制开销。 + +```php +$cli = new swoole_http_client('127.0.0.1', 80); + +$cli->addFile(__DIR__.'/post.data', 'post'); +$cli->addFile(dirname(__DIR__).'/test.jpg', 'debug'); + +$cli->post('/dump2.php', array("xxx" => 'abc', 'x2' => 'rango'), function ($cli) { + echo $cli->body; + $cli->close(); +}); +``` diff --git "a/doc/1.6.7 - 1.7.16 \344\275\277\347\224\250\350\277\255\344\273\243\345\231\250\351\201\215\345\216\206Server\346\211\200\346\234\211\350\277\236\346\216\245.md" "b/doc/1.6.7 - 1.7.16 \344\275\277\347\224\250\350\277\255\344\273\243\345\231\250\351\201\215\345\216\206Server\346\211\200\346\234\211\350\277\236\346\216\245.md" new file mode 100644 index 0000000..fd65dc3 --- /dev/null +++ "b/doc/1.6.7 - 1.7.16 \344\275\277\347\224\250\350\277\255\344\273\243\345\231\250\351\201\215\345\216\206Server\346\211\200\346\234\211\350\277\236\346\216\245.md" @@ -0,0 +1,20 @@ +#1.7.16 使用迭代器遍历Server所有连接 + +swoole-1.7.16版本增加了客户端连接迭代器接口,可以非常轻松实现遍历当前服务器的所有连接。 + +遍历连接并广播 +---- + +```php +foreach($server->connections as $fd) +{ + $server->send($fd, "hello world\n"); +} +``` + +获取连接总数 +---- +```php +echo count($server->connections); +``` + diff --git "a/doc/1.6.8 - 1.7.5 \345\234\250Server\344\270\255\344\275\277\347\224\250swoole_table.md" "b/doc/1.6.8 - 1.7.5 \345\234\250Server\344\270\255\344\275\277\347\224\250swoole_table.md" new file mode 100644 index 0000000..b6d0c16 --- /dev/null +++ "b/doc/1.6.8 - 1.7.5 \345\234\250Server\344\270\255\344\275\277\347\224\250swoole_table.md" @@ -0,0 +1,27 @@ +#1.7.5 在Server中使用swoole_table + +1.7.5增加了swoole_table共享内存表,swoole_table可以与swoole_server结合使用。使用方法也很简单 + +* 在swoole_server->start()之前创建swoole_table对象。并存入全局变量或者类静态变量/对象属性中。 +* 在worker/task进程中获取table对象,并使用 + +> 只有在swoole_server->start()之前创建的table对象才能在子进程中使用 +> swoole_table构造方法中指定了最大容量,一旦超过此数据容量将无法分配内存导致set操作失败。所以使用swoole_table之前一定要规划好数据容量 + +```php +$table = new swoole_table(1024); +$table->column('fd', swoole_table::TYPE_INT); +$table->column('from_id', swoole_table::TYPE_INT); +$table->column('data', swoole_table::TYPE_STRING, 64); +$table->create(); + +$serv = new swoole_server('127.0.0.1', 9501); +//将table保存在serv对象上 +$serv->table = $table; + +$serv->on('receive', function ($serv, $fd, $from_id, $data) { + $ret = $serv->table->set($fd, array('from_id' => $data, 'fd' => $fd, 'data' => $data)); +}); + +$serv->start(); +``` diff --git "a/doc/1.6.9 - 1.7.5 swoole_client\346\224\257\346\214\201sendfile\346\216\245\345\217\243.md" "b/doc/1.6.9 - 1.7.5 swoole_client\346\224\257\346\214\201sendfile\346\216\245\345\217\243.md" new file mode 100644 index 0000000..4882668 --- /dev/null +++ "b/doc/1.6.9 - 1.7.5 swoole_client\346\224\257\346\214\201sendfile\346\216\245\345\217\243.md" @@ -0,0 +1,26 @@ +#1.7.5 swoole_client支持sendfile接口 + +1.7.5增加了swoole_client->sendfile接口,在客户端中也可以直接发送一个文件到服务器。使用方法 + +```php +$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 +if (!$client->connect('127.0.0.1', 9501, -1)) +{ + exit("connect failed. Error: {$client->errCode}\n"); +} +if ($client->sendfile(__DIR__.'/test.txt') === false) +{ + echo "send failed. Error: {$client->errCode}\n"; + break; +} +$data = $client->recv(7000); +if ($data === false) +{ + echo "recv failed. Error: {$client->errCode}\n"; + break; +} +var_dump($data); +$client->close(); +``` + +sendfile只需要传入文件名即可发送到服务器。当文件不存在时会返回false。 diff --git "a/doc/1.7 - \351\241\271\347\233\256\350\267\257\347\272\277\345\233\276.md" "b/doc/1.7 - \351\241\271\347\233\256\350\267\257\347\272\277\345\233\276.md" new file mode 100644 index 0000000..c692642 --- /dev/null +++ "b/doc/1.7 - \351\241\271\347\233\256\350\267\257\347\272\277\345\233\276.md" @@ -0,0 +1,31 @@ +#项目路线图 + +Swoole1.0 +==== + +工作列表:[https://trello.com/b/SEdDCrCu/swoole-kernel-developer](https://trello.com/b/SEdDCrCu/swoole-kernel-developer) +``` +欢迎大家在trello平台上提交idea +``` + + +特性支持列表 +----- +* 支持直接接收超大文件 +* Moniter监控进程 + +网络服务协议 +----- +* SMTP/IMAP/POP3 +* 异步Memcache客户端 +* Cocos2dx +* Socket.io +* 异步MongoDB客户端 + +分布式系统 +----- +* 分布式文件系统 +* 分布式运算框架 +* Master/Worker架构 +* SOA逻辑层服务化架构 +* Proxy/Router/RealServer diff --git "a/doc/1.8 - php.ini\351\200\211\351\241\271.md" "b/doc/1.8 - php.ini\351\200\211\351\241\271.md" new file mode 100644 index 0000000..d6fba07 --- /dev/null +++ "b/doc/1.8 - php.ini\351\200\211\351\241\271.md" @@ -0,0 +1,57 @@ +#php.ini选项 + +swoole.aio_thread_num +---- +设置AIO异步文件读写的线程数量 + +swoole.display_errors +--- +关闭/开启Swoole错误信息 + +swoole.unixsock_buffer_size +------ +设置进程间通信的UnixSocket缓存区尺寸 + +swoole.fast_serialize +------ +swoole_server中的task功能中是否使用swoole_serialize对异步任务数据序列化 + +swoole.use_namespace +------ +> 此配置不支持运行时修改,必须运行前设置php.ini来启用命名空间 +> 1.8.7或更高版本不再需要设置此选项,可同时使用命名空间/非命名空间2种风格 + +使用命名空间类风格,默认为关闭。启用命名空间后,所有的swoole类必须修改为命名空间格式。 +对应关系如下: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
下划线类名风格命名空间风格
swoole_serverSwoole\Server
swoole_clientSwoole\Client
swoole_processSwoole\Process
swoole_timerSwoole\Timer
swoole_tableSwoole\Table
swoole_lockSwoole\Lock
swoole_atomicSwoole\Atomic
swoole_bufferSwoole\Buffer
swoole_redisSwoole\Redis
swoole_eventSwoole\Event
swoole_mysqlSwoole\MySQL
swoole_mmapSwoole\Mmap
swoole_channelSwoole\Channel
swoole_serializeSwoole\Serialize
swoole_http_serverSwoole\Http\Server
swoole_http_clientSwoole\Http\Client
swoole_http_requestSwoole\Http\Request
swoole_http_responseSwoole\Http\Response
swoole_websocket_serverSwoole\WebSocket\Server
+ +> 需要swoole-1.8.1或更高版本 diff --git "a/doc/1.9 - \345\206\205\346\240\270\345\217\202\346\225\260\350\260\203\346\225\264.md" "b/doc/1.9 - \345\206\205\346\240\270\345\217\202\346\225\260\350\260\203\346\225\264.md" new file mode 100644 index 0000000..41d16bd --- /dev/null +++ "b/doc/1.9 - \345\206\205\346\240\270\345\217\202\346\225\260\350\260\203\346\225\264.md" @@ -0,0 +1,101 @@ +#内核参数调整 + +ulimit设置 +---- +ulimit -n 要调整为100000甚至更大。 命令行下执行 ulimit -n 100000即可修改。如果不能修改,需要设置 /etc/security/limits.conf,加入 +``` +* soft nofile 262140 +* hard nofile 262140 +root soft nofile 262140 +root hard nofile 262140 +* soft core unlimited +* hard core unlimited +root soft core unlimited +root hard core unlimited +``` +注意,修改`limits.conf`文件后,需要重启系统生效 + +内核设置 +------ +`Linux`操作系统修改内核参数有3种方式: + +* 修改`/etc/sysctl.conf`文件,加入配置选项,格式为`key = value`,修改保存后调用`sysctl -p`加载新配置 +* 使用`sysctl`命令临时修改,如:`sysctl -w net.ipv4.tcp_mem="379008 505344 758016"` +* 直接修改`/proc/sys/`目录中的文件,如:`echo "379008 505344 758016" > /proc/sys/net/ipv4/tcp_mem` + +> 第一种方式在操作系统重启后会自动生效,第二和第三种方法重启后失效 + +###net.unix.max_dgram_qlen = 100### +swoole使用unix socket dgram来做进程间通信,如果请求量很大,需要调整此参数。系统默认为10,可以设置为100或者更大。 +或者增加worker进程的数量,减少单个worker进程分配的请求量。 + +### net.core.wmem_max### +修改此参数增加socket缓存区的内存大小 + +``` +net.ipv4.tcp_mem = 379008 505344 758016 +net.ipv4.tcp_wmem = 4096 16384 4194304 +net.ipv4.tcp_rmem = 4096 87380 4194304 +net.core.wmem_default = 8388608 +net.core.rmem_default = 8388608 +net.core.rmem_max = 16777216 +net.core.wmem_max = 16777216 +``` + +### net.ipv4.tcp_tw_reuse ### +是否socket reuse,此函数的作用是Server重启时可以快速重新使用监听的端口。如果没有设置此参数,会导致server重启时发生端口未及时释放而启动失败 +### net.ipv4.tcp_tw_recycle ### +使用socket快速回收,短连接Server需要开启此参数。此参数表示开启TCP连接中TIME-WAIT sockets的快速回收,Linux系统中默认为0,表示关闭。打开此参数可能会造成NAT用户连接不稳定,请谨慎测试后再开启。 + +消息队列设置 +----- +当使用消息队列作为进程间通信方式时,需要调整此内核参数 + +* kernel.msgmnb = 4203520,消息队列的最大字节数 +* kernel.msgmni = 64,最多允许创建多少个消息队列 +* kernel.msgmax = 8192,消息队列单条数据最大的长度 + +FreeBSD/MacOS +---- +* sysctl -w net.local.dgram.maxdgram=8192 +* sysctl -w net.local.dgram.recvspace=200000 +修改Unix Socket的buffer区尺寸 + +开启CoreDump +------ +设置内核参数 +``` +kernel.core_pattern = /data/core_files/core-%e-%p-%t +``` + +通过ulimit -c命令查看当前coredump文件的限制 +```shell +ulimit -c +``` +如果为0,需要修改/etc/security/limits.conf,进行limit设置。 + +> 开启core-dump后,一旦程序发生异常,会将进程导出到文件。对于调查程序问题有很大的帮助 + + +其他重要配置 +----- + +* net.ipv4.tcp_syncookies=1 +* net.ipv4.tcp_max_syn_backlog=81920 +* net.ipv4.tcp_synack_retries=3 +* net.ipv4.tcp_syn_retries=3 +* net.ipv4.tcp_fin_timeout = 30 +* net.ipv4.tcp_keepalive_time = 300 +* net.ipv4.tcp_tw_reuse = 1 +* net.ipv4.tcp_tw_recycle = 1 +* net.ipv4.ip_local_port_range = 20000 65000 +* net.ipv4.tcp_max_tw_buckets = 200000 +* net.ipv4.route.max_size = 5242880 + +查看配置是否生效 +---- +如:修改net.unix.max_dgram_qlen = 100后,通过 +```shell +cat /proc/sys/net/unix/max_dgram_qlen +``` +如果修改成功,这里是新设置的值。 diff --git "a/doc/10 - \345\215\217\347\250\213 Server.md" "b/doc/10 - \345\215\217\347\250\213 Server.md" new file mode 100644 index 0000000..543f200 --- /dev/null +++ "b/doc/10 - \345\215\217\347\250\213 Server.md" @@ -0,0 +1,68 @@ +#协程 Server + +Swoole在`2.0`开始内置**协程(Coroutine)**的能力,提供了具备协程能力IO接口(统一在命名空间`Swoole\Coroutine\*`)。 + +> `2.0.2`或更高版本已支持`PHP7` +> `2.0.8`或更高版本已默认开启`--enable-coroutine`,可使用`--disable-coroutine`关闭协程特性 + +协程可以理解为纯用户态的线程,其通过**协作**而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。Swoole可以为每一个请求创建对应的协程,根据IO的状态来合理的调度协程,这会带来了以下优势: + +1. 开发者可以无感知的用同步的代码编写方式达到异步IO的效果和性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护。 + +2. 同时由于swoole是在底层封装了协程,所以对比传统的php层协程框架,开发者不需要使用yield关键词来标识一个协程IO操作,所以不再需要对yield的语义进行深入理解以及对每一级的调用都修改为yield,这极大的提高了开发效率。 + +协程API目前针对了TCP,UDP等主流协议client的封装,包括: + +- UDP +- TCP +- HTTP +- Mysql +- Redis + +可以满足大部分开发者的需求。对于私有协议,开发者可以使用协程的TCP或者UDP接口去方便的封装。 + +## 启用 +### Prerequisite: + +* PHP版本要求:**>= 7.0**,包括7.0、7.1、7.2 +* 基于`swoole_server`或者`swoole_http_server`进行开发,目前只支持在`onRequet`, `onReceive`, `onConnect`等事件回调函数中使用协程。 + +`swoole_server`和`swoole_http_server`将为每一个请求创建对应的协程,开发者可以在`onRequet`、`onReceive`、`onConnect` 事件回调中使用协程客户端。 + +### 相关配置 +在`Swoole\Server`的`set`方法中增加了一个配置参数`max_coro_num`,用于配置一个`Worker`进程最多同时处理的协程数目。因为随着`Worker`进程处理的协程数目的增加,其占用的内存也会增加,为了避免超出php的`memory_limit`限制,请根据实际业务的压测结果设置该值,默认为`3000`。 + +## 使用示例 + +```php +$http = new swoole_http_server("127.0.0.1", 9501); + +$http->on("request", function ($request, $response) { + $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $client->connect("127.0.0.1", 8888, 0.5); + //调用connect将触发协程切换 + $client->send("hello world from swoole"); + //调用recv将触发协程切换 + $ret = $client->recv(); + $response->header("Content-Type", "text/plain"); + $response->end($ret); + $client->close(); +}); + +$http->start(); +``` + +当代码执行到`connect()和recv()`函数时,swoole会触发进行协程切换,此时swoole可以去处理其他的事件或者接受新的请求。当此client`连接`成功或者后端服务`回包`后,swoole server会恢复协程上下文,代码逻辑继续从切换点开始恢复执行。开发者整个过程不需要关心整个切换过程。具体使用可以参考client的文档。 + +## 注意事项 + +1. 全局变量:协程使得原有的异步逻辑同步化,但是在协程的切换是隐式发生的,所以在协程切换的前后不能保证全局变量以及static变量的一致性。 +2. 请勿在**2.2以下的版本**的以下场景中触发协程切换: + * 析构函数 + * 魔术方法`__call()` `__get()` `__set()` 等 +3. gcc 4.4下如果在编译swoole的时候(即make阶段),出现gcc warning: +`dereferencing pointer ‘v.327’ does break strict-aliasing rules`、 +`dereferencing type-punned pointer will break strict-aliasing rules` +请手动编辑Makefile,将` CFLAGS = -Wall -pthread -g -O2`替换为`CFLAGS = -Wall -pthread -g -O2 -fno-strict-aliasing`,然后重新编译`make clean;make;make install` +4. 与xdebug、xhprof、blackfire等zend扩展不兼容,例如不能使用xhprof对协程server进行性能分析采样。 +5. 原生的call_user_func和call_user_func_array中无法使用协程client,请使用\Swoole\Coroutine::call_user_func和\Swoole\Coroutine::call_user_func_array代替,在PHP7中如果无法保证在编译时反射调用的类是编译器已知的,请统一使用协程版反射调用 diff --git "a/doc/10.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" "b/doc/10.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" new file mode 100644 index 0000000..3cdddc4 --- /dev/null +++ "b/doc/10.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" @@ -0,0 +1,43 @@ +#方法列表 + +并发执行 +---- +使用协程后`onConnect`、`onReceive`、`onClose`是在不同的协程中并发执行的,需要注意进行状态检测。 + +短名称 +---- +在`2.0.13`与`2.1.0`或更高版本中,增加了协程短名特性,简化了协程相关`API`的名称书写。可修改`php.ini`设置`swoole.use_shortname`来关闭/开启短名,默认为开启。 + +#### 创建协程 +```php +go(function () { + co::sleep(0.5); + echo "hello"; +}); +go("test"); +go([$object, "method"]); +``` + +#### 通道操作 +```php +$c = new chan(1); +$c->push($data); +$c->pop(); +``` + +#### 协程客户端 +```php +$redis = new Co\Redis; +$mysql = new Co\MySQL; +$http = new Co\Http\Client; +$tcp = new Co\Client; +$http2 = new Co\Http2\Client; +``` + +#### 其他 API +```php +co::sleep(100); +co::fread($fp); +co::gethostbyname('www.baidu.com'); +``` + diff --git a/doc/10.1.1 - getDefer.md b/doc/10.1.1 - getDefer.md new file mode 100644 index 0000000..1f41219 --- /dev/null +++ b/doc/10.1.1 - getDefer.md @@ -0,0 +1,8 @@ +#getDefer + +#### getDefer() +- - - +```php +bool getDefer(); +``` +* 返回值:返回当前设置的defer \ No newline at end of file diff --git a/doc/10.1.2 - setDefer.md b/doc/10.1.2 - setDefer.md new file mode 100644 index 0000000..84938eb --- /dev/null +++ b/doc/10.1.2 - setDefer.md @@ -0,0 +1,10 @@ +#setDefer + +#### setDefer() +- - - +```php +bool setDefer([bool $is_defer = true]); +``` +* $is_defer:bool值,为true时,表明该Client要延迟收包,为false时,表明该Client非延迟收包,默认值为true +* 返回值:设置成功返回true,否则返回false。只有一种情况会返回false,当设置defer(true)并发包后,尚未recv()收包,就设置defer(false),此时返回false。 +* 如果需要进行延迟收包,需要在发包之前调用 diff --git a/doc/10.1.3 - recv.md b/doc/10.1.3 - recv.md new file mode 100644 index 0000000..937d7c7 --- /dev/null +++ b/doc/10.1.3 - recv.md @@ -0,0 +1,8 @@ +#recv + +#### recv() +- - - +```php +mixed recv(); +``` +* 返回值:获取延迟收包的结果,当没有进行延迟收包或者收包超时,返回false。 \ No newline at end of file diff --git a/doc/10.1.4 - Coroutine::create.md b/doc/10.1.4 - Coroutine::create.md new file mode 100644 index 0000000..0333493 --- /dev/null +++ b/doc/10.1.4 - Coroutine::create.md @@ -0,0 +1,29 @@ +#Coroutine::create + +创新协程。函数原型: +```php +function Swoole\Coroutine::create(callable $function); +``` + +> 此方法在Swoole-2.0.7或更高版本+PHP7环境下可用 + +使用实例 +---- +```php +for($i = 0; $i < 100; $i++) { + Swoole\Coroutine::create(function() use ($i) { + $redis = new Swoole\Coroutine\Redis(); + $res = $redis->connect('127.0.0.1', 6379); + $ret = $redis->incr('coroutine'); + $redis->close(); + if ($i == 50) { + Swoole\Coroutine::create(function() use ($i) { + $redis = new Swoole\Coroutine\Redis(); + $res = $redis->connect('127.0.0.1', 6379); + $ret = $redis->set('coroutine_i', 50); + $redis->close(); + }); + } + }); +} +``` \ No newline at end of file diff --git a/doc/10.1.5 - Coroutine::getuid.md b/doc/10.1.5 - Coroutine::getuid.md new file mode 100644 index 0000000..1c7e933 --- /dev/null +++ b/doc/10.1.5 - Coroutine::getuid.md @@ -0,0 +1,26 @@ +#Coroutine::getuid + +获取当前协程的ID。函数原型 +```php +int function Swoole\Coroutine::getuid(); +``` + +协程task的结构是: + +```c +struct _coro_task +{ + int cid; //协程ID + /** + * user coroutine + */ + zval *function; + time_t start_time; + void (*post_callback)(void *param); + void *post_callback_params; +}; +``` + + +* 返回当前协程ID,是一个由0增长的长整型。 +* 服务器被请求周期内,获取到的数值是固定的不变的,可用来作为一些全局变量下标使用 \ No newline at end of file diff --git "a/doc/10.2 - \345\271\266\345\217\221\350\260\203\347\224\250.md" "b/doc/10.2 - \345\271\266\345\217\221\350\260\203\347\224\250.md" new file mode 100644 index 0000000..c1897f6 --- /dev/null +++ "b/doc/10.2 - \345\271\266\345\217\221\350\260\203\347\224\250.md" @@ -0,0 +1,57 @@ +#并发调用 + +Client并发请求 +--------- +在协程版本的`Client`中,实现了多个客户端并发发包功能(`setDefer`机制)。 + +通常,如果一个业务请求中需要做一次redis请求和一次mysql请求,那么网络IO会是这样子: + +`redis发包->redis收包->mysql发包->mysql收包` + +以上流程网络IO的时间就等于 redis网络IO时间 + mysql网络IO时间。 + +而对于协程版本的Client,网络IO可以是这样子: + +`redis发包->mysql发包->redis收包->mysql收包` + +以上流程网络IO的时间就接近于 MAX(redis网络IO时间, mysql网络IO时间)。 + +现在支持并发请求的Client有: + +* Swoole\Coroutine\Client +* Swoole\Coroutine\Redis +* Swoole\Coroutine\MySQL +* Swoole\Coroutine\Http\Client + +除了`Swoole\Coroutine\Client`,其他`Client`都实现了`defer`特性,用于声明延迟收包。 + +因为`Swoole\Coroutine\Client`的发包和收包方法是分开的,所以就不需要实现`defer`特性了,而其他Client的发包和收包都是在一个方法中,所以需要一个`setDefer()`方法声明延迟收包,然后通过`recv()`方法收包。 + +setDefer 使用实例 +---- +```php +function onRequest($request, $response) +{ + //并发请求 n + $n = 5; + for ($i = 0; $i < $n; $i++) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80); + $cli->setHeaders([ + 'Host' => "local.ad.oa.com", + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $cli->set([ 'timeout' => 2]); + $cli->setDefer(); + $cli->get('/test.php'); + $clients[] = $cli; + } + + for ($i = 0; $i < $n; $i++) { + $r = $clients [$i]->recv(); + $result[] = $clients[$i]->body; + } + $response->end(json_encode($data)); +} +``` diff --git "a/doc/10.2.1 - \344\275\277\347\224\250\345\256\236\344\276\213.md" "b/doc/10.2.1 - \344\275\277\347\224\250\345\256\236\344\276\213.md" new file mode 100644 index 0000000..0327b07 --- /dev/null +++ "b/doc/10.2.1 - \344\275\277\347\224\250\345\256\236\344\276\213.md" @@ -0,0 +1,47 @@ +#使用实例 + +协程版本Client并发请求示例代码: +```php +set([ + 'worker_num' => 1, +]); + +$server->on('Request', function ($request, $response) { + + $tcpclient = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $tcpclient->connect('127.0.0.1', 9501,0.5) + $tcpclient->send("hello world\n"); + + $redis = new Swoole\Coroutine\Redis(); + $redis->connect('127.0.0.1', 6379); + $redis->setDefer(); + $redis->get('key'); + + $mysql = new Swoole\Coroutine\MySQL(); + $mysql->connect([ + 'host' => '127.0.0.1', + 'user' => 'user', + 'password' => 'pass', + 'database' => 'test', + ]); + $mysql->setDefer(); + $mysql->query('select sleep(1)'); + + $httpclient = new Swoole\Coroutine\Http\Client('0.0.0.0', 9599); + $httpclient->setHeaders(['Host' => "api.mp.qq.com"]); + $httpclient->set([ 'timeout' => 1]); + $httpclient->setDefer(); + $httpclient->get('/'); + + $tcp_res = $tcpclient->recv(); + $redis_res = $redis->recv(); + $mysql_res = $mysql->recv(); + $http_res = $httpclient->recv(); + + $response->end('Test End'); +}); +$server->start(); +``` \ No newline at end of file diff --git "a/doc/10.3 - \345\256\236\347\216\260\345\216\237\347\220\206.md" "b/doc/10.3 - \345\256\236\347\216\260\345\216\237\347\220\206.md" new file mode 100644 index 0000000..d9f279a --- /dev/null +++ "b/doc/10.3 - \345\256\236\347\216\260\345\216\237\347\220\206.md" @@ -0,0 +1,97 @@ +#实现原理 + + `Swoole-2.0`基于`setjmp`、`longjmp`实现,在进行协程切换时会自动保存Zend VM的内存状态(主要是`EG`全局内存和`vm stack`)。 + +* `setjmp`和`longjmp`主要是用于从`ZendVM`的`C`堆栈跳回`Swoole`的`C`回调函数 +* 协程的创建、切换、挂起、销毁全部为内存操作,消耗是非常低的 + +示例代码 +---- +```php +$server = new Swoole\Http\Server('127.0.0.1', 9501, SWOOLE_BASE); + +#1 +$server->on('Request', function($request, $response) { + $mysql = new Swoole\Coroutine\MySQL(); + #2 + $res = $mysql->connect([ + 'host' => '127.0.0.1', + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', + ]); + #3 + if ($res == false) { + $response->end("MySQL connect fail!"); + return; + } + $ret = $mysql->query('show tables', 2); + $response->end("swoole response is ok, result=".var_export($ret, true)); +}); + +$server->start(); +``` + +* 此程序仅启动了一个1个进程,就可以并发处理大量请求。 +* 程序的性能基本上与异步回调方式相同,但是代码完全是同步编写的 + +运行过程 +---- +* 调用`onRequest`事件回调函数时,底层会调用C函数`coro_create`创建一个协程(#1位置),同时保存这个时间点的CPU寄存器状态和ZendVM stack信息。 +* 调用`mysql->connect`时发生IO操作,底层会调用C函数`coro_save`保存当前协程的状态,包括Zend VM上下文以及协程描述信息,并调用`coro_yield`让出程序控制权,当前的请求会挂起(#2位置) +* 协程让出程序控制权后,会继续进入EventLoop处理其他事件,这时Swoole会继续去处理其他客户端发来的Request +* IO事件完成后,MySQL连接成功或失败,底层调用C函数`coro_resume`恢复对应的协程,恢复ZendVM上下文,继续向下执行PHP代码(#3位置) +* `mysql->query`的执行过程与`mysql->connect`一致,也会进行一次协程切换调度 +* 所有操作完成后,调用`end`方法返回结果,并销毁此协程 + +协程开销 +---- +相比普通的异步回调程序,协程多增加额外的内存占用。 + +* Swoole2.0协程需要为每个并发保存zend stack栈内存并维护对应的虚拟机状态。如果程序并发很大可能会占用大量内存,取决于C函数、ZendVM 调用栈深度 +* 协程调度会增加额外的一些CPU开销 + +压力测试 +---- +* 环境:`Ubuntu16.04 + Core I5 4核 + 8G内存 PHP7.0.10` +* 脚本:`ab -c 100 -n 10000 http://127.0.0.1:9501/` + +测试结果: +```shell +Server Software: swoole-http-server +Server Hostname: 127.0.0.1 +Server Port: 9501 + +Document Path: / +Document Length: 348 bytes + +Concurrency Level: 100 +Time taken for tests: 0.883 seconds +Complete requests: 10000 +Failed requests: 168 + (Connect: 0, Receive: 0, Length: 168, Exceptions: 0) +Total transferred: 4914560 bytes +HTML transferred: 3424728 bytes +Requests per second: 11323.69 [#/sec] (mean) +Time per request: 8.831 [ms] (mean) +Time per request: 0.088 [ms] (mean, across all concurrent requests) +Transfer rate: 5434.67 [Kbytes/sec] received + +Connection Times (ms) + min mean[+/-sd] median max +Connect: 0 0 0.2 0 2 +Processing: 0 9 9.6 6 96 +Waiting: 0 9 9.6 6 96 +Total: 0 9 9.6 6 96 + +Percentage of the requests served within a certain time (ms) + 50% 6 + 66% 9 + 75% 11 + 80% 12 + 90% 19 + 95% 27 + 98% 43 + 99% 51 + 100% 96 (longest request) +``` diff --git "a/doc/10.3.1 - \345\215\217\347\250\213\344\270\216\347\272\277\347\250\213.md" "b/doc/10.3.1 - \345\215\217\347\250\213\344\270\216\347\272\277\347\250\213.md" new file mode 100644 index 0000000..e930e4f --- /dev/null +++ "b/doc/10.3.1 - \345\215\217\347\250\213\344\270\216\347\272\277\347\250\213.md" @@ -0,0 +1,7 @@ +#协程与线程 + + `Swoole`的协程在底层实现上是单线程的,因此同一时间只有一个协程在工作,协程的执行是串行的。这与线程不同,多个线程会被操作系统调度到多个CPU`并行`执行。 + +一个协程正在运行时,其他协程会停止工作。当前协程执行阻塞`IO`操作时会挂起,底层调度器会进入事件循环。当有`IO`完成事件时,底层调度器恢复事件对应的协程的执行。 + +对`CPU`多核的利用,仍然依赖于`Swoole`引擎的多进程机制。 \ No newline at end of file diff --git "a/doc/10.3.2 - \345\217\221\351\200\201\346\225\260\346\215\256\345\215\217\347\250\213\350\260\203\345\272\246.md" "b/doc/10.3.2 - \345\217\221\351\200\201\346\225\260\346\215\256\345\215\217\347\250\213\350\260\203\345\272\246.md" new file mode 100644 index 0000000..0144466 --- /dev/null +++ "b/doc/10.3.2 - \345\217\221\351\200\201\346\225\260\346\215\256\345\215\217\347\250\213\350\260\203\345\272\246.md" @@ -0,0 +1,52 @@ +#发送数据协程调度 + +现状 +---- +现在 `Server/Client->send` 在缓存区已满的情况下,会直接返回`false`,需要借助`onBufferFull`和`onBufferEmpty`这样复杂的事件通知机制才能实现任务的暂停和恢复。 + +在实现需要大量发送的场景下,现有机制虽然可以实现,但非常复杂。 + +思路 +---- +现在基于协程可以实现一种机制,直接在当前协程内`yield`,等待数据发送完成,缓存区清空时,自动`resume`当前协程,继续`send`数据。 + +* `Server/Client->send`返回`false`并且错误码为`SW_ERROR_OUTPUT_BUFFER_OVERFLOW`时,不返回`false`到`php`层,而是`yield`挂起当前协程 +* `Server/Client`监听`onBufferEmpty`事件,在该事件触发后,缓存区内的数据已被发送完毕,这时`resume`对应的协程 +* 协程恢复后,继续调用`Server/Client->send`向缓存区内写入数据,这时因为缓存区已空,发送必然是成功的 + +实例 +--- +#### 改进前 +```php +for ($i = 0; $i < 100; $i++) +{ + //在缓存区塞满时会直接返回`false` + $server->send($fd, $data_2m); +} +``` + +#### 改进后 +```php +for ($i = 0; $i < 100; $i++) +{ + //在缓存区塞满时会 yield 当前协程,发送完成后 resume 继续向下执行 + $server->send($fd, $data_2m); +} +``` + +选项 +--- +此项特性会改变底层的默认行为,因此需要额外的一个参数来开启。 +```php +$serv->set([ + 'send_yield' => true, +]); +``` + +影响范围 +---- +* `Swoole\Server::send` +* `Swoole\Http\Response::write` +* `Swoole\WebSocket\Server::push` +* `Swoole\Coroutine\Client::send` +* `Swoole\Coroutine\Http\Client::push` diff --git "a/doc/10.4 - \345\270\270\350\247\201\351\227\256\351\242\230.md" "b/doc/10.4 - \345\270\270\350\247\201\351\227\256\351\242\230.md" new file mode 100644 index 0000000..363ae38 --- /dev/null +++ "b/doc/10.4 - \345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -0,0 +1,16 @@ +#常见问题 + +协程是并行执行的吗? +----- +协程与线程不同,在一个进程内创建的多个协程,实际上是串行的。同一`CPU`时间,只有一个协程在执行,因此协程不存在数据同步问题。 + +协程内可以安全的修改全局变量的值,而不考虑锁的问题。 + +在协程的执行过程中,调用`IO`操作时,会自动让出控制权。这时其他`IO`操作完成的协程才会执行。当`IO`操作完成后,底层会重新切换会当前的协程。 + +协程之间如何通信? +---- +可使用`SplQueue`或`Swoole\Coroutine\Channel`实现协程之间的通信。 + + + diff --git "a/doc/10.4.1 - \350\277\220\350\241\214\344\270\255\345\207\272\347\216\260 Fatal error: Maximum function nesting level of '1000' reached, aborting!.md" "b/doc/10.4.1 - \350\277\220\350\241\214\344\270\255\345\207\272\347\216\260 Fatal error: Maximum function nesting level of '1000' reached, aborting!.md" new file mode 100644 index 0000000..37aff52 --- /dev/null +++ "b/doc/10.4.1 - \350\277\220\350\241\214\344\270\255\345\207\272\347\216\260 Fatal error: Maximum function nesting level of '1000' reached, aborting!.md" @@ -0,0 +1,3 @@ +#运行中出现 Fatal error: Maximum function nesting level of '1000' reached, aborting! + +这是由于`xdebug`与`Swoole-2.0`协程冲突导致,请配置`php.ini`去掉`xdebug`扩展。 \ No newline at end of file diff --git "a/doc/10.4.2 - \344\270\272\344\273\200\344\271\210\345\217\252\350\203\275\345\234\250\345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\215\217\347\250\213\345\256\242\346\210\267\347\253\257.md" "b/doc/10.4.2 - \344\270\272\344\273\200\344\271\210\345\217\252\350\203\275\345\234\250\345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\215\217\347\250\213\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 0000000..861e914 --- /dev/null +++ "b/doc/10.4.2 - \344\270\272\344\273\200\344\271\210\345\217\252\350\203\275\345\234\250\345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\344\275\277\347\224\250\345\215\217\347\250\213\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,7 @@ +#为什么只能在回调函数中使用协程客户端 + +因为只有`onReceive`、`onRequest`等回调函数中创建了协程,因此只能在这些回调函数的生命周期内使用协程客户端。 + +但不影响PHP代码对协程客户端进行一些代码封装。 + +最新的`2.0.12`版本已经支持用户代码自行创建协程,在不支持协程的回调函数中,可以调用`Coroutine::create`自行创建协程。 diff --git "a/doc/10.4.3 - \346\224\257\346\214\201\345\215\217\347\250\213\347\232\204\345\233\236\350\260\203\346\226\271\346\263\225\345\210\227\350\241\250.md" "b/doc/10.4.3 - \346\224\257\346\214\201\345\215\217\347\250\213\347\232\204\345\233\236\350\260\203\346\226\271\346\263\225\345\210\227\350\241\250.md" new file mode 100644 index 0000000..f9cca05 --- /dev/null +++ "b/doc/10.4.3 - \346\224\257\346\214\201\345\215\217\347\250\213\347\232\204\345\233\236\350\260\203\346\226\271\346\263\225\345\210\227\350\241\250.md" @@ -0,0 +1,35 @@ +#支持协程的回调方法列表 + +目前`Swoole2`仅有部分事件回调函数底层自动创建了协程,可以调用协程客户端。本节列出了支持协程客户端的回调列表以及实现的版本号。 + +> 在不支持协程的位置可以使用`go`或`Co::create`创建协程 + +v2.0.5 +----- +* onConnect +* onReceive +* onPacket +* onRequest +* onHandShake + +v2.0.6 +--- +* onMessage + +v2.0.7 +---- +* onOpen +* Redis\Server->handler + +v2.0.9 +--- +* tick/after 定时器 + +v2.0.10 +--- +* onPipeMessage + +v2.0.13 +---- +* onWorkerStart +* onClose \ No newline at end of file diff --git "a/doc/10.5 - \347\274\226\347\250\213\351\241\273\347\237\245.md" "b/doc/10.5 - \347\274\226\347\250\213\351\241\273\347\237\245.md" new file mode 100644 index 0000000..971b46e --- /dev/null +++ "b/doc/10.5 - \347\274\226\347\250\213\351\241\273\347\237\245.md" @@ -0,0 +1,29 @@ +#编程须知 + +使用`Swoole-2.x`内置协程,请认真阅读本页编程须知。 + + +严重错误 +---- +以下行为会导致出现严重错误,某些情况下可能会导致`PHP`发生崩溃。 + +* [在多个协程间共用一个连接](/wiki/page/852.html) +* [在魔术方法中使用协程 API](/wiki/page/853.html) +* [在反射函数中使用协程 API](/wiki/page/853.html) +* [在对象析构函数中使用协程 API](/wiki/page/853.html) +* [call_user_func/call_user_func_array中使用协程 API](/wiki/page/853.html) +* [使用类静态变量/全局变量保存上下文](/wiki/page/865.html) + +扩展冲突 +---- +某些跟踪调试的`PHP`扩展可能会导致`Swoole 2.0`协程发生崩溃。请关闭这些相关扩展。 + +* xdebug +* phptrace +* aop +* molten +* xhprof + +缓存区控制 +---- +请勿在`ob_start`和`ob_get_clean`、`ob_end_clean`中间使用协程`API`,否则会引起错乱。可将页面渲染与逻辑分离,仅在逻辑代码中使用协程`API`,在页面渲染(`ob`系列操作)处理过程中不要使用协程。 \ No newline at end of file diff --git "a/doc/10.5.1 - \345\234\250\345\244\232\344\270\252\345\215\217\347\250\213\351\227\264\345\205\261\347\224\250\345\220\214\344\270\200\344\270\252\345\215\217\347\250\213\345\256\242\346\210\267\347\253\257.md" "b/doc/10.5.1 - \345\234\250\345\244\232\344\270\252\345\215\217\347\250\213\351\227\264\345\205\261\347\224\250\345\220\214\344\270\200\344\270\252\345\215\217\347\250\213\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 0000000..40943d5 --- /dev/null +++ "b/doc/10.5.1 - \345\234\250\345\244\232\344\270\252\345\215\217\347\250\213\351\227\264\345\205\261\347\224\250\345\220\214\344\270\200\344\270\252\345\215\217\347\250\213\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,92 @@ +#在多个协程间共用同一个协程客户端 + +同步阻塞程序不同,协程是并发处理请求的,因此同一时间可能会有很多个请求在并行处理,一旦共用客户端连接,就会导致不同协程之间发生数据错乱。 + +#### 错误的代码 +```php +$server = new Swoole\Http\Server('127.0.0.1', 9501); + +$server->on('Receive', function ($serv, $fd, $rid, $data) { + $redis = RedisFactory::getRedis(); + $result = $redis->hgetall('key'); + $resp->end(var_export($result, true)); +}); + +$server->start(); + +class RedisFactory +{ + private static $_redis = null; + + public static function getRedis() + { + if (null === self::$_redis) { + $redis = new \Swoole\Coroutine\Redis(); + $redis->connect('127.0.0.1', 6379); + self::$_redis = $redis; + } + return self::$_redis; + } +} +``` + +#### 正确的代码 +基于`SplQueue`实现协程客户端的连接池,可以复用协程客户端,实现长连接。 + +```php +$pool = new RedisPool(); +$server = new Swoole\Http\Server('127.0.0.1', 9501); + +$server->on('Request', function($req, $resp) use ($pool) { + //从连接池中获取一个Redis协程客户端 + $redis = $pool->get(); + //连接失败 + if ($redis === false) + { + $resp->end("ERROR"); + return; + } + $result = $redis->hgetall('key'); + $resp->end(var_export($result, true)); + //释放客户端,其他协程可复用此对象 + $pool->put($redis); +}); + +$server->start(); + +class RedisPool +{ + protected $pool; + + function __construct() + { + $this->pool = new SplQueue; + } + + function put($redis) + { + $this->pool->push($redis); + } + + function get() + { + //有空闲连接 + if (count($this->pool) > 0) + { + return $this->pool->pop(); + } + + //无空闲连接,创建新连接 + $redis = new Swoole\Coroutine\Redis(); + $res = $redis->connect('127.0.0.1', 6379); + if ($res == false) + { + return false; + } + else + { + return $redis; + } + } +} +``` diff --git "a/doc/10.5.2 - \347\246\201\346\255\242\344\275\277\347\224\250\345\215\217\347\250\213 API \347\232\204\345\234\272\346\231\257 ( ver < 2.2 ).md" "b/doc/10.5.2 - \347\246\201\346\255\242\344\275\277\347\224\250\345\215\217\347\250\213 API \347\232\204\345\234\272\346\231\257 ( ver < 2.2 ).md" new file mode 100644 index 0000000..62be1a8 --- /dev/null +++ "b/doc/10.5.2 - \347\246\201\346\255\242\344\275\277\347\224\250\345\215\217\347\250\213 API \347\232\204\345\234\272\346\231\257 ( ver < 2.2 ).md" @@ -0,0 +1,49 @@ +#禁止使用协程 API 的场景 ( ver < 2.2 ) + +> **注: 在`Swoole2.2`版本后解决了此问题, 可以在所有地方使用协程** + +在`ZendVM`中魔术方法、反射函数、`call_user_func`、`call_user_func_array`是由`C`函数实现的,并未`opcode`,这些操作可能会与`Swoole`底层的协程调度发生冲突。因此严禁在这些地方使用协程的`API`。请使用`PHP`提供的动态函数调用语法来实现相同的功能。 + +禁止使用协程的场景 +---- +* `__get` +* `__set` +* `__call` +* `__callStatic` +* `__toString` +* `__invoke` +* `__destruct` +* `call_user_func` +* `call_user_func_array` +* `ReflectionFunction::invoke` +* `ReflectionFunction::invokeArgs` +* `ReflectionMethod::invoke` +* `ReflectionMethod::invokeArgs` +* `array_walk`/`array_map` + +字符串函数 +--- +#### 错误的代码 +```php +$func = "test"; +$retval = call_user_func($func, "hello"); +``` + +#### 正确的代码 +```php +$func = "test"; +$retval = $func("hello"); +``` + +对象方法 +---- +#### 错误的代码 +```php +$retval = call_user_func(array($obj, "test"), "hello"); +``` + +#### 正确的代码 +```php +$method = "test"; +$retval = $obj->$method("hello"); +``` \ No newline at end of file diff --git "a/doc/11 - \345\215\217\347\250\213 Client.md" "b/doc/11 - \345\215\217\347\250\213 Client.md" new file mode 100644 index 0000000..c8af466 --- /dev/null +++ "b/doc/11 - \345\215\217\347\250\213 Client.md" @@ -0,0 +1,15 @@ +#协程 Client + + `Swoole`提供了`6`种协程`Client`: + +1. TCP/UDP Client:`Swoole\Coroutine\Client` +2. HTTP/WebSocket Client:`Swoole\Coroutine\HTTP\Client` +3. HTTP2 Client:`Swoole\Coroutine\HTTP2\Client` +3. Redis Client:`Swoole\Coroutine\Redis` +4. Mysql Client:`Swoole\Coroutine\MySQL` +4. Postgresql Client:`Swoole\Coroutine\PostgreSQL` + +* 在协程`Server`中需要使用协程版`Client`,可以实现全异步`Server` +* 其他程序中可以使用`go`关键词手工创建协程 +* 同时`Swoole`提供了协程工具集:`Swoole\Coroutine`,提供了获取当前协程`id`,反射调用等能力。 + diff --git "a/doc/11.2.1 - \345\261\236\346\200\247\345\210\227\350\241\250.md" "b/doc/11.2.1 - \345\261\236\346\200\247\345\210\227\350\241\250.md" new file mode 100644 index 0000000..038d468 --- /dev/null +++ "b/doc/11.2.1 - \345\261\236\346\200\247\345\210\227\350\241\250.md" @@ -0,0 +1,34 @@ +#属性列表 + +errCode +---- +类型为`int`型。当`connect`/`send`/`recv`/`close`失败或者超时时,会自动设置`Swoole\Coroutine\Http\Client->errCode`的值。 +`errCode`的值等于`Linux errno`。可使用`socket_strerror`将错误码转为错误信息。 + +```php +echo socket_strerror($client->errCode); +``` + +* 如果connect refuse,错误码为111 +* 如果超时,错误码为110 + +> [附录:Linux的errno定义](/wiki/page/172.html) + +body +---- + +存储上次请求的返回包体 +```php +$cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80); +$cli->get('/index.php'); +echo $cli->body; +$cli->close(); +``` + +statusCode +---- +`Http`状态码,如`200`、`404`等。状态码如果为负数,表示连接存在问题。 + +* `-1`:连接超时,服务器未监听端口或网络丢失,可以读取`$errCode`获取具体的网络错误码 +* `-2`:请求超时,服务器未在规定的`timeout`时间内返回`response` +* `-3`:客户端请求发出后,服务器强制切断连接 diff --git "a/doc/11.4.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" "b/doc/11.4.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" new file mode 100644 index 0000000..de9ae46 --- /dev/null +++ "b/doc/11.4.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" @@ -0,0 +1,22 @@ +#方法列表 + +方法的使用基本与[phpredis](https://github.com/phpredis/phpredis)保持一致。 + +#### 不同于[phpredis](https://github.com/phpredis/phpredis)的实现 +- - - +1、尚未实现的Redis命令:`scan object sort migrate hscan sscan zscan` + +2、`subscribe pSubscribe`的使用方式,无需设置回调函数: + +```php +$redis = new Swoole\Coroutine\Redis(); +$redis->connect('127.0.0.1', 6379); +while (true) { + $val = $redis->subscribe(['pubsub']); + //订阅的channel,以第一次调用subscribe时的channel为准,后续的subscribe调用是为了收取Redis Server的回包 + //如果需要改变订阅的channel,请close掉连接,再调用subscribe + var_dump($val); +} +``` + +3、序列化PHP变量的支持,在connect方法的第三个参数设置为true时,开启序列化php变量特性,默认为false \ No newline at end of file diff --git "a/doc/11.4.2 - \345\261\236\346\200\247\345\210\227\350\241\250.md" "b/doc/11.4.2 - \345\261\236\346\200\247\345\210\227\350\241\250.md" new file mode 100644 index 0000000..367d8fb --- /dev/null +++ "b/doc/11.4.2 - \345\261\236\346\200\247\345\210\227\350\241\250.md" @@ -0,0 +1,36 @@ +#属性列表 + +errCode +----- +错误代码 + +* 1 Error in read or write +* 2 Everything else... +* 3 End of file +* 4 Protocol error +* 5 Out of memory + +errMsg +----- +错误消息 + +connected +--- +当前`Redis`客户端是否连接到了服务器。 + + +定义的PHP常量 +----- +用于`multi($mode)`方法,默认为`SWOOLE_REDIS_MODE_MULTI`模式: + +* SWOOLE_REDIS_MODE_MULTI +* SWOOLE_REDIS_MODE_PIPELINE + +用于判断`type()`命令的返回值: + +* SWOOLE_REDIS_TYPE_NOT_FOUND +* SWOOLE_REDIS_TYPE_STRING +* SWOOLE_REDIS_TYPE_SET +* SWOOLE_REDIS_TYPE_LIST +* SWOOLE_REDIS_TYPE_ZSET +* SWOOLE_REDIS_TYPE_HASH \ No newline at end of file diff --git "a/doc/11.5.1 - \345\261\236\346\200\247\345\210\227\350\241\250.md" "b/doc/11.5.1 - \345\261\236\346\200\247\345\210\227\350\241\250.md" new file mode 100644 index 0000000..4bd0419 --- /dev/null +++ "b/doc/11.5.1 - \345\261\236\346\200\247\345\210\227\350\241\250.md" @@ -0,0 +1,29 @@ +#属性列表 + +serverInfo +---- +连接信息,保存的是传递给构造函数的数组 + +sock +---- +连接使用的文件描述符 + +connected +---- +是否连接上了MySQL服务器 + +connect_error connect_errno +---- +发生在sock上的连接错误信息 + +error errno +---- +MySQL服务器返回的错误信息 + +affected_rows +---- +影响的行数 + +insert_id +---- +最后一个插入的记录id \ No newline at end of file diff --git a/doc/11.6.1 - Coroutine::getUid.md b/doc/11.6.1 - Coroutine::getUid.md new file mode 100644 index 0000000..7f3a12c --- /dev/null +++ b/doc/11.6.1 - Coroutine::getUid.md @@ -0,0 +1,16 @@ +#Coroutine::getUid + +获取当前协程的唯一`ID` +``` +function \Swoole\Coroutine::getUid() : int +``` + +返回值 +---- + +* 成功时返回当前协程`ID(int)` +* 如果当前不在协程环境中,则返回`-1` + +```php +echo Swoole\Coroutine::getUid(); +``` diff --git a/doc/11.6.10 - Coroutine::getaddrinfo.md b/doc/11.6.10 - Coroutine::getaddrinfo.md new file mode 100644 index 0000000..39ee1b0 --- /dev/null +++ b/doc/11.6.10 - Coroutine::getaddrinfo.md @@ -0,0 +1,19 @@ +#Coroutine::getaddrinfo + +进行`DNS`解析,查询域名对应的`IP`地址,与`gethostbyname`不同,`getaddrinfo`支持更多参数设置,而且会返回多个`IP`结果。 + +```php +function Coroutine::getaddrinfo(string $domain, int $family = AF_INET, int $socktype = SOCK_STREAM, + int $protocol = IPPROTO_TCP, string $service = null): array | bool +``` + +* `$domain`域名,如`www.baidu.com` +* `$family`默认为`AF_INET`表示返回`IPv4`地址,使用`AF_INET6`时返回`IPv6`地址 +* 其他参数设置请参考`man getaddrinfo`文档 +* 成功返回多个`IP`地址组成的数组,失败返回`false` + +示例 +---- +```php +$array = co::getaddrinfo("www.baidu.com"); +``` \ No newline at end of file diff --git a/doc/11.6.11 - Coroutine::call_user_func.md b/doc/11.6.11 - Coroutine::call_user_func.md new file mode 100644 index 0000000..7bbde34 --- /dev/null +++ b/doc/11.6.11 - Coroutine::call_user_func.md @@ -0,0 +1,10 @@ +#Coroutine::call_user_func + +协程版反射调用函数 +``` +mixed Swoole\Coroutine::call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) +``` +**参数**: + +* `callback`: 调用函数 +* `parameter`: 参数列表 \ No newline at end of file diff --git a/doc/11.6.12 - Coroutine::call_user_func_array.md b/doc/11.6.12 - Coroutine::call_user_func_array.md new file mode 100644 index 0000000..ff88325 --- /dev/null +++ b/doc/11.6.12 - Coroutine::call_user_func_array.md @@ -0,0 +1,14 @@ +#Coroutine::call_user_func_array + +协程版反射调用函数 +``` +mixd \Swoole\Coroutine::call_user_func_array(callable $callback , array $param_array) +``` +**参数**: + +* `callback`: 调用函数 +* `param_array`: 参数数组 + +```php +\Swoole\Coroutine::call_user_func_array([$controller, $action], $params) +``` \ No newline at end of file diff --git a/doc/11.6.13 - Coroutine::exec.md b/doc/11.6.13 - Coroutine::exec.md new file mode 100644 index 0000000..f1eab98 --- /dev/null +++ b/doc/11.6.13 - Coroutine::exec.md @@ -0,0 +1,30 @@ +#Coroutine::exec + +执行一条`shell`指令。底层自动进行协程调度。 +```php +function Coroutine::exec(string $cmd) : array; +``` + +* `$cmd` 要执行的`shell`指令 + + +返回值 +---- +执行失败返回`false`,执行成功返回数组,包含了进程退出的状态码、信号、输出内容。 + +```php +array( + 'code' => 0, + 'signal' => 0, + 'output' => '', +); +``` + + +使用实例 +---- +```php +go(function() { + $ret = Co::exec("md5sum ".__FILE__); +}); +``` \ No newline at end of file diff --git a/doc/11.6.14 - Coroutine::readFile.md b/doc/11.6.14 - Coroutine::readFile.md new file mode 100644 index 0000000..ceb5303 --- /dev/null +++ b/doc/11.6.14 - Coroutine::readFile.md @@ -0,0 +1,31 @@ +#Coroutine::readFile + +协程方式读取文件。 + + +```php +function Coroutine::readFile(string $filename); +``` + +> 需要`2.1.2`或更高版本 + +参数 +---- +* `$filename`文件名 + +返回值 +---- +* 读取成功返回字符串内容,读取失败返回`false` +* `readFile`方法没有尺寸限制,读取的内容会存放在内存中,因此读取超大文件时可能会占用过多内存 + +示例 +--- +```php +use Swoole\Coroutine as co; +$filename = __DIR__ . "/defer_client.php"; +co::create(function () use ($filename) +{ + $r = co::readFile($filename); + var_dump($r); +}); +``` \ No newline at end of file diff --git a/doc/11.6.15 - Coroutine::writeFIle.md b/doc/11.6.15 - Coroutine::writeFIle.md new file mode 100644 index 0000000..c84e729 --- /dev/null +++ b/doc/11.6.15 - Coroutine::writeFIle.md @@ -0,0 +1,32 @@ +#Coroutine::writeFIle + +协程方式写入文件。 + + +```php +function Coroutine::writeFile(string $filename, string $fileContent, int $flags); +``` + +> 需要`2.1.2`或更高版本 + +参数 +---- +* `$filename`为文件的名称,必须有可写权限,文件不存在会自动创建。打开文件失败会立即返回false +* `$fileContent`为要写入到文件的内容,最大可写入4M +* `$flags`为写入的选项,可以使用`FILE_APPEND`表示追加到文件末尾,默认会清空当前文件内容 + +返回值 +---- +写入成功返回`true`,写入失败返回`false` + +示例 +--- +```php +use Swoole\Coroutine as co; +$filename = __DIR__ . "/defer_client.php"; +co::create(function () use ($filename) +{ + $r = co::writeFile($filename,"hello swoole!"); + var_dump($r); +}); +``` \ No newline at end of file diff --git a/doc/11.6.2 - Coroutine::create.md b/doc/11.6.2 - Coroutine::create.md new file mode 100644 index 0000000..92e1aea --- /dev/null +++ b/doc/11.6.2 - Coroutine::create.md @@ -0,0 +1,62 @@ +#Coroutine::create + +创建一个新的协程,并立即执行。 + +```php +function Swoole\Coroutine::create(callable $function); +``` + +* `$function` 协程执行的代码,系统能创建的协程总数量受限于`server->max_coroutine`设置 +* 创建成功返回`true`,失败返回`false` + + +在`2.1.0`或更高版本中如果开启了`swoole.use_shortname`,可以直接使用`go`关键词创建新的协程。 + +```php +go(function () { + $db = new Co\MySQL(); + $server = array( + 'host' => '127.0.0.1', + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', + ); + + $db->connect($server); + + $result = $db->query('SELECT * FROM userinfo WHERE id = 3'); + var_dump($result); +}); +``` + +执行顺序 +---- +在一个协程中使用`go`嵌套创建新的协程。因为`Swoole`的协程是单线程模型,因此: + +* 使用`go`创建的子协程会优先执行,子协程执行完毕或挂起时,将重新回到父协程向下执行代码 +* 如果子协程挂起后,父协程退出,不影响子协程的执行 + +```php +go(function() { + go(function () { + co::sleep(3.0); + go(function () { + co::sleep(2.0); + echo "co[3] end\n"; + }); + echo "co[2] end\n"; + }); + + co::sleep(1.0); + echo "co[1] end\n"; +}); +``` + +协程开销 +---- +协程需要创建单独的内存栈,在`PHP-7.2`版本中底层会分配`8K`的`stack`来存储协程的变量,`zval`的尺寸为`16`字节,因此`8K`的`stack`最大可以保存`512`个变量。协程栈内存占用超过`8K`后`ZendVM`会自动扩容。 + +协程退出时会释放申请的`stack`内存。 + +> `PHP-7.1`、`PHP-7.0`默认会分配`256K`栈内存 +> 可调用`Co::set(['stack_size' => 4096])`修改默认的栈内存尺寸 \ No newline at end of file diff --git a/doc/11.6.3 - Coroutine::resume.md b/doc/11.6.3 - Coroutine::resume.md new file mode 100644 index 0000000..082e5da --- /dev/null +++ b/doc/11.6.3 - Coroutine::resume.md @@ -0,0 +1,9 @@ +#Coroutine::resume + +恢复某个协程,使其继续运行。 +```php +function Swoole\Coroutine::resume(string $coroutineId); +``` + +* 参数`$coroutineId`为要恢复的协程ID,在协程内可以使用`getuid`获取到协程的ID +* 当前协程处于挂起状态时,另外的协程中可以使用`resume`再次唤醒当前协程 \ No newline at end of file diff --git a/doc/11.6.4 - Coroutine::suspend.md b/doc/11.6.4 - Coroutine::suspend.md new file mode 100644 index 0000000..33bdb93 --- /dev/null +++ b/doc/11.6.4 - Coroutine::suspend.md @@ -0,0 +1,8 @@ +#Coroutine::suspend + +挂起当前协程。 +```php +function Swoole\Coroutine::suspend(string $corouindId); +``` + +* 参数为要挂起协程的ID diff --git a/doc/11.6.5 - Coroutine::fread.md b/doc/11.6.5 - Coroutine::fread.md new file mode 100644 index 0000000..adf4f73 --- /dev/null +++ b/doc/11.6.5 - Coroutine::fread.md @@ -0,0 +1,32 @@ +#Coroutine::fread + +协程方式读取文件。 + + +```php +function Coroutine::fread(resource $handle, int $length = 0); +``` + +> 需要`2.0.11`或更高版本 + +参数 +---- +* `$handle`文件句柄,必须是`fopen`打开的文件类型`stream`资源 +* `$length`读取的长度,默认为`0`,表示读取文件的全部内容 + +返回值 +---- +读取成功返回字符串内容,读取失败返回`false` + +示例 +--- +```php +use Swoole\Coroutine as co; +$fp = fopen(__DIR__ . "/defer_client.php", "r"); +co::create(function () use ($fp) +{ + fseek($fp, 256); + $r = co::fread($fp); + var_dump($r); +}); +``` \ No newline at end of file diff --git a/doc/11.6.6 - Coroutine::fgets.md b/doc/11.6.6 - Coroutine::fgets.md new file mode 100644 index 0000000..46a0fbc --- /dev/null +++ b/doc/11.6.6 - Coroutine::fgets.md @@ -0,0 +1,31 @@ +#Coroutine::fgets + +协程方式按行读取文件内容。 +```php +function Coroutine::fgets(resource $handle); +``` +`Co::fgets`底层使用了`php_stream`缓存区,默认大小为`8192`字节,可使用`stream_set_chunk_size`设置缓存区尺寸。 + +> 需要`2.1.1`或更高版本 + +参数 +---- +* `$handle`文件句柄,必须是`fopen`打开的文件类型`stream`资源 + +返回值 +---- +* 读取到`EOL`(`\r`或`\n`)将返回一行数据,包括`EOL` +* 未读取到`EOL`,但内容长度超过`php_stream`缓存区`8192`字节,将返回`8192`字节的数据,不包含`EOL` +* 达到文件末尾`EOF`时,返回空字符串,可用`feof`判断文件是否已读完 +* 读取失败返回`false`,使用`swoole_last_error`函数获取错误码 + +示例 +--- +```php +$fp = fopen(__DIR__ . "/defer_client.php", "r"); +go(function () use ($fp) +{ + $r = co::fgets($fp); + var_dump($r); +}); +``` \ No newline at end of file diff --git a/doc/11.6.7 - Coroutine::fwrite.md b/doc/11.6.7 - Coroutine::fwrite.md new file mode 100644 index 0000000..1e1627b --- /dev/null +++ b/doc/11.6.7 - Coroutine::fwrite.md @@ -0,0 +1,32 @@ +#Coroutine::fwrite + +协程方式向文件写入数据。 + + +```php +function Coroutine::fwrite(resource $handle, string $data, int $length = 0); +``` + +> 需要`2.0.11`或更高版本 + +参数 +---- +* `$handle`文件句柄,必须是`fopen`打开的文件类型`stream`资源 +* `$data`要写入的数据内容,可以是文本或二进制数据 +* `$length`写入的长度,默认为`0`,表示写入`$data`的全部内容,`$length`必须小于`$data`的长度 + +返回值 +---- +写入成功返回数据长度,失败返回`false` + +示例 +--- +```php +use Swoole\Coroutine as co; +$fp = fopen(__DIR__ . "/test.data", "a+"); +co::create(function () use ($fp) +{ + $r = co::fwrite($fp, "hello world\n", 5); + var_dump($r); +}); +``` \ No newline at end of file diff --git a/doc/11.6.8 - Coroutine::sleep.md b/doc/11.6.8 - Coroutine::sleep.md new file mode 100644 index 0000000..2c919a3 --- /dev/null +++ b/doc/11.6.8 - Coroutine::sleep.md @@ -0,0 +1,26 @@ +#Coroutine::sleep + +进入等待状态。相当于PHP的`sleep`函数,不同的是`Coroutine::sleep`是协程调度器实现的,底层会`yield`当前协程,让出时间片,并添加一个异步定时器,当超时时间到达时重新`resume`当前协程,恢复运行。使用`sleep`接口可以方便地实现超时等待功能。 + +```php +function Coroutine::sleep(float $seconds); +``` + +* `$seconds`为睡眠的时间,单位为秒,支持浮点型,最小精度为毫秒(`0.001秒`) +* `$seconds`必须大于`0`,最大不得超过一天时间(`86400秒`) + +> 在`2.0.9`或更高版本可用 + +使用实例 +---- +```php +$serv = new Swoole\Http\Server("127.0.0.1", 9502); + +$serv->on('Request', function($request, $response) { + //等待200ms后向浏览器发送响应 + Swoole\Coroutine::sleep(0.2); + $response->end("

Hello Swoole!

"); +}); + +$serv->start(); +``` \ No newline at end of file diff --git a/doc/11.6.9 - Coroutine::gethostbyname.md b/doc/11.6.9 - Coroutine::gethostbyname.md new file mode 100644 index 0000000..98ab234 --- /dev/null +++ b/doc/11.6.9 - Coroutine::gethostbyname.md @@ -0,0 +1,30 @@ +#Coroutine::gethostbyname + +将域名解析为`IP`,基于同步的线程池模拟实现。底层自动进行协程调度。 + +```php +function Coroutine::gethostbyname(string $domain, int $family = AF_INET): string | bool +``` + +* `$domain`域名,如`www.baidu.com` +* `$family`默认为`AF_INET`表示返回`IPv4`地址,使用`AF_INET6`时返回`IPv6`地址 +* 成功返回域名对应的`IP`地址,失败返回`false` + +示例 +---- +```php +use Swoole\Coroutine as co; +$ip = co::gethostbyname("www.baidu.com"); +``` + +swoole_async_dns_lookup_coro +---- +协程`DNS`查询。与`co::gethostbyname`不同,`swoole_async_dns_lookup_coro`是基于`UDP`客户端实现。不支持`/etc/hosts`配置。 + +函数原型: +```php +function swoole_async_dns_lookup_coro(string $domain) : string | bool; +``` + +* 查询成功返回对应的`IP`地址 +* 失败返回`false`,可使用`swoole_errno`和`swoole_last_error`得到错误信息 \ No newline at end of file diff --git "a/doc/12 - \345\215\217\347\250\213 Socket.md" "b/doc/12 - \345\215\217\347\250\213 Socket.md" new file mode 100644 index 0000000..f4b4c35 --- /dev/null +++ "b/doc/12 - \345\215\217\347\250\213 Socket.md" @@ -0,0 +1,15 @@ +#协程 Socket + + `Swoole-2.2`版本增加了更底层的`Coroutine\Socket`模块,相比`Server`和`Client`相关模块`Socket`可以实现更细粒度的一些`IO`操作。 + +可使用`Co\Socket`短命名简化类名。 + +> 需要`2.2.0`或更高版本 + +协程调度 +---- +`Coroutine\Socket`模块提供的`IO`操作接口均为同步编程风格,底层自动使用协程调度器实现异步非阻塞`IO`。 + +错误码 +---- +在执行`socket`相关系统调用时,可能返回`-1`错误,底层会设置`Coroutine\Socket->$errCode`属性为系统错误编号`errno`,请参考响应的`man`文档。如`$socket->accept()`返回错误时,`errCode`含义可以参考`man accept`中列出的错误码文档。 diff --git "a/doc/13.1 - \346\226\271\346\263\225.md" "b/doc/13.1 - \346\226\271\346\263\225.md" new file mode 100644 index 0000000..f1b1ac8 --- /dev/null +++ "b/doc/13.1 - \346\226\271\346\263\225.md" @@ -0,0 +1,5 @@ +#方法 + + `Swoole\Redis\Server`继承自`Swoole\Server`,可以使用父类提供的所有方法。 + + `Redis\Server`不需要设置`onReceive`回调。只需使用`setHandler`方法设置对应命令的处理函数,收到未支持的命令后会自动向客户端发送`ERROR`响应,消息为`ERR unknown command '$command'`。 diff --git a/doc/13.1.1 - setHandler.md b/doc/13.1.1 - setHandler.md new file mode 100644 index 0000000..2090d85 --- /dev/null +++ b/doc/13.1.1 - setHandler.md @@ -0,0 +1,40 @@ +#setHandler + +设置Redis命令字的处理器。 + +```php +function swoole_redis_server->setHandler(string $command, callable $callback); +``` + +* $command 命令的名称 +* $callback 命令的处理函数,回调函数返回字符串类型时会自动发送给客户端 +* $callback 返回的数据必须为Redis格式,可使用`format`静态方法进行打包 + +服务器实例 +---- +```php +use Swoole\Redis\Server; + +$server = new Server('127.0.0.1', 9501); + +//同步模式 +$server->setHandler('Set', function($fd, $data) use ($server) { + $server->array($data[0], $data[1]); + return Server::format(Server::INT, 1); +}); + +//异步模式 +$server->setHandler('Get', function ($fd, $data) use ($server) { + $db->query($sql, function($db, $result) use ($fd) { + $server->send($fd, Server::format(Server::LIST, $result)); + }); +}); + +$server->start(); +``` + +客户端实例 +---- +```shell +redis-cli -h 127.0.0.1 -p 9501 set name rango +``` \ No newline at end of file diff --git a/doc/13.1.2 - format.md b/doc/13.1.2 - format.md new file mode 100644 index 0000000..ec52915 --- /dev/null +++ b/doc/13.1.2 - format.md @@ -0,0 +1,9 @@ +#format + +格式化命令响应数据。 + +```php +function swoole_redis_server::format(int $type, mixed $value = null); +``` +* `format`为静态方法 +* `$type`表示数据类型,`NIL`类型不需要传入`$value`,`ERROR`和`STATUS`类型`$value`可选,`INT`、`STRING`、`SET`、`MAP`必选 diff --git "a/doc/13.2 - \345\270\270\351\207\217.md" "b/doc/13.2 - \345\270\270\351\207\217.md" new file mode 100644 index 0000000..98d3361 --- /dev/null +++ "b/doc/13.2 - \345\270\270\351\207\217.md" @@ -0,0 +1,14 @@ +#常量 + +格式参数常量 +------ +主要用于`format`函数打包Redis响应数据 + +* **Server::NIL** 返回`nil`数据 +* **Server::ERROR** 返回错误码 +* **Server::STATUS** 返回状态 +* **Server::INT** 返回整数,,`format`必须传入参数值,类型必须为整数 +* **Server::STRING** 返回字符串,`format`必须传入参数值,类型必须为字符串 +* **Server::SET** 返回列表,`format`必须传入参数值,类型必须为数组 +* **Server::MAP** 返回Map,`format`必须传入参数值,类型必须为关联索引数组 + diff --git "a/doc/14 - \351\253\230\347\272\247.md" "b/doc/14 - \351\253\230\347\272\247.md" new file mode 100644 index 0000000..2a96d48 --- /dev/null +++ "b/doc/14 - \351\253\230\347\272\247.md" @@ -0,0 +1,6 @@ +#高级 + +* [MySQL连接池](http://git.oschina.net/swoole/swoole_framework/blob/master/libs/Swoole/Async/MySQL.php) +* [Redis连接池](http://git.oschina.net/swoole/swoole_framework/blob/master/libs/Swoole/Async/Redis.php) +* [PHP解析C/C++结构体struct](http://git.oschina.net/swoole/swoole_framework/blob/master/libs/Swoole/Memory/Struct.php) +* [基于共享内存文件系统的Key-Value存储](http://git.oschina.net/swoole/swoole_framework/blob/master/libs/Swoole/Memory/Storage.php) \ No newline at end of file diff --git "a/doc/14.1 - Swoole\347\232\204\345\256\236\347\216\260.md" "b/doc/14.1 - Swoole\347\232\204\345\256\236\347\216\260.md" new file mode 100644 index 0000000..c8243a6 --- /dev/null +++ "b/doc/14.1 - Swoole\347\232\204\345\256\236\347\216\260.md" @@ -0,0 +1,38 @@ +#Swoole的实现 + +swoole使用纯C编写,不依赖其他第三方库。 + +* swoole并没有用libevent,所以不需要安装libevent +* swoole并不依赖php的stream/sockets/pcntl/posix/sysvmsg等扩展 + +socket部分 +----- +swoole使用底层的socket系统调用。参见 sys/socket.h + +IO事件循环 +----- + +* 主进程的事件循环使用select/poll,因为主线程中的文件描述符只有几个,使用select/poll即可 +* reactor线程/worker进程中使用epoll/kqueue +* task进程没有事件循环,进程会循环阻塞读取管道 + + +> 有很多人使用strace -p去查看swoole主进程只能看到poll系统调用。正确的查看方法是strace -f -p + +多进程/多线程 +----- +* 多进程使用fork()系统调用 +* 多线程使用pthread线程库 + +EventFd +---- +Swoole中使用了eventfd作为线程/进程间消息通知的机制。 + +Timerfd +---- +Swoole使用timerfd来实现定时器 + +SIgnalfd +---- +swoole中使用了signalfd来实现对信号的屏蔽和处理。可以有效地避免线程/进程被信号打断,系统调用restart的问题。在主进程中reactor线程不会接受任何信号。 + diff --git "a/doc/14.10 - swoole\346\234\215\345\212\241\345\231\250\345\246\202\344\275\225\345\201\232\345\210\260\346\227\240\344\272\272\345\200\274\345\256\210100%\345\217\257\347\224\250.md" "b/doc/14.10 - swoole\346\234\215\345\212\241\345\231\250\345\246\202\344\275\225\345\201\232\345\210\260\346\227\240\344\272\272\345\200\274\345\256\210100%\345\217\257\347\224\250.md" new file mode 100644 index 0000000..c2430ef --- /dev/null +++ "b/doc/14.10 - swoole\346\234\215\345\212\241\345\231\250\345\246\202\344\275\225\345\201\232\345\210\260\346\227\240\344\272\272\345\200\274\345\256\210100%\345\217\257\347\224\250.md" @@ -0,0 +1,37 @@ +#swoole服务器如何做到无人值守100%可用 + +在某些情况下,如系统负载过大swoole无法申请到内存而挂掉、swoole底层发生段错误、Server占用内存过大被内核Kill,或者被某些程序误杀。那swoole-server将无法提供服务,导致业务中断,公司收入出现损失。 + +有一个非常有效并且在BAT等大型公司常用的方案是crontab重启监控。 +原理是每1分钟执行一次shell脚本,检测server的master进程是否存活,如果存在则跳过。如果发现主进程已经挂掉,则执行restart逻辑,先kill掉所有残留的子进程,然后重新启动Server。 + +> 使用下面的脚本需要将Server程序的进程名称设置为master,如 `cli_set_process_title("php server.php: master")` + +如果在系统的crontab中加入: +```shell +*/1 * * * * /data/script/check_server.sh +``` + +/data/script/check_server.sh: +```shell +count=`ps -fe |grep "server.php" | grep -v "grep" | grep "master" | wc -l` + +echo $count +if [ $count -lt 1 ]; then +ps -eaf |grep "server.php" | grep -v "grep"| awk '{print $2}'|xargs kill -9 +sleep 2 +ulimit -c unlimited +/usr/local/bin/php /data/webroot/server.php +echo "restart"; +echo $(date +%Y-%m-%d_%H:%M:%S) >/data/log/restart.log +fi +``` + +可以改进的地方 +---- +* 可以通过netstat -lnp 检测端口是否在监听,如果未在监听,则执行restart +* 通过一个check.php发送一段带有逻辑的请求,试探服务器是否可以正常工作,如果无法工作,执行restart +* 使用supervisor监控进程的工具 +* 如果在Docker容器中使用,可以在`docker run`时增加参数`--restart=always` + + diff --git "a/doc/14.11 - MySQL\347\232\204\350\277\236\346\216\245\346\261\240\343\200\201\345\274\202\346\255\245\343\200\201\346\226\255\347\272\277\351\207\215\350\277\236.md" "b/doc/14.11 - MySQL\347\232\204\350\277\236\346\216\245\346\261\240\343\200\201\345\274\202\346\255\245\343\200\201\346\226\255\347\272\277\351\207\215\350\277\236.md" new file mode 100644 index 0000000..4a4fc63 --- /dev/null +++ "b/doc/14.11 - MySQL\347\232\204\350\277\236\346\216\245\346\261\240\343\200\201\345\274\202\346\255\245\343\200\201\346\226\255\347\272\277\351\207\215\350\277\236.md" @@ -0,0 +1,64 @@ +#MySQL的连接池、异步、断线重连 + +MySQL长连接 +---- +MySQL短连接每次请求操作数据库都需要建立与MySQL服务器建立TCP连接,这是需要时间开销的。TCP连接需要3次网络通信。这样就增加了一定的延时和额外的IO消耗。请求结束后会关闭MySQL连接,还会发生3/4次网络通信。 + +> close操作不会增加响应延时,原因是close后是由操作系统自动进行通信的,应用程序感知不到 + +长连接就可以避免每次请求都创建连接的开销,节省了时间和IO消耗。提升了PHP程序的性能。 + + +断线重连 +---- +在cli环境下,PHP程序需要长时间运行,客户端与MySQL服务器之间的TCP连接是不稳定的。 + +* MySQL-Server会在一定时间内自动切断连接 +* PHP程序遇到空闲期时长时间没有MySQL查询,MySQL-Server也会切断连接回收资源 +* 其他情况,在MySQL服务器中执行kill process杀掉某个连接,MySQL服务器重启 + +这时PHP程序中的MySQL连接就失效了。如果仍然执行mysql_query,就会报一个“MySQL server has gone away”的错误。程序处理不到就直接遇到致命错误并退出了。所以PHP程序中需要断线重连。 + +有很多人提出了mysql_ping的方案,每次mysql_query进行连接检测或者定时连接检测。这个方案不是最好的。原因是 + +* mysql_ping需要主动侦测连接,带来了额外的消耗 +* 定时执行mysql_ping不能解决问题,如刚刚执行过mysql_ping检测之后,连接就关闭了 + +最佳的方案是,进行断线重连 。它的原理是: + +1. mysql_query执行后检测返回值 +2. 如果mysql_query返回失败,检测错误码发现为2006/2013(这2个错误表示连接失败),再执行一次mysql_connect +3. 执行mysql_connect后,重新执行mysql_query,这时必然会成功,因为已经重新建立了连接 +4. 如果mysql_query返回成功,那么连接是有效的,这是一次正常的调用 + +> 可参考[swoole_framework中的代码](https://github.com/swoole/framework/blob/master/libs/Swoole/Database/MySQL.php) + + +MySQL异步 +---- +MySQL异步是指将MySQL连接事件驱动化,这样就变成了非阻塞IO。数据库操作并不会阻塞进程,在MySQL-Server返回结果时再执行对应的逻辑。 + +有几个点需要注意一下: + +* 异步MySQL并没有节省SQL执行的时间 +* 一个MySQL连接同时只能执行1个SQL,如果异步MySQL存在并发那么必须创建多个MySQL连接 + +异步回调程序中,异步MySQL并没有提升性能。异步最大的好处是可以高并发,如果并发1万个请求,那么就需要建立1万个MySQL连接,这会给MySQL-Server带来巨大的压力。 + +> MySQL是根据连接数分配资源的,一个连接需要开启一个线程。1000连接那么需要维持1000线程才可以。线程数量增加后,线程间切换会占用大量CPU资源 +> MySQL短连接反而不会出现此问题,因为短连接在使用完后就释放了。不会占用MySQL-Server的连接资源 + +虽然应用层代码使用异步回调避免了自身的阻塞,实际上真正的瓶颈是数据库服务器。异步MySQL还带来了额外的编程复杂度,所以除非是特殊场景的需求,否则不建议使用异步MySQL。 + +如果程序中坚持要使用异步,那么必须是异步MySQL+连接池的形式。超过规定的MySQL最大连接后,应当对SQL请求进行排队,而不是创建新连接,避免大量并发请求导致MySQL服务器崩溃。 + +MySQL连接池 +---- +连接池是可以有效降低MySQL-Server负载的。原理是 连接池使用一个共享资源的模式,如并发100个请求,实际上并不是每个请求的所有时间都在执行SQL查询。这样100个请求,共享20个MySQL连接就可以满足需求了。当一个请求操作完数据库后,开始进入模板渲染等流程,这时就会释放数据库连接给其他的请求使用。 + +连接池仅在超大型应用中才有价值。普通的应用采用MySQL长连接方案,每个php-fpm创建一个MySQL连接,每台机器开启100个php-fpm进程。如果有10台机器,每台机器并发的请求为100。实际上只需要创建1000个MySQL连接就能满足需求,数据库的压力并不大。即使有100台机器,硬件配置好的存储服务器依然可以承受。 + +达到数百或者数千台应用服务器时,MySQL服务器就需要维持十万级的连接。这时数据库的压力就会非常大了。连接池技术就可以派上用场了,可以大大降低数据库连接数。 + +> 基于swoole的AsyncTask模块实现的连接池是完美方案,编程简单,没有数据同步和锁的问题。甚至可以多个服务共享连接池。缺点是1, 灵活性不如多线程连接池,无法动态增减连接。2, 有一次进程间通信的开销。 +> node.js/ngx_lua等在多进程的模式下,无法开发出真正的连接池,除非也像swoole_task这样来实现 \ No newline at end of file diff --git "a/doc/14.12 - PHP\344\270\255\345\223\252\344\272\233\345\207\275\346\225\260\346\230\257\345\220\214\346\255\245\351\230\273\345\241\236\347\232\204.md" "b/doc/14.12 - PHP\344\270\255\345\223\252\344\272\233\345\207\275\346\225\260\346\230\257\345\220\214\346\255\245\351\230\273\345\241\236\347\232\204.md" new file mode 100644 index 0000000..b14ec6e --- /dev/null +++ "b/doc/14.12 - PHP\344\270\255\345\223\252\344\272\233\345\207\275\346\225\260\346\230\257\345\220\214\346\255\245\351\230\273\345\241\236\347\232\204.md" @@ -0,0 +1,27 @@ +#PHP中哪些函数是同步阻塞的 + +同步阻塞函数 +------ +* mysql、mysqli、pdo以及其他DB操作函数 +* sleep、usleep +* curl +* stream、socket扩展的函数 +* swoole_client同步模式 +* memcache、redis扩展函数 +* file_get_contents/fread等文件读取函数 +* swoole_server->taskwait +* swoole_server->sendwait + +> swoole_server的PHP代码中有上述函数,Server就是同步服务器 +> 代码中没有上述函数就是异步服务器 + +异步非阻塞函数 +------ +* swoole_client异步模式 +* mysql-async库 +* redis-async库 +* swoole_timer_tick/swoole_timer_after +* swoole_event系列函数 +* swoole_table/swoole_atomic/swoole_buffer +* swoole_server->task/finish函数 + diff --git "a/doc/14.13 - \345\256\210\346\212\244\350\277\233\347\250\213\347\250\213\345\272\217\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/doc/14.13 - \345\256\210\346\212\244\350\277\233\347\250\213\347\250\213\345\272\217\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" new file mode 100644 index 0000000..2285145 --- /dev/null +++ "b/doc/14.13 - \345\256\210\346\212\244\350\277\233\347\250\213\347\250\213\345\272\217\345\270\270\347\224\250\346\225\260\346\215\256\347\273\223\346\236\204.md" @@ -0,0 +1,2 @@ +#守护进程程序常用数据结构 + diff --git "a/doc/14.13.1 - \351\230\237\345\210\227\357\274\210Queue\357\274\211.md" "b/doc/14.13.1 - \351\230\237\345\210\227\357\274\210Queue\357\274\211.md" new file mode 100644 index 0000000..bee3362 --- /dev/null +++ "b/doc/14.13.1 - \351\230\237\345\210\227\357\274\210Queue\357\274\211.md" @@ -0,0 +1,83 @@ +#队列(Queue) + +异步并发的服务器里经常使用队列实现生产者消费者模型,解决并发排队问题。PHP的SPL标准库中提供了`SplQueue`扩展内置的队列数据结构。另外PHP的数组也提供了`array_pop`和`array_shift`可以使用数组模拟队列数据结构。 + +SplQueue +------ +```php +$queue = new SplQueue; +//入队 +$queue->push($data); +//出队 +$data = $queue->shift(); +//查询队列中的排队数量 +$n = count($queue); +``` + +Array模拟队列 +----- +```php +$queue = array(); +//入队 +$queue[] = $data; +//出队 +$data = array_shift($queue); +//查询队列中的排队数量 +$n = count($queue); +``` + +性能对比 +---- +虽然使用Array可以实现队列,但实际上性能会非常差。在一个大并发的服务器程序上,建议使用`SplQueue`作为队列数据结构。 + +100万条数据随机入队、出队,使用`SplQueue`仅用`2312.345ms`即可完成,而使用Array模拟的队列的程序根本无法完成测试,CPU一直持续在100%。 + +降低数据条数到1万条后(100倍),也需要`260ms`才能完成测试。 + +SplQueue +```php +$splq = new SplQueue; +for($i = 0; $i < 1000000; $i++) +{ + $data = "hello $i\n"; + $splq->push($data); + + if ($i % 100 == 99 and count($splq) > 100) + { + $popN = rand(10, 99); + for ($j = 0; $j < $popN; $j++) + { + $splq->shift(); + } + } +} + +$popN = count($splq); +for ($j = 0; $j < $popN; $j++) +{ + $splq->pop(); +} +``` + +Array队列 +```php +$arrq = array(); +for($i = 0; $i <1000000; $i++) +{ + $data = "hello $i\n"; + $arrq[] = $data; + if ($i % 100 == 99 and count($arrq) > 100) + { + $popN = rand(10, 99); + for ($j = 0; $j < $popN; $j++) + { + array_shift($arrq); + } + } +} +$popN = count($arrq); +for ($j = 0; $j < $popN; $j++) +{ + array_shift($arrq); +} +``` diff --git "a/doc/14.13.2 - \345\240\206\357\274\210Heap\357\274\211.md" "b/doc/14.13.2 - \345\240\206\357\274\210Heap\357\274\211.md" new file mode 100644 index 0000000..eb464e9 --- /dev/null +++ "b/doc/14.13.2 - \345\240\206\357\274\210Heap\357\274\211.md" @@ -0,0 +1,84 @@ +#堆(Heap) + +在服务器程序开发中经常要用到排序功能,如会员积分榜。普通的`array`数据结构,使用`sort`进行排序,即使使用了最快的快速排序方法,实际上也会存在较大的计算开销。因此在内存中维护一个有序的内存结构可以有效低避免`sort`排序的计算开销。 + +在`PHP`中`SplHeap`就是一种有序的数据结构。数据总是按照最小在前或最大在前排序。新插入的数据会自动进行排序。 + +定义 +---- +`SplHeap`数据结构需要指定一个`compare`方法来进行元素的对比,从而实现自动排序。`SplHeap`类本身是`abstract`的,不能直接`new`。 + +需要编写一个子类,并实现`compare`方法。 + + +```php +//最大堆 +class MaxHeap extends SplHeap +{ + protected function compare($a, $b) + { + return $a - $b; + } +} + +//最小堆 +class MinHeap extends SplHeap +{ + protected function compare($a, $b) + { + return $b - $a; + } +} +``` + +使用 +--- +定义好子类后,可使用`insert`方法插入元素,插入的元素会使用`compare`方法与已有元素进行对比,自动排序。 +```php +$list = new MaxHeap; +$list->insert(56); +$list->insert(22); +$list->insert(35); +$list->insert(11); +$list->insert(88); +$list->insert(36); +$list->insert(97); +$list->insert(98); +$list->insert(26); +``` + +* `SplHeap`底层使用跳表数据结构,`insert`操作的时间复杂度为`O(Log(n))` + +注意这里只能插入数字,因为我们定义的`compare`不支持非数字对比。如果要支持插入数组或对象,可重新实现`compare`方法。 + +```php +class MyHeap extends SplHeap +{ + protected function compare($a, $b) + { + return $a->value - $b->value; + } +} +class MyObject +{ + public $value; + + function __construct($value) + { + $this->value = $value; + } +} + +$list = new MyHeap; +$list->insert(new MyObject(56)); +$list->insert(new MyObject(12)); +``` + +使用`foreach`遍历堆,可以发现是有序输出。 +```php +foreach($list as $li) +{ + echo $li."\n"; +} +``` + diff --git "a/doc/14.13.3 - \345\256\232\351\225\277\346\225\260\347\273\204\357\274\210SplFixedArray\357\274\211.md" "b/doc/14.13.3 - \345\256\232\351\225\277\346\225\260\347\273\204\357\274\210SplFixedArray\357\274\211.md" new file mode 100644 index 0000000..c57779f --- /dev/null +++ "b/doc/14.13.3 - \345\256\232\351\225\277\346\225\260\347\273\204\357\274\210SplFixedArray\357\274\211.md" @@ -0,0 +1,30 @@ +#定长数组(SplFixedArray) + +PHP官方的SPL库提供了一个定长数组的数据结构,类似与C语言中的数组。和普通的PHP数组不同,定长数组读写性能更好。 + +官方测试数据 +---- +> 测试使用PHP 5.4,64位Linux系统 +```shell +* small data (1,000): + * write: SplFixedArray is 15 % faster + * read: SplFixedArray is 5 % faster +* larger data (512,000): + * write: SplFixedArray is 33 % faster + * read: SplFixedArray is 10 % faster +``` + +使用方法 +---- +SplFixedArray使用方法与Array相同,但只支持数字索引的访问方式。 + +```php +$array = new SplFixedArray(5); +$array[1] = 2; +$array[4] = "foo"; + +var_dump($array[0]); // NULL +var_dump($array[1]); // int(2) +``` + +可以使用`setSize()`方法动态改变定长数组的尺寸。 \ No newline at end of file diff --git "a/doc/14.14 - \344\275\277\347\224\250jemalloc\344\274\230\345\214\226swoole\345\206\205\345\255\230\345\210\206\351\205\215\346\200\247\350\203\275.md" "b/doc/14.14 - \344\275\277\347\224\250jemalloc\344\274\230\345\214\226swoole\345\206\205\345\255\230\345\210\206\351\205\215\346\200\247\350\203\275.md" new file mode 100644 index 0000000..74135a9 --- /dev/null +++ "b/doc/14.14 - \344\275\277\347\224\250jemalloc\344\274\230\345\214\226swoole\345\206\205\345\255\230\345\210\206\351\205\215\346\200\247\350\203\275.md" @@ -0,0 +1,39 @@ +#使用jemalloc优化swoole内存分配性能 + +关于jemalloc +---- +jemalloc是一个比glibc malloc更高效的内存池技术,在Facebook公司被大量使用,在FreeBSD和FireFox项目中使用了jemalloc作为默认的内存管理器。使用jemalloc可以使程序的内存管理性能提升,减少内存碎片。 + +安装jemalloc +---- +* GITHUB主页: +* 下载地址: + +编译安装: +```shell +cd jemalloc +./configure --with-jemalloc-prefix=je_ +make -j 4 +``` + +使用jemalloc +---- +编译`Swoole`时增加`--with-jemalloc-dir=/path/to/jemalloc` + +```php +phpize +./configure --with-jemalloc-dir=/path/to/jemalloc +make +make install +``` + +预先载入 +---- +除了手工编译`jemalloc`增加命名空间的使用方式之外也可以使用`LD_PRELOAD`使用`jemalloc`。此方法也可以用于`Google`的`tcmalloc`内存池技术。 + +```shell +LD_PRELOAD="/usr/lib/libjemalloc.so" php server.php +LD_PRELOAD="/usr/lib/libtcmalloc.so" php server.php +``` + +> `/usr/lib/libjemalloc.so` 请填写正确的`jemalloc.so`路径 diff --git "a/doc/14.15 - C\345\274\200\345\217\221\350\200\205\345\246\202\344\275\225\344\275\277\347\224\250Swoole.md" "b/doc/14.15 - C\345\274\200\345\217\221\350\200\205\345\246\202\344\275\225\344\275\277\347\224\250Swoole.md" new file mode 100644 index 0000000..5169c20 --- /dev/null +++ "b/doc/14.15 - C\345\274\200\345\217\221\350\200\205\345\246\202\344\275\225\344\275\277\347\224\250Swoole.md" @@ -0,0 +1,59 @@ +#C开发者如何使用Swoole + +swoole使用cmake来做编译配置,示例程序在examples/server.c中。 +您可以在此基础上进行代码开发。 +如果需要修改编译细节的选项,请直接修改CMakeLists.txt + +生成config.h +---- +swoole依赖`phpize`和`configure`检测系统环境,生成`config.h` +```shell +cd swoole-src/ +phpize +./configure +``` +执行成功后`swoole-src`目录下会有`config.h` + +Build & Install +----- +```bash +cmake . +make +make install +``` +* `cmake`命令可以增加`cmake . -DCMAKE_INSTALL_PREFIX=/opt/swoole`参数指定安装的路径 +* `make`命令可以使用 `make DESTDIR=/opt/swoole install`参数指定安装的路径 + +安装路径非系统默认的lib目录时,需要配置`ld.so.conf`将swoole动态连接库所在的目录添加到link路径中。 + +```shell +#需要root权限 +echo "/opt/swoole/lib" >> /etc/ld.so.conf +#或者 +echo "/opt/swoole/lib" > /etc/ld.so.conf.d/swoole.conf +#更新动态连接库信息 +ldconfig +``` + +Example +----- +示例代码:examples/server.c +在C代码中只需要引入swoole头即可。 +```c +#include +#include + +int main() +{ + swServer serv; + swServer_create(&serv); + serv.onStart = my_onStart; + ... + swServer_start(&serv); +} +``` +编译运行 +``` +gcc -o server server.c -lswoole +./server +``` diff --git "a/doc/14.16 - C++\345\274\200\345\217\221\350\200\205\345\246\202\344\275\225\344\275\277\347\224\250Swoole.md" "b/doc/14.16 - C++\345\274\200\345\217\221\350\200\205\345\246\202\344\275\225\344\275\277\347\224\250Swoole.md" new file mode 100644 index 0000000..c5fee98 --- /dev/null +++ "b/doc/14.16 - C++\345\274\200\345\217\221\350\200\205\345\246\202\344\275\225\344\275\277\347\224\250Swoole.md" @@ -0,0 +1,164 @@ +#C++开发者如何使用Swoole + +PHP编写的Server程序在某些情况下表现会较差 + +* 内存占用敏感的场景,PHP底层使用内存结构`zval`来管理所有变量,会额外占用内存,如一个int32的整数可能需要占用16(PHP7)或24字节(PHP5)的内存,而C/C++只需要4字节。如果系统需要存储大量整数,占用的内存会非常大。 +* PHP是动态解释执行的,计算性能较差,纯运算的代码可能会比C/C++程序差几十甚至上百倍。此类场景下不适合使用PHP + +C/C++的支持弥补了这些不足,在上述场景下可以使用`c-swoole`或者`cpp-swoole`来编写Server程序。 + +`cpp-swoole`是对`c-swoole`的面向对象封装,支持了绝大部分swoole_server的特性包括task功能,另外还支持高精度定时器特性。 + +`cpp-swoole`依赖`libswoole.so`,需要先编译`c-swoole`生成`libswoole.so` + +编译libswoole.so +---- +```shell +git clone https://github.com/swoole/swoole-src.git +phpize +./configure +cmake . +#cmake -DCMAKE_INSTALL_PREFIX=/opt/swoole . +sudo make install +``` + +编译安装好`libswoole.so`后就可以下载`cpp-swoole`源码,编译`libswoole_cpp.so` + +编译libswoole_cpp.so +----- +```shell +git clone https://github.com/swoole/cpp-swoole.git +cmake . +make +sudo make install +``` + +编写程序 +---- +头文件: +```c +#include +#include +``` +服务器程序只需要继承`swoole::Server`,并实现响应的回调函数即可。 +```cpp +#include +#include +#include + +using namespace std; +using namespace swoole; + +class MyServer : public Server +{ +public: + MyServer(string _host, int _port, int _mode = SW_MODE_PROCESS, int _type = SW_SOCK_TCP) : + Server(_host, _port, _mode, _type) + { + serv.worker_num = 4; + SwooleG.task_worker_num = 2; + } + + virtual void onStart(); + virtual void onShutdown() {}; + virtual void onWorkerStart(int worker_id) {} + virtual void onWorkerStop(int worker_id) {} + virtual void onPipeMessage(int src_worker_id, const DataBuffer &) {} + virtual void onReceive(int fd, const DataBuffer &data); + virtual void onConnect(int fd); + virtual void onClose(int fd); + virtual void onPacket(const DataBuffer &data, ClientInfo &clientInfo) {}; + + virtual void onTask(int task_id, int src_worker_id, const DataBuffer &data); + virtual void onFinish(int task_id, const DataBuffer &data); +}; + +void MyServer::onReceive(int fd, const DataBuffer &data) +{ + swConnection *conn = swWorker_get_connection(&this->serv, fd); + printf("onReceive: fd=%d, ip=%s|port=%d Data=%s|Len=%ld\n", fd, swConnection_get_ip(conn), + swConnection_get_port(conn), (char *) data.buffer, data.length); + + int ret; + char resp_data[SW_BUFFER_SIZE]; + int n = snprintf(resp_data, SW_BUFFER_SIZE, (char *) "Server: %*s\n", (int) data.length, (char *) data.buffer); + ret = this->send(fd, resp_data, (uint32_t) n); + if (ret < 0) + { + printf("send to client fail. errno=%d\n", errno); + } + else + { + printf("send %d bytes to client success. data=%s\n", n, resp_data); + } + DataBuffer task_data("hello world\n"); + this->task(task_data); +// this->close(fd); +} + +void MyServer::onConnect(int fd) +{ + printf("PID=%d\tConnect fd=%d\n", getpid(), fd); +} + +void MyServer::onClose(int fd) +{ + printf("PID=%d\tClose fd=%d\n", getpid(), fd); +} + +void MyServer::onTask(int task_id, int src_worker_id, const DataBuffer &data) +{ + printf("PID=%d\tTaskID=%d\n", getpid(), task_id); +} + +void MyServer::onFinish(int task_id, const DataBuffer &data) +{ + printf("PID=%d\tClose fd=%d\n", getpid(), task_id); +} + +void MyServer::onStart() +{ + printf("server start\n"); +} + +class MyTimer : Timer +{ +public: + MyTimer(long ms, bool interval) : + Timer(ms, interval) + { + + } + + MyTimer(long ms) : + Timer(ms) + { + + } + +protected: + virtual void callback(void); + int count = 0; +}; + +void MyTimer::callback() +{ + printf("#%d\thello world\n", count); + if (count > 9) + { + this->clear(); + } + count++; +} + +int main(int argc, char **argv) +{ + MyServer server("127.0.0.1", 9501, SW_MODE_SINGLE); + server.listen("127.0.0.1", 9502, SW_SOCK_UDP); + server.listen("::1", 9503, SW_SOCK_TCP6); + server.listen("::1", 9504, SW_SOCK_UDP6); + server.setEvents(EVENT_onStart | EVENT_onReceive | EVENT_onClose | EVENT_onTask | EVENT_onFinish); + server.start(); +} +``` + diff --git "a/doc/14.17 - \344\275\277\347\224\250systemd\347\256\241\347\220\206swoole\346\234\215\345\212\241.md" "b/doc/14.17 - \344\275\277\347\224\250systemd\347\256\241\347\220\206swoole\346\234\215\345\212\241.md" new file mode 100644 index 0000000..f9c8473 --- /dev/null +++ "b/doc/14.17 - \344\275\277\347\224\250systemd\347\256\241\347\220\206swoole\346\234\215\345\212\241.md" @@ -0,0 +1,66 @@ +#使用systemd管理swoole服务 + +Systemd 是 Linux 系统中新一代的初始化系统(init),它主要的设计目标是克服 sysvinit 固有的缺点,提高系统的启动速度。很多新的Linux发行版已经使用`Systemd`取代了`init`,作为初始守护进程。 + +Swoole的服务器程序可以编写一段`service`脚本,交由`systemd`进行管理。实现故障重启、开机自启动等功能。 + +编写Service脚本 +---- +Systemd的Service配置在`/etc/systemd/system/`目录中,可以创建一个`echo.service`文件,实际项目应当改为对应的名称。编辑此文件,添加下列内容: +```shell +[Unit] +Description=Echo Http Server +After=network.target +After=syslog.target + +[Service] +Type=simple +LimitNOFILE=65535 +ExecStart=/usr/bin/php /opt/servers/echo/server.php +ExecReload=/bin/kill -USR1 $MAINPID +Restart=always + +[Install] +WantedBy=multi-user.target graphical.target +``` + +* `After` 指令约定了启动的顺序,必须在`network`和`syslog`启动后才启动`echo`服务 +* `Service`中填写了应用程序的路径信息,请修改为实际项目对应的路径 +* `Restart=always` 表示如果进程挂掉会自动拉起 +* `WantedBy` 约定了在哪些环境下启动,multi-user.target graphical.target表示在图形界面和命令行环境都会启动 + +编写完成后需要reload守护进程使其生效 +```shell +sudo systemctl --system daemon-reload +``` + +程序代码 +---- +```php +$http = new swoole_http_server("0.0.0.0", 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(); +``` + +管理服务 +--- +```shell +#启动服务 +sudo systemctl start echo.service +#reload服务 +sudo systemctl reload echo.service +#关闭服务 +sudo systemctl stop echo.service +``` + +查看服务状态 +---- +```shell +sudo systemctl status echo.service +``` +![echo.service状态](http://www.swoole.com/static/uploads//wiki/201702/28/517610894071.png) \ No newline at end of file diff --git "a/doc/14.18 - \347\275\221\345\215\241\344\270\255\346\226\255\350\256\276\347\275\256.md" "b/doc/14.18 - \347\275\221\345\215\241\344\270\255\346\226\255\350\256\276\347\275\256.md" new file mode 100644 index 0000000..6d97f0d --- /dev/null +++ "b/doc/14.18 - \347\275\221\345\215\241\344\270\255\346\226\255\350\256\276\347\275\256.md" @@ -0,0 +1,27 @@ +#网卡中断设置 + +密集网络IO的服务器,需要设置网卡中断来解决性能瓶颈。通过使用`top`观察每个核的`si`是否很高。如果处理网络中断的CPU达到瓶颈,将会影响网卡收发包,严重的情况下会出现大量丢包。通过下面的脚本,可以将软中断平均到CPU的每个核上,解决网卡中断瓶颈问题。 + +> `ffffff` 这里是根据CPU核数进行计算的 + + +```shell +#!/bin/bash +# Enable RPS (Receive Packet Steering) + +rfc=4096 +cc=$(grep -c processor /proc/cpuinfo) +rsfe=$(echo $cc*$rfc | bc) +sysctl -w net.core.rps_sock_flow_entries=$rsfe +for fileRps in $(ls /sys/class/net/eth*/queues/rx-*/rps_cpus) +do + echo ffffff > $fileRps +done + +for fileRfc in $(ls /sys/class/net/eth*/queues/rx-*/rps_flow_cnt) +do + echo $rfc > $fileRfc +done + +tail /sys/class/net/eth*/queues/rx-*/{rps_cpus,rps_flow_cnt} +``` \ No newline at end of file diff --git "a/doc/14.19 - \345\260\206Swoole\351\235\231\346\200\201\347\274\226\350\257\221\345\206\205\345\265\214\345\210\260PHP.md" "b/doc/14.19 - \345\260\206Swoole\351\235\231\346\200\201\347\274\226\350\257\221\345\206\205\345\265\214\345\210\260PHP.md" new file mode 100644 index 0000000..cebf818 --- /dev/null +++ "b/doc/14.19 - \345\260\206Swoole\351\235\231\346\200\201\347\274\226\350\257\221\345\206\205\345\265\214\345\210\260PHP.md" @@ -0,0 +1,27 @@ +#将Swoole静态编译内嵌到PHP + + `Swoole-1.9.15`支持了静态编译,可以将`Swoole`内嵌到`PHP`中。 + +准备 +---- +* 需要`swoole-src`和`php-src`两份源代码 +* 将`swoole`源码放置到`php-src/ext`目录中 +* 清理`swoole`源码目录,使用`phpize --clean`和`./clear.sh` + +配置 +---- +* 目前`swoole`只支持`cli`静态内联,必须关闭其他`SAPI`包括`php-fpm`、`CGI`、`phpdbg`等 +* 需要增加`--enable-swoole-static`和`--with-swoole`两项编译配置参数 + +构建 +---- +```shell +cd php-src/ +./buildconf --force +/configure --disable-all --enable-swoole-static --with-zlib --with-swoole --enable-cli --disable-cgi --disable-phpdbg +make -j +``` + +使用 +---- +编译完成后,在`sapi/cli`目录中可以得到`php`可执行文件。使用`./php --ri swoole`查看信息 \ No newline at end of file diff --git "a/doc/14.2 - Reactor\347\272\277\347\250\213.md" "b/doc/14.2 - Reactor\347\272\277\347\250\213.md" new file mode 100644 index 0000000..07b60a4 --- /dev/null +++ "b/doc/14.2 - Reactor\347\272\277\347\250\213.md" @@ -0,0 +1,15 @@ +#Reactor线程 + +Swoole的主进程是一个多线程的程序。其中有一组很重要的线程,称之为Reactor线程。它就是真正处理TCP连接,收发数据的线程。 + +Swoole的主线程在Accept新的连接后,会将这个连接分配给一个固定的Reactor线程,并由这个线程负责监听此socket。在socket可读时读取数据,并进行协议解析,将请求投递到Worker进程。在socket可写时将数据发送给TCP客户端。 + +> 分配的计算方式是fd % serv->reactor_num + +TCP和UDP的差异 +---- +* TCP客户端,Worker进程处理完请求后,调用`$server->send`会将数据发给`Reactor`线程,由`Reactor`线程再发给客户端 +* UDP客户端,Worker进程处理完请求后,调用`$server->sendto`会直接发给客户端,无需经过`Reactor`线程 + + + diff --git "a/doc/14.20 - \345\274\202\346\255\245\345\233\236\350\260\203\347\250\213\345\272\217\345\206\205\345\255\230\347\256\241\347\220\206.md" "b/doc/14.20 - \345\274\202\346\255\245\345\233\236\350\260\203\347\250\213\345\272\217\345\206\205\345\255\230\347\256\241\347\220\206.md" new file mode 100644 index 0000000..6150ffb --- /dev/null +++ "b/doc/14.20 - \345\274\202\346\255\245\345\233\236\350\260\203\347\250\213\345\272\217\345\206\205\345\255\230\347\256\241\347\220\206.md" @@ -0,0 +1,81 @@ +#异步回调程序内存管理 + +异步回调程序与同步阻塞程序的内存管理方式不同,异步程序是基于回调链引用计数实现内存的管理。本文会用一个最简单的实例讲解异步程序的内存管理。 + +实例程序 +---- +```php +$serv = new Swoole\Http\Server("127.0.0.1", 9502); + +$serv->on('Request', function($request, $response) { + $cli = new Swoole\Http\Client('127.0.0.1', 80); + $cli->post('/dump.php', array("key" => 'value'), function ($cli) use ($request, $response) { + $response->end("

{$cli->body}

"); + $cli->close(); + }); +}); + +$serv->start(); +``` + +onRequest +---- +* 请求到来这时会触发`onRequest`回调函数,可以得到`$request`和`$response`对象 +* 在`onRequest`回调函数中,创建了一个`Http\Client`,并发起一次`POST`请求 +* 然后`onRequest`函数结束并返回 + +这时按照正常的`PHP`函数调用流程,`$request`和`$response`对象会被销毁。但在上述程序中,`$request`和`$response`对象被使用了`use`语法,绑定到了匿名函数上,因此这2个对象的引用计数会被加`1`。`onRequest`函数返回时就不会真正销毁这2个对象了。 + +#### 引用链依赖 + +``` +request/response -> post(Closure 回调函数) -> $cli(HttpClient对象) -> post($cli->connect) +``` + +`$cli`对象,是在`onRequest`函数创建的局部变量,按照正常逻辑`$cli`对象在`onRequest`函数退出时也应该被销毁。但`Swoole`底层有一个特殊的逻辑,**所有异步客户端对象在发起连接时底层会自动增加一次引用计数,在连接关闭时减少一次引用计数**,因此`$cli`对象也不会销毁,`POST`请求中的匿名函数对象也不会销毁。 + + +Http响应 +---- +* 创建的`$cli`对象,接收到来自服务器端的响应,或者连接超时、响应超时,这时会回调指定的匿名函数,调用`end`向客户端发送响应 +* 回调函数中调用了`$cli->close`这时切断连接,`$cli`的引用计数减一。这时匿名函数退出底层会自动销毁`$cli`、`$request`、`$response` 3个对象 + +#### 引用链解除 + +``` +cli->close -> Closure 销毁 -> $cli 销毁 -> request/response 销毁 +``` + +多层嵌套 +---- +如果`Http\Client`的回调函数中调用了其他的异步客户端,如`Swoole\Redis`,对象会继续传读引用,形成一个异步调用链。当调用链的最后一个对象销毁时会向着调用链头部逐个递减引用计数,最终销毁对象。 + +```php +$serv = new Swoole\Http\Server("127.0.0.1", 9502); + +$serv->on('Request', function($request, $response) { + $cli = new Swoole\Http\Client('127.0.0.1', 80); + //发起连接,$cli 引用计数增加 + $cli->post('/dump.php', array("key" => 'value'), function ($cli) use ($request, $response) { + $redis = new Swoole\Redis; + //发起连接,$redis 引用计数增加 + $redis->connect('127.0.0.1', 6379, function ($redis, $result) use ($request, $response, $cli) { + $redis->get('test_key', function ($redis, $result) use ($request, $response, $cli) { + $response->end("

{$result}

"); + //关闭连接,$cli 引用计数减少 + $cli->close(); + //关闭连接,$redis 引用计数减少 + $redis->close(); + }); + }); + }); +}); + +$serv->start(); +``` + +* 这里`$response`和`$request`对象被`POST`匿名函数、`Redis->connect`匿名函数、`Redis->get`匿名函数引用,因此需要等到这3个函数执行后,引用计数减少为`0`,才会真正的销毁 +* `$cli`和`$redis`对象在发起`TCP`连接时,会被`Swoole`底层增加引用计数。只有`$cli->close()`和`$redis->close`被调用,或者远端服务器关闭连接,触发`$cli->onClose`和`$redis->onClose`,`$cli`和`$redis`这`2`个对象的,引用计数才会减少,函数退出时会销毁 +* `POST`匿名函数、`Redis->connect`匿名函数、`Redis->get`匿名函数,`3`个对象依附于`$cli`和`$redis`对象,当`$cli`和`$redis`对象销毁时,这`3`个对象也会被销毁 +* `POST`匿名函数、`Redis->connect`匿名函数、`Redis->get`匿名函数,匿名函数销毁时通过`use`语法引用的`$response`和`$request`对象也会销毁 + diff --git "a/doc/14.3 - Manager\350\277\233\347\250\213.md" "b/doc/14.3 - Manager\350\277\233\347\250\213.md" new file mode 100644 index 0000000..ba3faf3 --- /dev/null +++ "b/doc/14.3 - Manager\350\277\233\347\250\213.md" @@ -0,0 +1,9 @@ +#Manager进程 + +swoole中worker/task进程都是由Manager进程Fork并管理的。 + +* 子进程结束运行时,manager进程负责回收此子进程,避免成为僵尸进程。并创建新的子进程 +* 服务器关闭时,manager进程将发送信号给所有子进程,通知子进程关闭服务 +* 服务器reload时,manager进程会逐个关闭/重启子进程 + +> 为什么不是Master进程呢,主要原因是Master进程是多线程的,不能安全的执行fork操作。 \ No newline at end of file diff --git "a/doc/14.4 - Worker\350\277\233\347\250\213.md" "b/doc/14.4 - Worker\350\277\233\347\250\213.md" new file mode 100644 index 0000000..403d227 --- /dev/null +++ "b/doc/14.4 - Worker\350\277\233\347\250\213.md" @@ -0,0 +1,35 @@ +#Worker进程 + +Swoole提供了完善的进程管理机制,当Worker进程异常退出,如发生PHP的致命错误、被其他程序误杀,或达到max_request次数之后正常退出。主进程会重新拉起新的Worker进程。 +Worker进程内可以像普通的apache+php或者php-fpm中写代码。不需要像Node.js那样写异步回调的代码。 + +__主进程内的回调函数__: + +* onStart +* onShutdown +* onMasterConnect +* onMasterClose +* onTimer + +__Worker进程内的回调函数__ + +* onWorkerStart +* onWorkerStop +* onConnect +* onClose +* onReceive +* onTimer +* onFinish + +__task_worker进程内的回调函数__ + +* onTask +* onWorkerStart + +__管理进程内的回调函数__ + +* onManagerStart +* onManagerStop + + + diff --git "a/doc/14.5 - Reactor\343\200\201Worker\343\200\201TaskWorker\347\232\204\345\205\263\347\263\273.md" "b/doc/14.5 - Reactor\343\200\201Worker\343\200\201TaskWorker\347\232\204\345\205\263\347\263\273.md" new file mode 100644 index 0000000..55f64fb --- /dev/null +++ "b/doc/14.5 - Reactor\343\200\201Worker\343\200\201TaskWorker\347\232\204\345\205\263\347\263\273.md" @@ -0,0 +1,38 @@ +#Reactor、Worker、TaskWorker的关系 + +三种角色分别的职责是: + +Reactor线程 +------ +* 负责维护客户端`TCP`连接、处理网络`IO`、处理协议、收发数据 +* 完全是**异步非阻塞**的模式 +* 全部为`C`代码,除`Start`/`Shudown`事件回调外,不执行任何PHP代码 +* 将`TCP`客户端发来的数据缓冲、拼接、拆分成完整的一个请求数据包 +* `Reactor`以多线程的方式运行 + +Worker进程 +---- +* 接受由`Reactor`线程投递的请求数据包,并执行`PHP`回调函数处理数据 +* 生成响应数据并发给`Reactor`线程,由`Reactor`线程发送给`TCP`客户端 +* 可以是异步非阻塞模式,也可以是同步阻塞模式 +* `Worker`以多进程的方式运行 + +TaskWorker进程 +---- +* 接受由`Worker`进程通过`swoole_server->task/taskwait`方法投递的任务 +* 处理任务,并将结果数据返回(`swoole_server->finish`)给`Worker`进程 +* 完全是**同步阻塞**模式 +* `TaskWorker`以多进程的方式运行 + +关系 +---- +可以理解为`Reactor`就是`nginx`,`Worker`就是`php-fpm`。`Reactor`线程异步并行地处理网络请求,然后再转发给`Worker`进程中去处理。`Reactor`和`Worker`间通过`UnixSocket`进行通信。 + +在`php-fpm`的应用中,经常会将一个任务异步投递到`Redis`等队列中,并在后台启动一些`php`进程异步地处理这些任务。`Swoole`提供的`TaskWorker`是一套更完整的方案,将任务的投递、队列、`php`任务处理进程管理合为一体。通过底层提供的`API`可以非常简单地实现异步任务的处理。另外`TaskWorker`还可以在任务执行完成后,再返回一个结果反馈到`Worker`。 + +`Swoole`的`Reactor`、`Worker`、`TaskWorker`之间可以紧密的结合起来,提供更高级的使用方式。 + +一个更通俗的比喻,假设`Server`就是一个工厂,那`Reactor`就是销售,接受客户订单。而`Worker`就是工人,当销售接到订单后,`Worker`去工作生产出客户要的东西。而`TaskWorker`可以理解为行政人员,可以帮助`Worker`干些杂事,让`Worker`专心工作。 + +> 底层会为`Worker`进程、`TaskWorker`进程分配一个唯一的`ID` +> 不同的`Worker`和`TaskWorker`进程之间可以通过`sendMessage`接口进行通信 diff --git "a/doc/14.7 - \345\234\250php-fpm\346\210\226apache\344\270\255\344\275\277\347\224\250swoole.md" "b/doc/14.7 - \345\234\250php-fpm\346\210\226apache\344\270\255\344\275\277\347\224\250swoole.md" new file mode 100644 index 0000000..6c705e5 --- /dev/null +++ "b/doc/14.7 - \345\234\250php-fpm\346\210\226apache\344\270\255\344\275\277\347\224\250swoole.md" @@ -0,0 +1,14 @@ +#在php-fpm或apache中使用swoole + +swoole中绝大部分的模块只能用于`CLI命令行`环境,只有同步阻塞的`swoole_client`可以用于`php-fpm`或`apache`环境。 + +同步swoole_client +---- +```php +$client = new swoole_client(SWOOLE_SOCK_TCP); //同步阻塞 +$client->connect('127.0.0.1', 9501) or die("connect failed\n"); + +$client->send(str_repeat("A", 600)); +$data = $client->recv(700, 0) or die("recv failed\n"); +echo "recv: " . $data . "\n"; +``` diff --git "a/doc/14.8 - Swoole\345\274\202\346\255\245\344\270\216\345\220\214\346\255\245\347\232\204\351\200\211\346\213\251.md" "b/doc/14.8 - Swoole\345\274\202\346\255\245\344\270\216\345\220\214\346\255\245\347\232\204\351\200\211\346\213\251.md" new file mode 100644 index 0000000..b7ca63b --- /dev/null +++ "b/doc/14.8 - Swoole\345\274\202\346\255\245\344\270\216\345\220\214\346\255\245\347\232\204\351\200\211\346\213\251.md" @@ -0,0 +1,12 @@ +#Swoole异步与同步的选择 + +Swoole不仅支持异步,还支持同步。什么情况下使用同步,什么情况下使用异步。这里说明一下。 + +我们不赞成用异步回调的方式去做功能开发,传统的PHP同步方式实现功能和逻辑是最简单的,也是最佳的方案。像node.js这样到处callback,只是牺牲可维护性和开发效率。 + +但有些时候很适合用异步,比如FTP、聊天服务器,smtp,代理服务器等等此类以通信和读写磁盘为主,功能和业务逻辑其次的服务器程序。 + +“PHP的扩展函数API全是同步的”,这个说法并不正确,实际上同步阻塞的地方主要是网络调用,文件读写。例如mysql_query需要与mysql数据库服务器通信,curl需要调用网络,file_get_contents需要读写文件,以及其他fopen/fwrite/fread/fgets/fputs这些都是阻塞的API。除此之外PHP的array/string/mbstring等函数是非阻塞的。 + +swoole提供了异步的socket客户端,异步的mysql,而且1.6.12还提供了异步文件读写和异步DNS查询的功能。另外还提供了task/finish的API,完全可以解决阻塞IO问题。 + diff --git "a/doc/15 - \345\205\266\344\273\226.md" "b/doc/15 - \345\205\266\344\273\226.md" new file mode 100644 index 0000000..d9f5fe5 --- /dev/null +++ "b/doc/15 - \345\205\266\344\273\226.md" @@ -0,0 +1,2 @@ +#其他 + diff --git "a/doc/15.1 - \345\207\275\346\225\260\345\210\227\350\241\250.md" "b/doc/15.1 - \345\207\275\346\225\260\345\210\227\350\241\250.md" new file mode 100644 index 0000000..2c03397 --- /dev/null +++ "b/doc/15.1 - \345\207\275\346\225\260\345\210\227\350\241\250.md" @@ -0,0 +1,3 @@ +#函数列表 + +swoole除了网络通信相关的函数外,还提供了一些获取系统信息的函数供PHP程序使用。 \ No newline at end of file diff --git a/doc/15.1.1 - swoole_set_process_name.md b/doc/15.1.1 - swoole_set_process_name.md new file mode 100644 index 0000000..027be83 --- /dev/null +++ b/doc/15.1.1 - swoole_set_process_name.md @@ -0,0 +1,27 @@ +#swoole_set_process_name + +用于设置进程的名称。修改进程名称后,通过ps命令看到的将不再是php your_file.php。而是设定的字符串。 +此函数接受一个字符串参数。 +此函数与PHP5.5提供的cli_set_process_title功能是相同的。但swoole_set_process_name可用于PHP5.2之上的任意版本。swoole_set_process_name兼容性比cli_set_process_title要差,如果存在cli_set_process_title函数则优先使用cli_set_process_title。 + +```php +void swoole_set_process_name(string $name); +``` +示例代码: +```php +swoole_set_process_name("swoole server"); +var_dump($argv); +sleep(1000); +``` + +> swoole_set_process_name在1.6.3版本提供 +> 在onStart回调中执行此函数,将修改主进程的名称。在onWorkerStart中调用将修改worker子进程的名称。 + +###如何为Swoole Server重命名各个进程名称### +* 在swoole_server_create之前修改为manager进程名称 +* onStart调用时修改为主进程名称 +* onWorkerStart修改为worker进程名称 + +> 1.6.12后增加了onManagerStart事件回调,可以在这里设置管理进程的名称 +> 低版本Linux内核和Mac OSX不支持进程重命名 + diff --git a/doc/15.1.2 - swoole_version.md b/doc/15.1.2 - swoole_version.md new file mode 100644 index 0000000..7ab64ae --- /dev/null +++ b/doc/15.1.2 - swoole_version.md @@ -0,0 +1,6 @@ +#swoole_version + +获取swoole扩展的版本号,如1.6.10 +```php +string swoole_version(); +``` diff --git a/doc/15.1.3 - swoole_strerror.md b/doc/15.1.3 - swoole_strerror.md new file mode 100644 index 0000000..fc3ffb8 --- /dev/null +++ b/doc/15.1.3 - swoole_strerror.md @@ -0,0 +1,6 @@ +#swoole_strerror + +将标准的Unix Errno错误码转换成错误信息。函数原型: +```php +string swoole_strerror(int $errno); +``` diff --git a/doc/15.1.4 - swoole_errno.md b/doc/15.1.4 - swoole_errno.md new file mode 100644 index 0000000..dddd637 --- /dev/null +++ b/doc/15.1.4 - swoole_errno.md @@ -0,0 +1,7 @@ +#swoole_errno + +获取最近一次系统调用的错误码,等同于`C/C++`的`errno`变量。 +```php +int swoole_errno(); +``` +错误码的值与操作系统有关。可是使用`swoole_strerror`将错误转换为错误信息。 diff --git a/doc/15.1.5 - swoole_get_local_ip.md b/doc/15.1.5 - swoole_get_local_ip.md new file mode 100644 index 0000000..0d629f1 --- /dev/null +++ b/doc/15.1.5 - swoole_get_local_ip.md @@ -0,0 +1,12 @@ +#swoole_get_local_ip + +此函数用于获取本机所有网络接口的IP地 + +```php +function swoole_get_local_ip(); +$result = array("eth0" => "192.168.1.100"); +``` + +* 目前只返回IPv4地址,返回结果会过滤掉本地loop地址127.0.0.1。 +* 结果数组是以interface名称为key的关联数组。比如 array("eth0" => "192.168.1.100") +* 此函数会实时调用`ioctl`系统调用获取接口信息,底层无缓存 diff --git a/doc/15.1.6 - swoole_clear_dns_cache.md b/doc/15.1.6 - swoole_clear_dns_cache.md new file mode 100644 index 0000000..44399b9 --- /dev/null +++ b/doc/15.1.6 - swoole_clear_dns_cache.md @@ -0,0 +1,7 @@ +#swoole_clear_dns_cache + +清除swoole内置的DNS缓存,对`swoole_client`和`swoole_async_dns_lookup` 有效。 + +```php +function swoole_clear_dns_cache(); +``` \ No newline at end of file diff --git a/doc/15.1.7 - swoole_get_local_mac.md b/doc/15.1.7 - swoole_get_local_mac.md new file mode 100644 index 0000000..e823ace --- /dev/null +++ b/doc/15.1.7 - swoole_get_local_mac.md @@ -0,0 +1,23 @@ +#swoole_get_local_mac + +获取本机网卡`Mac`地址。 +```php +function swoole_get_local_mac() : array; +``` + +* 调用成功返回所有网卡的`Mac`地址 + +```php +array(4) { + ["lo"]=> + string(17) "00:00:00:00:00:00" + ["eno1"]=> + string(17) "64:00:6A:65:51:32" + ["docker0"]=> + string(17) "02:42:21:9B:12:05" + ["vboxnet0"]=> + string(17) "0A:00:27:00:00:00" +} +``` + +> 在`1.9.18`或更高版本可用 \ No newline at end of file diff --git a/doc/15.1.8 - swoole_cpu_num.md b/doc/15.1.8 - swoole_cpu_num.md new file mode 100644 index 0000000..657d0e3 --- /dev/null +++ b/doc/15.1.8 - swoole_cpu_num.md @@ -0,0 +1,12 @@ +#swoole_cpu_num + +获取本机CPU核数 +```php +function swoole_cpu_num() : int; +``` + +* 调用成功返回CPU核数,例如: + +```php +4 +``` \ No newline at end of file diff --git "a/doc/15.10 - \351\231\204\345\275\225\357\274\232gdb\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" "b/doc/15.10 - \351\231\204\345\275\225\357\274\232gdb\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..301a542 --- /dev/null +++ "b/doc/15.10 - \351\231\204\345\275\225\357\274\232gdb\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,58 @@ +#附录:gdb工具的使用 + +GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,可以用来调试C/C++开发的程序,PHP和Swoole是使用C语言开发的,所以可以用GDB来调试PHP+Swoole的程序。 + +gdb调试是命令行交互式的,需要掌握常用的指令。 + +使用方法 +---- +```shell +gdb -p 进程ID +gdb php +gdb php core +``` +gdb有3种使用方式: + +* 跟踪正在运行的PHP程序,使用gdb -p 进程ID +* 使用gdb运行并调试PHP程序,使用gdb php -> run server.php 进行调试 +* PHP程序发生coredump后使用gdb加载core内存镜像进行调试 gdb php core + +> 如果PATH环境变量中没有php,gdb时需要指定绝对路径,如gdb /usr/local/bin/php + +常用指令 +---- +* `p`:print,打印C变量的值 +* `c`:continue,继续运行被中止的程序 +* `b`:breakpoint,设置断点,可以按照函数名设置,如`b zif_php_function`,也可以按照源代码的行数指定断点,如`b src/networker/Server.c:1000` +* `t`:thread,切换线程,如果进程拥有多个线程,可以使用t指令,切换到不同的线程 +* `ctrl + c`:中断当前正在运行的程序,和c指令配合使用 +* `n`:next,执行下一行,单步调试 +* `info threads`:查看运行的所有线程 +* `l`:list,查看源码,可以使用`l 函数名` 或者 `l 行号` +* `bt`:backtrace,查看运行时的函数调用栈 +* `finish`:完成当前函数 +* `f`:frame,与bt配合使用,可以切换到函数调用栈的某一层 +* `r`:run,运行程序 + +zbacktrace +---- +zbacktrace是PHP源码包提供的一个gdb自定义指令,功能与bt指令类似,与bt不同的是zbacktrace看到的调用栈是PHP函数调用栈,而不是C函数。 + +下载php-src,解压后从根目录中找到一个`.gdbinit`文件,在gdb shell中输入 +```shell +source .gdbinit +zbacktrace +``` +.gdbinit还提供了其他更多指令,可以查看源码了解详细的信息。 + +使用gdb+zbacktrace跟踪死循环问题 +---- +```shell +gdb -p 进程ID +``` +* 使用`ps aux`工具找出发生死循环的Worker进程ID +* `gdb -p`跟踪指定的进程 +* 反复调用 `ctrl + c` 、`zbacktrace`、`c` 查看程序在哪段PHP代码发生循环 +* 找到对应的PHP代码进行解决 + + diff --git "a/doc/15.11 - \351\231\204\345\275\225\357\274\232lsof\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" "b/doc/15.11 - \351\231\204\345\275\225\357\274\232lsof\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..7cbbb36 --- /dev/null +++ "b/doc/15.11 - \351\231\204\345\275\225\357\274\232lsof\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,60 @@ +#附录:lsof工具的使用 + +Linux平台提供了`lsof`工具可以查看某个进程打开的文件句柄。可以用于跟踪swoole的工作进程所有打开的socket、file、资源。 + +使用方法 +---------- +```shell +lsof -p [进程ID] +``` + +运行结果 +---- +```shell +lsof -p 26821 +lsof: WARNING: can't stat() tracefs file system /sys/kernel/debug/tracing + Output information may be incomplete. +COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME +php 26821 htf cwd DIR 8,4 4096 5375979 /home/htf/workspace/swoole/examples +php 26821 htf rtd DIR 8,4 4096 2 / +php 26821 htf txt REG 8,4 24192400 6160666 /opt/php/php-5.6/bin/php +php 26821 htf DEL REG 0,5 7204965 /dev/zero +php 26821 htf DEL REG 0,5 7204960 /dev/zero +php 26821 htf DEL REG 0,5 7204958 /dev/zero +php 26821 htf DEL REG 0,5 7204957 /dev/zero +php 26821 htf DEL REG 0,5 7204945 /dev/zero +php 26821 htf mem REG 8,4 761912 6160770 /opt/php/php-5.6/lib/php/extensions/debug-zts-20131226/gd.so +php 26821 htf mem REG 8,4 2769230 2757968 /usr/local/lib/libcrypto.so.1.1 +php 26821 htf mem REG 8,4 162632 6322346 /lib/x86_64-linux-gnu/ld-2.23.so +php 26821 htf DEL REG 0,5 7204959 /dev/zero +php 26821 htf 0u CHR 136,20 0t0 23 /dev/pts/20 +php 26821 htf 1u CHR 136,20 0t0 23 /dev/pts/20 +php 26821 htf 2u CHR 136,20 0t0 23 /dev/pts/20 +php 26821 htf 3r CHR 1,9 0t0 11 /dev/urandom +php 26821 htf 4u IPv4 7204948 0t0 TCP *:9501 (LISTEN) +php 26821 htf 5u IPv4 7204949 0t0 UDP *:9502 +php 26821 htf 6u IPv6 7204950 0t0 TCP *:9503 (LISTEN) +php 26821 htf 7u IPv6 7204951 0t0 UDP *:9504 +php 26821 htf 8u IPv4 7204952 0t0 TCP localhost:8000 (LISTEN) +php 26821 htf 9u unix 0x0000000000000000 0t0 7204953 type=DGRAM +php 26821 htf 10u unix 0x0000000000000000 0t0 7204954 type=DGRAM +php 26821 htf 11u unix 0x0000000000000000 0t0 7204955 type=DGRAM +php 26821 htf 12u unix 0x0000000000000000 0t0 7204956 type=DGRAM +php 26821 htf 13u a_inode 0,11 0 9043 [eventfd] +php 26821 htf 14u unix 0x0000000000000000 0t0 7204961 type=DGRAM +php 26821 htf 15u unix 0x0000000000000000 0t0 7204962 type=DGRAM +php 26821 htf 16u unix 0x0000000000000000 0t0 7204963 type=DGRAM +php 26821 htf 17u unix 0x0000000000000000 0t0 7204964 type=DGRAM +php 26821 htf 18u a_inode 0,11 0 9043 [eventpoll] +php 26821 htf 19u a_inode 0,11 0 9043 [signalfd] +php 26821 htf 20u a_inode 0,11 0 9043 [eventpoll] +php 26821 htf 22u IPv4 7452776 0t0 TCP localhost:9501->localhost:59056 (ESTABLISHED) +``` + +* so文件是进程加载的动态连接库 +* IPv4/IPv6 TCP (LISTEN) 是服务器监听的端口 +* UDP 是服务器监听的UDP端口 +* unix type=DGRAM 时是进程创建的UnixSocket管道 +* IPv4 (ESTABLISHED) 表示连接到服务器的TCP客户端,包含了客户端的IP和PORT,以及状态(ESTABLISHED) +* 9u / 10u 表示该文件句柄的fd值(文件描述符) +* 其他更多信息可以参考lsof的手册 diff --git "a/doc/15.12 - \351\231\204\345\275\225\357\274\232perf\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" "b/doc/15.12 - \351\231\204\345\275\225\357\274\232perf\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..284fa75 --- /dev/null +++ "b/doc/15.12 - \351\231\204\345\275\225\357\274\232perf\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,18 @@ +#附录:perf工具的使用 + + `perf`工具是Linux内核提供一个非常强大的动态跟踪工具,`perf top`指令可用于实时分析正在执行程序的性能问题。与`callgrind`、`xdebug`、`xhprof`等工具不同,`perf`无需修改代码导出profile结果文件。 + +使用方法 +--- +```shell +perf top -p [进程ID] +``` + +输出结果 +--- +![perf top输出结果](http://www.swoole.com/static/uploads//wiki/201611/15/978050334623.png) + +perf结果中清楚地展示了当前进程运行时各个C函数的执行耗时,可以了解哪个C函数占用CPU资源较多。 + +如果你熟悉Zend VM,某些Zend函数调用过多,可以说明你的程序中大量使用了某些函数,导致CPU占用过高,针对性的进行优化。 + diff --git "a/doc/15.13 - \351\231\204\345\275\225\357\274\232\347\274\226\350\257\221PHP\346\211\251\345\261\225\347\232\204\347\233\270\345\205\263\345\267\245\345\205\267.md" "b/doc/15.13 - \351\231\204\345\275\225\357\274\232\347\274\226\350\257\221PHP\346\211\251\345\261\225\347\232\204\347\233\270\345\205\263\345\267\245\345\205\267.md" new file mode 100644 index 0000000..b01be9b --- /dev/null +++ "b/doc/15.13 - \351\231\204\345\275\225\357\274\232\347\274\226\350\257\221PHP\346\211\251\345\261\225\347\232\204\347\233\270\345\205\263\345\267\245\345\205\267.md" @@ -0,0 +1,40 @@ +#附录:编译PHP扩展的相关工具 + +首先你需要下载一份扩展的源码,可以到github或者pecl.php.net上下载,解压后放到一个目录中,cd进入此目录。 + +autoconf +---- +根据config.m4生成configure脚本,phpize是基于autoconf的封装。 + +phpize +---- +phpize这个工具是php官方提供的,用于将PHP扩展的config.m4解析生成./configure 脚本。 + +configure +---- +这个脚本是用来检测系统和环境状态,依赖库和头文件是否存在,编译配置等 + +php-config +----- +这个工具执行后会打印当前PHP安装在哪里目录,API版本号是什么,扩展目录在哪里等信息。configure脚本需要依赖它找到PHP安装的目录 + +make +----- +用来将.c源文件编译为目标文件。make install将编译好的扩展文件,如swoole.so安装到PHP的扩展目录下 + +gcc +---- +编译器,将*.c源文件编译为目标文件。并连接所有目标文件生成swoole.so + +clang +---- +另外一种编译器,FreeBSD/MacOS下用的比较多。 + +安装过程 +----- +```shell +phpize +./configure +make +make install +``` diff --git "a/doc/15.14 - \345\244\207\347\224\250\357\274\232\345\267\262\347\247\273\351\231\244\347\232\204\345\216\206\345\217\262\347\211\271\346\200\247.md" "b/doc/15.14 - \345\244\207\347\224\250\357\274\232\345\267\262\347\247\273\351\231\244\347\232\204\345\216\206\345\217\262\347\211\271\346\200\247.md" new file mode 100644 index 0000000..3eb22d0 --- /dev/null +++ "b/doc/15.14 - \345\244\207\347\224\250\357\274\232\345\267\262\347\247\273\351\231\244\347\232\204\345\216\206\345\217\262\347\211\271\346\200\247.md" @@ -0,0 +1,3 @@ +#备用:已移除的历史特性 + +本页面记录`Swoole`版本更新中废弃的特性。 \ No newline at end of file diff --git a/doc/15.14.1 - swoole_server->handler.md b/doc/15.14.1 - swoole_server->handler.md new file mode 100644 index 0000000..1823e23 --- /dev/null +++ b/doc/15.14.1 - swoole_server->handler.md @@ -0,0 +1,34 @@ +#swoole_server->handler + + 警告:此方法即将移除 +设置Server的事件回调函数,原型: +```php +bool swoole_server->handler(string $event_name, mixed $event_callback_function); +bool swoole_server_handler(swoole_server $serv, string $event_name, mixed $event_callback_function); +``` + +示例: +```php +$serv->handler('onStart', 'my_onStart'); +function my_onStart($serv) +{ + echo "Server:start\n"; +} +``` + +* 第一个参数是swoole的资源对象 +* 第二个参数是回调的名称, 大小写不敏感,具体内容参考回调函数列表 +* 第三个函数是回调的PHP函数,可以是字符串,数组,匿名函数。比如 +* handler/on/set 方法只能在swoole_server::start前调用 + +```php +$serv->handler('onStart', 'my_onStart'); +$serv->handler('onStart', array($this, 'my_onStart')); +$serv->handler('onStart', 'myClass::onStart'); +``` + +设置成功后返回true。如果$event_name填写错误将返回false。 + +> onConnect/onClose/onReceive这3个回调函数必须设置。其他事件回调函数可选 +> 如果设定了timer定时器,onTimer事件回调函数也必须设置 +> 如果启用了task_worker,onTask/onFinish事件回调函数必须设置 diff --git a/doc/15.14.10 - onMasterConnect.md b/doc/15.14.10 - onMasterConnect.md new file mode 100644 index 0000000..e66665e --- /dev/null +++ b/doc/15.14.10 - onMasterConnect.md @@ -0,0 +1,11 @@ +#onMasterConnect + +当连接进入时,回调此函数。与onConnect相同。onMasterConnect/onMasterClose都是在主进程中执行的。 + +```php +void onMasterConnect(resource $server, int $fd, int $from_id); +``` + +> 此回调函数中不要有阻塞操作,否则会导致服务器端无法及时Accept新的连接 +> 由于是在不同的进程空间内,onMasterConnect/onMasterClose对全局变量的修改在worker进程中是无效的 +> 1.7.5+此事件已被移除 diff --git a/doc/15.14.11 - onMasterClose.md b/doc/15.14.11 - onMasterClose.md new file mode 100644 index 0000000..764fe23 --- /dev/null +++ b/doc/15.14.11 - onMasterClose.md @@ -0,0 +1,10 @@ +#onMasterClose + +当连接被关闭时,回调此函数。与onClose相同。onMasterConnect/onMasterClose都是在主进程中执行的。 + +```php +void onMasterClose(resource $server, int $fd, int $from_id); +``` + +> 此回调函数中不要有阻塞操作,否则会导致服务器端无法及时Accept新的连接 +> 1.7.5+此事件已被移除 diff --git a/doc/15.14.2 - task_worker_max.md b/doc/15.14.2 - task_worker_max.md new file mode 100644 index 0000000..6e335a2 --- /dev/null +++ b/doc/15.14.2 - task_worker_max.md @@ -0,0 +1,4 @@ +#task_worker_max + +启用Task进程动态调整,task_worker_max用于设置Task进程的最大数量。 + diff --git a/doc/15.14.3 - swoole_server->addtimer.md b/doc/15.14.3 - swoole_server->addtimer.md new file mode 100644 index 0000000..9f2ff57 --- /dev/null +++ b/doc/15.14.3 - swoole_server->addtimer.md @@ -0,0 +1,34 @@ +#swoole_server->addtimer + + 此方法已移除,请勿使用 +设置定时器。1.6.12版本前此函数不能用在消息队列模式下,1.6.12后消息队列IPC模式也可以使用定时器。 +```php +bool swoole_server->addtimer(int $interval); +bool swoole_server_addtimer(swoole_server $serv, int $interval); +``` +第二个参数是定时器的间隔时间,单位为毫秒。swoole定时器的最小颗粒是1毫秒。支持多个定时器。此函数可以用于worker进程中。 + +* swoole1.6.5之前支持的单位是秒,所以1.6.5之前传入的参数为1,那在1.6.5后需要传入1000 +* swoole1.6.5之后,addtimer必须在onStart/onWorkerStart/onConnect/onReceive/onClose等回调函数中才可以使用,否则会抛出错误。并且定时器无效 +* 注意不能存在2个相同间隔时间的定时器 +* 即使在代码中多次添加一个定时器,也只会有1个生效 + +> 1.7.5之后onStart回调中不再支持定时器 +> 建议使用tick定时器,addtimer定时器未来或将移除 + +__增加定时器后需要为Server设置onTimer回调函数,否则Server将无法启动__。多个定时器都会回调此函数。在这个函数内需要自行switch,根据interval的值来判断是来自于哪个定时器。 + +面向对象风格: +```php +$serv->addtimer(1000); //1s +$serv->addtimer(20); //20ms +``` + +```php +$serv->on('Timer', 'my_OnTimer'); + +function my_OnTimer($serv, $interval) +{ + echo "Timer[$interval] is call\n"; +} +``` \ No newline at end of file diff --git a/doc/15.14.4 - swoole_server->deltimer.md b/doc/15.14.4 - swoole_server->deltimer.md new file mode 100644 index 0000000..f09c1f7 --- /dev/null +++ b/doc/15.14.4 - swoole_server->deltimer.md @@ -0,0 +1,9 @@ +#swoole_server->deltimer + + 警告:此方法已废弃 + +删除定时器。 +```php +void swoole_server->deltimer(int $interval); +``` +参数为定时器的间隔时间。 \ No newline at end of file diff --git a/doc/15.14.5 - onTimer.md b/doc/15.14.5 - onTimer.md new file mode 100644 index 0000000..febb838 --- /dev/null +++ b/doc/15.14.5 - onTimer.md @@ -0,0 +1,12 @@ +#onTimer + +定时器触发,函数原型为 +```php +function onTimer(swoole_server $server, int $interval); +``` +$interval是定时器时间间隔,根据$interval的值来区分是哪个定时器触发的。这里的定时器是由$serv->addtimer来添加的,是固定间隔循环触发的。 + +> `onTimer`中执行时间过长,会导致下一次定时延缓触发。如设定1秒的定时器,1秒后会触发onTimer,onTimer函数用时1.5s,那么第二次触发`onTimer`的时间为第3秒。中间第2秒的定时器会被丢弃 +> onTimer回调函数如果要执行一个耗时操作,最好是使用`$serv->task`投递到task进程池中执行 +> `1.8.0`或更高版本已废弃 + diff --git a/doc/15.14.6 - swoole_timer_add.md b/doc/15.14.6 - swoole_timer_add.md new file mode 100644 index 0000000..561eafb --- /dev/null +++ b/doc/15.14.6 - swoole_timer_add.md @@ -0,0 +1,19 @@ +#swoole_timer_add + +增加定时器,用于fpm/apache/cli环境下。 +```php +swoole_timer_add($interval, $callback); + +swoole_timer_add(3000, function($interval) { + echo "timer[$interval] :".date("H:i:s")." call\n"; +}); +``` + +* $interval为定时器间隔,单位是毫秒,不能存在同样时间间隔的2个定时器 +* $callback为定时器的事件回调函数 + +> 需要swoole-1.6.12+ +> swoole_server中不可使用 +> 1.8.0以上版本即将移除此定时器,请使用swoole_timer_tick +> 定时器必须在全异步模式下才能使用,同步阻塞的代码中不要使用 + diff --git a/doc/15.14.7 - swoole_timer_del.md b/doc/15.14.7 - swoole_timer_del.md new file mode 100644 index 0000000..301770c --- /dev/null +++ b/doc/15.14.7 - swoole_timer_del.md @@ -0,0 +1,8 @@ +#swoole_timer_del + +删除swoole_timer_add设置的定时器。 +```php +swoole_timer_del($interval); +``` + +> 需要swoole-1.6.12+ diff --git a/doc/15.14.8 - swoole_get_mysqli_sock.md b/doc/15.14.8 - swoole_get_mysqli_sock.md new file mode 100644 index 0000000..d2bd4d6 --- /dev/null +++ b/doc/15.14.8 - swoole_get_mysqli_sock.md @@ -0,0 +1,40 @@ +#swoole_get_mysqli_sock + +用于获取MySQLi的socket文件描述符。可将mysql的socket增加到swoole中,执行异步MySQL查询。 +```php +int swoole_get_mysqli_sock(mysqli $db) +``` + +> swoole_get_mysqli_sock仅支持mysqlnd驱动,php5.4以下版本不支持此特性 +> 1.8.6或更高版本已移除 + +示例: +```php +$db = new mysqli; +$db->connect('127.0.0.1', 'root', 'root', 'test'); +$db->query("show tables", MYSQLI_ASYNC); +swoole_event_add(swoole_get_mysqli_sock($db), function($db_sock) { + global $db; + $res = $db->reap_async_query(); + var_dump($res->fetch_all(MYSQLI_ASSOC)); + swoole_event_exit(); +}); +``` + +apt-get/yum安装的PHP无法编译通过 +----- +__建议自行编译PHP__,而不是使用apt-get或yum安装,避免因为缺少头文件导致编译不通过。编译PHP时要加入--enable-mysqlnd --with-mysqli + +同时并发执行2条以上SQL语句 +---- +一个MySQL连接只能执行1条SQL语句,下面的代码是错误的。 +```php +$db->query("select * from test", MYSQLI_ASYNC); +$db->query("select * from test", MYSQLI_ASYNC); +``` +在第一条SQL执行后,未调用reap_async_query返回结果前,不能再次执行$db->query()。如果想要并发执行SQL需要创建2个以上MySQL连接,并分别调用swoole_get_mysqli_sock和swoole_event_add,加入到事件循环中。可以参考 [https://github.com/swoole/swoole-src/blob/master/examples/mysql_proxy_server.php](https://github.com/swoole/swoole-src/blob/master/examples/mysql_proxy_server.php)。 + +异步并行MySQL是否只用1个连接即可? +---- +这是错误的理解,即使是异步MySQL也需要一个连接池。正如上一条信息所示,并发SQL必须有多个连接。 + diff --git a/doc/15.14.9 - swoole_mysql_query.md b/doc/15.14.9 - swoole_mysql_query.md new file mode 100644 index 0000000..417241e --- /dev/null +++ b/doc/15.14.9 - swoole_mysql_query.md @@ -0,0 +1,66 @@ +#swoole_mysql_query + +异步地执行一条SQL语言,需要依赖MySQLi和mysqlnd扩展。此函数是swoole底层提供的真异步函数。解决了PHP官方`mysqli->reap_async_query`方法存在的2个严重问题。 + +1. `mysqli->reap_async_query`的recv缓冲区设置过小,在读取较大的RecordSet时会浪费大量read系统调用,性能不佳 +2. MySQL服务器的RecordSet可能会分段发送,`mysqli->reap_async_query`方法会阻塞。导致程序退化为同步阻塞模式。并发能力大大降低 + +`swoole_mysql_query`底层使用`64K`内存缓冲区,即使读取很大的RecordSet也仅需少量的read系统调用。另外`swoole_mysql_query`借助swoole提供的Epoll接口异步读取MySQL服务器的`RecordSet`,整个过程没有任何阻塞。 + +函数原型 +----- +```php +function swoole_mysql_query(mysqli $link, string $sql, callable $callback); +``` + +* $link为已连接的mysqli对象 +* $sql为要执行的SQL语句 +* $callback执行成功后会回调此函数 +* 每个MySQLi连接只能同时执行一条SQL,必须等待返回结果后才能执行下一条SQL + +> swoole_mysql_query需要swoole-1.8.0或更高版本,并且仅可用于CLI命令行环境 +> 1.8.6或更高版本已移除 + +回调函数 +---- +```php +function onSQLReady(mysqli $link, mixed $result); +``` +* 执行失败,`$result`为`false`,读取`$link`对象的`_error`属性获得错误信息,`_errno`属性获得错误码 +* 执行成功,SQL为非查询语句,`$result`为`true`,读取`$link`对象的`_affected_rows`属性获得影响的行数,`_insert_id`属性获得`Insert`操作的自增ID +* 执行成功,SQL为查询语句,`$result`为结果数组 + +使用示例 +---- + +```php +$db = new mysqli; + +$db->connect('127.0.0.1', 'root', 'root', 'test'); +$sql = "SELECT * FROM `userinfo` LIMIT 0, 10000"; +$s = microtime(true); + +swoole_mysql_query($db, $sql, function(mysqli $db, $r) { + global $s; + //SQL执行失败了 + if ($r == false) + { + var_dump($db->_error, $db->_errno); + } + //执行成功,update/delete/insert语句,没有结果集 + elseif ($r == true) + { + var_dump($db->_affected_rows, $db->_insert_id); + } + //执行成功,$r是结果集数组 + else + { + echo "count=".count($r).", time=".(microtime(true) - $s), "\n"; + var_dump($r); + + swoole_mysql_query($db, "show tables", function ($db, $r) { + var_dump($r); + }); + } +}); +``` diff --git "a/doc/15.15 - \345\216\206\345\217\262\357\274\232\347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225\357\274\2101.x\357\274\211.md" "b/doc/15.15 - \345\216\206\345\217\262\357\274\232\347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225\357\274\2101.x\357\274\211.md" new file mode 100644 index 0000000..ef54c78 --- /dev/null +++ "b/doc/15.15 - \345\216\206\345\217\262\357\274\232\347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225\357\274\2101.x\357\274\211.md" @@ -0,0 +1,2 @@ +#历史:版本更新记录(1.x) + diff --git a/doc/15.15.1 - 1.9.19.md b/doc/15.15.1 - 1.9.19.md new file mode 100644 index 0000000..b3adf43 --- /dev/null +++ b/doc/15.15.1 - 1.9.19.md @@ -0,0 +1,10 @@ +#1.9.19 + +* 修复`Channel::push`超过8K时创建临时文件失败导致崩溃的问题 +* 修复`MacOS`下`onShutdown`无法执行的问题 +* 自动创建`task_tmpdir`和`upload_tmp_dir`目录 +* 修复`SSL`服务器设置`ciphers`或`ecdh_curve`导致崩溃的问题 +* 优化`Server::bind`方法的性能,使用自旋锁,允许在任何情况下使用 +* 增加`reload_async`选项,可以控制异步重启的开关 +* 增加`tcp_fastopen`选项,开启`TCP`快速握手 +* 优化异步客户端减少内存需求,每个客户端仅分配`64K`内存 diff --git a/doc/15.15.10 - 1.9.7.md b/doc/15.15.10 - 1.9.7.md new file mode 100644 index 0000000..beacdfc --- /dev/null +++ b/doc/15.15.10 - 1.9.7.md @@ -0,0 +1,14 @@ +#1.9.7 + +* 增加对`systemd.socket`的支持 +* 修复低版本gcc编译时MySQL客户端崩溃的问题 +* 增加`swoole\http2\client`新模块 +* 修复BASE模式下shutdown时发生崩溃的问题 +* 修复`openssl-1.1`配置检测存在错误问题 +* 增加客户端对`http-proxy`的支持 +* 修复BASE模式下启用`open_eof_split`后在`onReceive`回调函数中关闭连接导致崩溃的问题 +* 修复`Redis\Server::format`函数`SET`和`MAP`格式化错误的问题 +* 修复启用`swoole.fast_serialize`时直接传入字符串类型变量导致崩溃的问题 +* 当服务器主动关闭连接时`onClose`回调中的`$reactorId`变量会设置为`-1` +* 增加`swoole_process::close`参数,允许只关闭管道的其中一个 + diff --git a/doc/15.15.11 - 1.9.6.md b/doc/15.15.11 - 1.9.6.md new file mode 100644 index 0000000..6a01941 --- /dev/null +++ b/doc/15.15.11 - 1.9.6.md @@ -0,0 +1,10 @@ +#1.9.6 + +* 增加 [swoole_mysql::escape](/wiki/page/p-mysql_escape.html) 方法,用于转义 SQL 语句中的特殊字符 +* 修复添加超过1万个以上定时器时发生崩溃的问题 +* 增加`swoole_serialize`模块,PHP7下高性能序列化库 +* 修复`swoole_client->enableSSL`方法`ssl_cert_file`和`ssl_key_file`参数无效的问题 +* 增加`swoole_http_server`对POST多级KEY的支持 +* 修复监听`UDP`端口设置`onPacket`无效的问题 +* 增加对`openssl-1.1`的支持 + diff --git a/doc/15.15.12 - 1.9.5.md b/doc/15.15.12 - 1.9.5.md new file mode 100644 index 0000000..cd7be9c --- /dev/null +++ b/doc/15.15.12 - 1.9.5.md @@ -0,0 +1,7 @@ +#1.9.5 + +* 修复`taskWaitMulti`特殊情况下文件描述符泄漏的问题 +* 增加`pid_file`选项,在Server启动时将主进程ID写入指定的文件 +* 增加监听随机端口支持,Server的监听端口可以传入0,操作系统会自动会随机分配一个可用端口 +* 移除DNS缓存机制,包括`swoole_clear_dns_cache`函数 +* 增加`use_async_resolver`异步IO配置,用来启用异步IO的DNS查询 \ No newline at end of file diff --git a/doc/15.15.13 - 1.9.4.md b/doc/15.15.13 - 1.9.4.md new file mode 100644 index 0000000..4c2e257 --- /dev/null +++ b/doc/15.15.13 - 1.9.4.md @@ -0,0 +1,5 @@ +#1.9.4 + +* 修复WebSocket服务器默认`onRequest`方法内存泄漏问题 +* 增加`Client->reuseCount`属性,可查询socket被复用的计数 +* 解决缺少`zlib`库编译失败的问题 diff --git a/doc/15.15.14 - 1.9.3.md b/doc/15.15.14 - 1.9.3.md new file mode 100644 index 0000000..a153046 --- /dev/null +++ b/doc/15.15.14 - 1.9.3.md @@ -0,0 +1,6 @@ +#1.9.3 + +* 更新Http服务器响应逻辑,在没有响应体时不再设置`Content-Type`和`Content-Length`头 +* 增加[HttpClient->download](/wiki/page/p-http_client_download.html)方法,可以将请求结果保存到磁盘,而不是内存 +* 增加[package_length_func](/wiki/page/664.html)对PHP函数的支持,可使用PHP函数进行包长解析 + diff --git a/doc/15.15.15 - 1.9.2.md b/doc/15.15.15 - 1.9.2.md new file mode 100644 index 0000000..e200c78 --- /dev/null +++ b/doc/15.15.15 - 1.9.2.md @@ -0,0 +1,11 @@ +#1.9.2 + +* 修复PHP7下发生`zend_mm_heap corrupted`的问题 +* 修复BASE模式下重复wait出现错误信息的问题 +* 修复`Async::writeFile`参数`FILE_APPEND`在MacOS下无效的问题 +* 禁止`Async::writeFile`在LinuxAIO模式下使用`FILE_APPEND`参数 +* 增加`websocket_subprotocol`选项WebSocket服务器支持设置`Sec-WebSocket-Protocol` +* 修复CentOS4.3或更低操作系统不存在`O_CLOEXEC`导致编译失败的问题 +* 修复`tasking_num`发生溢出的问题 +* `http_response->header`方法增加`ucwords`参数 + diff --git a/doc/15.15.16 - 1.9.1.md b/doc/15.15.16 - 1.9.1.md new file mode 100644 index 0000000..c1b0fb9 --- /dev/null +++ b/doc/15.15.16 - 1.9.1.md @@ -0,0 +1,29 @@ +#1.9.1 + +主要更新内容 +----------- +* 修复使用`addProcess`添加用户进程后无法正常shutdown的问题 +* 异步读写文件函数`Async::writeFile`增加`FILE_APPEND`选项支持 +* 异步读写文件函数在进行read、write时对文件加锁 +* 修复`Async::write`函数未设置回调函数发生崩溃的问题 +* 重构`Async::write`函数追加模式的实现,使用`O_APPEND` +* 重构`reopen log file`特性,收到`SIGRTMIN`信号后重新打开日志文件并重定向标准输出 +* 修复`Table`迭代器遗漏数据的问题 +* 回调函数`onPacket`客户端信息参数增加服务器来源端口`server_port` +* 回调函数`onReceive`和`connection_info`方法即将移除对UDP的支持,UDP端口使用这2个特性时会抛出`E_DEPRECATED`警告信息 +* 服务器连接迭代器`Connection\Iterator`增加ArrayAccess接口 +* **修复`Server`在进程管道缓存区塞满后连续发送大数据导致死锁的问题(重要问题)** +* 修复PHP7下启用`opcache`导致崩溃的问题 +* 修复`taskWaitMulti`在超时后无法返回执行成功任务结果的问题 +* 定时器使用`MONOTONIC`单调时间,解决系统时间修改导致定时器错乱的问题 + +连接ArrayAccess用法 +----------- +```php +$serv->on('connect', function ($serv, $fd, $reactor_id){ + echo "IP Address: ".$serv->connections[$fd]['remote_ip']."\n"; + if (isset($serv->connections[6])) { + echo "connection 6 is exists.\n"; + } +}); +``` \ No newline at end of file diff --git a/doc/15.15.17 - 1.9.0.md b/doc/15.15.17 - 1.9.0.md new file mode 100644 index 0000000..2e003f3 --- /dev/null +++ b/doc/15.15.17 - 1.9.0.md @@ -0,0 +1,139 @@ +#1.9.0 + +1.9版本增加了多项新特性,修复了多个已知问题。1.9版本是100%向下兼容1.8的,用户可无缝升级。 + + +新增RedisServer框架 +---- +Swoole-1.9增加了一个兼容`Redis`服务器端协议的Server框架,可基于此框架实现Redis服务器,支持自定义指令。 + +#### 示例: +```php +use Swoole\Redis\Server; + +$server = new Server('127.0.0.1', 9501); + +$server->setHandler('Set', function($fd, $data) { + $server->array($data[0], $data[1]); + return Server::format(Server::INT, 1); +}); + +$server->start(); +``` + +客户端增加pipe函数 +--- +Swoole-1.9为异步TCP客户端增加了一个`pipe`方法,可以将客户端收到的数据重定向到另外一个文件描述符,可以是服务器的连接fd、stream资源、sockets资源、其他Swoole\Client、Swoole\Process的管道。 + +#### 示例: +```php +use Swoole\Client; + +$client = new Client(SWOOLE_TCP | SWOOLE_ASYNC); + +$client->on("error", function() { + echo "connect failed\n"; +}); + +$client->on("close", function() { + echo "connect closed\n"; +}); + +$client->on("connect", function($cli) { + //将数据重定向到标准输出,服务器向客户端发送的所有数据会打印到屏幕 + $cli->pipe(STDOUT); +}); + +$client->connect("127.0.0.1", 9501); +``` + +新增缓存区事件 +---- +1.9增加了2个新的事件回调`onBufferFull`和`onBufferEmpty`,以及2个新的配置项`buffer_high_watermark`和`buffer_low_watermark`。 + +如果写入的数据过多缓存区尺寸超过`buffer_high_watermark`就触发`onBufferFull`事件,当缓存区数据发送完成,水位低于`buffer_low_watermark`的值,会触发`onBufferEmpty`事件。 + +缓存区事件同时可用于`Server`和`Client`。 + +#### 示例: +```php +$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +//设置事件回调函数 +$client->on("connect", function($cli) { + $cli->send("hello world\n"); +}); +$client->on("receive", function($cli, $data) { + echo "Received: ".$data."\n"; +}); +$client->on("error", function($cli) { + echo "Connect failed\n"; +}); +$client->on("close", function($cli) { + echo "Connection close\n"; +}); +$client->on("bufferFull", function($cli) { + //暂停数据接收 + $cli->pause(); +}); +$client->on("bufferEmpty", function($cli) { + //恢复数据接收 + $cli->resume(); +}); +//发起网络连接 +$client->connect('127.0.0.1', 9501, 0.5); +``` + +新增Channel模块 +---- +swoole-1.9新增了一个新的内存数据结构[Channel](http://wiki.swoole.com/wiki/page/p-channel.html),类似于Go的chan,底层基于共享内存+Mutex互斥锁实现,可实现用户态的高性能内存队列。 + +* Channel可用于多进程环境下,底层在读取写入时会自动加锁,应用层不需要担心数据同步问题 +* 必须在父进程内创建才可以在子进程内使用 + +#### 示例: +```php +$chan = new Swoole\Channel(2 * 1024 * 1024); //2M +$chan->push(1234); +$chan->push("hello world"); +$chan->push(array(1234, 4567)); +while($r = $chan->pop()) +{ + var_dump($r); +} +``` + +新增mmap模块 +----- +swoole-1.9增加了一个新的模块,提供了对操作系统mmap的封装。使用[mmap](http://wiki.swoole.com/wiki/page/p-mmap.html) 可以很方便地将一个磁盘文件映射为内存,读写性能更高。 + +mmap可以减少读写磁盘操作的IO消耗、减少内存拷贝。在实现高性能的磁盘操作程序中,可以使用mmap来提升性能。 + +#### 示例: +```php +$file = __DIR__.'/data'; +$size = 8192; +$fp = swoole\mmap::open($file, 8192); + +fwrite($fp, "hello world\n"); +fwrite($fp, "hello swoole\n"); + +fflush($fp); +fclose($fp); +``` + +其他新增特性 +---- +* 增加`swoole_clear_dns_cache`函数,可清除底层的DNS缓存 + +问题修复 +---- +* 修复`Redis`客户端`type`方法无法返回字符串的问题 +* 修复`Http\Client`无法重用的问题 +* 修复同步客户端长度协议`package_max_length`无效的问题 +* 修复低版本gcc下长期运行偶然发生崩溃的问题 +* 修复异步`WebSocket`客户端`websocket_mask`无效的问题 +* 修复`MySQL`客户端在记录条数过多时偶然崩溃的问题 +* 修复Mac平台下task超过8K时创建临时文件失败的导致无法投递任务的问题 +* 修复`taskWaitMulti`在进程返回超过8K时接收超时的问题 +* 修复`atmoic`的`add`和`sub`返回值存在的数据同步问题 + diff --git a/doc/15.15.18 - 1.8.13.md b/doc/15.15.18 - 1.8.13.md new file mode 100644 index 0000000..10fd597 --- /dev/null +++ b/doc/15.15.18 - 1.8.13.md @@ -0,0 +1,10 @@ +#1.8.13 + +* 修复`WebSocket\Server`自动合并未完成数据帧存在的安全漏洞 +* 增加 [upload_tmp_dir](/wiki/page/p-upload_tmp_dir) 选项可以设置`Http\Server`上传文件的临时目录 +* 增加`Server->sendMessage`自动串化功能,现在`sendMessage`可以将任意PHP变量发到其他工作进程 +* 增加 [swoole_process::alarm](/wiki/page/p-alarm) 高精度定时器 +* 修复长度处理协议函数在连接关闭时发生崩溃的问题 +* 修复`swoole_select`函数在PHP7下无法修改引用数组的问题 + + diff --git a/doc/15.15.19 - 1.8.12.md b/doc/15.15.19 - 1.8.12.md new file mode 100644 index 0000000..b45de61 --- /dev/null +++ b/doc/15.15.19 - 1.8.12.md @@ -0,0 +1,17 @@ +#1.8.12 + +* 修复`Swoole\Table`在遍历数据时删除元素导致迭代器错误的问题 +* 增加`Swoole\Http\Client`新选项`websocket_mask`控制WebSocket客户端启用mask +* 修复`Swoole\Server`在BASE模式下无法使用`task_ipc_mode=3`配置 +* 优化`Swoole\Http\Server`响应体gzip压缩的性能 +* 修复`Swoole\Timer::after`定时器在Task进程中只能执行一次的问题 +* 增加`Swoole\WebSocket\Server`自动拼接未完成的数据帧功能 +* 移除`Swoole\Server`的`enable_reuse_port`选项,改为在`Swoole\Async::set`中设置 +* 增加`Swoole\Async`命名空间类别名,替换`swoole_async_`函数风格接口 +* 修复`Swoole\Http\Server`监听UDP端口时无法使用`onReceive`回调函数的问题 +* 修复`Swoole\Http\Client`使用WebSocket通信`Sec-WebSocket-Key`长度错误的问题 +* 修复`Swoole\MySQL`字段信息数据过长分段发送时长度判断错误的问题 + + + + diff --git a/doc/15.15.2 - 1.9.18.md b/doc/15.15.2 - 1.9.18.md new file mode 100644 index 0000000..2b437a0 --- /dev/null +++ b/doc/15.15.2 - 1.9.18.md @@ -0,0 +1,9 @@ +#1.9.18 + +* 修复`Process::signal`父子进程重复注册同一信号导致崩溃的问题 +* 优化`EOF`协议逻辑,减少`64K`栈内存需求 +* 修复`Client`设置`http_proxy`代理后无法正常工作的问题 +* 修复异步`reload`一定几率出现进程遗漏的问题 +* 允许`dispatch_func`传入PHP函数 +* 修复`defer`接口在`onWorkerStart`回调中无法执行的问题 +* 修复`EOF`同步客户端连续接收数据时发生`EAGIAN`错误的问题 \ No newline at end of file diff --git a/doc/15.15.20 - 1.8.11.md b/doc/15.15.20 - 1.8.11.md new file mode 100644 index 0000000..702f937 --- /dev/null +++ b/doc/15.15.20 - 1.8.11.md @@ -0,0 +1,8 @@ +#1.8.11 + +* 增加新的`package_length_type`类型C(无符号8位)/c(有符号8位) +* 增加`package_length_func`设置,可以使用C/C++函数解析包长度 +* 增加`SIGRTMIN`信号处理函数,用于重新打开日志文件 +* 修复Cygwin环境下Server发生崩溃的问题 + + diff --git a/doc/15.15.21 - 1.8.10.md b/doc/15.15.21 - 1.8.10.md new file mode 100644 index 0000000..b48582f --- /dev/null +++ b/doc/15.15.21 - 1.8.10.md @@ -0,0 +1,6 @@ +#1.8.10 + +* 修复`Swoole\Server::connection_list`接口未检测SSL状态的问题 +* 修复`Swoole\Http\Client`使用HEAD方法无法执行回调函数的问题 +* 修复`Swoole\Http\Client`在回调函数中关闭连接导致崩溃的问题 +* 修复`Swoole\Http\Server`无法支持超过1024并发的问题 diff --git a/doc/15.15.22 - 1.8.9.md b/doc/15.15.22 - 1.8.9.md new file mode 100644 index 0000000..6981f7a --- /dev/null +++ b/doc/15.15.22 - 1.8.9.md @@ -0,0 +1,13 @@ +#1.8.9 + +* 增加命名空间别名设置,现在命名空间类和非命名空间类可以同时使用 +* 修复`Swoole\MySQL`连接某些版本MySQL服务器发生错误的问题 +* 修复`Swoole\Http\Client`在回调函数中关闭连接发生崩溃的问题 +* 增加`Swoole\Http\Client->addFile`接口,支持POST文件 +* 修复`Swoole\Http\Server`在PHP7环境上传文件时偶然发生崩溃的问题 +* 修复`Swoole\MySQL`在PHP7下内存泄漏问题 +* 修复`Swoole\Redis`在PHP7下内存泄漏问题 +* 修复`Swoole\Http\Client`在PHP7下内存泄漏问题 +* 修复`Swoole\Server`启用SSL加密未输入证书密码导致崩溃的问题 +* 增加`Swoole\Http\Client`对没有ContentLength的响应体的支持 +* 增加`--with-openssl`编译选项,可指定openssl库的路径 diff --git a/doc/15.15.23 - 1.8.8.md b/doc/15.15.23 - 1.8.8.md new file mode 100644 index 0000000..b6003f1 --- /dev/null +++ b/doc/15.15.23 - 1.8.8.md @@ -0,0 +1,31 @@ +#1.8.8 + +* 增加`Swoole\Server\Port->getSocket`方法,可获取监听端口的socket句柄 +* 增加`Swoole\Server->getClientInfo()['close_errno']`属性,可获取连接关闭的错误码 +* 修复`Swoole\Server->close`无法关闭未完成握手的SSL客户端连接的问题 +* 修复`Swoole\Server->taskwait`在BASE模式下无法使用的问题 +* 增加`Swoole\Event`命名空间与类风格的API +* 修复`Swoole\MySQL`客户端无法支持超过250字段查询语句的问题 +* 增加`Swoole\MySQL`字符集设置支持 +* 修复`Swoole\Server->task`第三参数传入Callback在PHP7下发生崩溃的问题 +* 修复`Swoole\Http\Server`由于外部引用request对象后用户取消请求导致崩溃的问题 +* 增加`Swoole\Server->taskWaitMulti`可以并发执行多个任务 +* 修复`Swoole\Http\Client`的WebSocket客户端接收数据粘包的问题 +* 增加`Swoole\Server\Port`对`Request`、`Open`、`HandShake`、`Message`回调函数设置支持 +* 增加`Swoole\Client->getPeerCert`方法 +* 增加`Swoole\Client->pause`和`Swoole\Client->resume` +* 增加`Swoole\Http\Client`更多的客户端设置选项 +* 修复`Swoole\Http\Request->files`在PHP7下为NULL时被外部引用发生崩溃的问题 + +并发Task特性 +---- +之前的版本`taskwait`只支持同时执行一个任务,在一些场景下需要同时并发执行多个Task,并返回结果集。最新的`1.8.8`版本增加了并发Task的支持,可以解决此类问题。 + +```php +$tasks[] = mt_rand(1000, 9999); +$tasks[] = mt_rand(1000, 9999); +//等待所有Task结果返回,超时为10s +var_dump($tasks); +$results = $serv->taskWaitMulti($tasks, 10.0); +var_dump($results); +``` \ No newline at end of file diff --git a/doc/15.15.24 - 1.8.7.md b/doc/15.15.24 - 1.8.7.md new file mode 100644 index 0000000..a6e3d83 --- /dev/null +++ b/doc/15.15.24 - 1.8.7.md @@ -0,0 +1,15 @@ +#1.8.7 + +* 修复`Swoole\Http\Server`在PHP7下崩溃的问题(zdata请求数据内存释放问题) +* 修复`Swoole\Http\Client`的WebSocket模块未设置`Header`发生崩溃的问题 +* 修复`Swoole\MySQL`对unixsock服务器端主机地址解析错误的问题 +* 修复`Swoole\Http\Client`在低于`Linux-2.6.18`版本内核发生崩溃的问题 +* 修复`Swoole\MySQL`在PHP7下嵌套回调发生崩溃的问题 +* 更新`Swoole\Server`最大监听端口数,由`128`调整为`60000` +* 更新`Swoole\Server`主线程使用`epoll`或`kqueue` +* 修复`swoole_async_dns_lookup`命中缓存时发生崩溃的问题 +* 修复`Swoole\Table`迭代器在并发读写时发生数据读取错误的问题 +* 修复`Swoole\Timer::after`接口初始化添加时执行顺序错乱问题 +* 增加`Swoole\Timer::exists`接口用于查询定时器是否存在 +* 修复`Swoole\WebSocket\Server::exist`方法用于判断其他TCP端口时总是返回`false`的问题 +* 优化`Swoole\Server`关闭服务时Task进程的结束速度 diff --git a/doc/15.15.25 - 1.8.6.md b/doc/15.15.25 - 1.8.6.md new file mode 100644 index 0000000..a7a1511 --- /dev/null +++ b/doc/15.15.25 - 1.8.6.md @@ -0,0 +1,56 @@ +#1.8.6 + +1.8.6版本是一个重要的BUG修复版本,主要修复了PHP7环境下HttpServer、TCPClient、HttpClient、Redis等客户端存在的内存泄漏、崩溃问题。另外1.8.6版本对MySQL进行了彻底重构,提供了全新的面向对象风格API,彻底移除了对PHP的mysqli和mysqlnd的依赖。 + +__建议所有swoole开发者升级至此版本。__ + +主要更新内容 +--------- +* 修复`Swoole\Server->set`方法在关联索引数组的Value为NULL时错误地更改了zval类型 +* 更新`Swoole\Server->task`方法第三个参数可以直接传入回调函数 +* 修复`Swoole\WebSocket\Server`收到恶意请求时崩溃的问题,提升稳定性 +* 重构`Swoole\MySQL`客户端,移除对`mysqli`和`mysqlnd`的依赖,提供了面向对象风格的API +* 调整`Swoole\Http\Client`为内置,不需要额外的编译参数开启 +* 调整`Swoole\Client`和`Swoole\Http\Client`内存回收的时机,在连接发送关闭时回收内存资源 +* 增加`swoole_async_dns_lookup`查询结果缓存 +* 优化`Swoole\WebSocket\Server`性能,减少两次内存复制 +* 移除`Swoole\Http\Server->setGlobal`方法 +* 修复在Task进程中执行close时onClose回调函数未在Worker进程中执行的问题 +* 修复`Swoole\Table`删除KEY后未清空数据的问题 +* 增加SSL、TLS证书链的支持 +* 移除`gcc aio` +* 修复异步文件读写函数的相关问题 + +新版异步MySQL客户端 +---------- +```php +$db = new swoole_mysql; +$server = array( + 'host' => '192.168.56.102', + 'user' => 'test', + 'password' => 'test', + 'database' => 'test', +); + +$db->connect($server, function ($db, $r) { + if ($r === false) { + var_dump($db->connect_errno, $db->connect_error); + die; + } + $sql = 'show tables'; + $db->query($sql, function(swoole_mysql $db, $r) { + global $s; + if ($r === false) + { + var_dump($db->error, $db->errno); + } + elseif ($r === true ) + { + var_dump($db->affected_rows, $db->insert_id); + } + var_dump($r); + $db->close(); + }); +}); + +``` \ No newline at end of file diff --git a/doc/15.15.26 - 1.8.5.md b/doc/15.15.26 - 1.8.5.md new file mode 100644 index 0000000..1202bf2 --- /dev/null +++ b/doc/15.15.26 - 1.8.5.md @@ -0,0 +1,16 @@ +#1.8.5 + +* __修复`swoole_mysql_query`执行insert语句时`insert_id`错误的问题(严重问题)__ +* __修复`Swoole\WebSocket\Server`接收小于4字节数据时发生崩溃的问题(严重问题)__ +* 增加`swoole_mysql_query`对bigint自增ID的支持 +* 增加`swoole_mysql_query`嵌套回调出现致命错误的问题 +* 增加`execinfo`模块检测避免在不支持`execinfo`平台无法编译通过的问题 +* 修改`Swoole\Server`回调函数存储方式,使用对象属性保存`callback` +* 修复`Swoole\WebSocket\Server`多协议下发生崩溃的问题 +* 禁止异步`Swoole\Client`使用`SWOOKE_KEEP`长连接设置 +* 修复同步`Swoole\Client`内存泄漏问题 +* 增加`Swoole\Client`绑定地址和端口的支持 +* 增加`Swoole\Server->stats`方法的Task消息队列数量和字节计数 +* 修复`Swoole\Http\Client`连接关闭时发生崩溃的问题 +* 修复`Swoole\Server->taskwait`操作导致`tasking_num`计数错误问题 + diff --git a/doc/15.15.27 - 1.8.4.md b/doc/15.15.27 - 1.8.4.md new file mode 100644 index 0000000..67c94e3 --- /dev/null +++ b/doc/15.15.27 - 1.8.4.md @@ -0,0 +1,15 @@ +#1.8.4 + +* 同步客户端禁止使用`Swoole\Client->on`注册异步回调函数 +* 修复`Swoole\Http\Server`解析`form-data`格式数据发生错误的问题 +* 修复`Swoole\Redis`回调函数内存泄漏问题 +* 修复`swoole_mysql_query`回调函数内存泄漏问题 +* 修复[Issue#585](https://github.com/swoole/swoole-src/issues/585) +* 修复[Issue#590](https://github.com/swoole/swoole-src/issues/590) +* 修复`Swoole\Http\Client`内存泄漏问题 +* 修复`Swoole\Client`连接关闭时发生崩溃的问题 +* 修复`Swoole\Client->onError`时未设置`errCode`的问题 +* 修复`swoole_async_writefile`函数未设置回调函数发生coredump的问题 +* 增加`Swoole\Client`对异步unixsock的支持 +* 增加`Swoole\Http\Request`对高精度请求时间的支持 + diff --git a/doc/15.15.28 - 1.8.3.md b/doc/15.15.28 - 1.8.3.md new file mode 100644 index 0000000..5f0c8b3 --- /dev/null +++ b/doc/15.15.28 - 1.8.3.md @@ -0,0 +1,15 @@ +#1.8.3 + + +* 增加`swoole_server->getLastError`方法,用于获取最近一次操作的错误码 +* 增加Http2协议下对`COOKIE`的支持 +* 增加Http2协议下对`form-data`的支持 +* 修复[issue#527](https://github.com/swoole/swoole-src/issues/527) +* 修复[issue#553](https://github.com/swoole/swoole-src/issues/553) +* 修复`reactor_num`设置超过CPU核数时发生崩溃的问题 +* 修复`Swoole\Client->on`设置的回调函数发生内存泄漏问题 +* 修复打开文件未关闭并且`task_worker_num`设置过大超过256导致服务器程序崩溃 +* 增加全异步服务器安全`reload`的支持 +* 增加http客户端对`COOKIE`的支持 +* 增加http客户端对`SSL/TLS`加密的支持 +* 修复BASE模式下启用心跳检测检测后使用`swoole_client`出现异常的问题 diff --git a/doc/15.15.29 - 1.8.2.md b/doc/15.15.29 - 1.8.2.md new file mode 100644 index 0000000..2fb904f --- /dev/null +++ b/doc/15.15.29 - 1.8.2.md @@ -0,0 +1,10 @@ +#1.8.2 + +* __增加对Http2的支持(重要更新)__ +* 修复WebSocket服务器接收超过64K数据发生崩溃的问题 +* 修复多端口监听未设置回调函数导致程序崩溃的问题 +* 提升SSL/TLS隧道加密的安全等级,现在默认使用TLS1.2/ECDHA_RSA加密算法 +* 修复onFinish事件回调内存泄漏的问题 +* 修复BASE模式下task finish无法使用的问题 +* 增加`log_level`设置,可以选择错误日志的等级 + diff --git a/doc/15.15.3 - 1.9.17.md b/doc/15.15.3 - 1.9.17.md new file mode 100644 index 0000000..7dc95b5 --- /dev/null +++ b/doc/15.15.3 - 1.9.17.md @@ -0,0 +1,5 @@ +#1.9.17 + +* 异步模式支持安全的`stop`、`reload`、`max_request` +* 增加`HttpServer`静态文件处理器,可配置`document_root`和`enable_static_handler`来启用 +* 增加`SSL`连接`sendfile`支持 diff --git a/doc/15.15.30 - 1.8.1.md b/doc/15.15.30 - 1.8.1.md new file mode 100644 index 0000000..49d4006 --- /dev/null +++ b/doc/15.15.30 - 1.8.1.md @@ -0,0 +1,35 @@ +#1.8.1 + +主要更新 +----- +* 增加核心类的命名空间别名 +* 增加`swoole_server->protect`方法,用于保护某些连接不被心跳线程切断 +* 增加`swoole_websocker_server::pack`和`swoole_websocker_server::unpack`静态方法,用于手工打包/解包websocket数据帧 +* 修复日志打印标准输出被关闭不断产生`SIGPIPE`信号导致死循环的问题 +* 修复`MacOS`环境下启用`openssl`编译失败的问题 +* 增加对`redis`订阅和发布消息的支持 +* 修复多端口监听未设置监听端口回调发生core dump的问题 +* 修复异步Client发生内存泄漏的问题 +* 修复在其他事件回调函数中关闭异步Client偶然发生core dump的问题 +* 增加`swoole_http_client`对`gzip`内容压缩的支持 + +命名空间示例 +---- +> 使用命名空间类风格,需要修改`php.ini`,增加`swoole.use_namespace=On`开启。使用命名空间类名后,旧式的下划线风格类名将不可用 + +```php +use Swoole\Http\Server; +use Swoole\Http\Request; +use Swoole\Http\Response; + +$serv = new Server('127.0.0.1', 9501); + +$serv->on('Request', function(Request $req, Response $resp) { + var_dump($req->header, get_class($req)); + $resp->end("

Hello Swoole

"); +}); + +$serv->start(); +``` + + diff --git a/doc/15.15.31 - 1.8.0.md b/doc/15.15.31 - 1.8.0.md new file mode 100644 index 0000000..735964d --- /dev/null +++ b/doc/15.15.31 - 1.8.0.md @@ -0,0 +1,81 @@ +#1.8.0 + +客户端 +---- +* 增加原生异步MySQL客户端 +* 增加原生异步Redis客户端,基于Redis官方提供的[hiredis库](https://github.com/redis/hiredis) +* 增加原生异步Http客户端 +* 增加原生异步WebSocket客户端支持 +* 重构底层swClient,异步TCP客户端实现放到swoole内核中 +* 增加`swoole_client->reuse`属性,`SWOOLE_KEEP`长连接模式下标识是否为复用的连接 + +服务器端 +------ +* 重构websocket服务器代码,底层与`length_check`协议复用相同的处理函数,增强稳定性 +* 增加`Task`进程对`tick/after`定时器的支持,底层基于高精度的`setitimer`+信号实现 +* 保存构造函数中传入的host、port参数到`swoole_server`对象属性 +* __增加多端口多协议的支持(重要更新)__ +* 增加`swoole_server->defer`函数用于延时执行一些函数 +* 增加`swoole_server->close`强制切断连接的选项,设置第二个参数会true会清空发送队列并立即切断连接 + +__多端口多协议示例:__ +```php +$serv = new swoole_server("0.0.0.0", 9501); + +$port2 = $serv->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); + +$port2->set(array( + 'open_length_check' => true, + 'package_length_type' => 'N', + 'package_length_offset' => 0, //第N个字节是包长度的值 + 'package_body_offset' => 4, //第几个字节开始计算长度 + 'package_max_length' => 2000000, //协议最大长度 +)); + +$port2->on('receive', function (swoole_server $serv, $fd, $from_id, $data) { + echo "ServerPort2\n"; +}); + +$serv->on('connect', function ($serv, $fd, $from_id){ + echo "[#".posix_getpid()."]\tClient@[$fd:$from_id]: Connect.\n"; +}); + +$serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) { + echo "[#".$serv->worker_id."]\tClient[$fd]: $data\n"; + if ($serv->send($fd, "hello\n") == false) + { + echo "error\n"; + } +}); + +$serv->on('close', function ($serv, $fd, $from_id) { + echo "[#".posix_getpid()."]\tClient@[$fd:$from_id]: Close.\n"; +}); + +$serv->start(); +``` + +其他 +---- +* 增加swoole_table对key值的存储,`foreach`遍历table时可以获取到key值 +* 更改swoole_table的key对比模式,从crc32比对改为直接进行字符串对比 +* 更新utlist.h库到`1.9.9`版本 + +> swoole_table保存Key值会增加内存占用,如table的size为100万,KEY值存储会增加64M内存占用 + +问题修复 +----- +* 修复启用消息队列后发生double-free问题 +* 重构定时器,修复`after`、`tick`定时器偶然出现的`core dump`的问题 +* 定时器使用最小堆数据结构,插入/删除时间复杂度为`log(N)` +* 修复`swoole_process::signal`在PHP7下发生`core dump`的问题 +* 修复`swoole_async_write`在PHP7下发生`core dump`的问题 + +移除特性 +---- +* 移除未支持的特性相关历史遗留代码,如`heartbeat_ping`、`dispatch_key_type`等 +* 移除`swoole_server->addtimer`、`swoole_server->deltimer`、`swoole_server->gettimer` +* 移除`swoole_timer_add`、`swoole_timer_del` +* 移除`swoole_server`的`onTimer`事件 +* 移除`task_worker_max`配置及相关特性代码 +* 移除`swoole_server->handler`方法 diff --git a/doc/15.15.32 - 1.7.22.md b/doc/15.15.32 - 1.7.22.md new file mode 100644 index 0000000..455865b --- /dev/null +++ b/doc/15.15.32 - 1.7.22.md @@ -0,0 +1,11 @@ +#1.7.22 + +* 修复PHP7下HttpServer发生内存泄漏的问题 +* 修复PHP7下core dump的问题 +* __修复swoole_table->del出现错误的问题(重要问题)__ +* 增加swoole_client->send/recv的socket参数选项 +* 增加swoole_async_set新配置socket_dontwait/socket_buffer_size/enable_signalfd +* 增加SSL/TLS客户端证书验证支持 +* 修复tick定时器长时间运行整形溢出导致停止运行的问题 +* 增加swoole_websocket_server->exist用于判断一个fd是否为正确的连接 +* 增加原生MySQL异步客户端 diff --git a/doc/15.15.33 - 1.7.21.md b/doc/15.15.33 - 1.7.21.md new file mode 100644 index 0000000..745076e --- /dev/null +++ b/doc/15.15.33 - 1.7.21.md @@ -0,0 +1,12 @@ +#1.7.21 + +* 修复swoole_client同步模式在服务器主动关闭时发生内存泄漏的问题 +* 修复POST/文件上传超过8K无法处理的问题 +* 增加swoole_http_response->sendfile方法,用于发送大文件 +* 修复swoole_client启用SSL/TLS隧道加密后close发生coredump的问题 +* 增加swoole_server->getSocket/swoole_client->getSocket将Socket导出为sockets扩展资源 +* 增加UDP多播(udp multicast)示例 +* 修复MIPS平台编译错误的问题 +* 修复UDP大包在dispatch_mode=1/3时Worker进程发生死锁的问题 +* 增加swoole_client->sleep/wakeup方法,用于暂停/恢复数据接收事件 +* 修复UDP大包中间数据异常问题(重要BUG) diff --git a/doc/15.15.34 - 1.7.20.md b/doc/15.15.34 - 1.7.20.md new file mode 100644 index 0000000..8027a89 --- /dev/null +++ b/doc/15.15.34 - 1.7.20.md @@ -0,0 +1,16 @@ +#1.7.20 + +* swoole_http_request->rawContent() 函数在任意情况下都可以到POST Body +* 修复swoole_process::useQueue()第一个参数为0时消息队列泄漏的问题 +* 增加swoole_http_server的DELETE包体支持,可以在$req->post中得到请求参数 +* 增加swoole_client对SSL/TLS隧道加密的支持 +* 优化RINIT/RSHUTDOWN代码,减少扩展在php-fpm环境下的性能消耗 +* 优化SSL的onConnect事件顺序,在SSL握手完成后回调onConnect函数 +* 增加swoole_server/swoole_client的SSL方法配置 +* 修复swoole_websocket_server未设置onRequest时coredump的问题 +* 增加swoole_server->getClientInfo/getClientList别名 +* 修复swoole_server->finish在BASE模式下不可用的问题 +* 禁止在onStart回调函数中调用swoole_server->task/taskwait +* 增加swoole_client设置SSL证书的支持 +* 修复swoole_http_server内存泄漏的问题 + diff --git a/doc/15.15.35 - 1.7.19.md b/doc/15.15.35 - 1.7.19.md new file mode 100644 index 0000000..e1b6418 --- /dev/null +++ b/doc/15.15.35 - 1.7.19.md @@ -0,0 +1,15 @@ +#1.7.19 + +主要更新 +----- +* 增加swoole_atomic模块,支持原子整数操作 +* 修复定时器在系统休眠后无法恢复运行的BUG +* 修复SSL服务器在慢速网络中发送超过30K大包失败的问题 +* UDP/UDP6/UNIX_DGRAM协议支持64K大包 +* 修复addtimer/tick定时器在BASE模式下无法使用的问题 +* 修复swoole_process子进程退出时消息队列被自动销毁的问题 +* 增加SWOOLE_BASE模式下对addProcess的支持 +* 修复SWOOLE_KEEP发生段错误的问题 + + + diff --git a/doc/15.15.36 - 1.7.18.md b/doc/15.15.36 - 1.7.18.md new file mode 100644 index 0000000..fda5752 --- /dev/null +++ b/doc/15.15.36 - 1.7.18.md @@ -0,0 +1,26 @@ +#1.7.18 + +主要更新 +----------- + +* 增加[onPacket](/wiki/page/450.html)事件回调函数,使UDP包与TCP分离 +* 修复EOF协议处理时发生错误的问题 +* 兼容PHP7版本 +* 修复swoole_http_response->header导致内存泄漏的问题 +* 长度检测协议允许0长度无包体的请求 +* swoole_table支持有符号整数 +* 支持REUSEPORT特性,短连接TCP服务性能提升200%(仅支持Linux 3.9.0或更高版本) +* 修复swoole_client/swoole_timer内存泄漏问题 +* 增加[enable_unsafe_event](/wiki/page/448.html)配置,允许在dispatch_mode=1/3时开启Connect/Close事件 +* 增加[swoole_process::setaffinity](/wiki/page/451.html)方法用于设置CPU亲和性 +* swoole_client->set增加socket_buffer_size配置 +* 增加[swoole_server->exist](/wiki/page/454.html)方法,用于检测$fd对应的TCP客户端连接是否存在 + +废弃特性 +----------- +* 函数风格的API即将移除 +* swoole_server->handler方法即将移除 +* addtimer/deltimer/swoole_timer_add/swoole_timer_del接口即将移除,请使用tick/after定时器 + + + diff --git a/doc/15.15.37 - 1.7.17.md b/doc/15.15.37 - 1.7.17.md new file mode 100644 index 0000000..74805fd --- /dev/null +++ b/doc/15.15.37 - 1.7.17.md @@ -0,0 +1,18 @@ +#1.7.17 + +* 使用pthread_barrier_wait代替sleep加快程序启动速度 +* 修复swoole_client异步模式下send数据不完整的BUG +* 移除task_dispatch_mode配置项 +* 默认task进程的max_request改为0,不自动退出进程 +* Http服务器上传文件使用mktemp系统调用 +* 修復EOF_check特性导致主进程coredump的问题 +* 增加pipe_buffer_size配置项,用于调整管道通信的缓存区尺寸 +* 管道内存缓存区默认尺寸调整为32M +* Http服务器Header最大长度由128调整为8192 +* 修复swoole_process->pop发生错误的问题 +* 修复taskwait接口在MacOS/CygWin环境下超时的问题 +* 增加swoole_table->exist方法 + + + + diff --git a/doc/15.15.38 - 1.7.16.md b/doc/15.15.38 - 1.7.16.md new file mode 100644 index 0000000..062f073 --- /dev/null +++ b/doc/15.15.38 - 1.7.16.md @@ -0,0 +1,22 @@ +#1.7.16 + +* 修复swoole_server->addtimer与tick定时器冲突的BUG +* 增加server统计项request_count和worker_request_count +* 移除swoole底层对对象资源属性的依赖,直接读取指针,提升性能 +* 解决心跳线程无法强制杀掉遗留连接的问题 +* 增加server的连接迭代器,可以使用foreach遍历服务器的所有连接 +* 增加客户端自动协议处理的支持,可以使用open_eof_split/open_length_check配置 +* 增加SWOOLE_PACKET标志位用于支持TCP透传模式 +* 优化dispatch_mode=3模式,提升任务分配的效率 +* 增加http服务器multipart-form和上传文件的支持 +* 修复task_max_request参数失效的问题 +* 增加http服务器请求的query_string + + + + + + + + + diff --git a/doc/15.15.39 - 1.7.15.md b/doc/15.15.39 - 1.7.15.md new file mode 100644 index 0000000..96dae5d --- /dev/null +++ b/doc/15.15.39 - 1.7.15.md @@ -0,0 +1,16 @@ +#1.7.15 + +* 修复swoole_client的waitall参数失效问题 +* 修复swoole_table发生死循环的BUG +* 禁用swoole_websocket_server->send方法 +* 增加swoole_table->incr/decr原子自增/自减方法 +* BASE模式支持向任意FD发送数据 +* 修复Accept失败返回Too Many Connection重复打印日志的问题 +* 设置dispatch_mode = 1, 3 后关闭onClose/onConnect事件回调 +* 修复低版本Linux下Accept未设置阻塞的问题 +* 允许Worker进程内设置非系统保留信号 +* 增加open_eof_split配置,使用EOF检测可以支持自动分包 + + + + diff --git a/doc/15.15.4 - 1.9.16.md b/doc/15.15.4 - 1.9.16.md new file mode 100644 index 0000000..6dbe003 --- /dev/null +++ b/doc/15.15.4 - 1.9.16.md @@ -0,0 +1,7 @@ +#1.9.16 + +* 修改`Server`监听失败逻辑,失败时抛出`Swoole\Exception`异常而不是触发致命错误 +* 增加`Swoole\Async::set`函数`log_file`配置项 +* 修改`swSocket_bind`错误日志模式,接受`log_level`控制 +* 修复`UserProcess`中发送大包时死锁的问题 +* 修复高并发下`Close`碰撞导致连接泄漏的问题 diff --git a/doc/15.15.40 - 1.7.14.md b/doc/15.15.40 - 1.7.14.md new file mode 100644 index 0000000..e25273d --- /dev/null +++ b/doc/15.15.40 - 1.7.14.md @@ -0,0 +1,12 @@ +#1.7.14 + +* WebSocket服务器onOpen回调函数第2个参数由$fd调整为$request对象 +* WebSocket服务器onHandShake回调函数中执行close不回调onOpen +* PHP5.3不再需要脚本末尾手工加swoole_event_wait +* 增加swoole_server->tick和swoole_timer_tick函数 +* 修复onReceive数据合并失效的BUG +* Http服务器增加gzip压缩的支持 +* swoole_server->send支持发送swoole_buffer对象 +* Http服务器允许发送空body的response + + diff --git a/doc/15.15.41 - 1.7.13.md b/doc/15.15.41 - 1.7.13.md new file mode 100644 index 0000000..533b7ab --- /dev/null +++ b/doc/15.15.41 - 1.7.13.md @@ -0,0 +1,15 @@ +#1.7.13 + +* 服务器session_list尺寸调整为1M +* connection_info中的from_fd, from_port 修改为 server_fd, server_port +* 服务器只在监听多个端口,connection_info返回 server_fd, server_port +* connection_info增加对IPv6 TCP协议的支持 +* connection_info增加socket_type项表示客户端的类型 +* 优化Http服务器性能 +* 优化WebSocket服务器性能 +* 修复文件描述符过多后Accept发生死循环的BUG +* 修复reload期间某个worker进程退出导致管理进程死循环的BUG +* 增加swoole_client->getsockname用于获取本地socket的ip和port +* 增加swoole_client->getpeername用于获取远程对端socket的ip和port +* 增加swoole_client->sendto可向任意UDP服务器发送数据包 + diff --git a/doc/15.15.42 - 1.7.12.md b/doc/15.15.42 - 1.7.12.md new file mode 100644 index 0000000..4c2ff4a --- /dev/null +++ b/doc/15.15.42 - 1.7.12.md @@ -0,0 +1,6 @@ +#1.7.12 + +* 修复TCP缓存区发生错误的问题 +* 客户端recv方法未启用waitall, buffer_size最大限制为64K +* 删除无用的错误日志 + diff --git a/doc/15.15.43 - 1.7.11.md b/doc/15.15.43 - 1.7.11.md new file mode 100644 index 0000000..36f1e78 --- /dev/null +++ b/doc/15.15.43 - 1.7.11.md @@ -0,0 +1,17 @@ +#1.7.11 + +* 修复UDP服务器调用connection_info发生错误的问题 +* 修复task临时文件磁盘空间未释放的BUG +* 修复WebSocket服务器onOpen事件回调中执行close导致进程崩溃的问题 +* 修复在task进程中调用sendMessage导致进程崩溃的问题 +* 修复websocket服务器握手时Sec-WebSocket-Accept串偶发错误的问题 +* 修复Http服务器在开启KeepAlive时连续POST数据发生coredump的问题 +* 修复MacOS/FreeBSD在大量并发时出现ENOBUFF错误 +* 增加Http服务器分片(chunk)发送的支持 +* 增加pcre检测,如果未安装pcre库将关闭swoole_table的foreach遍历接口 + + + + + + diff --git a/doc/15.15.44 - 1.7.10.md b/doc/15.15.44 - 1.7.10.md new file mode 100644 index 0000000..fdd2756 --- /dev/null +++ b/doc/15.15.44 - 1.7.10.md @@ -0,0 +1,21 @@ +#1.7.10 + +* 修复UDP在reload之后发生死循环的BUG +* 修复Http服务器POST发生段错误的问题 +* 增加swoole_http_server->push方法,用于向WebSocket客户端推送消息 +* 增加Http请求头信息到swoole_http_server合并全局变量$_SERVER中 +* 增加swoole_process::wait的非阻塞设置 +* 修复合并全局变量未替换中横线为下划线的BUG +* 修复swoole_http_request的key变为大写的BUG +* 修复pecl脚本无法直接安装的问题 +* 修复swoole_server->addProcess在未使用管道时发送段错误的问题 +* 修复swoole_server->sendMessage失败的问题 +* 修复swoole_process子进程无法打开标准输入的问题 +* 增加swoole_server->sendto方法,用于向任意IP:PORT发送UDP包 +* 优化swoole_client/swoole_event性能,减少hashtable查找 +* 优化swoole_http_server性能 +* 修复ARM平台编译错误的问题,1.7.10版本可用于ARM平台 +* 修复task_tmpdir设置导致投递任务失败的问题 + + + diff --git a/doc/15.15.45 - 1.7.9.md b/doc/15.15.45 - 1.7.9.md new file mode 100644 index 0000000..2f5f4c1 --- /dev/null +++ b/doc/15.15.45 - 1.7.9.md @@ -0,0 +1,14 @@ +#1.7.9 + +* 增加内置WebSocket协议支持 +* swoole_process在构造之后就可以访问到pipe的值 +* 增加swoole_process::signal/swoole_async_signal用于异步信号 +* 增加swoole_server->addProcess,可以添加用户定义的子进程到Server +* 增加swoole_process::name方法 +* 增加swoole_server::listen方法 +* 增加swoole_server::sendMessage和onPipeMessage用于操作底层管道通信 +* 增加swoole_event_write函数 +* 增加swoole_process->close方法 +* 增加user/group配置项,可以修改工作进程的用户和组 +* swoole_server的task/finish方法可以直接发送任意PHP变量,扩展内对非字符类型自行串化/反串化 + diff --git a/doc/15.15.46 - 1.7.8.md b/doc/15.15.46 - 1.7.8.md new file mode 100644 index 0000000..4a12dd9 --- /dev/null +++ b/doc/15.15.46 - 1.7.8.md @@ -0,0 +1,23 @@ +#1.7.8 + +* 修复swoole_http_server::on未执行父类方法的问题 +* 修复swoole_http_server中COOKIE无法读取的问题 +* 增加swoole_http_server对POST RawContent的支持 +* after接口可以传入一个用户参数 +* swoole_client->recv和onReceive的data变量修改为零拷贝 +* swoole_client->onReceive改为ET模式 +* 修复swoole_table->set无法设置超过64K字符串的问题 +* 重构swoole_table,解决foreach可能存在的数据同步问题 +* 增加在php.ini中的配置项swoole.display_errors,用于关闭错误信息 +* swoole_timer_after/swoole_server->after接口增加一个可选参数,可以将一个自定义变量传递到回调函数中 +* 修复open_length_check连接内存缓冲区未重置的BUG +* 增加dispatch_mode=4,按照客户端IP取摸分配worker进程 +* 事件回调函数中不或到异常错误等级由E_WARNING调整为E_ERROR + + + + + + + + diff --git a/doc/15.15.47 - 1.7.7.md b/doc/15.15.47 - 1.7.7.md new file mode 100644 index 0000000..09dfcb6 --- /dev/null +++ b/doc/15.15.47 - 1.7.7.md @@ -0,0 +1,11 @@ +#1.7.7 + +* 增加对cygwin环境的支持,仅支持SWOOLE_BASE模式 +* 修复swoole_process子进程接收不到信号的问题 +* 定时器重构,使用EventLoop的超时机制来实现定时器 +* 定时器增加after接口,一次性时间回调 +* onClose事件调整为在close前触发 +* 支持在swoole_server的worker进程中创建swoole_process子进程 +* 默认package_max_length为2M +* 支持用户设置task临时文件目录 + diff --git a/doc/15.15.48 - 1.7.6.md b/doc/15.15.48 - 1.7.6.md new file mode 100644 index 0000000..ec32b06 --- /dev/null +++ b/doc/15.15.48 - 1.7.6.md @@ -0,0 +1,18 @@ +#1.7.6 + +1.7.6-beta +----- +* 修复定时器第一次触发时间错误的BUG +* sendfile增加nopush的设置 + +1.7.6-alpha +------ +此版本是针对1.7.5的BUG修复版。 + +* 增加php.ini中可配置aio_thread_num,AIO的线程数量 +* 增加Manager进程退出的监控 +* 修复onConnect/onClose无法正确投递到对应worker的BUG +* 修复消息队列模式发生死循环的BUG +* 修复swoole_table->set发生死循环问题 +* 修复enable-ringbuffer后执行错误的问题 +* 移除enable-msgqueue编译参数 \ No newline at end of file diff --git a/doc/15.15.49 - 1.7.5.md b/doc/15.15.49 - 1.7.5.md new file mode 100644 index 0000000..71565d6 --- /dev/null +++ b/doc/15.15.49 - 1.7.5.md @@ -0,0 +1,21 @@ +#1.7.5 + +* 增加swoole_table +* 增加swoole_client->sendfile() +* 增加swoole_server->stats()函数,用于统计服务器网络请求数 +* 增加worker和master之间unix的内存缓存 +* 增加swoole_process支持使用消息队列做进程间通信 +* 增加swoole_client构造函数增加第3个参数,作为长连接的ID +* 增加swoole_process::daemon函数 +* 增加Append写的支持,swoole_async_write的offset为-1表示追加到文件末尾 +* 增加swoole_server->gettimer()获取所有定时器设置 +* 增加task进程单独设置max_requset/ipc_mode的支持 +* 优化worker和master之间的通信 +* 优化TCP短链接,性能提升50% +* 修复connection_info在UDP客户端下端口号错误问题 +* 修复swoole_server->master_pid某些情况下不可用问题 +* 修复定时器第一次设置超过当前时间2倍的BUG +* 修复异步任务结束后进程无法自动退出问题 +* 修复swoole_event_add重复添加相同fd时出错的问题 +* 修复swoole_process->$pid不可用的问题,在任何环境下均可获取到正确的pid +* 移除onMasterClose/onMasterConnect \ No newline at end of file diff --git a/doc/15.15.5 - 1.9.15.md b/doc/15.15.5 - 1.9.15.md new file mode 100644 index 0000000..ce031cc --- /dev/null +++ b/doc/15.15.5 - 1.9.15.md @@ -0,0 +1,11 @@ +#1.9.15 + +* 增加`MySQL`客户端对事务处理的支持 +* 增加内联到`PHP`源码静态编译支持 +* 异步`MySQL`客户端增加连接超时支持 +* 异步`Redis`客户端增加连接超时支持 +* 异步`Redis`客户端支持设置`password`和`database`选项 +* 增加`Atomic->wait/wakeup`函数,可使用原子计数实现通知和等待功能 +* 修复`Redis\Server`接收`8K`以上请求时发生崩溃的问题 +* 增加 `170` 个单元测试用例,覆盖 `90%` 以上 `API` + diff --git a/doc/15.15.50 - v1.5.md b/doc/15.15.50 - v1.5.md new file mode 100644 index 0000000..607b377 --- /dev/null +++ b/doc/15.15.50 - v1.5.md @@ -0,0 +1,24 @@ +#v1.5 + +v1.5.9 +----- +* 修复onClose回调$fd/$from_id错误的bug +* swoole_framework框架提供WebSocket支持 + +v1.5.8 +------ +* 增加swoole_connection_list接口,用于遍历所有连接 +* 增加swoole_connection_info接口,用于获取连接信息 +* swoole_server_send/swoole_server_close不再需要传入from_id参数 +* buffer功能测试通过,已增加到setting中 +* 提供对tcp_keepalive的支持 +* 增加日志模块,记录运行时的警告和错误信息 + + +v1.5.7 +----- +* 不再使用clock_gettime,不需要如此高精度的时间 +* 增加onWorkerStart/onWorkerStop回调函数 +* 增加onMasterConnect/onMasterClose回调函数 +* 可配置poll线程与worker进程间的通信方式 + diff --git a/doc/15.15.51 - v1.6.md b/doc/15.15.51 - v1.6.md new file mode 100644 index 0000000..4483ab8 --- /dev/null +++ b/doc/15.15.51 - v1.6.md @@ -0,0 +1,100 @@ +#v1.6 + +v1.6.13 +---- +1. 修复异常连接导致服务器死循环的BUG +2. 修复swReactorEpoll_del抛出WARN的BUG + +v1.6.11 +---- +1. task_worker启动时也会调用onWorkerStart,可以用worker_id参数来区分task_worker还是普通的worker +2. 增加onWorkerError回调,用来捕获worker进程异常退出 +3. 使用 $server->setting属性可以得到运行时配置数组 +4. swoole_server::task和taskwait可以指定发送给哪个task_worker进程 +5. 添加对字节流协议的分包支持,参见 examples/length_check_server.php & length_check_client.php +6. 增加 package_eof 参数,等同于 data_eof + +v1.6.10 +---- +* 简化异步客户端,当onReceive时不再需要调用$cli->recv,直接拿到数据。当onClose发生时也不需要再次调用$cli->close +* connect支持填写域名,swoole会自动进行DNS查询 +* 当connect失败时,如果直接仍然调用send/recv,会抛出错误 +* connection信息中增加connect_time和last_time,记录连接的时间和最后一次发送数据的时间 +* 增加TCP长连接心跳机制支持 +* 重构data_buffer功能 + +v1.6.9 +----- +* 增加到pecl.php.net,可通过pecl install swoole来安装 +* 修复task模块的bug +* 增加基于unixsock的争抢模式实现 + +v1.6.8 +----- +* 解决某些系统下worker进程段错误问题 +* 增加swoole_server_taskwait函数 +* 解决UDP多进程在FDMOD模式下的错误问题 + +v1.6.7 +---- +* 线程的数量加入限制最大不超过CPU数的4倍 +* 进程数量超过CPU数的100倍后会抛一一条警告信息 +* 修复onStart不能addtimer的bug +* 修复php5.5下异步mysql编译失败问题 +* poll_thread_num改为reactor_num + +v1.6.6 +---- +* 对FreeBSD/MacOS下的kqueue做了优化 +* 默认使用epoll/kqueue作为事件轮询 +* swoole_client内存泄露问题解决 +* 对主动发起close做优化,无需主进程再次发送通知 +* task_worker使用UnixSock-UDP通信方式 +* 对Epoll的RST事件优化 + +v1.6.5 +---- +* 启动100个worker进程时可能crash的问题解决 +* 支持MacOS +* 定时器重构,支持1ms粒度,并可用于Worker进程 + +v1.6.4 +---- +* 内存池修改为自动扩容 +* AsyncTask接口 +* 低版本系统bug解决 +* 提供swoole_lock锁 + + +v1.6.3 +---- +* SWOOLE_MODE_BASE模式重构,由于PHP在多线程下容易发生内存错误,BASE模式修改为单进程单线程模式 +* swoole_client->on/swoole_event_add可以用于任何环境 +* swoole_server增加面向对象风格 +* swoole_connection_info可用于UDP协议 +* 解决php,gcc低版本可能出现的段错误问题 +* 解决swoole扩展导致fpm段错误的问题 + +v1.6.2 +---- +* 增加swoole_event_add函数,可以将任意一个socket添加到swoole的主事件循环内 +* 增加swoole_event_del函数,删除添加的socket +* 增加examples/proxy.php实例代码,全异步非阻塞的代理服务器 +* 增加examples/async_mysql.php,实现异步非阻塞的MySQL调用 + +> 1.6.2新增的reactor操作接口,使得redis、mysql、mongodb等网络接口整合swoole_server中,实现全异步化高性能服务器 + + +v1.6.1 +---- +* 增加configure可选参数--enable-msgqueue,启用此参数后将使用消息队列作为IPC方式 +* 解决reload后,worker分配错误的bug +* 抢占式分配bug解决 +* 解决刷warn的问题 + +v1.6.0 +----- +* 优化UDP实现方式,实现高并发高可靠的UDP Server +* 可以切换IPC模式,队列或者Unsock +* close事件处理优化,解决丢失close的bug +* 使用全局内存池来分配内存 \ No newline at end of file diff --git a/doc/15.15.52 - v1.7.md b/doc/15.15.52 - v1.7.md new file mode 100644 index 0000000..1478463 --- /dev/null +++ b/doc/15.15.52 - v1.7.md @@ -0,0 +1,56 @@ +#v1.7 + +1.7.4-beta +---- +* Task进程支持定时器 +* 修复UDP发送错误的BUG +* 增加SSL-server的支持 + +1.7.4-alpha +----- +* reload对task_worker有效 +* 当管道写满是进行poll等待可写,而不是丢包 +* 增加对clang编译器的支持 +* 修复shutdown过程task_worker/manager非正常结束问题 + +1.7.3 +---- +* 增加swoole_process->exec接口 +* 修复连接后立即关闭的BUG +* 优化swoole_server->send发送大包 +* 修复dispatch_mode = 3不生效问题 +* 修复length_check错误问题 + +1.7.2 +---- +* 增加swoole_process模块 +* 支持标准输入输出重定向 +* 修复MacOS/FreeBSD/低版本Linux下时钟无法使用的BUG +* Task进程可以单独配置为使用消息队列通信 +* 修复心跳检测未关闭socket的BUG + + +1.7.1 +---- +* 增加unix sock监听的支持 +* 增加swoole_event_set函数 +* client对out_buffer的支持 +* 修复swoole_server->send后close发生错误的问题 +* 可动态配置IPC方式, 选择消息队列/UnixSock管道 + +1.7.0 & 1.6.12 +---- +* reactor线程与writer线程合并 +* 对send优化,加入out_buffer机制 +* 增加AIO异步读写文件的API +* 增加DNS异步查询函数 +* swoole_client在php-fpm或apache mod_php下支持长连接 +* 增加非Server模式下的异步定时器支持 +* 定时器优化 +* 修复bug +* 增加sendfile支持 +* onReceive的data变量使用引用方式,减少一次内存复制 +* 消息队列模式增加定时器的支持 +* 增加signalfd的支持,使信号事件也加入到Reactor + + diff --git a/doc/15.15.6 - 1.9.14.md b/doc/15.15.6 - 1.9.14.md new file mode 100644 index 0000000..efb64a2 --- /dev/null +++ b/doc/15.15.6 - 1.9.14.md @@ -0,0 +1,10 @@ +#1.9.14 + +* 更新`HttpClient`,当连接超时或被服务器重置时回调未完成的请求 +* 更新`HttpClient`,底层增加请求超时机制 +* 更新`Client`底层支持`mqtt`协议 +* 修复`WebSocket`服务器自定义握手方法多出header的问题 +* 修复`onTask`轻微内存泄漏问题 +* 修复`HttpServer`特殊情况下释放response发生崩溃的问题 +* 修复`WebSocket::unpack`传入空字符串导致解析错误的问题 + diff --git a/doc/15.15.7 - 1.9.12.md b/doc/15.15.7 - 1.9.12.md new file mode 100644 index 0000000..ad2f81a --- /dev/null +++ b/doc/15.15.7 - 1.9.12.md @@ -0,0 +1,7 @@ +#1.9.12 + +* 允许`WebSocket`客户端向服务器发送空包 +* 增加回调函数缓存,减少回调函数的CPU消耗 +* 修复`WebSocket`客户端内存泄漏问题 +* 修复不支持`SSE`指令集硬件环境下编译错误的问题 + diff --git a/doc/15.15.8 - 1.9.11.md b/doc/15.15.8 - 1.9.11.md new file mode 100644 index 0000000..3120047 --- /dev/null +++ b/doc/15.15.8 - 1.9.11.md @@ -0,0 +1,11 @@ +#1.9.11 + +* 修复`WebSocket`服务器`onOpen`回调函数存在内存泄漏的问题 +* 修复`Http2`客户端多次请求时错误释放内存的问题 +* 心跳检测支持时间轮算法 +* 发送文件sendfile相关API增加长度参数支持 +* 异步客户端支持超时设置 +* 修复Http服务器文件上传在5.6版本发生崩溃的问题 +* 优化添加Task和Timer的定时器性能,提升分支预测成功率 +* 允许WebSocket服务器发送空包到客户端 +* 为WebSocket客户端添加`Sec-WebSocket-Version`头 \ No newline at end of file diff --git a/doc/15.15.9 - 1.9.9.md b/doc/15.15.9 - 1.9.9.md new file mode 100644 index 0000000..21f7b91 --- /dev/null +++ b/doc/15.15.9 - 1.9.9.md @@ -0,0 +1,9 @@ +#1.9.9 + +* 修复真异步IO的DNS解析器请求ID整型溢出的问题 +* 禁止在mysql的`onClose`方法中执行`close`方法 +* 修改`table::get`方法,增加参数支持只获取一个字段的值 +* 更新`getClientInfo`返回值`from_id`项改为`reactor_id` +* 修复Http2客户端POST数据时协议错误问题 +* 修复`swoole_async_dns_lookup`目标主机IP包含0时返回值错误的问题 +* 修复启用`tcmalloc`和`jemalloc`时执行`strdup`时发生崩溃的问题 diff --git "a/doc/15.16 - \345\216\206\345\217\262\357\274\232\347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225\357\274\2102.x\357\274\211.md" "b/doc/15.16 - \345\216\206\345\217\262\357\274\232\347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225\357\274\2102.x\357\274\211.md" new file mode 100644 index 0000000..bf2f1ed --- /dev/null +++ "b/doc/15.16 - \345\216\206\345\217\262\357\274\232\347\211\210\346\234\254\346\233\264\346\226\260\350\256\260\345\275\225\357\274\2102.x\357\274\211.md" @@ -0,0 +1,2 @@ +#历史:版本更新记录(2.x) + diff --git a/doc/15.16.1 - 2.0.1-Alpha.md b/doc/15.16.1 - 2.0.1-Alpha.md new file mode 100644 index 0000000..999dd61 --- /dev/null +++ b/doc/15.16.1 - 2.0.1-Alpha.md @@ -0,0 +1,153 @@ +#2.0.1-Alpha + +Swoole-2.0 提供了**PHP原生协程调度器**,PHP代码可以按照同步方式编写,底层引擎使用异步IO,调度器会在IO完成后自动切换PHP函数调用栈。 + +内置协程不依赖PHP的[Yield/Generator](http://php.net/manual/zh/language.generators.syntax.php)语法,实现了真正的同步代码,异步IO。Swoole-2.0兼容[Swoole-1.0](http://www.swoole.com/)所有现存特性,同时支持同步阻塞、异步非阻塞回调、协程 3 种 IO 模型。 + +协程可以理解为用户态线程,通过**协作**而不是抢占来进行切换。相对于操作系统进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。Swoole可以为每一个请求创建对应的协程,根据IO的状态来合理地调度协程。 + +Swoole-2.0内置协程的优势: + +1. 开发者可以使用最传统的PHP同步编码方式即可实现异步IO。过去使用Node.js和Swoole1.0编写异步程序时必须使用嵌套回调,开发效率低,逻辑复杂之后开发维护都很困难。现在使用Swoole-2.0内置协程可以轻松编写异步程序,彻底告别`Callback Hell`。 + +2. 由于Swoole-2.0是在底层封装了协程,所以对比其他的PHP协程框架,如[TSF](https://github.com/tencent-php/tsf)、[Zan](http://zanphp.io/),开发者不需要额外使用`yield`关键词来标识一个协程IO切换操作,所以不再需要对yield的语义进行深入理解以及对每一级的调用都修改为yield,这极大的提高了开发效率。 + +3. Swoole-2.0除了支持常见的如TCP、UDP、HTTP、Redis、MySQL协程客户端之外,还支持并发Client,可以同时并发执行多个不同类型的协程客户端调用,时间为Max(耗时最长Client的时间)。而Go语言需要使用非常复杂的`goroutine`+`chan`+`select`才能实现。Swoole-2.0的并发调用使用更加简单。 + +## 主要特性: + +- 底层内置协程,同步的代码,异步的IO +- TCP/UDP/UnixSocket协程客户端`Swoole\Coroutine\Client` +- Http/WebSocket协程客户端`Swoole\Coroutine\HttpClient` +- Redis协程客户端`Swoole\Coroutine\Redis` +- MySQL协程客户端`Swoole\Coroutine\MySQL` +- 并发Client +- 支持异步超时 +- 默认开启命名空间 + +## 要求: +- 需要PHP5.5或更高版本 +- 暂时不支持PHP7 +- 服务器端基于`Swoole\Server`或者`Swoole\Http\Server`进行开发,目前仅支持在`onRequet`、`onReceive`、`onConnect`回调中使用协程 + +## 安装方式: +Swoole-2.0需要通过添加`--enable-coroutine`编译参数启用协程能力,示例如下: + +```shell +phpize +./configure --with-php-config={path-to-php-config} --enable-coroutine +make +make install +``` +添加编译参数,swoole server将切换到协程模式 + +## 使用示例 +### TCP协程客户端 +```php +$client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); +$client->connect('127.0.0.1', 9501, 0.5) +//调用connect将触发协程切换 +$client->send("hello world\n"); +echo $client->recv(); +//调用recv将再次触发协程切换 +$client->close(); +``` +### Http协程客户端 +```php +$cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80); +$cli->setHeaders([ + 'Host' => "localhost", + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', +]); +$cli->set([ 'timeout' => 1]); +$cli->get('/index.php'); +echo $cli->body; +$cli->close(); +``` +### Redis协程客户端 +```php +$redis = new Swoole\Coroutine\Redis(); +$redis->connect('127.0.0.1', 6379); +$val = $redis->get('key'); +``` + +### MySQL协程客户端 +```php +$swoole_mysql = new Swoole\Coroutine\MySQL(); +$swoole_mysql->connect(['host' => '127.0.0.1', + 'user' => 'user', + 'password' => 'pass', + 'database' => 'test', +]); +$res = $swoole_mysql->query('select sleep(1)'); +``` +### 并发调用 +```php +$tcpclient = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); +$tcpclient->connect('127.0.0.1', 9501, 0.5) +$tcpclient->send("hello world\n"); + +$redis = new Swoole\Coroutine\Redis(); +$redis->connect('127.0.0.1', 6379); +$redis->setDefer(); +$redis->get('key'); + +$mysql = new Swoole\Coroutine\MySQL(); +$mysql->connect(['host' => '127.0.0.1', + 'user' => 'user', + 'password' => 'pass', + 'database' => 'test', +]); +$mysql->setDefer(); +$mysql->query('select sleep(1)'); + +$httpclient = new Swoole\Coroutine\Http\Client('0.0.0.0', 9599); +$httpclient->setHeaders(['Host' => "api.mp.qq.com"]); +$httpclient->set([ 'timeout' => 1]); +$httpclient->setDefer(); +$httpclient->get('/'); + +$tcp_res = $tcpclient->recv(); +$redis_res = $redis->recv(); +$mysql_res = $mysql->recv(); +$http_res = $httpclient->recv(); +``` +通常,如果一个业务请求中需要做一次redis请求和一次mysql请求,那么网络IO会是这样: + +`redis发包->redis收包->mysql发包->mysql收包` + +以上流程网络IO的时间就等于 redis网络IO时间 + mysql网络IO时间。 + +而对于协程版本的Client,网络IO可以是这样: + +`redis发包->mysql发包->redis收包->mysql收包` + +以上流程网络IO的时间就接近于 `MAX(redis网络IO时间, mysql网络IO时间)`。 + +现在支持并发请求的Client有: + +* Swoole\Coroutine\Client +* Swoole\Coroutine\Redis +* Swoole\Coroutine\MySQL +* Swoole\Coroutine\Http\Client + +除了`Swoole\Coroutine\Client`,其他Client都实现了defer特性,用于声明延迟收包。 + +## 注意事项 +1. 全局变量:协程使得原有的异步逻辑同步化,但是在协程的切换是隐式发生的,所以在协程切换的前后不能保证全局变量以及static变量的一致性。 +2. 请勿在下列场景中调用协程客户端: + * 析构函数`__destruct()` + * 魔术方法`__call()` +3. 当前版本为Alpha预览版本,不建议在生产环境上使用 + +## 使用新版本 +- GitHub下载地址:[https://github.com/swoole/swoole-src/tree/2.0.1](https://github.com/swoole/swoole-src/tree/2.0.1) +- 开源中国下载地址:[http://git.oschina.net/matyhtf/swoole/tree/2.0.1-alpha](http://git.oschina.net/matyhtf/swoole/tree/2.0.1-alpha) +- Swoole2.0使用文档:[http://wiki.swoole.com/wiki/page/p-coroutine.html](http://wiki.swoole.com/wiki/page/p-coroutine.html) + +## 交流互助群 494412579 + +![](http://www.swoole.com/static/uploads//wiki/201608/04/863670177591.jpg) + diff --git a/doc/15.16.2 - 2.0.5.md b/doc/15.16.2 - 2.0.5.md new file mode 100644 index 0000000..79fbcb9 --- /dev/null +++ b/doc/15.16.2 - 2.0.5.md @@ -0,0 +1,110 @@ +#2.0.5 + +Swoole 2.0正式版发布了。2.0版本最大的更新是增加了对协程(Coroutine)的支持。正式版已同时支持PHP5和PHP7。基于Swoole2.0协程PHP开发者可以已同步的方式编写代码,底层自动进行协程调度,转变为异步IO。解决了传统异步编程嵌套回调的问题。 + +与Node.js(ES6+)、Python等语言使用yield/generator、async/await的实现方式相比,Swoole协程无需修改代码添加额外的关键词。 + +与Go语言的goroutine相比,Swoole协程是内置式的,应用层代码无需添加go关键词启动协程,只需要使用封装好的协程客户端即可,使用更简单。另外Swoole协程的IO组件在底层内置了超时机制,不需要使用复杂的select/chan/timer实现客户端超时。 + +目前Swoole底层内置的协程客户端组件包括:udpclient、tcpclient、httpclient、redisclient、mysqlclient,基本涵盖了开发者常用的几种通信协议。协程组件只能在服务器的onConnect、onRequest、onReceive、onMessage 回调函数中使用。 + +使用示例 +---- +```php +$server = new Swoole\Http\Server('127.0.0.1', 9501); + +/* + 触发on request事件时,SWOOLE会开辟一个协程栈,对协程栈进行初始化 + */ +$server->on('Request', function ($request, $response) { + $tcp_cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + /** + client在调用connect函数后,SWOOLE会将PHP上下文信息保存到当前栈内 + 然后将协程挂起,待确认连接成功后,触发epoll事件,然后协程切换 + 恢复PHP上下文信息,返回结果,继续执行PHP代码 + */ + if ($tcp_cli->connect('127.0.0.1', 9906) === false) { + $response->end("connect server failed."); + return; + } + $tcp_cli->send('test for the coro'); + /* + client在调用recv函数后,SWOOLE会将PHP上下文信息保存到当前栈内 + 然后将协程挂起待后端svr回包,触发epoll事件,然后协程切换 + 恢复PHP上下文信息,返回结果,继续执行PHP代码 + 如果后端在设定的超时时间内,未能回包,返回false + client的errCode定为110 + */ + $ret = $tcp_cli->recv(100); + $tcp_cli->close(); + if ($ret) { + $response->end(" swoole response is ok"); + } else { + $response->end(" recv failed error : {$tcp_cli->errCode}"); + } +}); + +$server->start(); +``` + +UDP客户端 +---- +```php +$udp_cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); + +$ret = $udp_cli->connect('127.0.0.1', 9906); +$udp_cli->send('test for the coro'); + +$ret = $udp_cli->recv(100); +$udp_cli->close(); + +if ($ret) +{ + $response->end("swoole response is ok"); +} +else +{ + $response->end("recv failed error : {$client->errCode}"); +} +``` + +MySQL客户端 +---- +```php +$swoole_mysql = new Swoole\Coroutine\MySQL(); +$swoole_mysql->connect([ + 'host' => '127.0.0.1', + 'user' => 'user', + 'password' => 'pass', + 'database' => 'test' +]); +$res = $swoole_mysql->query('select sleep(1)'); +Redis客户端 + +$redis = new Swoole\Coroutine\Redis(); +$redis->connect('127.0.0.1', 6379); +$val = $redis->get('key'); +``` + +Http客户端 +-------- +```php +$cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 80); +$cli->setHeaders([ + 'Host' => "localhost", + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', +]); +$cli->set([ 'timeout' => 1]); +$cli->get('/index.php'); +echo $cli->body; +$cli->close(); +``` + +下载地址: +--------- + +- Github: +- 开源中国: +- PECL: \ No newline at end of file diff --git a/doc/15.16.3 - 2.0.9.md b/doc/15.16.3 - 2.0.9.md new file mode 100644 index 0000000..d8d0fed --- /dev/null +++ b/doc/15.16.3 - 2.0.9.md @@ -0,0 +1,5 @@ +#2.0.9 + +* 合并`1.9`分支 +* 增加`Coroutine::sleep` +* 增加`Server::taskCo` diff --git a/doc/15.16.4 - 2.0.10.md b/doc/15.16.4 - 2.0.10.md new file mode 100644 index 0000000..da285fe --- /dev/null +++ b/doc/15.16.4 - 2.0.10.md @@ -0,0 +1,5 @@ +#2.0.10 + +* 定时器回调函数中支持协程客户端 +* `Redis`协程客户端支持连接超时 +* 修复`Redis::zAdd`指令导致崩溃的问题 diff --git "a/doc/15.2 - Swoole\347\244\276\345\214\272.md" "b/doc/15.2 - Swoole\347\244\276\345\214\272.md" new file mode 100644 index 0000000..dfb0dbe --- /dev/null +++ "b/doc/15.2 - Swoole\347\244\276\345\214\272.md" @@ -0,0 +1,27 @@ +#Swoole社区 + +社区 +---- +推荐使用:[group.swoole.com](http://group.swoole.com/) + +QQ群 +------ +- 5群 399424487(2000人群,未满可加入) +* 4群 193772828(2000人群,已满) +* 3群 472153539(2000人群,已满) +- 2群 364153814(2000人群,已满) +- 1群 321637118(2000人群,已满) +- VIP群 626330624(年费:599 RMB) + + +Gmail +------ + + + +贡献代码 +----- +* GitHub:https://github.com/swoole/swoole-src +* 您可以发送邮件至 +* Fork项目,并提交pull请求 + diff --git "a/doc/15.3 - \346\215\220\350\265\240Swoole\351\241\271\347\233\256.md" "b/doc/15.3 - \346\215\220\350\265\240Swoole\351\241\271\347\233\256.md" new file mode 100644 index 0000000..3de82d9 --- /dev/null +++ "b/doc/15.3 - \346\215\220\350\265\240Swoole\351\241\271\347\233\256.md" @@ -0,0 +1,129 @@ +#捐赠Swoole项目 + +您的捐赠是对Swoole项目开发组最大的鼓励和支持。我们会坚持开发维护下去。 您的捐赠将被用于: + +* 持续和深入地开发 +* 文档和社区的建设和维护 + +捐赠方式 +---------- +[开源中国-捐赠Swoole项目](https://gitee.com/swoole/swoole?donate=true) + +捐赠列表 +---------- +``` +如果您捐赠了Swoole开源项目但不在下面的列表中,或者希望修改相关信息,请联系Rango-韩天峰(QQ:12811247)。 +``` + +#### 感谢 [codecasts.com](https://codecasts.com/) 捐赠 8000元 RMB +[![](https://user-images.githubusercontent.com/6011686/28741127-791014ae-7442-11e7-929e-114e5047d9e5.png)](https://codecasts.com/) +
+ +#### 感谢 [51Talk](http://www.51talk.com/) 捐赠 10000元 RMB +[![](http://static.51talk.com/static/images/html/www_new/common/log.png?v=2017-10-19)](http://www.51talk.com/) +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金额捐赠者寄语渠道
3072.00wanjochan wan***@gmail.com因开始用于生产环境决定追赠1k支付宝
3000.00易软天创 http://www.cnezsoft.com/-开源中国
2998.00Kimi jian***@gmail.com 希望Rango继续做好Swoole支付宝
1024.00陈赫 dan***@gmail.com-支付宝
1000.00韩冬辉开源项目需要大家的贡献,
何况是swoole这么好的项目,
你不捐点儿么?
支付宝
1000.00徐杰星 jes***@jesin.net-支付宝
1000.00e袋洗—苏高峰,e袋洗—何松涛,e袋洗—王建武
e袋洗—王树荣,e袋洗—何俊良,e袋洗~王维军
e袋洗—李洋,e袋洗~赵玉昆
-支付宝
750.00JellyBool 497***@qq.com赞助swoole的,哈哈哈支付宝
500.00北京php-ios小郭希望swoole越做越好微信
500.00周冲 027***@gmail.com-支付宝
500.00nosun nos***@126.com感谢swoole, 感谢峰哥!支付宝
307.20九月 nnn***@163.com支持Swoole项目支付宝
300.00徐长龙 xcl***@qq.com-支付宝
100.00sunny 844700118@qq.com真心祝愿swoole开源项目走向世界支付宝
10.00fafa 131****1929微薄之力,正在学习,谢谢支付宝
39.90hong 198***@163.com谢谢swoole支付宝
10.00Robert 158****2981小小支持支付宝
20.00红生 999***@sina.com支持swoole,支持开源,支持大牛分享支付宝
50.00Fay 136****0965小小程序媛的膜拜支付宝
100.00JACK tuy***@gmail.com感谢你做的事情,略表心意支付宝
1.00百里江山 fre***@126.comswoole支付宝
100.00小军 ban***@126.com加油支付宝
200.00LinQ zzu***@foxmail.com支持Swoole,支持天峰,加油支付宝
10.00维子 809***@qq.com很喜欢swoole这个项目,小小支持哈支付宝
50.00蒋强 991***@qq.com 略表心意,支持Swoole.支付宝
100.00刘志铭-微信
66.66规速 275***@qq.comswoole捐款支付宝
20.00[大天使长]-微信
5.00进戈-微信
10.00myersguo-微信
10.00罗东菊-微信
10.00我是一只小尼玛-微信
6.66不死鸟 bir***@sina.com加油! swoole一统天下。支付宝
128.00一路向北 134****4818继续努力支付宝
100.00雅方同学 135****5404板牙救援——杨雅方支付宝
100.00勇辉 133****2017板牙救援-陈勇辉支付宝
58.00行者 gej***@sohu.com支持swoole 加油!!支付宝
100.00 钱大 zhi***@163.com感谢swoole支付宝
100.00强 dre***@126.comswoole支付宝
10.00True adm***@yyzg.cc支持一下支付宝
100.00wink hlm***@163.com加油支付宝
50.00明志 610***@qq.com目前准备使用支付宝
100.00李高峰 gao***@126.com 支持swoole支付宝
50.00Lobtao lob***@qq.com支持swoole支付宝
100.00阿智 pan***@126.com希望swoole能坚持下去支付宝
50.00艳康 657***@qq.com-支付宝
20.00LeesLee峰 lee***@163.com支持支付宝
100.00满鑫 gma***@163.com-支付宝
100.00龙宝 che***@163.com37350968-支持swoole支付宝
16.00麻辣烫 137****0761小小心意支付宝
100.00鹏 186****8597支持下支付宝
100.00浩天 185****2081希望swoole越来越好支付宝
100.00aibo aib***@vhost.com.cn支持下支付宝
10.00白枫 133****8172捐赠swoole支付宝
100.00错因太帅 189***@163.comswool蒸蒸日上支付宝
18.00拭血 lsg***@126.com为开源点赞支付宝
100.00老马 chanyu@21cn.netswoole脚踏实地,永远向前!支付宝
100.00Persi ipe***@sixsir.com小小心意,感谢Swoole团队。支付宝
50.00林 131****1019必须支持支付宝
10.00singer lan***@qq.com用上swoole了,项目未上线,小小心意支付宝
20.00超亮 156****9731感谢大神支付宝
10.00沐鑫 yun***@qq.com心意支付宝
100.00琦(xia***@gmail.com)大神多谢了,swoole实在是神器支付宝
10.00醇(lov***@qq.com)小小心意支付宝
100.00承燕(151***8370)支持下支付宝
200.00以海(ab1***@163.com)感谢swoole及rango的帮助.熊熊支付宝
100.00dr(244***@qq.com)感谢swoole支付宝
66.00elarity(4677833@qq.com)祝我早日也能为swoole做一份贡献支付宝
99.00coldstar(saf***@163.com)开源是一种精神支付宝
200.00严 wud***@163.com支持swoole支付宝
10.00晓 wux***@163.com转账支付宝
100.00盘军 315***@qq.com支持开源事业!支付宝
100.00大为 bet***@foxmail.com开发捐赠支付宝
20.00陈希 cx1***@gmail.com捐赠swoole支付宝
50.00贤涛 xsi***@163.comswoole支付宝
25.00路 120***@qq.com-支付宝
50.00海滨 hea***@163.com加油支付宝
20.00高露 187***@163.com支持下,加油支付宝
10.00陆庆松 131****0160-支付宝
50.00嘉振 wnd***@126.com支持下,加油支付宝
1.00谭积海 121***@qq.com-支付宝
120.00徐汉传 sun***@yahoo.com -支付宝
20.00屈永昌 576***@qq.com-支付宝
188.00王雪兵 xue***@163.com-支付宝
10.00周振忠 as1***@tom.com-支付宝
1.00侯伟 hw8***@126.com-支付宝
100.00李云飞 158****5024-支付宝
100.00张鑫海 zxh***@sina.com-支付宝
25.00周曦 336***@qq.com今早拼车赚的,贡献给swoole。支付宝
10.00刘杰 150****4670-支付宝
100.00黄仁运 657***@qq.com-支付宝
27.00周曦 336***@qq.com-支付宝
100.00贺晶新 671***@qq.com-支付宝
diff --git "a/doc/15.4 - \345\212\240\345\205\245Swoole\345\274\200\345\217\221\347\273\204.md" "b/doc/15.4 - \345\212\240\345\205\245Swoole\345\274\200\345\217\221\347\273\204.md" new file mode 100644 index 0000000..3cf2add --- /dev/null +++ "b/doc/15.4 - \345\212\240\345\205\245Swoole\345\274\200\345\217\221\347\273\204.md" @@ -0,0 +1,11 @@ +#加入Swoole开发组 + +我们非常欢迎各位`PHP/C/C++`程序员加入,共同开发维护`Swoole`开源项目。`Swoole`开发组是一个民主公平并且自由开放的开源组织。目前已有`31`位成员,遍布国内各大互联网企业,包括腾讯,新浪,YY语音,360等。 + +目前共有3种加入方式: + +* 为`swoole`项目贡献`C/C++`代码,并被官方采纳 +* 为`swoole`项目撰写文档,并被官方采纳 +* 为`swoole`项目贡献单元测试脚本 + +加入`swoole`开发组后,我们会为你开通`@swoole.com`邮件组和`Github`账户权限。 \ No newline at end of file diff --git "a/doc/15.5 - \351\231\204\345\275\225\357\274\232Linux\344\277\241\345\217\267\345\210\227\350\241\250.md" "b/doc/15.5 - \351\231\204\345\275\225\357\274\232Linux\344\277\241\345\217\267\345\210\227\350\241\250.md" new file mode 100644 index 0000000..cd2b9c9 --- /dev/null +++ "b/doc/15.5 - \351\231\204\345\275\225\357\274\232Linux\344\277\241\345\217\267\345\210\227\350\241\250.md" @@ -0,0 +1,63 @@ +#附录:Linux信号列表 + +1. SIGHUP +2. SIGINT +3. SIGQUIT +4. SIGILL +5. SIGTRAP +6. SIGABRT +7. SIGBUS +8. SIGFPE +9. SIGKILL +10. SIGUSR1 +11. SIGSEGV +12. SIGUSR2 +13. SIGPIPE +14. SIGALRM +15. SIGTERM +17. SIGCHLD +18. SIGCONT +19. SIGSTOP +20. SIGTSTP +21. SIGTTIN +22. SIGTTOU +23. SIGURG +24. SIGXCPU +25. SIGXFSZ +26. SIGVTALRM +27. SIGPROF +28. SIGWINCH +29. SIGIO +30. SIGPWR +31. SIGSYS +34. SIGRTMIN +35. SIGRTMIN+1 +36. SIGRTMIN+2 +37. SIGRTMIN+3 +38. SIGRTMIN+4 +39. SIGRTMIN+5 +40. SIGRTMIN+6 +41. SIGRTMIN+7 +42. SIGRTMIN+8 +43. SIGRTMIN+9 +44. SIGRTMIN+10 +45. SIGRTMIN+11 +46. SIGRTMIN+12 +47. SIGRTMIN+13 +48. SIGRTMIN+14 +49. SIGRTMIN+15 +50. SIGRTMAX-14 +51. SIGRTMAX-13 +52. SIGRTMAX-12 +53. SIGRTMAX-11 +54. SIGRTMAX-10 +55. SIGRTMAX-9 +56. SIGRTMAX-8 +57. SIGRTMAX-7 +58. SIGRTMAX-6 +59. SIGRTMAX-5 +60. SIGRTMAX-4 +61. SIGRTMAX-3 +62. SIGRTMAX-2 +63. SIGRTMAX-1 +64. SIGRTMAX \ No newline at end of file diff --git "a/doc/15.6 - \351\231\204\345\275\225\357\274\232Linux\351\224\231\350\257\257\344\277\241\346\201\257(errno)\345\210\227\350\241\250.md" "b/doc/15.6 - \351\231\204\345\275\225\357\274\232Linux\351\224\231\350\257\257\344\277\241\346\201\257(errno)\345\210\227\350\241\250.md" new file mode 100644 index 0000000..d4ba76e --- /dev/null +++ "b/doc/15.6 - \351\231\204\345\275\225\357\274\232Linux\351\224\231\350\257\257\344\277\241\346\201\257(errno)\345\210\227\350\241\250.md" @@ -0,0 +1,248 @@ +#附录:Linux错误信息(errno)列表 + + +124 EMEDIUMTYPE Wrong medium type + +123 ENOMEDIUM No medium found + +122 EDQUOT Disk quota exceeded + +121 EREMOTEIO Remote I/O error + +120 EISNAM Is a named type file + +119 ENAVAIL No XENIX semaphores available + +118 ENOTNAM Not a XENIX named type file + +117 EUCLEAN Structure needs cleaning + +116 ESTALE Stale NFS file handle + +115 EINPROGRESS +Operation now in progress + +114 EALREADY Operation already in progress + +113 EHOSTUNREACH No route to host + +112 EHOSTDOWN Host is down + +111 ECONNREFUSED Connection refused + +110 ETIMEDOUT +Connection timed out + +109 ETOOMANYREFS Too many references: cannot splice + +108 ESHUTDOWN Cannot send after transport endpoint shutdown + +107 ENOTCONN Transport endpoint is not connected + +106 EISCONN Transport endpoint is already connected + +105 ENOBUFS No buffer space available + +104 ECONNRESET Connection reset by peer + +103 ECONNABORTED Software caused connection abort + +102 ENETRESET Network dropped connection on reset + +101 ENETUNREACH Network is unreachable + +100 ENETDOWN Network is down + +99 EADDRNOTAVAIL Cannot assign requested address + +98 EADDRINUSE Address already in use + +97 EAFNOSUPPORT Address family not supported by protocol + +96 EPFNOSUPPORT Protocol family not supported + +95 EOPNOTSUPP Operation not supported + +94 ESOCKTNOSUPPORT Socket type not supported + +93 EPROTONOSUPPORT Protocol not supported + +92 ENOPROTOOPT Protocol not available + +91 EPROTOTYPE Protocol wrong type for socket + +90 EMSGSIZE +Message too long + +89 EDESTADDRREQ Destination address required + +88 ENOTSOCK Socket operation on non-socket + +87 EUSERS Too many users + +86 ESTRPIPE Streams pipe error + +85 ERESTART Interrupted system call should be restarted + +84 EILSEQ Invalid or incomplete multibyte or wide character + +83 ELIBEXEC Cannot exec a shared library directly + +82 ELIBMAX Attempting to link in too many shared libraries + +81 ELIBSCN .lib section in a.out corrupted + +80 ELIBBAD Accessing a corrupted shared library + +79 ELIBACC Can not access a needed shared library + +78 EREMCHG Remote address changed + +77 EBADFD File descriptor in bad state + +76 ENOTUNIQ Name not unique on network + +75 EOVERFLOW Value too large for defined data type + +74 EBADMSG +Bad message + +73 EDOTDOT RFS specific error + +72 EMULTIHOP Multihop attempted + +71 EPROTO Protocol error + +70 ECOMM Communication error on send + +69 ESRMNT Srmount error + +68 EADV Advertise error + +67 ENOLINK Link has been severed + +66 EREMOTE Object is remote + +65 ENOPKG Package not installed + +64 ENONET Machine is not on the network + +63 ENOSR Out of streams resources + +62 ETIME Timer expired + +61 ENODATA No data available + +60 ENOSTR Device not a stream + +59 EBFONT Bad font file format + +57 EBADSLT Invalid slot + +56 EBADRQC Invalid request code + +55 ENOANO No anode + +54 EXFULL Exchange full + +53 EBADR Invalid request descriptor + +52 EBADE Invalid exchange + +51 EL2HLT Level 2 halted + +50 ENOCSI No CSI structure available + +49 EUNATCH Protocol driver not attached + +48 ELNRNG Link number out of range + +47 EL3RST Level 3 reset + +46 EL3HLT Level 3 halted + +45 EL2NSYNC Level 2 not synchronized + +44 ECHRNG Channel number out of range + +43 EIDRM Identifier removed + +42 ENOMSG No message of desired type + +40 ELOOP Too many levels of symbolic links + +39 ENOTEMPTY +Directory not empty + +38 ENOSYS +Function not implemented + +37 ENOLCK +No locks available + +36 ENAMETOOLONG +File name too long + +35 EDEADLK +Resource deadlock avoided + +34 ERANGE +Numerical result out of range + +33 EDOM +Numerical argument out of domain + +32 EPIPE +Broken pipe + +31 EMLINK +Too many links + +30 EROFS +Read-only file system + +29 ESPIPE +Illegal seek + +28 ENOSPC +No space left on device + +27 EFBIG +File too large + +26 ETXTBSY Text file busy + +25 ENOTTY +Inappropriate ioctl for device + +24 EMFILE +Too many open files + +23 ENFILE +Too many open files in system + +22 EINVAL +Invalid argument + +21 EISDIR +Is a directory + +20 ENOTDIR +Not a directory + +19 ENODEV +No such device + +18 EXDEV +Invalid cross-device link + +17 EEXIST +File exists + +16 EBUSY +Device or resource busy + +15 ENOTBLK Block device required + +14 EFAULT +Bad address + +13 EACCES +Permission denied + +12 ENOMEM +Cannot allocate memory + +11 EAGAIN +Resource temporarily unavailable + +10 ECHILD +No child processes + +9 EBADF +Bad file descriptor + +8 ENOEXEC +Exec format error + +7 E2BIG +Argument list too long + +6 ENXIO +No such device or address + +5 EIO +Input/output error + +4 EINTR +Interrupted system call + +3 ESRCH +No such process + +2 ENOENT +No such file or directory + +1 EPERM +Operation not permitted + +0 Success diff --git "a/doc/15.7 - \351\231\204\345\275\225\357\274\232TCP\350\277\236\346\216\245\347\232\204\347\212\266\346\200\201.md" "b/doc/15.7 - \351\231\204\345\275\225\357\274\232TCP\350\277\236\346\216\245\347\232\204\347\212\266\346\200\201.md" new file mode 100644 index 0000000..5b242af --- /dev/null +++ "b/doc/15.7 - \351\231\204\345\275\225\357\274\232TCP\350\277\236\346\216\245\347\232\204\347\212\266\346\200\201.md" @@ -0,0 +1,13 @@ +#附录:TCP连接的状态 + +1. LISTEN:首先服务端需要打开一个socket进行监听,状态为LISTEN. /* The socket is listening for incoming connections. 侦听来自远方TCP端口的连接请求 */ +2. SYN_SENT:客户端通过应用程序调用connect进行active open.于是客户端tcp发送一个SYN以请求建立一个连接.之后状态置为SYN_SENT. /*The socket is actively attempting to establish a connection. 在发送连接请求后等待匹配的连接请求 */ +3. SYN_RECV:服务端应发出ACK确认客户端的SYN,同时自己向客户端发送一个SYN.之后状态置为SYN_RECV /* A connection request has been received from the network. 在收到和发送一个连接请求后等待对连接请求的确认 */ +4. ESTABLISHED: 代表一个打开的连接,双方可以进行或已经在数据交互了。/* The socket has an established connection. 代表一个打开的连接,数据可以传送给用户 */ +5. FIN_WAIT1:主动关闭(active close)端应用程序调用close,于是其TCP发出FIN请求主动关闭连接,之后进入FIN_WAIT1状态./* The socket is closed, and the connection is shutting down. 等待远程TCP的连接中断请求,或先前的连接中断请求的确认 */ +6. CLOSE_WAIT:被动关闭(passive close)端TCP接到FIN后,就发出ACK以回应FIN请求(它的接收也作为文件结束符传递给上层应用程序),并进入CLOSE_WAIT. /* The remote end has shut down, waiting for the socket to close. 等待从本地用户发来的连接中断请求 */ +7. FIN_WAIT2:主动关闭端接到ACK后,就进入了FIN-WAIT-2 ./* Connection is closed, and the socket is waiting for a shutdown from the remote end. 从远程TCP等待连接中断请求 */ +8. LAST_ACK:被动关闭端一段时间后,接收到文件结束符的应用程序将调用CLOSE关闭连接。这导致它的TCP也发送一个 FIN,等待对方的ACK.就进入了LAST-ACK . /* The remote end has shut down, and the socket is closed. Waiting for acknowledgement. 等待原来发向远程TCP的连接中断请求的确认 */ +9. TIME_WAIT:在主动关闭端接收到FIN后,TCP就发送ACK包,并进入TIME-WAIT状态。/* The socket is waiting after close to handle packets still in the network.等待足够的时间以确保远程TCP接收到连接中断请求的确认 */ +10. CLOSING: 比较少见./* Both sockets are shut down but we still don't have all our data sent. 等待远程TCP对连接中断的确认 */ +11. CLOSED: 被动关闭端在接受到ACK包后,就进入了closed的状态。连接结束./* The socket is not being used. 没有任何连接状态 */ diff --git "a/doc/15.8 - \351\231\204\345\275\225\357\274\232tcpdump\346\212\223\345\214\205\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" "b/doc/15.8 - \351\231\204\345\275\225\357\274\232tcpdump\346\212\223\345\214\205\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..cb11828 --- /dev/null +++ "b/doc/15.8 - \351\231\204\345\275\225\357\274\232tcpdump\346\212\223\345\214\205\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,38 @@ +#附录:tcpdump抓包工具的使用 + +在调试网络通信程序是tcpdump是必备工具。tcpdump很强大,可以看到网络通信的每个细节。如TCP,可以看到3次握手,PUSH/ACK数据推送,close4次挥手,全部细节。包括每一次网络收包的字节数,时间等。 + +最简单的一个使用示例: +----- +```shell +sudo tcpdump -i any tcp port 9501 +``` +* -i 参数制定了网卡,any表示所有网卡 +* tcp 指定仅监听TCP协议 +* port 制定监听的端口 + +> tcpdump需要root权限 +> 需要要看通信的数据内容,可以加 -Xnlps0 参数,其他更多参数请参见网上的文章 + +运行结果 +``` +13:29:07.788802 IP localhost.42333 > localhost.9501: Flags [S], seq 828582357, win 43690, options [mss 65495,sackOK,TS val 2207513 ecr 0,nop,wscale 7], length 0 +13:29:07.788815 IP localhost.9501 > localhost.42333: Flags [S.], seq 1242884615, ack 828582358, win 43690, options [mss 65495,sackOK,TS val 2207513 ecr 2207513,nop,wscale 7], length 0 +13:29:07.788830 IP localhost.42333 > localhost.9501: Flags [.], ack 1, win 342, options [nop,nop,TS val 2207513 ecr 2207513], length 0 +13:29:10.298686 IP localhost.42333 > localhost.9501: Flags [P.], seq 1:5, ack 1, win 342, options [nop,nop,TS val 2208141 ecr 2207513], length 4 +13:29:10.298708 IP localhost.9501 > localhost.42333: Flags [.], ack 5, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 0 +13:29:10.298795 IP localhost.9501 > localhost.42333: Flags [P.], seq 1:13, ack 5, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 12 +13:29:10.298803 IP localhost.42333 > localhost.9501: Flags [.], ack 13, win 342, options [nop,nop,TS val 2208141 ecr 2208141], length 0 +13:29:11.563361 IP localhost.42333 > localhost.9501: Flags [F.], seq 5, ack 13, win 342, options [nop,nop,TS val 2208457 ecr 2208141], length 0 +13:29:11.563450 IP localhost.9501 > localhost.42333: Flags [F.], seq 13, ack 6, win 342, options [nop,nop,TS val 2208457 ecr 2208457], length 0 +13:29:11.563473 IP localhost.42333 > localhost.9501: Flags [.], ack 14, win 342, options [nop,nop,TS val 2208457 ecr 2208457], length 0 +``` +* 13:29:11.563473 时间带有精确到微妙 +* localhost.42333 > localhost.9501 表示通信的流向,42333是客户端,9501是服务器端 +* [S] 表示这是一个SYN请求 +* [.] 表示这是一个ACK确认包,(client)SYN->(server)SYN->(client)ACK 就是3次握手过程 +* [P] 表示这个是一个数据推送,可以是从服务器端向客户端推送,也可以从客户端向服务器端推 +* [F] 表示这是一个FIN包,是关闭连接操作,client/server都有可能发起 +* [R] 表示这是一个RST包,与F包作用相同,但RST表示连接关闭时,仍然有数据未被处理。可以理解为是强制切断连接 +* win 342是指滑动窗口大小 +* length 12指数据包的大小 diff --git "a/doc/15.9 - \351\231\204\345\275\225\357\274\232strace\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" "b/doc/15.9 - \351\231\204\345\275\225\357\274\232strace\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..9cc7597 --- /dev/null +++ "b/doc/15.9 - \351\231\204\345\275\225\357\274\232strace\345\267\245\345\205\267\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,22 @@ +#附录:strace工具的使用 + +strace可以跟踪系统调用的执行情况,在程序发生问题后,可以用strace分析和跟踪问题。 +使用方法: + +> FreeBSD/MacOS下可以使用truss + +```shell +strace -o /tmp/strace.log -f -p $PID +``` +* -f 表示跟踪多线程和多进程,如果不加-f参数,无法抓取到子进程和子线程的运行情况 +* -o 表示将结果输出到一个文件中 +* -p $PID,指定跟踪的进程ID,通过ps aux可以看到 +* -tt 打印系统调用发生的时间,精确到微妙 +* -s 限定字符串打印的长度,如recvfrom系统调用收到的数据,默认只打印32字节 +* -c 实时统计每个系统调用的耗时 +* -T 打印每个系统调用的耗时 + + + + + diff --git a/doc/2 - Server.md b/doc/2 - Server.md new file mode 100644 index 0000000..aeccf5b --- /dev/null +++ b/doc/2 - Server.md @@ -0,0 +1,53 @@ +#Server + +创建一个异步服务器程序,支持TCP、UDP、UnixSocket 3种协议,支持IPv4和IPv6,支持SSL/TLS单向双向证书的隧道加密。使用者无需关注底层实现细节,仅需要设置网络事件的回调函数即可。 + +**请勿在使用`swoole_server`之前调用其他异步IO的API,否则将无法创建`swoole_server`。**可以在Server启动后,`onWorkerStart`回调函数中使用。 + +> swoole_server只能用于php-cli环境,否则会抛出致命错误 + +构建Server对象 +---- +```php +$serv = new swoole_server('0.0.0.0', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP); +``` + +设置运行时参数 +---- +```php +$serv->set(array( + 'worker_num' => 4, + 'daemonize' => true, + 'backlog' => 128, +)); +``` +注册事件回调函数 +---- +```php +$serv->on('Connect', 'my_onConnect'); +$serv->on('Receive', 'my_onReceive'); +$serv->on('Close', 'my_onClose'); +``` +PHP中可以使用[4种回调函数的风格](/wiki/page/458.html) + +启动服务器 +---- +```php +$serv->start(); +``` + +属性列表 +---- +```php +$serv->manager_pid; //管理进程的PID,通过向管理进程发送SIGUSR1信号可实现柔性重启 +$serv->master_pid; //主进程的PID,通过向主进程发送SIGTERM信号可安全关闭服务器 +$serv->connections; //当前服务器的客户端连接,可使用foreach遍历所有连接 +``` + +运行流程图 +----- +![Swoole扩展架构图](/static/uploads/swoole.jpg) + +进程/线程结构图 +----- +![Swoole进程/线程结构图](/static/image/process.jpg) diff --git "a/doc/2.1 - \345\207\275\346\225\260\345\210\227\350\241\250.md" "b/doc/2.1 - \345\207\275\346\225\260\345\210\227\350\241\250.md" new file mode 100644 index 0000000..2453562 --- /dev/null +++ "b/doc/2.1 - \345\207\275\346\225\260\345\210\227\350\241\250.md" @@ -0,0 +1,14 @@ +#函数列表 + +符号表示规则 +------------ +* `class->function` 对象方法 +* `class::function` 类静态方法 +* `class->$var` 对象属性 +* `class::$var` 类静态属性 + +> 上述规则仅用于文档编写,不代表真实的`PHP`语法 + +其他函数 +--- +与`swoole_server`无关的函数已迁移至 [其他 - 函数列表](/wiki/page/548.html) diff --git a/doc/2.1.1 - swoole_server::__construct.md b/doc/2.1.1 - swoole_server::__construct.md new file mode 100644 index 0000000..e20c8cf --- /dev/null +++ b/doc/2.1.1 - swoole_server::__construct.md @@ -0,0 +1,101 @@ +#swoole_server::__construct + +创建一个异步`Server`对象。 + +```php +$serv = new swoole_server(string $host, int $port = 0, int $mode = SWOOLE_PROCESS, + int $sock_type = SWOOLE_SOCK_TCP); +``` + +参数 +---- +* `$host`参数用来指定监听的ip地址,如`127.0.0.1`,或者外网地址,或者`0.0.0.0`监听全部地址 + * IPv4使用 `127.0.0.1`表示监听本机,`0.0.0.0`表示监听所有地址 + * IPv6使用`::1`表示监听本机,`::` (相当于`0:0:0:0:0:0:0:0`) 表示监听所有地址 +* `$port`监听的端口,如`9501` + - 如果`$sock_type`为`UnixSocket Stream/Dgram`,此参数将被忽略 + - 监听小于`1024`端口需要`root`权限 + - 如果此端口被占用`server->start`时会失败 +* `$mode`运行的模式 + - `SWOOLE_PROCESS`多进程模式(默认) + - `SWOOLE_BASE`基本模式 +* `$sock_type`指定`Socket`的类型,支持`TCP`、`UDP`、`TCP6`、`UDP6`、`UnixSocket Stream/Dgram` 6种 +* 使用`$sock_type | SWOOLE_SSL`可以启用`SSL`隧道加密。启用`SSL`后必须配置[ssl_key_file和ssl_cert_file](/wiki/page/318.html) +* `1.7.11`版本增加了对`Unix Socket`的支持,详细请参见 [/wiki/page/16.html](/wiki/page/16.html) +* 构造函数中的参数与`swoole_server::addlistener`中是完全相同的 +* 监听端口失败,在`1.9.16`以上版本会抛出异常,可以使用`try/catch`捕获异常,在`1.9.16`以下版本抛出致命错误 +* __高负载的服务器,请务必调整[Linux内核参数](/wiki/page/11)__ +* [ 3种Server运行模式介绍](/wiki/page/353.html) + +注意事项 +---- +* 底层有保护机制,一个`PHP`程序内只能创建启动一个`Server`实例 +* 如果要实现多个`Server`实例的管理,父进程必须使用`exec`,不得使用`fork` + +随机可用端口 +---- +swoole-1.9.6增加了随机监听可用端口的支持,`$port`参数可以设置为0,操作系统会随机分配一个可用的端口,进行监听。可以通过读取`$server->port`得到分配到的端口号。 + +```php +$http = new swoole_http_server("0.0.0.0"); + +$http->on('request', function ($request, $response) { + $response->header("Content-Type", "text/html; charset=utf-8"); + $response->end("

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

"); +}); + +$http->start(); +``` + +SYSTEMD监听端口 +---- +swoole-1.9.7增加了对`systemd socket`的支持。监听端口由`systemd`配置指定。 + +#### swoole.socket +```shell +[Unit] +Description=Swoole Socket + +[Socket] +ListenStream=9501 +Accept=false +[Install] +WantedBy = sockets.target +``` + +#### swoole.service +```shell +[Service] +Type=forking +PIDFile=/var/run/swoole.pid +ExecStart=/usr/bin/php /var/www/swoole/server.php +ExecStop=/bin/kill $MAINPID +ExecReload=/bin/kill -USR1 $MAINPID + +[Install] +WantedBy = multi-user.target +``` + +#### server.php +```php +$http = new swoole_http_server("systemd"); + +$http->set([ + 'daemonize' => true, + 'pid_file' => '/var/run/swoole.pid', +]); + +$http->on('request', function ($request, $response) { + $response->header("Content-Type", "text/html; charset=utf-8"); + $response->end("

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

"); +}); + +$http->start(); +``` + +#### 启动服务 +```shell +sudo systemctl enable swoole.socket +sudo systemctl start swoole.socket +sudo systemctl start swoole.service +``` \ No newline at end of file diff --git a/doc/2.1.10 - swoole_server->shutdown.md b/doc/2.1.10 - swoole_server->shutdown.md new file mode 100644 index 0000000..9b4a776 --- /dev/null +++ b/doc/2.1.10 - swoole_server->shutdown.md @@ -0,0 +1,12 @@ +#swoole_server->shutdown + +关闭服务器 +```php +void swoole_server->shutdown(); +``` + +此函数可以用在worker进程内。向主进程发送SIGTERM也可以实现关闭服务器。 + +```shell +kill -15 主进程PID +``` \ No newline at end of file diff --git a/doc/2.1.11 - swoole_server->tick.md b/doc/2.1.11 - swoole_server->tick.md new file mode 100644 index 0000000..e6f4c42 --- /dev/null +++ b/doc/2.1.11 - swoole_server->tick.md @@ -0,0 +1,36 @@ +#swoole_server->tick + +tick定时器,可以自定义回调函数。此函数是[swoole_timer_tick](/wiki/page/412.html)的别名。 + +> worker进程结束运行后,所有定时器都会自动销毁 +> tick/after定时器不能在swoole_server->start之前使用 + +在onReceive中使用 +-------------- + +```php +function onReceive($server, $fd, $from_id, $data) { + $server->tick(1000, function() use ($server, $fd) { + $server->send($fd, "hello world"); + }); +} +``` +在onWorkerStart中使用 +-------------- +* 低于`1.8.0`版本task进程不能使用`tick/after`定时器,所以需要使用$serv->taskworker进行判断 +* task进程可以使用`addtimer`间隔定时器 + +```php +function onWorkerStart(swoole_server $serv, $worker_id) +{ + if (!$serv->taskworker) { + $serv->tick(1000, function ($id) { + var_dump($id); + }); + } + else + { + $serv->addtimer(1000); + } +} +``` diff --git a/doc/2.1.12 - swoole_server->after.md b/doc/2.1.12 - swoole_server->after.md new file mode 100644 index 0000000..9b72010 --- /dev/null +++ b/doc/2.1.12 - swoole_server->after.md @@ -0,0 +1,19 @@ +#swoole_server->after + +在指定的时间后执行函数,需要swoole-1.7.7以上版本。 +```php +swoole_server->after(int $after_time_ms, mixed $callback_function); +``` +swoole_server::after函数是一个一次性定时器,执行完成后就会销毁。 + +* $after_time_ms 指定时间,单位为毫秒 +* $callback_function 时间到期后所执行的函数,必须是可以调用的。callback函数不接受任何参数 +* 低于`1.8.0`版本task进程不支持after定时器,仅支持`addtimer`定时器 + +> $after_time_ms 最大不得超过 86400000 +> 此方法是swoole_timer_after函数的别名 + +生命周期 +---- +* 定时器的生命周期是进程级的,当使用`reload`或`kill`重启关闭进程时,定时器会全部被销毁 +* 如果有某些定时器存在关键逻辑和数据,请在`onWorkerStop`回调函数中实现保存 diff --git a/doc/2.1.13 - swoole_server->defer.md b/doc/2.1.13 - swoole_server->defer.md new file mode 100644 index 0000000..8a7c27c --- /dev/null +++ b/doc/2.1.13 - swoole_server->defer.md @@ -0,0 +1,23 @@ +#swoole_server->defer + +延后执行一个PHP函数。Swoole底层会在`EventLoop`循环完成后执行此函数。此函数的目的是为了让一些PHP代码延后执行,程序优先处理IO事件。底层不保证`defer`的函数会立即执行,如果是系统关键逻辑,需要尽快执行,请使用`after`定时器实现。 + +```php +function swoole_server->defer(callable $callback); +``` + +* defer函数的别名是`swoole_event_defer` +* `$callback`为可执行的函数变量,可以是字符串、数组、匿名函数 +* 在`onWorkerStart`回调中执行`defer`时,必须要等到有事件发生才会回调 + +> defer函数在swoole-1.8.0或更高版本可用 + +使用实例 +---- +```php +function query($server, $db) { + $server->defer(function() use ($db) { + $db->close(); + }); +} +``` \ No newline at end of file diff --git a/doc/2.1.14 - swoole_server->clearTimer.md b/doc/2.1.14 - swoole_server->clearTimer.md new file mode 100644 index 0000000..a97c181 --- /dev/null +++ b/doc/2.1.14 - swoole_server->clearTimer.md @@ -0,0 +1,12 @@ +#swoole_server->clearTimer + +清除tick/after定时器,此函数是[swoole_timer_clear](/wiki/page/387.html)的别名。 + +使用示例: + +```php +$timer_id = $server->tick(1000, function ($id) use ($server) { + $server->clearTimer($id); +}); +``` + diff --git a/doc/2.1.15 - swoole_server->close.md b/doc/2.1.15 - swoole_server->close.md new file mode 100644 index 0000000..6c87f47 --- /dev/null +++ b/doc/2.1.15 - swoole_server->close.md @@ -0,0 +1,12 @@ +#swoole_server->close + +关闭客户端连接,函数原型: +```php +bool swoole_server->close(int $fd, bool $reset = false); +``` +> swoole-1.8.0或更高版本可以使用$reset方法 + +* 操作成功返回true,失败返回false. +* Server主动close连接,也一样会触发onClose事件。 +* 不要在close之后写清理逻辑。应当放置到onClose回调中处理 +* $reset设置为true会强制关闭连接,丢弃发送队列中的数据 diff --git a/doc/2.1.16 - swoole_server->send.md b/doc/2.1.16 - swoole_server->send.md new file mode 100644 index 0000000..b647648 --- /dev/null +++ b/doc/2.1.16 - swoole_server->send.md @@ -0,0 +1,31 @@ +#swoole_server->send + +向客户端发送数据,函数原型: +```php +bool swoole_server->send(int $fd, string $data, int $extraData = 0); +``` + +* `$data`,发送的数据,TCP协议最大不得超过`2M`,可修改 [buffer_output_size](/wiki/page/p-buffer_output_size.html) 改变允许发送的最大包长度 +* UDP协议不得超过`65507`,`UDP`包头占`8`字节, `IP`包头占`20`字节,`65535-28 = 65507` +* UDP服务器使用`$fd`保存客户端IP,`$extraData`保存`server_fd`和`port` +* 发送成功会返回`true` +* 发送失败会返回`false`,调用`$server->getLastError()`方法可以得到失败的错误码 + +TCP服务器 +----- +* `send`操作具有原子性,多个进程同时调用`send`向同一个`TCP`连接发送数据,不会发生数据混杂 +* 如果要发送超过`2M`的数据,可以将数据写入临时文件,然后通过`sendfile`接口进行发送 +* 通过设置 [buffer_output_size](/wiki/page/p-buffer_output_size.html) 参数可以修改发送长度的限制 +* 在发送超过`8K`的数据时,底层会启用`Worker`进程的共享内存,需要进行一次`Mutex->lock`操作 +* 当`Worker`进程的管道缓存区已满时,发送`8K`数据将启用临时文件存储 +* 不需要关心客户端的带宽,底层会自动监听可写,将数据逐步发送给客户端 +* 如果连续向同一个客户端发送大量数据,可能会导致`Socket`内存缓存区塞满,底层会立即返回`false`,应用程序可以调整`socket_buffer_size`设置,或 将数据保存到磁盘,等待客户端收完已发送的数据后再进行发送 + +> `TCP`客户端发送数据,不需要`$extraData`参数 + +UDP服务器 +----- +* `send`操作会直接在`Worker`进程内发送数据包,不会再经过主进程转发 +* 如果在`onReceive`后立即向客户端发送数据,可以不传`$extraData` +* 如果向其他`UDP`客户端发送数据,必须要传入`$extraData` +* 在外网服务中发送超过`64K`的数据会分成多个传输单元进行发送,如果其中一个单元丢包,会导致整个包被丢弃。所以外网服务,建议发送`1.5K`以下的数据包 diff --git a/doc/2.1.17 - swoole_server->sendfile.md b/doc/2.1.17 - swoole_server->sendfile.md new file mode 100644 index 0000000..5242374 --- /dev/null +++ b/doc/2.1.17 - swoole_server->sendfile.md @@ -0,0 +1,16 @@ +#swoole_server->sendfile + +发送文件到TCP客户端连接。使用示例: +```php +bool swoole_server->sendfile(int $fd, string $filename, int $offset =0, int $length = 0); +``` +sendfile函数调用OS提供的sendfile系统调用,由操作系统直接读取文件并写入socket。sendfile只有2次内存拷贝,使用此函数可以降低发送大量文件时操作系统的CPU和内存占用。 + +* `$filename` 要发送的文件路径,如果文件不存在会返回false +* `$offset` 指定文件偏移量,可以从文件的某个位置起发送数据。默认为0,表示从文件头部开始发送 +* `$length` 指定发送的长度,默认为文件尺寸。 +* 操作成功返回`true`,失败返回`false` + +> 此函数与`swoole_server->send`都是向客户端发送数据,不同的是sendfile的数据来自于指定的文件 +> `sendfile`在低于`1.9.17`版本中不能用于`SSL`客户端连接 +> `$length`和`$offset`在`1.9.11`版本后可用 \ No newline at end of file diff --git a/doc/2.1.18 - swoole_server->sendto.md b/doc/2.1.18 - swoole_server->sendto.md new file mode 100644 index 0000000..1e81e0f --- /dev/null +++ b/doc/2.1.18 - swoole_server->sendto.md @@ -0,0 +1,27 @@ +#swoole_server->sendto + +向任意的客户端IP:PORT发送UDP数据包。 + +函数原型: + +```php +bool swoole_server->sendto(string $ip, int $port, string $data, int $server_socket = -1); +``` +* $ip为IPv4字符串,如192.168.1.102。如果IP不合法会返回错误 +* $port为 1-65535的网络端口号,如果端口错误发送会失败 +* $data要发送的数据内容,可以是文本或者二进制内容 +* $server_socket 服务器可能会同时监听多个UDP端口,此参数可以指定使用哪个端口发送数据包 + +示例: + +```php +//向IP地址为220.181.57.216主机的9502端口发送一个hello world字符串。 +$server->sendto('220.181.57.216', 9502, "hello world"); +//向IPv6服务器发送UDP数据包 +$server->sendto('2600:3c00::f03c:91ff:fe73:e98f', 9501, "hello world"); +``` + +> swoole_server->sendto 在1.7.10+版本可用 +> server必须监听了UDP的端口,才可以使用swoole_server->sendto +> server必须监听了UDP6的端口,才可以使用swoole_server->sendto向IPv6地址发送数据 + diff --git a/doc/2.1.19 - swoole_server->sendwait.md b/doc/2.1.19 - swoole_server->sendwait.md new file mode 100644 index 0000000..a605b9a --- /dev/null +++ b/doc/2.1.19 - swoole_server->sendwait.md @@ -0,0 +1,14 @@ +#swoole_server->sendwait + +阻塞地向客户端发送数据。 + +有一些特殊的场景,`Server`需要连续向客户端发送数据,而`swoole_server->send`数据发送接口是纯异步的,大量数据发送会导致内存发送队列塞满。 + +使用`swoole_server->sendwait`就可以解决此问题,`swoole_server->sendwait`会阻塞等待连接可写。直到数据发送完毕才会返回。 + +```php +bool swoole_server->sendwait(int $fd, string $send_data); +``` + +> `sendwait`目前仅可用于`SWOOLE_BASE`模式 +> `sendwait`建议只用于本机或内网通信,外网连接请勿使用`sendwait` \ No newline at end of file diff --git a/doc/2.1.2 - swoole_server->set.md b/doc/2.1.2 - swoole_server->set.md new file mode 100644 index 0000000..e00da91 --- /dev/null +++ b/doc/2.1.2 - swoole_server->set.md @@ -0,0 +1,103 @@ +#swoole_server->set + +swoole_server->set函数用于设置swoole_server运行时的各项参数。服务器启动后通过$serv->setting来访问set函数设置的参数数组。 + +原型: +```php +function swoole_server->set(array $setting); +``` +示例: +```php +$serv->set(array( + 'reactor_num' => 2, //reactor thread num + 'worker_num' => 4, //worker process num + 'backlog' => 128, //listen backlog + 'max_request' => 50, + 'dispatch_mode' => 1, +)); +``` + +> swoole_server->set只能在swoole_server->start前调用 + +最大连接 +----- +max_conn => 10000, 此参数用来设置Server最大允许维持多少个tcp连接。超过此数量后,新进入的连接将被拒绝。 + +> 此参数不要调整的过大,根据机器内存的实际情况来设置。Swoole会根据此数值一次性分配一块大内存来保存Connection信息 + + +守护进程化 +---- +daemonize => 1,加入此参数后,执行php server.php将转入后台作为守护进程运行 + +reactor线程数 +---- +reactor_num => 2,通过此参数来调节poll线程的数量,以充分利用多核 + +> reactor_num和writer_num默认设置为CPU核数 +> 1.6.9之前版本请使用 poll_thread_num => 2来设置 + +worker进程数 +---- +worker_num => 4,设置启动的worker进程数量。swoole采用固定worker进程的模式。 +PHP代码中是全异步非阻塞,worker_num配置为CPU核数的1-4倍即可。如果是同步阻塞,worker_num配置为100或者更高,具体要看每次请求处理的耗时和操作系统负载状况。 + +> 当设定的worker进程数小于reactor线程数时,会自动调低reactor线程的数量 + +max_request +---- +max_request => 2000,此参数表示worker进程在处理完n次请求后结束运行。manager会重新创建一个worker进程。此选项用来防止worker进程内存溢出。 + +> PHP代码也可以使用memory_get_usage来检测进程的内存占用情况,发现接近memory_limit时,调用exit()退出进程。manager进程会回收此进程,然后重新启动一个新的Worker进程。 +> onConnect/onClose不增加计数 + +```shell +max_request => 0 +``` +设置为0表示不自动重启。在Worker进程中需要保存连接信息的服务,需要设置为0. + +Listen队列长度 +---- +backlog => 128,此参数将决定最多同时有多少个待accept的连接,swoole本身accept效率是很高的,基本上不会出现大量排队情况。 + +CPU亲和设置 +---- +open_cpu_affinity => 1 ,启用CPU亲和设置 + +TCP_NoDelay启用 +---- +open_tcp_nodelay => 1 ,启用tcp_nodelay + +TCP_DEFER_ACCEPT +---- +tcp_defer_accept => 5,此参数设定一个秒数,当客户端连接连接到服务器时,在约定秒数内并不会触发accept,直到有数据发送,或者超时时才会触发。 + +日志文件路径 +---- +log_file => '/data/log/swoole.log', 指定swoole错误日志文件。在swoole运行期发生的异常信息会记录到这个文件中。默认会打印到屏幕。 + +> 此配置仅在swoole-1.5.8以上版本中可用 + +数据buffer +---- +buffer主要是用于检测数据是否完整,如果不完整swoole会继续等待新的数据到来。直到收到完整的一个请求,才会一次性发送给worker进程。这时onReceive会收到一个超过SW_BUFFER_SIZE, +小于$serv->setting['package_max_length']的数据。 +目前仅提供了EOF检测、固定包头长度检测2种buffer模式。 + +open_eof_check => true打开buffer +package_eof => "\r\n\r\n" 设置EOF + +心跳检测机制 +----- +heartbeat_check_interval => 30 //每隔多少秒检测一次,单位秒,Swoole会轮询所有TCP连接,将超过心跳时间的连接关闭掉 +heartbeat_idle_time => 60 //TCP连接的最大闲置时间,单位s , 如果某fd最后一次发包距离现在的时间超过heartbeat_idle_time会把这个连接关闭。 +> 心跳检测特性需要swoole-1.6.10+ +> heartbeat_idle_time必须大于或等于heartbeat_check_interval + +worker进程数据包分配模式 +---- +dispatch_mode = 1 //1平均分配,2按FD取模固定分配,3抢占式分配,默认为取模(dispatch=2) + +> 抢占式分配,每次都是空闲的worker进程获得数据。很合适SOA/RPC类的内部服务框架 +> 当选择为dispatch=3抢占模式时,worker进程内发生onConnect/onReceive/onClose/onTimer会将worker进程标记为忙,不再接受新的请求。reactor会将新请求投递给其他状态为闲的worker进程 +> 如果希望每个连接的数据分配给固定的worker进程,dispatch_mode需要设置为2 \ No newline at end of file diff --git a/doc/2.1.20 - swoole_server->sendMessage.md b/doc/2.1.20 - swoole_server->sendMessage.md new file mode 100644 index 0000000..0a634a7 --- /dev/null +++ b/doc/2.1.20 - swoole_server->sendMessage.md @@ -0,0 +1,60 @@ +#swoole_server->sendMessage + +此函数可以向任意`worker`进程或者`task`进程发送消息。在非主进程和管理进程中可调用。收到消息的进程会触发`onPipeMessage`事件。 + +```php +bool swoole_server->sendMessage(mixed $message, int $dst_worker_id); +``` + +参数 +---- +* `$message`为发送的消息数据内容,没有长度限制,但超过`8K`时会启动内存临时文件 +* `$dst_worker_id`为目标进程的`ID`,范围是`0 ~ (worker_num + task_worker_num - 1)` +* 在`Task`进程内调用`sendMessage`是阻塞等待的,发送消息完成后返回 +* 在`Worker`进程内调用`sendMessage`是异步的,消息会先存到发送队列,可写时向管道发送此消息 +* 在`User`进程内调用`sendMessage`底层会自动判断当前的进程是异步还是同步选择不同的发送方式 + +返回值 +--- +发送成功返回`true`,失败返回`false` + +> `sendMessage`接口在`1.7.9`以上版本可用 +> `MacOS/FreeBSD`下超过`2K`就会使用临时文件存储 + +注意事项 +--- +* 使用`sendMessage`必须注册`onPipeMessage`事件回调函数 +* 设置了`task_ipc_mode = 3`将无法使用`sendMessage`向特定的`task`进程发送消息 + +实例 +---- +```php +$serv = new swoole_server("0.0.0.0", 9501); +$serv->set(array( + 'worker_num' => 2, + 'task_worker_num' => 2, +)); +$serv->on('pipeMessage', function($serv, $src_worker_id, $data) { + echo "#{$serv->worker_id} message from #$src_worker_id: $data\n"; +}); +$serv->on('task', function ($serv, $task_id, $from_id, $data){ + var_dump($task_id, $from_id, $data); +}); +$serv->on('finish', function ($serv, $fd, $from_id){ + +}); +$serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) { + if (trim($data) == 'task') + { + $serv->task("async task coming"); + } + else + { + $worker_id = 1 - $serv->worker_id; + $serv->sendMessage("hello task process", $worker_id); + } +}); + +$serv->start(); +``` + \ No newline at end of file diff --git a/doc/2.1.21 - swoole_server->exist.md b/doc/2.1.21 - swoole_server->exist.md new file mode 100644 index 0000000..7b85d58 --- /dev/null +++ b/doc/2.1.21 - swoole_server->exist.md @@ -0,0 +1,10 @@ +#swoole_server->exist + +检测fd对应的连接是否存在。 +```php +bool function swoole_server->exist(int $fd) +``` +* $fd对应的TCP连接存在返回true,不存在返回false + +> 此接口是基于共享内存计算,没有任何IO操作 +> swoole_server->exist在1.7.18以上版本可用 diff --git a/doc/2.1.22 - swoole_server->pause.md b/doc/2.1.22 - swoole_server->pause.md new file mode 100644 index 0000000..5077866 --- /dev/null +++ b/doc/2.1.22 - swoole_server->pause.md @@ -0,0 +1,12 @@ +#swoole_server->pause + +停止接收数据。 +```php +function swoole_server->pause(int $fd); +``` + +* $fd为连接的文件描述符 +* 调用此函数后会将连接从`EventLoop`中移除,不再接收客户端数据。 +* 此函数不影响发送队列的处理 + +> pause方法仅可用于BASE模式 \ No newline at end of file diff --git a/doc/2.1.23 - swoole_server->resume.md b/doc/2.1.23 - swoole_server->resume.md new file mode 100644 index 0000000..0fc17d7 --- /dev/null +++ b/doc/2.1.23 - swoole_server->resume.md @@ -0,0 +1,11 @@ +#swoole_server->resume + +恢复数据接收。与`pause`方法成对使用 +```php +function swoole_server->resume(int $fd); +``` + +* $fd为连接的文件描述符 +* 调用此函数后会将连接重新加入到EventLoop中,继续接收客户端数据 + +> resume方法仅可用于BASE模式 \ No newline at end of file diff --git a/doc/2.1.24 - swoole_server->getClientInfo.md b/doc/2.1.24 - swoole_server->getClientInfo.md new file mode 100644 index 0000000..06c3fda --- /dev/null +++ b/doc/2.1.24 - swoole_server->getClientInfo.md @@ -0,0 +1,49 @@ +#swoole_server->getClientInfo + + `swoole_server->getClientInfo`函数用来获取连接的信息,别名是`swoole_server->connection_info` + +```php +function swoole_server->getClientInfo(int $fd, int $extraData, bool $ignoreError = false) +``` + +* 如果传入的`$fd`存在,将会返回一个数组 +* 连接不存在或已关闭,返回`false` +* 第`3`个参数表示是否忽略错误,如果设置为`true`,即使连接关闭也会返回连接的信息 + +> `connect_time`, `last_time` 在`1.6.10`或更高版本可用 + +```php +$fdinfo = $serv->connection_info($fd); +var_dump($fdinfo); +array(5) { + ["reactor_id"]=> + int(3) + ["server_fd"]=> + int(14) + ["server_port"]=> + int(9501) + ["remote_port"]=> + int(19889) + ["remote_ip"]=> + string(9) "127.0.0.1" + ["connect_time"]=> + int(1390212495) + ["last_time"]=> + int(1390212760) +} + +$udp_client = $serv->connection_info($fd, $from_id); +var_dump($udp_client); +``` + +* __reactor_id__ 来自哪个`Reactor`线程 +* __server_fd__ 来自哪个监听端口`socket`,这里不是客户端连接的`fd` +* __server_port__ 来自哪个监听端口 +* __remote_port__ 客户端连接的端口 +* __remote_ip__ 客户端连接的`IP`地址 +* __connect_time__ 客户端连接到`Server`的时间,单位秒,由`master`进程设置 +* __last_time__ 最后一次收到数据的时间,单位秒,由`master`进程设置 +* __close_errno__ 连接关闭的错误码,如果连接异常关闭,close_errno的值是非零,可以参考[Linux错误信息列表](/wiki/page/172.html) +* __websocket_status__ [可选项] `WebSocket`连接状态,当服务器是`Swoole\WebSocket\Server`时会额外增加此项信息 +* __uid__ [可选项] 使用`bind`绑定了用户`ID`时会额外增加此项信息 +* __ssl_client_cert__ [可选项] 使用`SSL`隧道加密,并且客户端设置了证书时会额外添加此项信息 \ No newline at end of file diff --git a/doc/2.1.25 - swoole_server->getClientList.md b/doc/2.1.25 - swoole_server->getClientList.md new file mode 100644 index 0000000..e08fe38 --- /dev/null +++ b/doc/2.1.25 - swoole_server->getClientList.md @@ -0,0 +1,38 @@ +#swoole_server->getClientList + +用来遍历当前`Server`所有的客户端连接,`Server::getClientList`方法是基于共享内存的,不存在`IOWait`,遍历的速度很快。另外`getClientList`会返回所有`TCP`连接,而不仅仅是当前`Worker`进程的`TCP`连接。 + +> 推荐使用 [swoole_server::$connections](/wiki/page/427.html) 迭代器来遍历连接 +> `getClientList`的别名是`connection_list` +> `getClientList`仅可用于`TCP`客户端,`UDP`服务器需要自行保存客户端信息 +> `SWOOLE_BASE`模式下只能获取当前进程的连接 + +函数原型: +```php +swoole_server::getClientList(int $start_fd = 0, int $pagesize = 10); +``` + +此函数接受`2`个参数,第`1`个参数是起始`fd`,第`2`个参数是每页取多少条,最大不得超过`100`。 + +* 调用成功将返回一个数字索引数组,元素是取到的`$fd`。数组会按从小到大排序。最后一个`$fd`作为新的`start_fd`再次尝试获取 +* 调用失败返回`false` + +示例: +```php +$start_fd = 0; +while(true) +{ + $conn_list = $serv->getClientList($start_fd, 10); + if ($conn_list===false or count($conn_list) === 0) + { + echo "finish\n"; + break; + } + $start_fd = end($conn_list); + var_dump($conn_list); + foreach($conn_list as $fd) + { + $serv->send($fd, "broadcast"); + } +} +``` \ No newline at end of file diff --git a/doc/2.1.26 - swoole_server->bind.md b/doc/2.1.26 - swoole_server->bind.md new file mode 100644 index 0000000..b6390b7 --- /dev/null +++ b/doc/2.1.26 - swoole_server->bind.md @@ -0,0 +1,21 @@ +#swoole_server->bind + +将连接绑定一个用户定义的`UID`,可以设置`dispatch_mode=5`设置以此值进行`hash`固定分配。可以保证某一个`UID`的连接全部会分配到同一个`Worker`进程。 + +在默认的`dispatch_mode=2`设置下,`server`会按照`socket fd`来分配连接数据到不同的`Worker`进程。因为`fd`是不稳定的,一个客户端断开后重新连接,`fd`会发生改变。这样这个客户端的数据就会被分配到别的`Worker`。使用`bind`之后就可以按照用户定义的`UID`进行分配。即使断线重连,相同`UID`的`TCP`连接数据会被分配相同的`Worker`进程。 + +```php +bool swoole_server::bind(int $fd, int $uid) +``` +* `$fd` 连接的文件描述符 +* `$uid` 指定`UID` +* 未绑定`UID`时默认使用`fd`取模进行分配 + +> 同一个连接只能被`bind`一次,如果已经绑定了`UID`,再次调用`bind`会返回`false` +> 可以使用`$serv->connection_info($fd)` 查看连接所绑定`UID`的值 + +时序问题 +---- +客户端连接服务器后,连续发送多个包,可能会存在时序问题。在`bind`操作时,后续的包可能已经`dispatch`,这些数据包仍然会按照`fd`取模分配到当前进程。只有在`bind`之后新收到的数据包才会按照`UID`取模分配。 + +因此如果要使用`bind`机制,网络通信协议需要设计握手步骤。客户端连接成功后,先发一个握手请求,之后客户端不要发任何包。在服务器`bind`完后,并回应之后。客户端再发送新的请求。 \ No newline at end of file diff --git a/doc/2.1.27 - swoole_server->stats.md b/doc/2.1.27 - swoole_server->stats.md new file mode 100644 index 0000000..22fef45 --- /dev/null +++ b/doc/2.1.27 - swoole_server->stats.md @@ -0,0 +1,54 @@ +#swoole_server->stats + +得到当前Server的活动TCP连接数,启动时间,accpet/close的总次数等信息。 + +```php +array swoole_server->stats(); +``` + +返回的结果数组示例: +```php +array ( + 'start_time' => 1409831644, + 'connection_num' => 1, + 'accept_count' => 1, + 'close_count' => 0, +); +``` + +* start_time 服务器启动的时间 +* connection_num 当前连接的数量 +* accept_count 接受了多少个连接 +* close_count 关闭的连接数量 +* tasking_num 当前正在排队的任务数 + +> stats()方法在1.7.5+后可用 + +请求数量 +---- +* request_count => 1000, Server收到的请求次数 +* worker_request_count => 当前Worker进程收到的请求次数 + +消息队列状态 +---- +swoole-1.8.5版本增加了Task消息队列的统计数据。 + +```php +array ( + 'task_queue_num' => 10, + 'task_queue_bytes' => 65536, +); +``` + +* `task_queue_num` 消息队列中的Task数量 +* `task_queue_bytes` 消息队列的内存占用字节数 + +协程相关 +---- +```php +array ( + 'coroutine_num' => 10, +); +``` +* 当前协程数量`coroutine_num` + diff --git a/doc/2.1.28 - swoole_server->task.md b/doc/2.1.28 - swoole_server->task.md new file mode 100644 index 0000000..ca0da65 --- /dev/null +++ b/doc/2.1.28 - swoole_server->task.md @@ -0,0 +1,54 @@ +#swoole_server->task + +投递一个异步任务到`task_worker`池中。此函数是非阻塞的,执行完毕会立即返回。`Worker`进程可以继续处理新的请求。使用`Task`功能,必须先设置 [task_worker_num](/wiki/page/276.html),并且必须设置`Server`的`onTask`和`onFinish`事件回调函数。 + +```php +int swoole_server::task(mixed $data, int $dst_worker_id = -1) +$task_id = $serv->task("some data"); +//swoole-1.8.6或更高版本 +$serv->task("taskcallback", -1, function (swoole_server $serv, $task_id, $data) { + echo "Task Callback: "; + var_dump($task_id, $data); +}); +``` +参数 +---- +* `$data`要投递的任务数据,可以为除资源类型之外的任意PHP变量 +* `$dst_worker_id`可以制定要给投递给哪个task进程,传入ID即可,范围是`0 - (serv->task_worker_num -1)` + + +返回值 +---- +* 调用成功,返回值为整数`$task_id`,表示此任务的ID。如果有finish回应,`onFinish`回调中会携带`$task_id`参数 +* 调用失败,返回值为`false`,`$task_id`可能为`0`,因此必须使用`===`判断是否失败 + +说明 +---- +* 未指定目标`Task`进程,调用`task`方法会判断`Task`进程的忙闲状态,底层只会向处于空闲状态的`Task`进程投递任务。如果所有`Task`进程均处于忙的状态,底层会轮询投递任务到各个进程。可以使用 [server->stats](/wiki/page/288.html) 方法获取当前正在排队的任务数量。 +* 1.8.6版本增加了第三个参数,可以直接设置`onFinish`函数,如果任务设置了回调函数,Task返回结果时会直接执行指定的回调函数,不再执行Server的`onFinish`回调 + +> `$dst_worker_id`在`1.6.11+`后可用,默认为随机投递 +> `$task_id`是从`0-42亿`的整数,在当前进程内是唯一的 +> `task`方法不能在`task`进程/用户自定义进程中调用 + +此功能用于将慢速的任务异步地去执行,比如一个聊天室服务器,可以用它来进行发送广播。当任务完成时,在task进程中调用`$serv->finish("finish")`告诉worker进程此任务已完成。当然`swoole_server->finish`是可选的。 + +task底层使用Unix Socket管道通信,是全内存的,没有IO消耗。单进程读写性能可达100万/s,不同的进程使用不同的管道通信,可以最大化利用多核。 + +> AsyncTask功能在1.6.4版本增加,默认不启动task功能,需要在手工设置task_worker_num来启动此功能 +> task_worker的数量在swoole_server::set参数中调整,如task_worker_num => 64,表示启动64个进程来接收异步任务 + +配置参数 +----- +swoole_server->task/taskwait/finish 3个方法当传入的`$data`数据超过8K时会启用临时文件来保存。当临时文件内容超过 `server->package_max_length` 时底层会抛出一个警告。此警告不影响数据的投递,过大的Task可能会存在性能问题。 +```shell +WARN: task package is too big. +``` +> server->package_max_length 默认为2M + +注意事项 +------- +* 使用`task`必须为Server设置`onTask`和`onFinish`回调,否则`swoole_server->start`会失败 +* `task`操作的次数必须小于`onTask`处理速度,如果投递容量超过处理能力,task会塞满缓存区,导致worker进程发生阻塞。worker进程将无法接收新的请求 +* 使用`addProcess`添加的用户进程中无法使用`task`投递任务,请使用`sendMessage`接口与`Task`工作进程通信 + diff --git a/doc/2.1.29 - swoole_server->taskwait.md b/doc/2.1.29 - swoole_server->taskwait.md new file mode 100644 index 0000000..a7d394c --- /dev/null +++ b/doc/2.1.29 - swoole_server->taskwait.md @@ -0,0 +1,17 @@ +#swoole_server->taskwait + +函数原型: +```php +function Server->taskwait(mixed $data, float $timeout = 0.5, int $dstWorkerId = -1) : string | bool +``` +`taskwait`与`task`方法作用相同,用于投递一个异步的任务到`task`进程池去执行。与`task`不同的是`taskwait`是阻塞等待的,直到任务完成或者超时返回。 +`$result`为任务执行的结果,由`$serv->finish`函数发出。如果此任务超时,这里会返回`false`。 + +> `taskwait`是阻塞接口,如果你的Server是全异步的请使用`swoole_server::task`和`swoole_server::finish`,不要使用`taskwait` +> 第3个参数可以指定要给投递给哪个task进程,传入ID即可,范围是`0 - serv->task_worker_num` +> `$dstWorkerId`在`1.6.11`以上版本可用,可以指定目标Task进程的ID,默认为-1表示随机投递 +> `taskwait`方法不能在`task`进程中调用 + +特例 +---- +如果`onTask`中没有任何阻塞IO操作,底层仅有2次进程切换的开销,并不会产生IO等待,因此这种情况下 `taskwait` 可以视为非阻塞。实际测试`onTask`中仅读写PHP数组,进行10万次`taskwait`操作,总耗时仅为`1秒`,平均每次消耗为`10微秒` \ No newline at end of file diff --git a/doc/2.1.3 - swoole_server->on.md b/doc/2.1.3 - swoole_server->on.md new file mode 100644 index 0000000..4c21cd1 --- /dev/null +++ b/doc/2.1.3 - swoole_server->on.md @@ -0,0 +1,25 @@ +#swoole_server->on + +注册Server的事件回调函数。 +```php +bool swoole_server->on(string $event, mixed $callback); +``` +* 第1个参数是回调的名称, 大小写不敏感,具体内容参考回调函数列表,事件名称字符串不要加on +* 第2个函数是回调的PHP函数,可以是函数名的字符串,类静态方法,对象方法数组,匿名函数。 + +> 重复调用`on`方法时会覆盖上一次的设定 + +```php +$serv = new swoole_server("127.0.0.1", 9501); +$serv->on('connect', function ($serv, $fd){ + echo "Client:Connect.\n"; +}); +$serv->on('receive', function ($serv, $fd, $from_id, $data) { + $serv->send($fd, 'Swoole: '.$data); + $serv->close($fd); +}); +$serv->on('close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); +$serv->start(); +``` diff --git a/doc/2.1.30 - swoole_server->taskWaitMulti.md b/doc/2.1.30 - swoole_server->taskWaitMulti.md new file mode 100644 index 0000000..bbfa36c --- /dev/null +++ b/doc/2.1.30 - swoole_server->taskWaitMulti.md @@ -0,0 +1,37 @@ +#swoole_server->taskWaitMulti + +并发执行多个Task +```php +array swoole_server->taskWaitMulti(array $tasks, double $timeout = 0.5); +``` + +* $tasks 必须为数字索引数组,不支持关联索引数组,底层会遍历$tasks将任务逐个投递到Task进程 +* $timeout 为浮点型,单位为秒,默认为`0.5` +* 任务完成或超时,返回结果数组。结果数组中每个任务结果的顺序与`$tasks`对应,如:`$tasks[2]`对应的结果为`$result[2]` +* 某个任务执行超时不会影响其他任务,返回的结果数据中将不包含超时的任务 + +> `taskWaitMulti`接口在`1.8.8`或更高版本可用 +> 最大并发任务不得超过`1024` + +使用实例 +---- +```php +$tasks[] = mt_rand(1000, 9999); //任务1 +$tasks[] = mt_rand(1000, 9999); //任务2 +$tasks[] = mt_rand(1000, 9999); //任务3 +var_dump($tasks); + +//等待所有Task结果返回,超时为10s +$results = $serv->taskWaitMulti($tasks, 10.0); + +if (!isset($results[0])) { + echo "任务1执行超时了\n"; +} +if (isset($results[1])) { + echo "任务2的执行结果为{$results[1]}\n"; +} +if (isset($results[2])) { + echo "任务3的执行结果为{$results[2]}\n"; +} +``` + diff --git a/doc/2.1.31 - swoole_server->taskCo.md b/doc/2.1.31 - swoole_server->taskCo.md new file mode 100644 index 0000000..7959801 --- /dev/null +++ b/doc/2.1.31 - swoole_server->taskCo.md @@ -0,0 +1,51 @@ +#swoole_server->taskCo + +并发执行`Task`并进行协程调度。仅用于`2.0`版本。 + +```php +function swoole_server->taskCo(array $tasks, float $timeout = 0.5) : array; +``` + +* `$tasks`任务列表,必须为数组。底层会遍历数组,将每个元素作为`task`投递到`Task`进程池 +* `$timeout`超时时间,默认为`0.5`秒,当规定的时间内任务没有全部完成,立即中止并返回结果 +* 任务完成或超时,返回结果数组。结果数组中每个任务结果的顺序与`$tasks`对应,如:`$tasks[2]`对应的结果为`$result[2]` +* 某个任务执行失败或超时,对应的结果数组项为`false`,如:`$tasks[2]`失败了,那么`$result[2]`的值为`false` + +> 最大并发任务不得超过`1024` +> `taskCo`在`2.0.9`或更高版本可用 + +调度过程 +---- +* `$tasks`列表中的每个任务会随机投递到一个`Task`工作进程,投递完毕后,`yield`让出当前协程,并设置一个`$timeout`秒的定时器 +* 在`onFinish`中收集对应的任务结果,保存到结果数组中。判断是否所有任务都返回了结果,如果为否,继续等待。如果为是,进行`resume`恢复对应协程的运行,并清除超时定时器 +* 在规定的时间内任务没有全部完成,定时器先触发,底层清除等待状态。将未完成的任务结果标记为`false`,立即`resume`对应协程 + +使用示例 +---- +```php +set([ + 'worker_num' => 1, + 'task_worker_num' => 2, +]); + +$server->on('Task', function (swoole_server $serv, $task_id, $worker_id, $data) { + echo "#{$serv->worker_id}\tonTask: worker_id={$worker_id}, task_id=$task_id\n"; + if ($serv->worker_id == 1) { + sleep(1); + } + return $data; +}); + +$server->on('Request', function ($request, $response) use ($server) +{ + $tasks[0] = "hello world"; + $tasks[1] = ['data' => 1234, 'code' => 200]; + $result = $server->taskCo($tasks, 0.5); + $response->end('Test End, Result: '.var_export($result, true)); +}); + +$server->start(); +``` \ No newline at end of file diff --git a/doc/2.1.32 - swoole_server->finish.md b/doc/2.1.32 - swoole_server->finish.md new file mode 100644 index 0000000..3663793 --- /dev/null +++ b/doc/2.1.32 - swoole_server->finish.md @@ -0,0 +1,13 @@ +#swoole_server->finish + +此函数用于在task进程中通知worker进程,投递的任务已完成。此函数可以传递结果数据给worker进程。 +```php +$serv->finish("response"); +``` +__使用swoole_server::finish函数必须为Server设置onFinish回调函数。此函数只可用于task进程的onTask回调中__ + +* finish方法可以连续多次调用,Worker进程会多次触发`onFinish`事件 +* 在`onTask`回调函数中调用过`finish`方法后,return数据依然会触发`onFinish`事件 + +> swoole_server::finish是可选的。如果worker进程不关心任务执行的结果,不需要调用此函数 +> 在onTask回调函数中return字符串,等同于调用finish diff --git a/doc/2.1.33 - swoole_server->heartbeat.md b/doc/2.1.33 - swoole_server->heartbeat.md new file mode 100644 index 0000000..e64c232 --- /dev/null +++ b/doc/2.1.33 - swoole_server->heartbeat.md @@ -0,0 +1,20 @@ +#swoole_server->heartbeat + +检测服务器所有连接,并找出已经超过约定时间的连接。如果指定if_close_connection,则自动关闭超时的连接。未指定仅返回连接的fd数组。 + +> 需要swoole-1.6.10 以上版本 + +函数原型: +```php +array swoole_server::heartbeat(bool $if_close_connection = true); +``` +* $if_close_connection是否关闭超时的连接,默认为true +* 调用成功将返回一个连续数组,元素是已关闭的$fd。 +* 调用失败返回false + +> $if_close_connection 在1.7.4+可用 + +示例: +```php +$closeFdArr = $serv->heartbeat(); +``` diff --git a/doc/2.1.34 - swoole_server->getLastError.md b/doc/2.1.34 - swoole_server->getLastError.md new file mode 100644 index 0000000..d47a23e --- /dev/null +++ b/doc/2.1.34 - swoole_server->getLastError.md @@ -0,0 +1,19 @@ +#swoole_server->getLastError + +获取最近一次操作错误的错误码。业务代码中可以根据错误码类型执行不同的逻辑。 +```php +function swoole_server->getLastError() +``` + +* 返回一个整型数字错误码 + +常见发送失败错误 +---- +* 1001 连接已经被Server端关闭了,出现这个错误一般是代码中已经执行了`$serv->close()`关闭了某个连接,但仍然调用`$serv->send()`向这个连接发送数据 +* 1002 连接已被Client端关闭了,Socket已关闭无法发送数据到对端 +* 1003 正在执行close,`onClose`回调函数中不得使用`$serv->send()` +* 1004 连接已关闭 +* 1005 连接不存在,传入`$fd` 可能是错误的 +* 1007 接收到了超时的数据,TCP关闭连接后,可能会有部分数据残留在管道缓存区内,这部分数据会被丢弃 +* 1008 发送缓存区已满无法执行`send`操作,出现这个错误表示这个连接的对端无法及时收数据导致发送缓存区已塞满 +* 1202 发送的数据超过了 [server->buffer_output_size](/wiki/page/440.html) 设置 diff --git a/doc/2.1.35 - swoole_server->getSocket.md b/doc/2.1.35 - swoole_server->getSocket.md new file mode 100644 index 0000000..b9165c7 --- /dev/null +++ b/doc/2.1.35 - swoole_server->getSocket.md @@ -0,0 +1,56 @@ +#swoole_server->getSocket + +调用此方法可以得到底层的socket句柄,返回的对象为`sockets`资源句柄。 + +> 此方法需要依赖PHP的`sockets`扩展,并且编译swoole时需要开启`--enable-sockets`选项 + +使用`socket_set_option`函数可以设置更底层的一些socket参数。 + +```php +$socket = $server->getSocket(); +if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { + echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL; +} +``` + +监听端口 +---- +使用`listen`方法增加的端口,可以使用`Swoole\Server\Port`对象提供的`getSocket`方法。 + +```php +$port = $server->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); +$socket = $port->getSocket(); +``` + +支持组播 +---- +使用`socket_set_option`设置`MCAST_JOIN_GROUP`参数可以将Socket加入组播,监听网络组播数据包。 + +```php +$server = new swoole_server('0.0.0.0', 9905, SWOOLE_BASE, SWOOLE_SOCK_UDP); +$server->set(['worker_num' => 1]); +$socket = $server->getSocket(); + +$ret = socket_set_option( + $socket, + IPPROTO_IP, + MCAST_JOIN_GROUP, + array('group' => '224.10.20.30', 'interface' => 'eth0') +); + +if ($ret === false) +{ + throw new RuntimeException('Unable to join multicast group'); +} + +$server->on('Packet', function (swoole_server $serv, $data, $addr) +{ + $serv->sendto($addr['address'], $addr['port'], "Swoole: $data"); + var_dump( $addr, strlen($data)); +}); + +$server->start(); +``` + +* `group` 表示组播地址 +* `interface` 表示网络接口的名称,可以为数字或字符串,如`eth0`、`wlan0` \ No newline at end of file diff --git a/doc/2.1.36 - swoole_server->protect.md b/doc/2.1.36 - swoole_server->protect.md new file mode 100644 index 0000000..1a17ea6 --- /dev/null +++ b/doc/2.1.36 - swoole_server->protect.md @@ -0,0 +1,10 @@ +#swoole_server->protect + +设置客户端连接为保护状态,不被心跳线程切断。 +```php +function swoole_server->protect(int $fd, bool $value = 1); +``` + +* `$fd` 要设置保护状态的客户端连接`fd` +* `$value` 设置的状态,`true`表示保护状态,`false`表示不保护 + diff --git a/doc/2.1.37 - swoole_server->confirm.md b/doc/2.1.37 - swoole_server->confirm.md new file mode 100644 index 0000000..fb139c6 --- /dev/null +++ b/doc/2.1.37 - swoole_server->confirm.md @@ -0,0 +1,10 @@ +#swoole_server->confirm + +确认连接,与`enable_delay_receive`或`wait_for_bind `配合使用。当客户端建立连接后,并不监听可读事件。仅触发`onConnect`事件回调,在`onConnect`回调中执行`confirm`确认连接,这时服务器才会监听可读事件,接收来自客户端连接的数据。 + +```php +function swoole_server->confirm(int $fd); +``` +* `$fd` 连接的唯一标识符 +* 确认成功返回`true`, +* `$fd`对应的连接不存在、已关闭或已经处于监听状态时,返回`false`,确认失败 \ No newline at end of file diff --git a/doc/2.1.4 - swoole_server->addListener.md b/doc/2.1.4 - swoole_server->addListener.md new file mode 100644 index 0000000..0744d1b --- /dev/null +++ b/doc/2.1.4 - swoole_server->addListener.md @@ -0,0 +1,59 @@ +#swoole_server->addListener + +Swoole提供了swoole_server::addListener来增加监听的端口。业务代码中可以通过调用swoole_server::connection_info来获取某个连接来自于哪个端口。 + +函数原型: +```php +function swoole_server->addListener(string $host, int $port, $type = SWOOLE_SOCK_TCP); +``` + +* 监听1024以下的端口需要root权限 +* 1.8.0版本增加了多端口监听的功能,监听成功后会返回`Swoole\Server\Port`对象 +* 在此对象上可以设置另外的事件回调函数和运行参数 +* 监听失败返回`false`,可调用`getLastError`方法获取错误码 +* 主服务器是WebSocket或Http协议,新监听的TCP端口默认会继承主Server的协议设置。必须单独调用set方法设置新的协议才会启用新协议 [查看详细说明](https://wiki.swoole.com/wiki/page/525.html "连接") + +swoole支持的Socket类型 +---- +* __SWOOLE_TCP__/__SWOOLE_SOCK_TCP__ tcp ipv4 socket +* __SWOOLE_TCP6__/__SWOOLE_SOCK_TCP6__ tcp ipv6 socket +* __SWOOLE_UDP__/__SWOOLE_SOCK_UDP__ udp ipv4 socket +* __SWOOLE_UDP6__/__SWOOLE_SOCK_UDP6__ udp ipv6 socket +* __SWOOLE_UNIX_DGRAM__ unix socket dgram +* __SWOOLE_UNIX_STREAM__ unix socket stream + +> Unix Socket仅在1.7.1+后可用,此模式下$host参数必须填写可访问的文件路径,$port参数忽略 +> Unix Socket模式下,客户端$fd将不再是数字,而是一个文件路径的字符串 +> SWOOLE_TCP等是1.7.0+后提供的简写方式,与1.7.0前的SWOOLE_SOCK_TCP是等同的 + +您可以混合使用UDP/TCP,同时监听内网和外网端口。 +示例: +```php +$serv->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP); +$serv->addlistener("192.168.1.100", 9503, SWOOLE_SOCK_TCP); +$serv->addlistener("0.0.0.0", 9504, SWOOLE_SOCK_UDP); +//UnixSocket Stream +$serv->addlistener("/var/run/myserv.sock", 0, SWOOLE_UNIX_STREAM); +//TCP + SSL +$serv->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP | SWOOLE_SSL); +``` + +IPv4与IPv6 +---- +* IPv4使用 127.0.0.1表示监听本机,0.0.0.0表示监听所有地址 +* IPv6使用::1表示监听本机,:: (0:0:0:0:0:0:0:0) 表示监听所有地址 +* **Linux系统下监听IPv6端口后使用IPv4地址也可以进行连接** + +随机监听端口 +---- +swoole-1.9.6增加了随机监听端口的特性,`$port`参数可以设置为0,操作系统会随机分配一个可用的端口进行监听。可以读取`$listen_port->port`获取分配到的端口号。 + +```php +$port = $serv->addListener("0.0.0.0", 0, SWOOLE_SOCK_TCP); +echo $port->port; +``` + + + + + diff --git a/doc/2.1.5 - swoole_server->addProcess.md b/doc/2.1.5 - swoole_server->addProcess.md new file mode 100644 index 0000000..653b7a9 --- /dev/null +++ b/doc/2.1.5 - swoole_server->addProcess.md @@ -0,0 +1,51 @@ +#swoole_server->addProcess + +添加一个用户自定义的工作进程。此函数通常用于创建一个特殊的工作进程,用于监控、上报或者其他特殊的任务。 + +```php +bool swoole_server->addProcess(swoole_process $process); +``` + +> 此函数在`swoole-1.7.9`以上版本可用 + +参数 +---- +* `$process` 为`swoole_process`对象,注意不需要执行`start`。在`swoole_server`启动时会自动创建进程,并执行指定的子进程函数 +* 创建的子进程可以调用`$server`对象提供的各个方法,如`connection_list`/`connection_info`/`stats` +* 在`worker/task`进程中可以调用`$process`提供的方法与子进程进行通信 +* 在用户自定义进程中可以调用`$server->sendMessage`与`worker/task`进程通信 + +返回值 +---- +添加成功返回`true`,失败返回`false` + +注意事项 +---- +* 自定义进程会托管到`Manager`进程,如果发生致命错误,`Manager`进程会重新创建一个 +* 自定义进程不受`reload`指令控制,`reload`时不会向用户进程发送任何信息 +* 在`shutdown`关闭服务器时,会向自定义进程发送`SIGTERM`信号 +* 自定义进程内不能使用`swoole_server->task/taskwait`接口 + +示例程序 +----- +```php +$server = new swoole_server('127.0.0.1', 9501); + +$process = new swoole_process(function($process) use ($server) { + while (true) { + $msg = $process->read(); + foreach($server->connections as $conn) { + $server->send($conn, $msg); + } + } +}); + +$server->addProcess($process); + +$server->on('receive', function ($serv, $fd, $from_id, $data) use ($process) { + //群发收到的消息 + $process->write($data); +}); + +$server->start(); +``` diff --git a/doc/2.1.6 - swoole_server->listen.md b/doc/2.1.6 - swoole_server->listen.md new file mode 100644 index 0000000..55cee9a --- /dev/null +++ b/doc/2.1.6 - swoole_server->listen.md @@ -0,0 +1,8 @@ +#swoole_server->listen + +监听一个新的Server端口,此方法是[addlistener](/wiki/page/16.html)的别名。 +```php +bool swoole_server->listen(string $host, int $port, int $type); +``` + +> listen方法在swoole-1.7.9以上版本可用 \ No newline at end of file diff --git a/doc/2.1.7 - swoole_server->start.md b/doc/2.1.7 - swoole_server->start.md new file mode 100644 index 0000000..570964b --- /dev/null +++ b/doc/2.1.7 - swoole_server->start.md @@ -0,0 +1,43 @@ +#swoole_server->start + +启动server,监听所有TCP/UDP端口,函数原型: +```php +bool swoole_server->start() +``` +* 启动成功后会创建worker_num+2个进程。`Master`进程+`Manager`进程+`serv->worker_num`个`Worker`进程。 +* 启动失败会立即返回`false` +* 启动成功后将进入事件循环,等待客户端连接请求。`start`方法之后的代码不会执行 +* 服务器关闭后,`start`函数返回`true`,并继续向下执行 + +> 设置了`task_worker_num`会增加相应数量的`Task`进程 +> 函数列表中start之前的方法仅可在start调用前使用,在`start`之后的方法仅可在`onWorkerStart`、`onReceive`等事件回调函数中使用 + + +###主进程## +主进程内有多个Reactor线程,基于epoll/kqueue进行网络事件轮询。收到数据后转发到worker进程去处理 + +###Manager进程### +对所有worker进程进行管理,worker进程生命周期结束或者发生异常时自动回收,并创建新的worker进程 + +###worker进程### +对收到的数据进行处理,包括协议解析和响应请求。 + +------------------------------- + +启动失败扩展内会抛出致命错误,请检查php error_log的相关信息。errno={number}是标准的Linux Errno,可参考相关文档。 +如果开启了log_file设置,信息会打印到指定的Log文件中。 + +如果想要在开机启动时,自动运行你的Server,可以在/etc/rc.local文件中加入 +``` +/usr/bin/php /data/webroot/www.swoole.com/server.php +``` + +常见的错误有: +----- +* bind端口失败,原因是其他进程已占用了此端口 +* 未设置必选回调函数,启动失败 +* php有代码致命错误,请检查php的错误信息php_err.log +* 执行ulimit -c unlimited,打开core dump,查看是否有段错误 +* 关闭daemonize,关闭log,使错误信息可以打印到屏幕 + + diff --git a/doc/2.1.8 - swoole_server->reload.md b/doc/2.1.8 - swoole_server->reload.md new file mode 100644 index 0000000..276d060 --- /dev/null +++ b/doc/2.1.8 - swoole_server->reload.md @@ -0,0 +1,61 @@ +#swoole_server->reload + +重启所有worker进程。 +```php +bool swoole_server->reload(bool $only_reload_taskworkrer = false) +``` +* `$only_reload_taskworkrer` 是否仅重启task进程 + +一台繁忙的后端服务器随时都在处理请求,如果管理员通过kill进程方式来终止/重启服务器程序,可能导致刚好代码执行到一半终止。 + +这种情况下会产生数据的不一致。如交易系统中,支付逻辑的下一段是发货,假设在支付逻辑之后进程被终止了。会导致用户支付了货币,但并没有发货,后果非常严重。 + +Swoole提供了柔性终止/重启的机制,管理员只需要向SwooleServer发送特定的信号,Server的worker进程可以安全的结束。 + +* `SIGTERM`: 向主进程/管理进程发送此信号服务器将安全终止 +* 在PHP代码中可以调用`$serv->shutdown()`完成此操作 +* `SIGUSR1`: 向主进程/管理进程发送`SIGUSR1`信号,将平稳地restart所有worker进程 +* 在PHP代码中可以调用`$serv->reload()`完成此操作 +* swoole的reload有保护机制,当一次reload正在进行时,收到新的重启信号会丢弃 +* 如果设置了user/group,Worker进程可能没有权限向master进程发送信息,这种情况下必须使用root账户,在shell中执行kill指令进行重启 +* reload指令对`addProcess`添加的用户进程无效 + +```shell +#重启所有worker进程 +kill -USR1 主进程PID +``` + +1.7.7版本增加了仅重启task_worker的功能。只需向服务器发送SIGUSR2即可。 +```shell +#仅重启task进程 +kill -USR2 主进程PID +``` + +> 平滑重启只对onWorkerStart或onReceive等在Worker进程中include/require的PHP文件有效,Server启动前就已经include/require的PHP文件,不能通过平滑重启重新加载 +> 对于Server的配置即$serv->set()中传入的参数设置,必须关闭/重启整个Server才可以重新加载 +> Server可以监听一个内网端口,然后可以接收远程的控制命令,去重启所有worker + +Process模式 +---- +在`Process`模式下,来自客户端的TCP连接是在`Master`进程内维持的,`worker`进程的重启和异常退出,不会影响连接本身。 + +Reload有效范围 +---- +Reload操作只能重新载入Worker进程启动后加载的PHP文件,建议使用`get_included_files`函数来列出哪些文件是在`WorkerStart`之前就加载的PHP文件,在此列表中的PHP文件,即使进行了reload操作也无法重新载入。比如要关闭服务器重新启动才能生效。 + +```php +$serv->on('WorkerStart', function($serv, $workerId) { + var_dump(get_included_files()); //此数组中的文件表示进程启动前就加载了,所以无法reload +}); +``` + +APC/OpCache +---- +如果PHP开启了APC/OpCache,reload重载入时会受到影响,有2种解决方案 + +* 打开APC/OpCache的stat检测,如果发现文件更新APC/OpCache会自动更新OpCode +* 在onWorkerStart中执行apc_clear_cache或opcache_reset刷新OpCode缓存 + +参考 +---- +* [附录:Linux信号列表](/wiki/page/158.html) diff --git a/doc/2.1.9 - swoole_server->stop.md b/doc/2.1.9 - swoole_server->stop.md new file mode 100644 index 0000000..2895bb3 --- /dev/null +++ b/doc/2.1.9 - swoole_server->stop.md @@ -0,0 +1,19 @@ +#swoole_server->stop + +使当前worker进程停止运行,并立即触发`onWorkerStop`回调函数。 +```php +function swoole_server->stop(int $worker_id = -1, bool $waitEvent = false); +``` + +* 使用此函数代替`exit/die`结束Worker进程的生命周期 +* `$waitEvent`可以控制退出策略,默认为`false`表示立即退出,设置为`true`表示等待事件循环为空时再退出 +* 如果要结束其他Worker进程,可以在`stop`里面加上`worker_id`作为参数或者使用`swoole_process::kill($worker_pid)` + +> 此方法在`1.8.2`或更高版本可用 +> `$waitEvent`在`1.9.19`或更高版本可用 + +异步退出 +---- +异步服务器在调用`stop`退出进程时,可能仍然有事件在等待。比如使用了`Swoole\MySQL->query`,发送了`SQL`语句,但还在等待`MySQL`服务器返回结果。这时如果进程强制退出,`SQL`的执行结果就会丢失了。 + +设置`$waitEvent = true`后,底层会使用异步安全重启策略。先通知`Manager`进程,重新启动一个新的`Worker`来处理新的请求。当前旧的`Worker`会等待事件,直到事件循环为空或者超过`max_wait_time`后,退出进程,最大限度的保证异步事件的安全性。 \ No newline at end of file diff --git "a/doc/2.2 - \345\261\236\346\200\247\345\210\227\350\241\250.md" "b/doc/2.2 - \345\261\236\346\200\247\345\210\227\350\241\250.md" new file mode 100644 index 0000000..016c1f7 --- /dev/null +++ "b/doc/2.2 - \345\261\236\346\200\247\345\210\227\350\241\250.md" @@ -0,0 +1,2 @@ +#属性列表 + diff --git a/doc/2.2.1 - swoole_server::$setting.md b/doc/2.2.1 - swoole_server::$setting.md new file mode 100644 index 0000000..7de308d --- /dev/null +++ b/doc/2.2.1 - swoole_server::$setting.md @@ -0,0 +1,14 @@ +#swoole_server::$setting + +swoole_server::set()函数所设置的参数会保存到swoole_server::$setting属性上。在回调函数中可以访问运行参数的值。 + +> 在swoole-1.6.11+可用 + +示例: +```php +$serv = new swoole_server('127.0.0.1', 9501); +$serv->set(array('worker_num' => 4)); + +echo $serv->setting['worker_num']; + +``` diff --git a/doc/2.2.2 - swoole_server::$master_pid.md b/doc/2.2.2 - swoole_server::$master_pid.md new file mode 100644 index 0000000..67a9c63 --- /dev/null +++ b/doc/2.2.2 - swoole_server::$master_pid.md @@ -0,0 +1,5 @@ +#swoole_server::$master_pid + +返回当前服务器主进程的PID。 + +> 只能在onStart/onWorkerStart之后获取到 \ No newline at end of file diff --git a/doc/2.2.3 - swoole_server::$manager_pid.md b/doc/2.2.3 - swoole_server::$manager_pid.md new file mode 100644 index 0000000..192e41a --- /dev/null +++ b/doc/2.2.3 - swoole_server::$manager_pid.md @@ -0,0 +1,5 @@ +#swoole_server::$manager_pid + +返回当前服务器管理进程的PID。 + +> 只能在onStart/onWorkerStart之后获取到 \ No newline at end of file diff --git a/doc/2.2.4 - swoole_server::$worker_id.md b/doc/2.2.4 - swoole_server::$worker_id.md new file mode 100644 index 0000000..762b03d --- /dev/null +++ b/doc/2.2.4 - swoole_server::$worker_id.md @@ -0,0 +1,14 @@ +#swoole_server::$worker_id + +得到当前Worker进程的编号,包括Task进程。 + +```php +int $server->worker_id; +``` + +这个属性与`onWorkerStart`时的`$worker_id`是相同的。 + +* `Worker`进程编号范围是`[0, $serv->setting['worker_num'])` +* `Task`进程编号范围是`[$serv->setting['worker_num'], $serv->setting['worker_num'] + $serv->setting['task_worker_num'])` + +> 工作进程重启后`worker_id`的值是不变的 \ No newline at end of file diff --git a/doc/2.2.5 - swoole_server::$worker_pid.md b/doc/2.2.5 - swoole_server::$worker_pid.md new file mode 100644 index 0000000..107201f --- /dev/null +++ b/doc/2.2.5 - swoole_server::$worker_pid.md @@ -0,0 +1,7 @@ +#swoole_server::$worker_pid + +得到当前Worker进程的操作系统进程ID。与posix_getpid()的返回值相同。 + +```php +int $serv->worker_pid; +``` \ No newline at end of file diff --git a/doc/2.2.6 - swoole_server::$taskworker.md b/doc/2.2.6 - swoole_server::$taskworker.md new file mode 100644 index 0000000..b983edc --- /dev/null +++ b/doc/2.2.6 - swoole_server::$taskworker.md @@ -0,0 +1,8 @@ +#swoole_server::$taskworker + +布尔类型 + +* true表示当前的进程是Task工作进程 +* false表示当前的进程是Worker进程 + +> 此属性在swoole-1.7.15以上版本可用 \ No newline at end of file diff --git a/doc/2.2.7 - swoole_server::$connections.md b/doc/2.2.7 - swoole_server::$connections.md new file mode 100644 index 0000000..3df816d --- /dev/null +++ b/doc/2.2.7 - swoole_server::$connections.md @@ -0,0 +1,22 @@ +#swoole_server::$connections + + `TCP`连接迭代器,可以使用`foreach`遍历服务器当前所有的连接,此属性的功能与`swoole_server->connnection_list`是一致的,但是更加友好。遍历的元素为单个连接的fd。 + +注意`$connections`属性是一个迭代器对象,不是`PHP`数组,所以不能用`var_dump`或者数组下标来访问,只能通过`foreach`进行遍历操作。 + +```php +foreach($server->connections as $fd) +{ + $server->send($fd, "hello"); +} + +echo "当前服务器共有 ".count($server->connections). " 个连接\n"; +``` + +> 此属性在1.7.16以上版本可用 +> 连接迭代器依赖pcre库(不是PHP的pcre扩展),未安装pcre库无法使用此功能 +> pcre库的安装方法, + +Base 模式 +---- +`SWOOLE_BASE`模式下不支持跨进程操作`TCP`连接,因此在`BASE`模式中,只能在当前进程内使用`$connections`迭代器。 diff --git a/doc/2.2.8 - swoole_server::$ports.md b/doc/2.2.8 - swoole_server::$ports.md new file mode 100644 index 0000000..0ce80aa --- /dev/null +++ b/doc/2.2.8 - swoole_server::$ports.md @@ -0,0 +1,12 @@ +#swoole_server::$ports + +监听端口数组,如果服务器监听了多个端口可以遍历`swoole_server::$ports`得到所有`Swoole\Server\Port`对象。 +其中`swoole_server::$ports[0]`为构造方法所设置的主服务器端口。 + +```php +$ports = swoole_server::$ports; +$ports[0]->set($settings); +$ports[1]->on("Receive", function () { + //callback +}); +``` \ No newline at end of file diff --git "a/doc/2.3 - \351\205\215\347\275\256\351\200\211\351\241\271.md" "b/doc/2.3 - \351\205\215\347\275\256\351\200\211\351\241\271.md" new file mode 100644 index 0000000..1bfa20e --- /dev/null +++ "b/doc/2.3 - \351\205\215\347\275\256\351\200\211\351\241\271.md" @@ -0,0 +1,13 @@ +#配置选项 + + `swoole_server::set`函数用于设置`swoole_server`运行时的各项参数。本节所有的子页面均为配置数组的元素。 + +示例: +```php +$serv->set(array( + 'worker_num' => 4, //worker process num + 'backlog' => 128, //listen backlog + 'max_request' => 50, + 'dispatch_mode'=>1, +)); +``` \ No newline at end of file diff --git a/doc/2.3.1 - reactor_num.md b/doc/2.3.1 - reactor_num.md new file mode 100644 index 0000000..9dae31a --- /dev/null +++ b/doc/2.3.1 - reactor_num.md @@ -0,0 +1,10 @@ +#reactor_num + + `Reactor`线程数,`reactor_num => 2`,通过此参数来调节主进程内事件处理线程的数量,以充分利用多核。默认会启用`CPU`核数相同的数量。 + +`reactor_num`一般设置为`CPU`核数的`1-4`倍,在swoole中`reactor_num`最大不得超过`CPU核数*4`。 + +`swoole`的`Reactor`线程是可以利用多核,如:机器有`128`核,那么底层会启动`128`线程。每个线程能都会维持一个`EventLoop`。线程之间是无锁的,指令可以被`128`核`CPU`并行执行。考虑到操作系统调度存在一定程度的性能损失,可以设置为`CPU核数*2`,以便最大化利用`CPU`的每一个核。 + +> `reactor_num`必须小于或等于`worker_num`。如果设置的`reactor_num`大于`worker_num`,那么swoole会自动调整使`reactor_num`等于`worker_num` +> `1.7.14`以上版本在超过`8`核的机器上`reactor_num`默认设置为`8` diff --git a/doc/2.3.10 - dispatch_func.md b/doc/2.3.10 - dispatch_func.md new file mode 100644 index 0000000..9e4fa89 --- /dev/null +++ b/doc/2.3.10 - dispatch_func.md @@ -0,0 +1,62 @@ +#dispatch_func + +设置`dispatch`函数,`swoole`底层了内置了`5`种`dispatch_mode`,如果仍然无法满足需求。可以使用编写`C++`函数或`PHP`函数,实现`dispatch`逻辑。使用方法: +```php +$serv->set(array( + 'dispatch_func' => 'my_dispatch_function', +)); +``` + +* 设置`dispatch_func`后底层会自动忽略`dispatch_mode`配置 +* `dispatch_func`对应的函数不存在,底层将抛出致命错误 +* 如果需要`dispatch`一个超过`8K`的包,`dispatch_func`只能获取到 `0-8180` 字节的内容 + +> `dispatch_func`在`1.9.7`或更高版本可用 +> `dispatch_func`在`1.9.18`或更高版本可以设置为`PHP`函数 +> `dispatch_func`仅在`SWOOLE_PROCESS`模式下有效,`UDP/TCP/UnixSocket`均有效 + +编写PHP函数 +---- +由于`ZendVM`无法支持多线程环境,即使设置了多个`Reactor`线程,同一时间只能执行一个`dispatch_func`。因此底层在执行此`PHP`函数时会进行加锁操作,可能会存在锁的争抢问题。请勿在`dispatch_func`中执行任何阻塞操作,否则会导致`Reactor`线程组停止工作。 + +```php +$serv->set(array( + 'dispatch_func' => function ($serv, $fd, $type, $data) { + var_dump($fd, $type, $data); + return intval($data[0]); + }, +)); +``` + +* `$fd`为客户端连接的唯一标识符,可使用`Server::getClientInfo`获取连接信息 +* `$type`数据的类型,`0`表示来自客户端的数据发送,`4`表示客户端连接关闭,`5`表示客户端连接建立 +* `$data`数据内容,需要注意:如果启用了`Http`、`EOF`、`Length`等协议处理参数后,底层会进行包的拼接。但在`dispatch_func`函数中只能传入数据包的前`8K`内容,不能得到完整的包内容。 +* 必须返回一个`[0-serv->worker_num)`的数字,表示数据包投递的目标工作进程ID +* 小于`0`或大于等于`serv->worker_num`为异常目标ID,`dispatch`的数据将会被丢弃 + +编写C++函数 +---- +在其他PHP扩展中,使用`swoole_add_function`注册长度函数到`Swoole`引擎中。 + +> C++函数调用时底层不会加锁,需要调用方自行保证线程安全性 + +```cpp +int dispatch_function(swServer *serv, swConnection *conn, swEventData *data); + +int dispatch_function(swServer *serv, swConnection *conn, swEventData *data) +{ + printf("cpp, type=%d, size=%d\n", data->info.type, data->info.len); + return data->info.len % serv->worker_num; +} + +int register_dispatch_function(swModule *module) +{ + swoole_add_function("my_dispatch_function", (void *) dispatch_function); +} +``` + +* `dispatch`函数必须返回投递的目标worker进程id +* 返回的`worker_id`不得超过`serv->worker_num`,否则底层会抛出段错误 +* 返回负数(`return -1`)表示丢弃此数据包 +* `data`可以读取到事件的类型和长度 +* `conn`是连接的信息,如果是`UDP`数据包,`conn`为`NULL` diff --git a/doc/2.3.11 - message_queue_key.md b/doc/2.3.11 - message_queue_key.md new file mode 100644 index 0000000..aa2dc1f --- /dev/null +++ b/doc/2.3.11 - message_queue_key.md @@ -0,0 +1,10 @@ +#message_queue_key + +设置消息队列的KEY,仅在`task_ipc_mode = 2/3`时使用。设置的Key仅作为Task任务队列的KEY,此参数的默认值为`ftok($php_script_file, 1)` + +task队列在server结束后不会销毁,重新启动程序后,task进程仍然会接着处理队列中的任务。如果不希望程序重新启动后不执行旧的Task任务。可以手工删除此消息队列。 + +```php +ipcs -q +ipcrm -Q [msgkey] +``` diff --git a/doc/2.3.12 - daemonize.md b/doc/2.3.12 - daemonize.md new file mode 100644 index 0000000..db01c31 --- /dev/null +++ b/doc/2.3.12 - daemonize.md @@ -0,0 +1,15 @@ +#daemonize + +守护进程化。设置`daemonize => 1`时,程序将转入后台作为守护进程运行。长时间运行的服务器端程序必须启用此项。 + +如果不启用守护进程,当ssh终端退出后,程序将被终止运行。 + +* 启用守护进程后,标准输入和输出会被重定向到 `log_file` +* 如果未设置`log_file`,将重定向到 `/dev/null`,所有打印屏幕的信息都会被丢弃 +* 启用守护进程后,`CWD`(当前目录)环境变量的值会发生变更,相对路径的文件读写会出错。PHP程序中必须使用绝对路径 + +systemd +---- +使用`systemd`管理`Swoole`服务时,请勿设置`daemonize = 1`。主要原因是`systemd`的机制与`init`不同。`init`进程的`PID`为`1`,程序使用`daemonize`后,会脱离终端,最终被`init`进程托管,与`init`关系变为父子进程关系。 + +但`systemd`是启动了一个单独的后台进程,自行`fork`管理其他服务进程,因此不需要`daemonize`,反而使用了`daemonize = 1`会使得`Swoole`程序与该管理进程失去父子进程关系。 \ No newline at end of file diff --git a/doc/2.3.13 - backlog.md b/doc/2.3.13 - backlog.md new file mode 100644 index 0000000..cb4c2b9 --- /dev/null +++ b/doc/2.3.13 - backlog.md @@ -0,0 +1,16 @@ +#backlog + +Listen队列长度,如backlog => 128,此参数将决定最多同时有多少个等待accept的连接。 + + +关于tcp的backlog +----- +我们知道tcp有三次握手的过程,`客户端syn=>服务端syn+ack=>客户端ack`,当服务器收到客户端的ack后会将连接放到一个叫做accept queue的队列里面(注1),队列的大小由backlog参数和配置`somaxconn` 的最小值决定,我们可以通过` ss -lt `命令查看最终的accept queue队列大小,swoole的主进程调用accept(注2)从accept queue里面取走。 + 当accept queue满了之后连接有可能成功(注4),也有可能失败,失败后客户端的表现就是连接被重置(注3)或者连接超时,而服务端会记录失败的记录,可以通过 `netstat -s|grep 'times the listen queue of a socket overflowed'`来查看日志。如果出现了上述现象,你就应该调大该值了。 + 幸运的是swoole与php-fpm/apache等软件不同,并不依赖backlog来解决连接排队的问题。所以基本不会遇到上述现象。 + + +* 注1:linux2.2之后握手过程分为syn queue和accept queue两个队列, syn queue长度由`tcp_max_syn_backlog `决定。 +* 注2:高版本内核调用的是accept4,为了节省一次set no block系统调用。 +* 注3:客户端收到syn+ack包就认为连接成功了,实际上服务端还处于半连接状态,有可能发送rst包给客户端,客户端的表现就是`Connection reset by peer`。 +* 注4:成功是通过tcp的重传机制,相关的配置有`tcp_synack_retries`和`tcp_abort_on_overflow`。 \ No newline at end of file diff --git a/doc/2.3.14 - log_file.md b/doc/2.3.14 - log_file.md new file mode 100644 index 0000000..d9f98c1 --- /dev/null +++ b/doc/2.3.14 - log_file.md @@ -0,0 +1,27 @@ +#log_file + +log_file => '/data/log/swoole.log', 指定swoole错误日志文件。在swoole运行期发生的异常信息会记录到这个文件中。默认会打印到屏幕。 + +注意log_file不会自动切分文件,所以需要定期清理此文件。观察log_file的输出,可以得到服务器的各类异常信息和警告。 + +log_file中的日志仅仅是做运行时错误记录,没有长久存储的必要。 + +> 开启守护进程模式后(daemonize => true),标准输出将会被重定向到log_file。在PHP代码中echo/var_dump/print等打印到屏幕的内容会写入到log_file文件 + +日志标号 +----- +在日志信息中,进程ID前会加一些标号,表示日志产生的线程/进程类型。 + +* `#` Master进程 +* `$` Manager进程 +* `*` Worker进程 +* `^` Task进程 + +重新打开日志文件 +---- +在服务器程序运行期间日志文件被`mv`移动或`unlink`删除后,日志信息将无法正常写入,这时可以向Server发送`SIGRTMIN`信号实现重新打开日志文件。 + +* 在1.8.10或更高版本可用 +* 仅支持`Linux`平台 + + diff --git a/doc/2.3.15 - log_level.md b/doc/2.3.15 - log_level.md new file mode 100644 index 0000000..25a1b8f --- /dev/null +++ b/doc/2.3.15 - log_level.md @@ -0,0 +1,20 @@ +#log_level + +设置`swoole_server`错误日志打印的等级,范围是0-5。低于`log_level`设置的日志信息不会抛出。 + +```php +$serv->set(array( + 'log_level' => 1, +)); +``` + +级别对应 +----- +* 0 =>DEBUG +* 1 =>TRACE +* 2 =>INFO +* 3 =>NOTICE +* 4 =>WARNING +* 5 =>ERROR + +*默认是0 也就是所有级别都打印 \ No newline at end of file diff --git a/doc/2.3.16 - heartbeat_check_interval.md b/doc/2.3.16 - heartbeat_check_interval.md new file mode 100644 index 0000000..289e896 --- /dev/null +++ b/doc/2.3.16 - heartbeat_check_interval.md @@ -0,0 +1,7 @@ +#heartbeat_check_interval + +启用心跳检测,此选项表示每隔多久轮循一次,单位为秒。如 heartbeat_check_interval => 60,表示每60秒,遍历所有连接,如果该连接在60秒内,没有向服务器发送任何数据,此连接将被强制关闭。 + +swoole_server并不会主动向客户端发送心跳包,而是被动等待客户端发送心跳。服务器端的heartbeat_check仅仅是检测连接上一次发送数据的时间,如果超过限制,将切断连接。 + +> heartbeat_check仅支持TCP连接 diff --git a/doc/2.3.17 - heartbeat_idle_time.md b/doc/2.3.17 - heartbeat_idle_time.md new file mode 100644 index 0000000..c10853e --- /dev/null +++ b/doc/2.3.17 - heartbeat_idle_time.md @@ -0,0 +1,14 @@ +#heartbeat_idle_time + +与heartbeat_check_interval配合使用。表示连接最大允许空闲的时间。如 +```php +array( + 'heartbeat_idle_time' => 600, + 'heartbeat_check_interval' => 60, +); +``` + +* 表示每60秒遍历一次,一个连接如果600秒内未向服务器发送任何数据,此连接将被强制关闭 +* 启用`heartbeat_idle_time`后,服务器并不会主动向客户端发送数据包 +* 如果只设置了`heartbeat_idle_time`未设置`heartbeat_check_interval`底层将不会创建心跳检测线程,PHP代码中可以调用`heartbeat`方法手工处理超时的连接 + diff --git a/doc/2.3.18 - open_eof_check.md b/doc/2.3.18 - open_eof_check.md new file mode 100644 index 0000000..31d2ea4 --- /dev/null +++ b/doc/2.3.18 - open_eof_check.md @@ -0,0 +1,16 @@ +#open_eof_check + +打开EOF检测,此选项将检测客户端连接发来的数据,当数据包结尾是指定的字符串时才会投递给Worker进程。否则会一直拼接数据包,直到超过缓存区或者超时才会中止。当出错时swoole底层会认为是恶意连接,丢弃数据并强制关闭连接。 + +```php +array( +'open_eof_check' => true, //打开EOF检测 +'package_eof' => "\r\n", //设置EOF +) +``` + +常见的Memcache/SMTP/POP等协议都是以\r\n结束的,就可以使用此配置。开启后可以保证Worker进程一次性总是收到一个或者多个完整的数据包。 + +> EOF检测不会从数据中间查找eof字符串,所以Worker进程可能会同时收到多个数据包,需要在应用层代码中自行explode("\r\n", $data) 来拆分数据包 +> 1.7.15版本增加了open_eof_split,支持从数据中查找EOF,并切分数据 + diff --git a/doc/2.3.19 - open_eof_split.md b/doc/2.3.19 - open_eof_split.md new file mode 100644 index 0000000..64e1f00 --- /dev/null +++ b/doc/2.3.19 - open_eof_split.md @@ -0,0 +1,22 @@ +#open_eof_split + +启用EOF自动分包。当设置`open_eof_check`后,底层检测数据是否以特定的字符串结尾来进行数据缓冲。但默认只截取收到数据的末尾部分做对比。这时候可能会产生多条数据合并在一个包内。 + +启用`open_eof_split`参数后,底层会从数据包中间查找EOF,并拆分数据包。onReceive每次仅收到一个以EOF字串结尾的数据包。 + +启用`open_eof_split`参数后,无论参数`open_eof_check`是否设置,`open_eof_split`都将生效。 + +> open_eof_split在1.7.15以上版本可用 + +与 `open_eof_check` 的差异 +---- +* `open_eof_check` 只检查接收数据的末尾是否为 `EOF`,因此它的性能最好,几乎没有消耗 +* `open_eof_check` 无法解决多个数据包合并的问题,比如同时发送两条带有 `EOF` 的数据,底层可能会一次全部返回 +* `open_eof_split` 会从左到右对数据进行逐字节对比,查找数据中的 `EOF` 进行分包,性能较差。但是每次只会返回一个数据包 + +```php +array( + 'open_eof_split' => true, //打开EOF_SPLIT检测 + 'package_eof' => "\r\n", //设置EOF +) +``` diff --git a/doc/2.3.2 - worker_num.md b/doc/2.3.2 - worker_num.md new file mode 100644 index 0000000..423cbd2 --- /dev/null +++ b/doc/2.3.2 - worker_num.md @@ -0,0 +1,10 @@ +#worker_num + +设置启动的worker进程数。 + +* 业务代码是全异步非阻塞的,这里设置为CPU的`1-4`倍最合理 +* 业务代码为同步阻塞,需要根据请求响应时间和系统负载来调整 + +比如`1`个请求耗时`100ms`,要提供`1000QPS`的处理能力,那必须配置`100`个进程或更多。但开的进程越多,占用的内存就会大大增加,而且进程间切换的开销就会越来越大。所以这里适当即可。不要配置过大。 + +* 每个进程占用`40M`内存,那`100`个进程就需要占用`4G`内存 diff --git a/doc/2.3.20 - package_eof.md b/doc/2.3.20 - package_eof.md new file mode 100644 index 0000000..b090fd6 --- /dev/null +++ b/doc/2.3.20 - package_eof.md @@ -0,0 +1,5 @@ +#package_eof + +与 `open_eof_check` 或者 `open_eof_split` 配合使用,设置EOF字符串。 + +> package_eof最大只允许传入8个字节的字符串 \ No newline at end of file diff --git a/doc/2.3.21 - open_length_check.md b/doc/2.3.21 - open_length_check.md new file mode 100644 index 0000000..7331139 --- /dev/null +++ b/doc/2.3.21 - open_length_check.md @@ -0,0 +1,44 @@ +#open_length_check + +打开包长检测特性。包长检测提供了固定包头+包体这种格式协议的解析。启用后,可以保证`Worker`进程`onReceive`每次都会收到一个完整的数据包。 + +长度协议提供了`3`个选项来控制协议细节。 + + +package_length_type +---- +包头中某个字段作为包长度的值,底层支持了`10`种长度类型。请参考 [package_length_type](/wiki/page/463.html) + +package_body_offset +----- +从第几个字节开始计算长度,一般有2种情况: + +* `length`的值包含了整个包(包头+包体),`package_body_offset` 为`0` +* 包头长度为`N`字节,`length`的值不包含包头,仅包含包体,`package_body_offset`设置为`N` + +package_length_offset +---- +`length`长度值在包头的第几个字节。 + +### 示例:### +```c +struct +{ + uint32_t type; + uint32_t uid; + uint32_t length; + uint32_t serid; + char body[0]; +} +``` +以上通信协议的设计中,包头长度为`4`个整型,`16`字节,`length`长度值在第`3`个整型处。因此`package_length_offset`设置为`8`,`0-3`字节为`type`,`4-7`字节为`uid`,`8-11`字节为`length`,`12-15`字节为`serid`。 + +```php +$server->set(array( + 'open_length_check' => true, + 'package_max_length' => 81920, + 'package_length_type' => 'N', + 'package_length_offset' => 8, + 'package_body_offset' => 16, +)); +``` diff --git a/doc/2.3.22 - package_length_type.md b/doc/2.3.22 - package_length_type.md new file mode 100644 index 0000000..263b281 --- /dev/null +++ b/doc/2.3.22 - package_length_type.md @@ -0,0 +1,14 @@ +#package_length_type + +长度值的类型,接受一个字符参数,与`php`的 [pack](http://php.net/manual/zh/function.pack.php) 函数一致。目前`Swoole`支持`10`种类型: + +* `c`:有符号、`1`字节 +* `C`:无符号、`1`字节 +* `s` :有符号、主机字节序、`2`字节 +* `S`:无符号、主机字节序、`2`字节 +* `n`:无符号、网络字节序、`2`字节 +* `N`:无符号、网络字节序、`4`字节 +* `l`:有符号、主机字节序、`4`字节(小写`L`) +* `L`:无符号、主机字节序、`4`字节(大写`L`) +* `v`:无符号、小端字节序、`2`字节 +* `V`:无符号、小端字节序、`4`字节 diff --git a/doc/2.3.23 - package_length_func.md b/doc/2.3.23 - package_length_func.md new file mode 100644 index 0000000..9f8854e --- /dev/null +++ b/doc/2.3.23 - package_length_func.md @@ -0,0 +1,72 @@ +#package_length_func + +设置长度解析函数,支持C++或PHP的2种类型的函数。长度函数必须返回一个整数。 + +* 返回`0`,数据不足,需要接收更多数据 +* 返回`-1`,数据错误,底层会自动关闭连接 +* 返回包长度值(包括包头和包体的总长度),底层会自动将包拼好后返回给回调函数 + +默认底层最大会读取`8K`的数据,如果包头的长度较小可能会存在内存复制的消耗。可设置`package_body_offset`参数,底层只读取包头进行长度解析。 + +PHP长度解析函数 +---- +由于`ZendVM`不支持运行在多线程环境,因此底层会自动使用`Mutex`互斥锁对`PHP`长度函数进行加锁,避免并发执行`PHP`函数。在`1.9.3`或更高版本可用。 + +> 请勿在长度解析函数中执行阻塞`IO`操作,可能导致所有`Reactor`线程发生阻塞 + +```php +$serv = new swoole_server("127.0.0.1", 9501); + +$serv->set(array( + 'open_length_check' => true, + 'dispatch_mode' => 1, + 'package_length_func' => function ($data) { + if (strlen($data) < 8) { + return 0; + } + $length = intval(trim(substr($data, 0, 8))); + if ($length <= 0) { + return -1; + } + return $length + 8; + }, + 'package_max_length' => 2000000, //协议最大长度 +)); + +$serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) +{ + var_dump($data); + echo "#{$serv->worker_id}>> received length=" . strlen($data) . "\n"; +}); + +$serv->start(); +``` + +C++长度解析函数 +---- +在其他PHP扩展中,使用`swoole_add_function`注册长度函数到`Swoole`引擎中。 + +> C++长度函数调用时底层不会加锁,需要调用方自行保证线程安全性 + +### 实例: +```php +#include +#include +#include "swoole.h" + +using namespace std; + +int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length); + +void register_length_function(void) +{ + swoole_add_function((char *) "test_get_length", (void *) test_get_length); + return SW_OK; +} + +int test_get_length(swProtocol *protocol, swConnection *conn, char *data, uint32_t length) +{ + printf("cpp, size=%d\n", length); + return 100; +} +``` \ No newline at end of file diff --git a/doc/2.3.24 - package_max_length.md b/doc/2.3.24 - package_max_length.md new file mode 100644 index 0000000..858e998 --- /dev/null +++ b/doc/2.3.24 - package_max_length.md @@ -0,0 +1,12 @@ +#package_max_length + +设置最大数据包尺寸,单位为**字节**。开启open_length_check/open_eof_check/open_http_protocol等协议解析后。swoole底层会进行数据包拼接。这时在数据包未收取完整时,所有数据都是保存在内存中的。 + +所以需要设定`package_max_length`,一个数据包最大允许占用的内存尺寸。如果同时有1万个TCP连接在发送数据,每个数据包2M,那么最极限的情况下,就会占用20G的内存空间。 + +* `open_length_check`,当发现包长度超过`package_max_length`,将直接丢弃此数据,并关闭连接,不会占用任何内存。包括`websocket`、`mqtt`、`http2`协议。 +* `open_eof_check`,因为无法事先得知数据包长度,所以收到的数据还是会保存到内存中,持续增长。当发现内存占用已超过`package_max_length`时,将直接丢弃此数据,并关闭连接 +* `open_http_protocol`,GET请求最大允许8K,而且无法修改配置。POST请求会检测Content-Length,如果`Content-Length`超过`package_max_length`,将直接丢弃此数据,发送`http 400`错误,并关闭连接 + +> 此参数不宜设置过大,否则会占用很大的内存 + diff --git a/doc/2.3.25 - open_cpu_affinity.md b/doc/2.3.25 - open_cpu_affinity.md new file mode 100644 index 0000000..5b408fd --- /dev/null +++ b/doc/2.3.25 - open_cpu_affinity.md @@ -0,0 +1,17 @@ +#open_cpu_affinity + +启用CPU亲和性设置。在多核的硬件平台中,启用此特性会将swoole的reactor线程/worker进程绑定到固定的一个核上。可以避免进程/线程的运行时在多个核之间互相切换,提高CPU Cache的命中率。 + +使用taskset命令查看进程的CPU亲和设置: +```shell +taskset -p 进程ID +pid 24666's current affinity mask: f +pid 24901's current affinity mask: 8 +``` + +mask是一个掩码数字,按bit计算每bit对应一个CPU核,如果某一位为0表示绑定此核,进程会被调度到此CPU上,为0表示进程不会被调度到此CPU。 + +示例中pid为24666的进程mask = f 表示未绑定到CPU,操作系统会将此进程调度到任意一个CPU核上。 +pid为24901的进程mask = 8,8转为二进制是 1000,表示此进程绑定在第4个CPU核上。 + +> 仅推荐在全异步非阻塞的Server程序中启用 \ No newline at end of file diff --git a/doc/2.3.26 - cpu_affinity_ignore.md b/doc/2.3.26 - cpu_affinity_ignore.md new file mode 100644 index 0000000..8c5b369 --- /dev/null +++ b/doc/2.3.26 - cpu_affinity_ignore.md @@ -0,0 +1,46 @@ +#cpu_affinity_ignore + +IO密集型程序中,所有网络中断都是用CPU0来处理,如果网络IO很重,CPU0负载过高会导致网络中断无法及时处理,那网络收发包的能力就会下降。 + +如果不设置此选项,swoole将会使用全部CPU核,底层根据reactor_id或worker_id与CPU核数取模来设置CPU绑定。 + +> 如果内核与网卡有多队列特性,网络中断会分布到多核,可以缓解网络中断的压力 +> 此选项必须与open_cpu_affinity同时设置才会生效 + +```php +array('cpu_affinity_ignore' => array(0, 1)) +``` + +接受一个数组作为参数,array(0, 1) 表示不使用CPU0,CPU1,专门空出来处理网络中断。 + +查看网络中断 +---- +```shell +[~]$ cat /proc/interrupts + CPU0 CPU1 CPU2 CPU3 + 0: 1383283707 0 0 0 IO-APIC-edge timer + 1: 3 0 0 0 IO-APIC-edge i8042 + 3: 11 0 0 0 IO-APIC-edge serial + 8: 1 0 0 0 IO-APIC-edge rtc + 9: 0 0 0 0 IO-APIC-level acpi + 12: 4 0 0 0 IO-APIC-edge i8042 + 14: 25 0 0 0 IO-APIC-edge ide0 + 82: 85 0 0 0 IO-APIC-level uhci_hcd:usb5 + 90: 96 0 0 0 IO-APIC-level uhci_hcd:usb6 +114: 1067499 0 0 0 PCI-MSI-X cciss0 +130: 96508322 0 0 0 PCI-MSI eth0 +138: 384295 0 0 0 PCI-MSI eth1 +169: 0 0 0 0 IO-APIC-level ehci_hcd:usb1, uhci_hcd:usb2 +177: 0 0 0 0 IO-APIC-level uhci_hcd:usb3 +185: 0 0 0 0 IO-APIC-level uhci_hcd:usb4 +NMI: 11370 6399 6845 6300 +LOC: 1383174675 1383278112 1383174810 1383277705 +ERR: 0 +MIS: 0 +``` + +eth0/eth1就是网络中断的次数,如果CPU0 - CPU3 是平均分布的,证明网卡有多队列特性。如果全部集中于某一个核,说明网络中断全部由此CPU进行处理,一旦此CPU超过100%,系统将无法处理网络请求。这时就需要使用 cpu_affinity_ignore 设置将此CPU空出,专门用于处理网络中断。 + +如图上的情况,应当设置 cpu_affinity_ignore => array(0) + +> 可以使用`top`指令 -> 输入 1,查看到每个核的使用率 diff --git a/doc/2.3.27 - open_tcp_nodelay.md b/doc/2.3.27 - open_tcp_nodelay.md new file mode 100644 index 0000000..92b68ad --- /dev/null +++ b/doc/2.3.27 - open_tcp_nodelay.md @@ -0,0 +1,3 @@ +#open_tcp_nodelay + +启用open_tcp_nodelay,开启后TCP连接发送数据时会关闭Nagle合并算法,立即发往客户端连接。在某些场景下,如http服务器,可以提升响应速度。 diff --git a/doc/2.3.28 - tcp_defer_accept.md b/doc/2.3.28 - tcp_defer_accept.md new file mode 100644 index 0000000..0eb1921 --- /dev/null +++ b/doc/2.3.28 - tcp_defer_accept.md @@ -0,0 +1,13 @@ +#tcp_defer_accept + +启用tcp_defer_accept特性,可以设置为一个数值,表示当一个TCP连接有数据发送时才触发accept。 +``` +tcp_defer_accept => 5 +``` +启用tcp_defer_accept特性后,accept和onConnect对应的时间会发生变化。如果设置为5秒: + +* 客户端连接到服务器后不会立即触发accept +* 在5秒内客户端发送数据,此时会同时顺序触发accept/onConnect/onReceive +* 在5秒内客户端没有发送任何数据,此时会触发accept/onConnect + +> tcp_defer_accept的可以提高Accept操作的效率 diff --git a/doc/2.3.29 - ssl_cert_file.md b/doc/2.3.29 - ssl_cert_file.md new file mode 100644 index 0000000..4767c45 --- /dev/null +++ b/doc/2.3.29 - ssl_cert_file.md @@ -0,0 +1,30 @@ +#ssl_cert_file + +设置SSL隧道加密,设置值为一个文件名字符串,制定cert证书和key私钥的路径。 + +* https应用浏览器必须信任证书才能浏览网页 +* wss应用中,发起WebSocket连接的页面必须使用https +* 浏览器不信任SSL证书将无法使用wss +* 文件必须为`PEM`格式,不支持`DER`格式,可使用openssl工具进行转换 + +> 使用SSL必须在编译swoole时加入--enable-openssl选项 + +```php +$serv = new swoole_server('0.0.0.0', 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$serv->set(array( + 'ssl_cert_file' => __DIR__.'/config/ssl.crt', + 'ssl_key_file' => __DIR__.'/config/ssl.key', +)); +``` + +PEM转DER格式 +--- +```shell +openssl x509 -in cert.crt -outform der -out cert.der +``` + +DER转PEM格式 +---- +```shell +openssl x509 -in cert.crt -inform der -outform pem -out cert.pem +``` \ No newline at end of file diff --git a/doc/2.3.3 - max_request.md b/doc/2.3.3 - max_request.md new file mode 100644 index 0000000..23daa6a --- /dev/null +++ b/doc/2.3.3 - max_request.md @@ -0,0 +1,40 @@ +#max_request + +设置worker进程的最大任务数,默认为0,一个worker进程在处理完超过此数值的任务后将自动退出,进程退出后会释放所有内存和资源。 + +这个参数的主要作用是**解决PHP进程内存溢出问题**。PHP应用程序有缓慢的内存泄漏,但无法定位到具体原因、无法解决,可以通过设置`max_request`解决。 + +* `max_request`**只能用于同步阻塞、无状态的请求响应式服务器程序** +* 在swoole中真正维持客户端TCP连接的是master进程,worker进程仅处理客户端发送来的请求,因为客户端是不需要感知Worker进程重启的 +* 纯异步的Server不应当设置`max_request` +* 使用Base模式时`max_request`是无效的 + +> 当worker进程内发生致命错误或者人工执行`exit`时,进程会自动退出。master进程会重新启动一个新的worker进程来继续处理请求 + + +实例代码 +---- +创建一个swoole tcp server,我们开启两个worker进程,dispatch mode设置为3(抢占模式),文件名保存为server.php,代码如下: +```php +set(array( + 'worker_num' => 2, //开启两个worker进程 + 'max_request' => 3, //每个worker进程max request设置为3次 + 'dispatch_mode'=>3, +)); +//监听数据接收事件 +$serv->on('receive', function ($serv, $fd, $from_id, $data) { + $serv->send($fd, "Server: ".$data); +}); +//启动服务器 +$serv->start(); +``` +使用php server.php开启服务后,首先使用 ps aux | grep server.php 看下一下进程PID,一共有四个进程,如图所示: +![](http://www.swoole.com/static/uploads//wiki/201703/25/263880552319.png) + +其中8430和8431分别是master进程和manager进程,剩下两个8434和8435则是两个worker进程。 +按照预想,如果我们执行5次请求,那么必然会有一个worker进程会退出并被重新拉起一个新的,结果如下图所示: +![](http://www.swoole.com/static/uploads//wiki/201703/25/266250944309.png) + +注意pid为8434的worker进程已经没有了,新出现的则是pid为8457的worker进程 \ No newline at end of file diff --git a/doc/2.3.30 - ssl_method.md b/doc/2.3.30 - ssl_method.md new file mode 100644 index 0000000..729ba39 --- /dev/null +++ b/doc/2.3.30 - ssl_method.md @@ -0,0 +1,14 @@ +#ssl_method + +设置OpenSSL隧道加密的算法。Server与Client使用的算法必须一致,否则SSL/TLS握手会失败,连接会被切断。 +默认算法为 SWOOLE_SSLv23_METHOD + +```php +$server->set(array( + 'ssl_method' => SWOOLE_SSLv3_CLIENT_METHOD, +)); +``` + +> 此配置在1.7.20或更高版本可用 +> 支持的类型请参考 [预定义常量](/wiki/page/26.html) + diff --git a/doc/2.3.31 - ssl_ciphers.md b/doc/2.3.31 - ssl_ciphers.md new file mode 100644 index 0000000..bcb7ca6 --- /dev/null +++ b/doc/2.3.31 - ssl_ciphers.md @@ -0,0 +1,11 @@ +#ssl_ciphers + +启用SSL后,设置`ssl_ciphers`来改变`openssl`默认的加密算法。Swoole底层默认使用`EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH` + +```php +$server->set(array( + 'ssl_ciphers' => 'ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP', +)); +``` + +* `ssl_ciphers` 设置为空字符串时,由`openssl`自行选择加密算法 \ No newline at end of file diff --git a/doc/2.3.32 - user.md b/doc/2.3.32 - user.md new file mode 100644 index 0000000..e08a91d --- /dev/null +++ b/doc/2.3.32 - user.md @@ -0,0 +1,11 @@ +#user + +设置worker/task子进程的所属用户。服务器如果需要监听1024以下的端口,必须有root权限。但程序运行在root用户下,代码中一旦有漏洞,攻击者就可以以root的方式执行远程指令,风险很大。配置了user项之后,可以让主进程运行在root权限下,子进程运行在普通用户权限下。 + +```php +$serv->set(array('user' => 'apache')); +``` + + +> 此配置在swoole-1.7.9以上版本可用 +> 仅在使用root用户启动时有效 \ No newline at end of file diff --git a/doc/2.3.33 - group.md b/doc/2.3.33 - group.md new file mode 100644 index 0000000..62d0cfd --- /dev/null +++ b/doc/2.3.33 - group.md @@ -0,0 +1,10 @@ +#group + +设置worker/task子进程的进程用户组。与user配置相同,此配置是修改进程所属用户组,提升服务器程序的安全性。 + +```php +$serv->set(array('group' => 'www-data')); +``` + +> 此配置在swoole-1.7.9以上版本可用 +> 仅在使用root用户启动时有效 \ No newline at end of file diff --git a/doc/2.3.34 - chroot.md b/doc/2.3.34 - chroot.md new file mode 100644 index 0000000..8d543f3 --- /dev/null +++ b/doc/2.3.34 - chroot.md @@ -0,0 +1,9 @@ +#chroot + +重定向Worker进程的文件系统根目录。此设置可以使进程对文件系统的读写与实际的操作系统文件系统隔离。提升安全性。 + +```php +$serv->set(array('chroot' => '/data/server/')); +``` + +> 此配置在swoole-1.7.9以上版本可用 \ No newline at end of file diff --git a/doc/2.3.35 - pid_file.md b/doc/2.3.35 - pid_file.md new file mode 100644 index 0000000..ef5f0fa --- /dev/null +++ b/doc/2.3.35 - pid_file.md @@ -0,0 +1,13 @@ +#pid_file + +在Server启动时自动将master进程的PID写入到文件,在Server关闭时自动删除PID文件。 + +```php +$server->set(array( + 'pid_file' => __DIR__.'/server.pid', +)); +``` + +* 使用时需要注意如果Server非正常结束,PID文件不会删除,需要使用`swoole_process::kill($pid, 0)`来侦测进程是否真的存在 + +> 此选项在1.9.5或更高版本可用 \ No newline at end of file diff --git a/doc/2.3.36 - pipe_buffer_size.md b/doc/2.3.36 - pipe_buffer_size.md new file mode 100644 index 0000000..3f551e0 --- /dev/null +++ b/doc/2.3.36 - pipe_buffer_size.md @@ -0,0 +1,18 @@ +#pipe_buffer_size + +调整管道通信的内存缓存区长度。Swoole使用Unix Socket实现进程间通信。 +```shell +$server->set([ + 'pipe_buffer_size' => 32 * 1024 *1024, //必须为数字 +]) +``` + +* swoole的reactor线程与worker进程之间 +* worker进程与task进程之间 +* `1.9.16`或更高版本已移除此配置项,底层不再限制管道缓存区的长度 + +都是使用unix socket进行通信的,在收发大量数据的场景下,需要启用内存缓存队列。此函数可以修改内存缓存的长度。 + +> task_ipc_mode=2/3时会使用消息队列通信不受此参数控制 +> 管道缓存队列已满会导致reactor线程、worker进程发生阻塞 +> 此参数在1.7.17以上版本默认为32M,1.7.17以下版本默认为8M diff --git a/doc/2.3.37 - buffer_output_size.md b/doc/2.3.37 - buffer_output_size.md new file mode 100644 index 0000000..b19e535 --- /dev/null +++ b/doc/2.3.37 - buffer_output_size.md @@ -0,0 +1,14 @@ +#buffer_output_size + +配置发送输出缓存区内存尺寸。 + +```shell +$server->set([ + 'buffer_output_size' => 32 * 1024 *1024, //必须为数字 +]) +``` +* 单位为字节,默认为`2M`,如设置`32 * 1024 *1024`表示,单次`Server->send`最大允许发送`32M`字节的数据 +* 调用`swoole_server->send`, `swoole_http_server->end/write`,`swoole_websocket_server->push` 等发送数据指令时,单次最大发送的数据不得超过`buffer_output_size`配置。 + +> 注意此函数不应当调整过大,避免拥塞的数据过多,导致吃光机器内存 +> 开启大量worker进程时,将会占用`worker_num * buffer_output_size`字节的内存 diff --git a/doc/2.3.38 - socket_buffer_size.md b/doc/2.3.38 - socket_buffer_size.md new file mode 100644 index 0000000..499fe01 --- /dev/null +++ b/doc/2.3.38 - socket_buffer_size.md @@ -0,0 +1,31 @@ +#socket_buffer_size + +配置客户端连接的缓存区长度。从1.8.8版本开始swoole底层对于缓存区控制的参数分离成`buffer_output_size`和`socket_buffer_size`两项配置。 + +参数`buffer_output_size`用于设置单次最大发送长度。`socket_buffer_size`用于设置客户端连接最大允许占用内存数量。 + +```shell +$server->set([ + 'socket_buffer_size' => 128 * 1024 *1024, //必须为数字 +]) +``` + +* 单位为字节,如`128 * 1024 *1024`表示每个TCP客户端连接最大允许有`128M`待发送的数据 +* 默认为`2M`字节 + +数据发送缓存区 +----- +调整连接发送缓存区的大小。TCP通信有拥塞控制机制,服务器向客户端发送大量数据时,并不能立即发出。这时发送的数据会存放在服务器端的内存缓存区内。此参数可以调整内存缓存区的大小。 + +如果发送数据过多,客户端阻塞,数据占满缓存区后Server会报如下错误信息: +``` +swFactoryProcess_finish: send failed, session#1 output buffer has been overflowed. +``` +> 发送缓冲区塞满导致`send`失败,只会影响当前的客户端,其他客户端不受影响 +> 服务器有大量TCP连接时,最差的情况下将会占用`serv->max_connection * buffer_output_size`字节的内存 + +__尤其是外围通信的服务器程序,网络通信较慢,如果持续连续发送数据,缓冲区很快就会塞满。发送的数据会全部堆积在Server的内存里。因此此类应用应当从设计上考虑到网络的传输能力,先将消息存入磁盘,等客户端通知服务器已接受完毕后,再发送新的数据。__ + +如视频直播服务,A用户带宽是 100M,1秒内发送10M的数据是完全可以的。B用户带宽只有1M,如果1秒内发送10M的数据,B用户可能需要100秒才能接收完毕。这时数据会全部堆积在服务器内存中。 + +可以根据数据内容的类型,进行不同的处理。如果是可丢弃的内容,如视频直播等业务,网络差的情况下丢弃一些数据帧完全可以接受。如果内容是不可丢失的,如微信消息,可以先存储到服务器的磁盘中,按照100条消息为一组。当用户接受完这一组消息后,再从磁盘中取出下一组消息发送到客户端。 \ No newline at end of file diff --git a/doc/2.3.39 - enable_unsafe_event.md b/doc/2.3.39 - enable_unsafe_event.md new file mode 100644 index 0000000..28b879e --- /dev/null +++ b/doc/2.3.39 - enable_unsafe_event.md @@ -0,0 +1,7 @@ +#enable_unsafe_event + +swoole在配置dispatch_mode=1或3后,因为系统无法保证onConnect/onReceive/onClose的顺序,默认关闭了onConnect/onClose事件。 + +如果应用程序需要onConnect/onClose事件,并且能接受顺序问题可能带来的安全风险,可以通过设置`enable_unsafe_event`为`true`,启用onConnect/onClose事件 + +> enable_unsafe_event配置在1.7.18以上版本可用 diff --git a/doc/2.3.4 - max_conn (max_connection).md b/doc/2.3.4 - max_conn (max_connection).md new file mode 100644 index 0000000..360ab9b --- /dev/null +++ b/doc/2.3.4 - max_conn (max_connection).md @@ -0,0 +1,31 @@ +#max_conn (max_connection) + +服务器程序,最大允许的连接数,如`max_connection => 10000`, 此参数用来设置`Server`最大允许维持多少个`TCP`连接。超过此数量后,新进入的连接将被拒绝。 + +* `max_connection`最大不得超过操作系统`ulimit -n`的值,否则会报一条警告信息,并重置为`ulimit -n`的值 +* `max_connection`默认值为`ulimit -n`的值 + +``` +WARN swServer_start_check: serv->max_conn is exceed the maximum value[100000]. +``` + +最大上限 +---- +底层使用了`SESSION_LIST`来实现`session_id`(虚拟`fd`)与真实`fd`的对应,因此除了`max_sockets`限制之外,`max_connection`还受限于`SW_SESSION_LIST_SIZE`宏的设置。 + +目前`SW_SESSION_LIST_SIZE`底层的值为`1M`,请勿设置`max_connection`超过`1M` + +内存占用 +----- +`max_connection`参数不要调整的过大,根据机器内存的实际情况来设置。Swoole会根据此数值一次性分配一块大内存来保存`Connection`信息,可使用`gdb`跟踪运行中的进程,打印`p sizeof(swConnection)` 得到准确的数值。在`1.9.16`版本中一个TCP连接的`Connection`信息,需要占用`224`字节。 + +最小设置 +---- +此选项设置过小底层会抛出错误,并设置为`ulimit -n`的值。 +> 最小值为`(serv->worker_num + SwooleG.task_worker_num) * 2 + 32` + +``` +serv->max_connection is too small. +``` + + diff --git a/doc/2.3.40 - discard_timeout_request.md b/doc/2.3.40 - discard_timeout_request.md new file mode 100644 index 0000000..585f04f --- /dev/null +++ b/doc/2.3.40 - discard_timeout_request.md @@ -0,0 +1,8 @@ +#discard_timeout_request + +swoole在配置dispatch_mode=1或3后,系统无法保证onConnect/onReceive/onClose的顺序,因此可能会有一些请求数据在连接关闭后,才能到达Worker进程。 + +`discard_timeout_request`配置默认为true,表示如果worker进程收到了已关闭连接的数据请求,将自动丢弃。`discard_timeout_request`如果设置为false,表示无论连接是否关闭Worker进程都会处理数据请求。 + +> discard_timeout_request 在1.7.16以上可用 + diff --git a/doc/2.3.41 - enable_reuse_port.md b/doc/2.3.41 - enable_reuse_port.md new file mode 100644 index 0000000..3a9e26d --- /dev/null +++ b/doc/2.3.41 - enable_reuse_port.md @@ -0,0 +1,9 @@ +#enable_reuse_port + +设置端口重用,此参数用于优化TCP连接的Accept性能,启用端口重用后多个进程可以同时进行Accept操作。 + +* enable_reuse_port = true 打开端口重用 +* enable_reuse_port = false 关闭端口重用 + +> 仅在Linux-3.9.0以上版本的内核可用 +> 启用端口重用后可以重复启动同一个端口的Server程序 diff --git a/doc/2.3.42 - enable_delay_receive.md b/doc/2.3.42 - enable_delay_receive.md new file mode 100644 index 0000000..96d702f --- /dev/null +++ b/doc/2.3.42 - enable_delay_receive.md @@ -0,0 +1,20 @@ +#enable_delay_receive + +设置此选项为`true`后,accept客户端连接后将不会自动加入EventLoop,仅触发`onConnect`回调。worker进程可以调用`$serv->confirm($fd)`对连接进行确认,此时才会将fd加入EventLoop开始进行数据收发,也可以调用`$serv->close($fd)`关闭此连接。 + +实例: +```php +//开启enable_delay_receive选项 +$serv->set(array( + 'enable_delay_receive' => true, +)); + +$serv->on("Connect", function ($serv, $fd, $reactorId) { + $serv->after(2000, function() use ($serv, $fd) { + //确认连接,开始接收数据 + $serv->confirm($fd); + }); +}); +``` + +> enable_delay_receive在1.8.8或更高版本可用 \ No newline at end of file diff --git a/doc/2.3.43 - open_http_protocol.md b/doc/2.3.43 - open_http_protocol.md new file mode 100644 index 0000000..672ce48 --- /dev/null +++ b/doc/2.3.43 - open_http_protocol.md @@ -0,0 +1,3 @@ +#open_http_protocol + +启用Http协议处理,`Swoole\Http\Server`会自动启用此选项。设置为`false`表示关闭Http协议处理。 \ No newline at end of file diff --git a/doc/2.3.44 - open_http2_protocol.md b/doc/2.3.44 - open_http2_protocol.md new file mode 100644 index 0000000..ebe6756 --- /dev/null +++ b/doc/2.3.44 - open_http2_protocol.md @@ -0,0 +1,3 @@ +#open_http2_protocol + +启用`HTTP2`协议解析,需要依赖`--enable-http2`编译选项。默认为false \ No newline at end of file diff --git a/doc/2.3.45 - open_websocket_protocol.md b/doc/2.3.45 - open_websocket_protocol.md new file mode 100644 index 0000000..08aeef0 --- /dev/null +++ b/doc/2.3.45 - open_websocket_protocol.md @@ -0,0 +1,5 @@ +#open_websocket_protocol + +启用websocket协议处理,`Swoole\WebSocket\Server`会自动启用此选项。设置为`false`表示关闭websocket协议处理。 + +设置`open_websocket_protocol`选项为true后,会自动设置`open_http_protocol`协议也为true。 \ No newline at end of file diff --git a/doc/2.3.46 - open_mqtt_protocol.md b/doc/2.3.46 - open_mqtt_protocol.md new file mode 100644 index 0000000..14ec6e6 --- /dev/null +++ b/doc/2.3.46 - open_mqtt_protocol.md @@ -0,0 +1,7 @@ +#open_mqtt_protocol + +启用`mqtt`协议处理,启用后会解析`mqtt`包头,worker进程`onReceive`每次会返回一个完整的`mqtt`数据包。 + +```php +$serv->set(array('open_mqtt_protocol' => true)); +``` \ No newline at end of file diff --git a/doc/2.3.47 - reload_async.md b/doc/2.3.47 - reload_async.md new file mode 100644 index 0000000..ca3c8fa --- /dev/null +++ b/doc/2.3.47 - reload_async.md @@ -0,0 +1,7 @@ +#reload_async + +设置异步重启开关。设置为`true`时,将启用异步安全重启特性,`Worker`进程会等待异步事件完成后再退出。详细信息请参见 [异步安全重启特性](/wiki/page/775.html) + +```php +$serv->set(['reload_async' => true]); +``` \ No newline at end of file diff --git a/doc/2.3.48 - tcp_fastopen.md b/doc/2.3.48 - tcp_fastopen.md new file mode 100644 index 0000000..4ad8f6e --- /dev/null +++ b/doc/2.3.48 - tcp_fastopen.md @@ -0,0 +1,9 @@ +#tcp_fastopen + +开启`TCP`快速握手特性。此项特性,可以提升`TCP`短连接的响应速度,在客户端完成握手的第三步,发送`SYN`包时携带数据。 + +```php +$server->set(['tcp_fastopen' => true]); +``` + +* 此参数可以设置到监听端口上 diff --git a/doc/2.3.49 - request_slowlog_file.md b/doc/2.3.49 - request_slowlog_file.md new file mode 100644 index 0000000..d3bc20e --- /dev/null +++ b/doc/2.3.49 - request_slowlog_file.md @@ -0,0 +1,35 @@ +#request_slowlog_file + +开启请求慢日志。启用后`Manager`进程会设置一个时钟信号,定时侦测所有`Task`和`Worker`进程,一旦进程阻塞导致请求超过规定的时间,将自动打印进程的`PHP`函数调用栈。 + +底层基于`ptrace`系统调用实现,某些系统可能关闭了`ptrace`,无法跟踪慢请求。请确认`kernel.yama.ptrace_scope`内核参数是否`0`。 + + +```php +array( + 'request_slowlog_file' => '/tmp/trace.log', +) +``` + +与`trace_event_worker`和`request_slowlog_timeout`配置项配合使用。 + + +注意事项 +---- +* 需要`1.10.0`或更高版本 +* 仅在同步阻塞的程序中有效,请勿使用与协程和异步回调的服务器中 +* 必须是具有可写权限的文件,否则创建文件失败底层会抛出致命错误 +* 默认仅监听`Task`进程,通过增加`trace_event_worker => true`来开启对`Worker`进程的跟踪 + +超时时间 +---- +通过`request_slowlog_timeout`来设置请求超时时间,单位为秒。 + + +```php +array( + 'request_slowlog_timeout' => 2, //2秒 + 'request_slowlog_file' => '/tmp/trace.log', + 'trace_event_worker' => true, //跟踪 Task 和 Worker 进程 +) +``` \ No newline at end of file diff --git a/doc/2.3.5 - task_worker_num.md b/doc/2.3.5 - task_worker_num.md new file mode 100644 index 0000000..3b33f73 --- /dev/null +++ b/doc/2.3.5 - task_worker_num.md @@ -0,0 +1,14 @@ +#task_worker_num + +配置`Task`进程的数量,配置此参数后将会启用`task`功能。所以`Server`务必要注册`onTask`、`onFinish`2个事件回调函数。如果没有注册,服务器程序将无法启动。 + +`Task`进程是同步阻塞的,配置方式与`Worker`同步模式一致。 + +计算方法 +---- +* 单个task的处理耗时,如`100ms`,那一个进程1秒就可以处理`1/0.1=10`个task +* task投递的速度,如每秒产生`2000`个task +* `2000/10=200`,需要设置`task_worker_num => 200`,启用`200`个task进程 + +> Task进程内不能使用`swoole_server->task`方法 +> Task进程内不能使用`swoole_mysql`、`swoole_redis`、`swoole_event`等异步IO函数 diff --git a/doc/2.3.6 - task_ipc_mode.md b/doc/2.3.6 - task_ipc_mode.md new file mode 100644 index 0000000..09bbbd2 --- /dev/null +++ b/doc/2.3.6 - task_ipc_mode.md @@ -0,0 +1,18 @@ +#task_ipc_mode + +设置task进程与worker进程之间通信的方式。 + +* 1, 使用unix socket通信,默认模式 +* 2, 使用消息队列通信 +* 3, 使用消息队列通信,并设置为争抢模式 + +模式`2`和模式`3`的不同之处是,模式`2`支持定向投递,`$serv->task($data, $task_worker_id)` 可以指定投递到哪个`task`进程。模式`3`是完全争抢模式,`task`进程会争抢队列,将无法使用定向投递,`task`/`taskwait`将无法指定目标进程`ID`,即使指定了`$task_worker_id`,在模式`3`下也是无效的。 + +> 模式`3`会影响`sendMessage`方法,使`sendMessage`发送的消息会随机被某一个`task`进程获取 + +消息队列模式 +---- +* 消息队列模式使用操作系统提供的内存队列存储数据,未指定 [mssage_queue_key](/wiki/page/346.html) 消息队列`Key`,将使用私有队列,在`Server`程序终止后会删除消息队列。 +* 指定消息队列`Key`后`Server`程序终止后,消息队列中的数据不会删除,因此进程重启后仍然能取到数据 +* 可使用`ipcrm -q 消息队列ID`手工删除消息队列数据 + diff --git a/doc/2.3.7 - task_max_request.md b/doc/2.3.7 - task_max_request.md new file mode 100644 index 0000000..10568c2 --- /dev/null +++ b/doc/2.3.7 - task_max_request.md @@ -0,0 +1,6 @@ +#task_max_request + +设置task进程的最大任务数。一个task进程在处理完超过此数值的任务后将自动退出。这个参数是为了防止PHP进程内存溢出。如果不希望进程自动退出可以设置为0。 + +> `1.7.17`以下版本默认为`5000`,受`swoole_config.h`的`SW_MAX_REQUEST`宏控制 +> `1.7.17`以上版本默认值调整为`0`,不会主动退出进程 diff --git a/doc/2.3.8 - task_tmpdir.md b/doc/2.3.8 - task_tmpdir.md new file mode 100644 index 0000000..f7e693f --- /dev/null +++ b/doc/2.3.8 - task_tmpdir.md @@ -0,0 +1,7 @@ +#task_tmpdir + +设置task的数据临时目录,在`swoole_server`中,如果投递的数据超过8192字节,将启用临时文件来保存数据。这里的`task_tmpdir`就是用来设置临时文件保存的位置。 + +Swoole默认会使用`/tmp`目录存储task数据,如果你的Linux内核版本过低,`/tmp`目录不是内存文件系统,可以设置为 `/dev/shm/` + +> 需要swoole-1.7.7+ \ No newline at end of file diff --git a/doc/2.3.9 - dispatch_mode.md b/doc/2.3.9 - dispatch_mode.md new file mode 100644 index 0000000..b5e1a97 --- /dev/null +++ b/doc/2.3.9 - dispatch_mode.md @@ -0,0 +1,31 @@ +#dispatch_mode + +数据包分发策略。可以选择3种类型,默认为2 + +* 1,轮循模式,收到会轮循分配给每一个worker进程 +* 2,固定模式,根据连接的文件描述符分配worker。这样可以保证同一个连接发来的数据只会被同一个worker处理 +* 3,抢占模式,主进程会根据Worker的忙闲状态选择投递,只会投递给处于闲置状态的Worker +* 4,IP分配,根据客户端IP进行取模hash,分配给一个固定的worker进程。可以保证同一个来源IP的连接数据总会被分配到同一个worker进程。算法为 `ip2long(ClientIP) % worker_num` +* 5,UID分配,需要用户代码中调用 $serv-> [bind()](/wiki/page/369.html) 将一个连接绑定1个uid。然后swoole根据UID的值分配到不同的worker进程。算法为 `UID % worker_num`,如果需要使用字符串作为UID,可以使用`crc32(UID_STRING)` + +使用建议 +---- +* 无状态`Server`可以使用`1`或`3`,同步阻塞`Server`使用`3`,异步非阻塞`Server`使用`1` +* 有状态使用`2`、`4`、`5` + +> `dispatch_mode 4,5`两种模式,在`1.7.8`以上版本可用 +> `dispatch_mode=1/3`时,底层会屏蔽`onConnect`/`onClose`事件,原因是这2种模式下无法保证`onConnect`/`onClose`/`onReceive`的顺序 +> 非请求响应式的服务器程序,请不要使用模式1或3 + +UDP协议 +----- +* `dispatch_mode=2/4/5`时为固定分配,底层使用客户端IP取模散列到不同的worker进程,算法为 `ip2long(ClientIP) % worker_num` +* `dispatch_mode=1/3`时随机分配到不同的worker进程 + +BASE模式 +----- +`dispatch_mode`配置在BASE模式是无效的,因为BASE不存在投递任务,当`Reactor线程`收到客户端发来的数据后会立即在当前线程/进程回调`onReceive`,不需要投递Worker进程。 + + + + diff --git "a/doc/2.4 - \347\233\221\345\220\254\347\253\257\345\217\243.md" "b/doc/2.4 - \347\233\221\345\220\254\347\253\257\345\217\243.md" new file mode 100644 index 0000000..410ddad --- /dev/null +++ "b/doc/2.4 - \347\233\221\345\220\254\347\253\257\345\217\243.md" @@ -0,0 +1,101 @@ +#监听端口 + + `Swoole-1.8.0`新增了对`多端口混合协议`的支持。`Server`可以监听多个端口,每个端口都可以设置不同的协议处理方式(`set`)和回调函数(`on`)。`SSL/TLS`传输加密也可以只对特定的端口启用。 + +* **未调用`set`方法,设置协议处理选项的监听端口,默认继承主服务器的设置** +* **未调用`on`方法,设置回调函数的监听端口,默认使用主服务器的回调函数** +* 监听端口返回的对象类型为`swoole_server_port` +* 监听端口的`swoole_server_port`对象,可以调用`set`和`on`方法,使用方法与`swoole_server`完全一致 +* 监听端口只能设置少量特定的选项,只能设置数据收发的相关事件回调函数 +* 不同监听端口的回调函数,仍然是相同的`Worker`进程空间内执行 + +> 主服务器是WebSocket或Http协议,新监听的TCP端口默认会继承主Server的协议设置。必须单独调用`set`方法设置新的协议才会启用新协议 + +监听新端口 +---- +```php +$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); +$port2 = $server->listen("127.0.0.1", 9502, SWOOLE_SOCK_UDP); +$port3 = $server->listen("127.0.0.1", 9503, SWOOLE_SOCK_TCP | SWOOLE_SSL); +``` + +设置网络协议 +---- +```php +$port1->set([ + 'open_length_check' => true, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_max_length' => 800000,] +); + +$port3->set([ + 'open_eof_split' => true, + 'package_eof' => "\r\n", + 'ssl_cert_file' => 'ssl.cert', + 'ssl_key_file' => 'ssl.key',] +); +``` + +设置回调函数 +----- +```php +$port1->on('connect', function ($serv, $fd){ + echo "Client:Connect.\n"; +}); + +$port1->on('receive', function ($serv, $fd, $from_id, $data) { + $serv->send($fd, 'Swoole: '.$data); + $serv->close($fd); +}); + +$port1->on('close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); + +$port2->on('packet', function ($serv, $data, $addr) { + var_dump($data, $addr); +}); +``` + +Http/WebSocket +---- +`swoole_http_server`和`swoole_websocket_server`因为是使用继承子类实现的,无法通过调用`swoole_server`实例的`listen`来方法创建`Http/WebSocket`服务器。如果服务器的主要功能为`RPC`,但希望提供一个简单的Web管理界面。 + +在这样的场景中,可以先创建`Http/WebSocket`服务器,然后再进行`listen`监听`RPC服务器`的端口。 + +#### 实例 +```php +$http_server = new swoole_http_server('0.0.0.0',9998); +$http_server->set(array('daemonize'=> false)); +$http_server->on('request','request'); +//......设置各个回调...... +//多监听一个tcp端口,对外开启tcp服务,并设置tcp服务器的回调 +$tcp_server = $http_server->addListener('0.0.0.0', 9999, SWOOLE_SOCK_TCP); +//默认新监听的端口 9999 会继承主服务器的设置,也是 Http 协议 +//需要调用 set 方法覆盖主服务器的设置 +$tcp_server->set(array()); +$tcp_server->on("receive", function ($serv, $fd, $threadId, $data) { + echo $data; +}); +``` +通过这样的代码,我们便可以建立一个同时对外提供http服务,又同时对外提供tcp服务的server,具体更加的优雅代码组合则由你自己来实现。 + + +## TCP、Http、WebSocket 多协议端口复合设置 + +```php +$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); +$port1->set([ + 'open_websocket_protocol' => true, // 设置使得这个端口支持 webSocket 协议 +]); +``` + +```php +$port1 = $server->listen("127.0.0.1", 9501, SWOOLE_SOCK_TCP); +$port1->set([ + 'open_http_protocol' => false, // 设置这个端口关闭http协议功能 +]); +``` + +同理还有: `open_http_protocol`、`open_http2_protocol`、`open_mqtt_protocol` 等参数 diff --git "a/doc/2.4.1 - \345\217\257\351\200\211\345\217\202\346\225\260.md" "b/doc/2.4.1 - \345\217\257\351\200\211\345\217\202\346\225\260.md" new file mode 100644 index 0000000..826fb28 --- /dev/null +++ "b/doc/2.4.1 - \345\217\257\351\200\211\345\217\202\346\225\260.md" @@ -0,0 +1,23 @@ +#可选参数 + +监听端口调用`set`方法只能设置一些特定的参数,无法修改全局的Server设置。 + +* 监听端口未设置任何参数,将会继承主服务器的相关配置 +* 主服务器为`Http/WebSocket`服务器,如果未设置协议参数,监听的端口仍然会设置为`Http`或`WebSocket`协议,并且不会执行为端口设置的`onReceive`回调 +* 主服务器为`Http/WebSocket`服务器,监听端口调用`set`设置配置参数,会清除主服务器的协议设定。监听端口将变为`TCP`协议。监听的端口如果希望仍然使用`Http/WebSocket`协议,需要在配置中增加`open_http_protocol => true` 和 `open_websocket_protocol => true` + +可用的参数列表 +----- +* socket参数,如backlog、TCP_KEEPALIVE、open_tcp_nodelay、tcp_defer_accept等 +* 协议相关,如open_length_check、open_eof_check、package_length_type等 +* SSL证书相关,如ssl_cert_file、ssl_key_file等 + +不可用的参数列表 +---- +* worker_num、task_worker_num、reactor_num +* dispatch_mode、task_ipc_num +* heartbeart_check +* log_file +* user/group/chroot +* open_cpu_affinity +* max_request/task_max_request diff --git "a/doc/2.4.2 - \345\217\257\351\200\211\345\233\236\350\260\203.md" "b/doc/2.4.2 - \345\217\257\351\200\211\345\233\236\350\260\203.md" new file mode 100644 index 0000000..5782a35 --- /dev/null +++ "b/doc/2.4.2 - \345\217\257\351\200\211\345\233\236\350\260\203.md" @@ -0,0 +1,39 @@ +#可选回调 + +监听端口使用`on`方法可以设置部分回调函数。 + +TCP服务器 +---- +* onConnect +* onClose +* onReceive + +UDP服务器 +----- +* onPacket +* onReceive + +Http服务器 +--- +* onRequest + +WebSocket服务器 +---- +* onMessage +* onOpen + +不可用回调 +----- +以下事件回调函数是`Server`级别的,只能在`swoole_server`对象上设置。 + +* onStart +* onShutdown +* onWorkerStart +* onWorkerStop +* onManagerStart +* onManagerStop +* onTask +* onFinish +* onPipeMessage +* onWorkerError + diff --git "a/doc/2.4.3 - \350\277\236\346\216\245\350\277\255\344\273\243\345\231\250.md" "b/doc/2.4.3 - \350\277\236\346\216\245\350\277\255\344\273\243\345\231\250.md" new file mode 100644 index 0000000..d2c8bf3 --- /dev/null +++ "b/doc/2.4.3 - \350\277\236\346\216\245\350\277\255\344\273\243\345\231\250.md" @@ -0,0 +1,43 @@ +#连接迭代器 + + `1.10.0`或更高版本,提供了监听端口迭代器,可以只遍历当前服务器端口所维持的`TCP`连接,而不是遍历所有端口的连接。 + +> 连接迭代器依赖`pcre`库 + + +```php +$server = new swoole_websocket_server("0.0.0.0", 9514, SWOOLE_BASE); +$server->set([ + 'enable_static_handler' => true, + 'document_root' => __DIR__ . '/web', +]); + +$tcp = $server->listen("0.0.0.0", 9515, SWOOLE_SOCK_TCP); +$tcp->set([ + 'open_length_check' => true, + 'package_max_length' => 2 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_body_offset' => 16, + 'package_length_offset' => 0, +]); + +$server->on("open", function ($serv, $req) { + echo "new WebSocket Client, fd={$req->fd}\n"; +}); + +$tcp->on('receive', function ($server, $fd, $reactor_id, $data) { + $body = substr($data, 16); + $value = swoole_serialize::unpack($body); + //仅遍历 9514 端口的连接 + $websocket = $server->ports[0]; + foreach ($websocket->connections as $_fd) + { + if ($server->exist($_fd)) + { + $server->push($_fd, json_encode($value)); + } + } +}); + +$server->start(); +``` \ No newline at end of file diff --git "a/doc/2.5 - \351\242\204\345\256\232\344\271\211\345\270\270\351\207\217.md" "b/doc/2.5 - \351\242\204\345\256\232\344\271\211\345\270\270\351\207\217.md" new file mode 100644 index 0000000..10e9273 --- /dev/null +++ "b/doc/2.5 - \351\242\204\345\256\232\344\271\211\345\270\270\351\207\217.md" @@ -0,0 +1,47 @@ +#预定义常量 + +* **SWOOLE_VERSION** 当前Swoole的版本号,字符串类型,如1.6.0 + +swoole_server构造函数参数 +-------- +* **SWOOLE_BASE** 使用Base模式,业务代码在Reactor进程中直接执行 +* **SWOOLE_PROCESS** 使用进程模式,业务代码在Worker进程中执行 + +swoole_client构造函数参数 +----------- +* __SWOOLE_SOCK_TCP__ 创建tcp socket +* __SWOOLE_SOCK_TCP6__ 创建tcp ipv6 socket +* __SWOOLE_SOCK_UDP__ 创建udp socket +* __SWOOLE_SOCK_UDP6__ 创建udp ipv6 socket +* __SWOOLE_SOCK_SYNC__ 同步客户端 +* __SWOOLE_SOCK_ASYNC__ 异步客户端 + +swoole_lock构造函数参数 +---------- +* __SWOOLE_FILELOCK__ 创建文件锁 +* __SWOOLE_MUTEX__ 创建互斥锁 +* __SWOOLE_RWLOCK__ 创建读写锁 +* __SWOOLE_SPINLOCK__ 创建自旋锁 +* __SWOOLE_SEM__ 创建信号量 + +SSL加密方法 +----- +* SWOOLE_SSLv3_METHOD +* SWOOLE_SSLv3_SERVER_METHOD +* SWOOLE_SSLv3_CLIENT_METHOD +* SWOOLE_SSLv23_METHOD(默认加密方法) +* SWOOLE_SSLv23_SERVER_METHOD +* SWOOLE_SSLv23_CLIENT_METHOD +* SWOOLE_TLSv1_METHOD +* SWOOLE_TLSv1_SERVER_METHOD +* SWOOLE_TLSv1_CLIENT_METHOD +* SWOOLE_TLSv1_1_METHOD +* SWOOLE_TLSv1_1_SERVER_METHOD +* SWOOLE_TLSv1_1_CLIENT_METHOD +* SWOOLE_TLSv1_2_METHOD +* SWOOLE_TLSv1_2_SERVER_METHOD +* SWOOLE_TLSv1_2_CLIENT_METHOD +* SWOOLE_DTLSv1_METHOD +* SWOOLE_DTLSv1_SERVER_METHOD +* SWOOLE_DTLSv1_CLIENT_METHOD + diff --git "a/doc/2.6 - \344\272\213\344\273\266\345\233\236\350\260\203\345\207\275\346\225\260.md" "b/doc/2.6 - \344\272\213\344\273\266\345\233\236\350\260\203\345\207\275\346\225\260.md" new file mode 100644 index 0000000..f488a07 --- /dev/null +++ "b/doc/2.6 - \344\272\213\344\273\266\345\233\236\350\260\203\345\207\275\346\225\260.md" @@ -0,0 +1,39 @@ +#事件回调函数 + + `Swoole\Server`是事件驱动模式,所有的业务逻辑代码必须写在事件回调函数中。当特定的网络事件发生后,底层会主动回调指定的`PHP`函数。 + +* 共支持`13`种事件,具体详情请参考各个页面详细页 +* PHP语言有[4种回调函数的写法](/wiki/page/458.html) + +事件执行顺序 +---- +* 所有事件回调均在`$server->start`后发生 +* 服务器关闭程序终止时最后一次事件是`onShutdown` +* 服务器启动成功后,`onStart`/`onManagerStart`/`onWorkerStart`会在不同的进程内并发执行 +* `onReceive`/`onConnect`/`onClose`在`Worker`进程中触发 +* `Worker`/`Task`进程启动/结束时会分别调用一次`onWorkerStart`/`onWorkerStop` +* `onTask`事件仅在`task`进程中发生 +* `onFinish`事件仅在`worker`进程中发生 + +> `onStart/onManagerStart/onWorkerStart` 3个事件的执行顺序是不确定的 + +异常捕获 +---- +* `swoole`不支持`set_exception_handler`函数 +* 如果你的PHP代码有抛出异常逻辑,__必须在事件回调函数顶层进行try/catch来捕获异常__ + +```php +$serv->on('Receive', function() { + try + { + //some code + } + catch(Exception $e) + { + //exception code + } +} +``` + + + diff --git a/doc/2.6.1 - onStart.md b/doc/2.6.1 - onStart.md new file mode 100644 index 0000000..f62075b --- /dev/null +++ b/doc/2.6.1 - onStart.md @@ -0,0 +1,34 @@ +#onStart + +Server启动在主进程的主线程回调此函数,函数原型 +```php +function onStart(swoole_server $server); +``` + +在此事件之前Swoole Server已进行了如下操作 + +* 已创建了`manager`进程 +* 已创建了`worker`子进程 +* 已监听所有`TCP/UDP/UnixSocket`端口,但未开始`Accept`连接和请求 +* 已监听了定时器 + +接下来要执行 + +* 主Reactor开始接收事件,客户端可以connect到Server + +__onStart回调中,仅允许echo、打印Log、修改进程名称。不得执行其他操作。onWorkerStart和onStart回调是在不同进程中并行执行的,不存在先后顺序。__ + +可以在onStart回调中,将$serv->master_pid和$serv->manager_pid的值保存到一个文件中。这样可以编写脚本,向这两个PID发送信号来实现关闭和重启的操作。 + +从1.7.5+ Master进程内不再支持定时器,onMasterConnect/onMasterClose2个事件回调也彻底移除。Master进程内不再保留任何PHP的接口。 + +onStart事件在Master进程的主线程中被调用。 +> 在onStart中创建的**全局资源对象**不能在worker进程中被使用,因为发生onStart调用时,worker进程已经创建好了。 +> 新创建的对象在主进程内,worker进程无法访问到此内存区域。 +> 因此全局对象创建的代码需要放置在swoole_server_start之前。 + +安全提示 +---- +`onStart`和`onShutdown`事件是在`master`进程的`master`线程内执行,执行过多异步`IO`操作可能会带来安全隐患,因此底层关闭了异步`IO`。 + +`onStart`回调在`return`之前服务器程序不会接受任何客户端连接,因此可以安全地使用`CURL`等`PHP`提供的同步`IO`函数。 \ No newline at end of file diff --git a/doc/2.6.10 - onBufferFull.md b/doc/2.6.10 - onBufferFull.md new file mode 100644 index 0000000..1ccd9e6 --- /dev/null +++ b/doc/2.6.10 - onBufferFull.md @@ -0,0 +1,10 @@ +#onBufferFull + +当缓存区达到最高水位时触发此事件。 + +```php +function onBufferFull(Swoole\Server $serv, int $fd); +``` + +* 设置`server->buffer_high_watermark`选项来控制缓存区高水位线 +* 触发`onBufferFull`表明此连接`$fd`的发送队列已触顶即将塞满,这时不应当再向此`$fd`发送数据 \ No newline at end of file diff --git a/doc/2.6.11 - onBufferEmpty.md b/doc/2.6.11 - onBufferEmpty.md new file mode 100644 index 0000000..ff15def --- /dev/null +++ b/doc/2.6.11 - onBufferEmpty.md @@ -0,0 +1,10 @@ +#onBufferEmpty + +当缓存区低于最低水位线时触发此事件。 + +```php +function onBufferEmpty(Swoole\Server $serv, int $fd); +``` + +* 设置`server->buffer_low_watermark`来控制缓存区低水位线 +* 触发此事件后,表明当前的`$fd`发送队列中的数据已被发出,可以继续向此连接发送数据了 \ No newline at end of file diff --git a/doc/2.6.12 - onTask.md b/doc/2.6.12 - onTask.md new file mode 100644 index 0000000..eac8735 --- /dev/null +++ b/doc/2.6.12 - onTask.md @@ -0,0 +1,23 @@ +#onTask + +在task_worker进程内被调用。worker进程可以使用swoole_server_task函数向task_worker进程投递新的任务。当前的Task进程在调用`onTask`回调函数时会将进程状态切换为**忙碌**,这时将不再接收新的Task,当`onTask`函数返回时会将进程状态切换为**空闲**然后继续接收新的Task。 + +```php +function onTask(swoole_server $serv, int $task_id, int $src_worker_id, mixed $data); +``` + +* $task_id是任务ID,由swoole扩展内自动生成,用于区分不同的任务。__$task_id和$src_worker_id组合起来才是全局唯一的,不同的worker进程投递的任务ID可能会有相同__ +* $src_worker_id来自于哪个worker进程 +* $data 是任务的内容 + +> `onTask`函数执行时遇到致命错误退出,或者被外部进程强制kill,当前的任务会被丢弃,但不会影响其他正在排队的Task + + +返回执行结果到worker进程 +----- +1.7.2以上的版本,在onTask函数中 return字符串,表示将此内容返回给worker进程。worker进程中会触发onFinish函数,表示投递的task已完成。 + +* return的变量可以是任意非`null`的PHP变量 + +1.7.2以前的版本,需要调用`swoole_server->finish()`函数将结果返回给worker进程 + diff --git a/doc/2.6.13 - onFinish.md b/doc/2.6.13 - onFinish.md new file mode 100644 index 0000000..9e47c36 --- /dev/null +++ b/doc/2.6.13 - onFinish.md @@ -0,0 +1,14 @@ +#onFinish + +当worker进程投递的任务在task_worker中完成时,task进程会通过swoole_server->finish()方法将任务处理的结果发送给worker进程。 + +```php +void onFinish(swoole_server $serv, int $task_id, string $data) +``` + +* $task_id是任务的ID +* $data是任务处理的结果内容 + +> task进程的onTask事件中没有调用finish方法或者return结果,worker进程不会触发onFinish + +> 执行onFinish逻辑的worker进程与下发task任务的worker进程是同一个进程 \ No newline at end of file diff --git a/doc/2.6.14 - onPipeMessage.md b/doc/2.6.14 - onPipeMessage.md new file mode 100644 index 0000000..8369acd --- /dev/null +++ b/doc/2.6.14 - onPipeMessage.md @@ -0,0 +1,11 @@ +#onPipeMessage + +当工作进程收到由 [sendMessage](/wiki/page/363.html) 发送的管道消息时会触发`onPipeMessage`事件。worker/task进程都可能会触发`onPipeMessage`事件。函数原型: +```php +void onPipeMessage(swoole_server $server, int $src_worker_id, mixed $message); +``` + +* `$src_worker_id`消息来自哪个`Worker`进程 +* `$message`消息内容,可以是任意`PHP`类型 + +> `onPipeMessage`在`1.7.9`以上版本可用 \ No newline at end of file diff --git a/doc/2.6.15 - onWorkerError.md b/doc/2.6.15 - onWorkerError.md new file mode 100644 index 0000000..60c8e3a --- /dev/null +++ b/doc/2.6.15 - onWorkerError.md @@ -0,0 +1,13 @@ +#onWorkerError + +当worker/task_worker进程发生异常后会在Manager进程内回调此函数。 +```php +void onWorkerError(swoole_server $serv, int $worker_id, int $worker_pid, int $exit_code, int $signal); +``` + +* `$worker_id` 是异常进程的编号 +* `$worker_pid` 是异常进程的ID +* `$exit_code` 退出的状态码,范围是 1 ~255 +* `$signal` 进程退出的信号 + +此函数主要用于报警和监控,一旦发现Worker进程异常退出,那么很有可能是遇到了致命错误或者进程CoreDump。通过记录日志或者发送报警的信息来提示开发者进行相应的处理。 \ No newline at end of file diff --git a/doc/2.6.16 - onManagerStart.md b/doc/2.6.16 - onManagerStart.md new file mode 100644 index 0000000..f286315 --- /dev/null +++ b/doc/2.6.16 - onManagerStart.md @@ -0,0 +1,9 @@ +#onManagerStart + +当管理进程启动时调用它,函数原型: +```php +void onManagerStart(swoole_server $serv); +``` +在这个回调函数中可以修改管理进程的名称。 +> 注意manager进程中不能添加定时器 +> manager进程中可以调用`sendMessage`接口向其他工作进程发送消息 \ No newline at end of file diff --git a/doc/2.6.17 - onManagerStop.md b/doc/2.6.17 - onManagerStop.md new file mode 100644 index 0000000..8dca67b --- /dev/null +++ b/doc/2.6.17 - onManagerStop.md @@ -0,0 +1,6 @@ +#onManagerStop + +当管理进程结束时调用它,函数原型: +```php +void onManagerStop(swoole_server $serv); +``` diff --git a/doc/2.6.2 - onShutdown.md b/doc/2.6.2 - onShutdown.md new file mode 100644 index 0000000..25fbcdd --- /dev/null +++ b/doc/2.6.2 - onShutdown.md @@ -0,0 +1,20 @@ +#onShutdown + +此事件在`Server`正常结束时发生,,函数原型 +```php +function onShutdown(swoole_server $server); +``` +在此之前`Swoole\Server`已进行了如下操作 + +* 已关闭所有`Reactor`线程、`HeartbeatCheck`线程、`UdpRecv`线程 +* 已关闭所有`Worker`进程、`Task`进程、`User`进程 +* 已`close`所有`TCP/UDP/UnixSocket`监听端口 +* 已关闭主`Reactor` + +> 强制`kill`进程不会回调`onShutdown`,如`kill -9` +> 需要使用`kill -15`来发送`SIGTREM`信号到主进程才能按照正常的流程终止 +> 在命令行中使用`Ctrl+C`中断程序会立即停止,底层不会回调`onShutdown` + +注意事项 +---- +请勿在`onShutdown`中调用任何异步或协程相关`API`。触发`onShutdown`时底层已销毁了所有事件循环设施。 diff --git a/doc/2.6.3 - onWorkerStart.md b/doc/2.6.3 - onWorkerStart.md new file mode 100644 index 0000000..8618470 --- /dev/null +++ b/doc/2.6.3 - onWorkerStart.md @@ -0,0 +1,35 @@ +#onWorkerStart + +此事件在`Worker`进程/`Task`进程启动时发生。这里创建的对象可以在进程生命周期内使用。原型: + +```php +function onWorkerStart(swoole_server $server, int $worker_id); +``` +* `1.6.11`之后`Task`进程中也会触发`onWorkerStart`事件 +* 发生致命错误或者代码中主动调用`exit`时,`Worker`/`Task`进程会退出,管理进程会重新创建新的进程 +* `onWorkerStart`/`onStart`是并发执行的,没有先后顺序 +* 可以通过`$server->taskworker`属性来判断当前是`Worker`进程还是`Task`进程 + +下面的示例用于为`Worker`进程/`Task`进程重命名。 +```php +$serv->on('WorkerStart', function ($serv, $worker_id){ + global $argv; + if($worker_id >= $serv->setting['worker_num']) { + swoole_set_process_name("php {$argv[0]} task worker"); + } else { + swoole_set_process_name("php {$argv[0]} event worker"); + } +}); +``` + +如果想使用`Reload`机制实现代码重载入,必须在`onWorkerStart`中`require`你的业务文件,而不是在文件头部。在`onWorkerStart`调用之前已包含的文件,不会重新载入代码。 + +> 可以将公用的、不易变的php文件放置到`onWorkerStart`之前。这样虽然不能重载入代码,但所有`Worker`是共享的,不需要额外的内存来保存这些数据。 +> `onWorkerStart`之后的代码每个进程都需要在内存中保存一份 + +* `$worker_id`是一个从`0-$worker_num`之间的数字,表示这个`Worker`进程的`ID` +* `$worker_id`和进程`PID`没有任何关系,可使用`posix_getpid`函数获取`PID` + +协程支持 +---- +`2.1.0`版本`onWorkerStart`回调函数中创建了协程,在`onWorkerStart`可以调用协程`API` diff --git a/doc/2.6.4 - onWorkerStop.md b/doc/2.6.4 - onWorkerStop.md new file mode 100644 index 0000000..27a0477 --- /dev/null +++ b/doc/2.6.4 - onWorkerStop.md @@ -0,0 +1,10 @@ +#onWorkerStop + +此事件在worker进程终止时发生。在此函数中可以回收worker进程申请的各类资源。原型: +```php +function onWorkerStop(swoole_server $server, int $worker_id); +``` + +* $worker_id是一个从0-$worker_num之间的数字,表示这个worker进程的ID +* $worker_id和进程PID没有任何关系 +* 进程异常结束,如被强制kill、致命错误、core dump 时无法执行`onWorkerStop`回调函数 \ No newline at end of file diff --git a/doc/2.6.5 - onWorkerExit.md b/doc/2.6.5 - onWorkerExit.md new file mode 100644 index 0000000..0a1d6d5 --- /dev/null +++ b/doc/2.6.5 - onWorkerExit.md @@ -0,0 +1,14 @@ +#onWorkerExit + +仅在开启`reload_async`特性后有效。异步重启特性,会先创建新的`Worker`进程处理新请求,旧的`Worker`进程自行退出。原型: + +```php +function onWorkerExit(swoole_server $server, int $worker_id); +``` + +- `Worker`进程未退出,`onWorkerExit`会持续触发 +- `onWorkerExit`仅在`Worker`进程内触发,`Task`进程不执行`onWorkerExit` + +旧的`Worker`进程,在退出时先会执行一次`onWorkerStop`事件回调,然后会在事件循环的每个周期结束时调用`onWorkerExit`通知`Worker`进程退出。 + +在`onWorkerExit`中尽可能地移除/关闭异步的`Socket`连接,最终底层检测到`Reactor`中事件监听的句柄数量为`0`时退出进程。 \ No newline at end of file diff --git a/doc/2.6.6 - onConnect.md b/doc/2.6.6 - onConnect.md new file mode 100644 index 0000000..8b92b36 --- /dev/null +++ b/doc/2.6.6 - onConnect.md @@ -0,0 +1,22 @@ +#onConnect + +有新的连接进入时,在worker进程中回调。函数原型: +```php +function onConnect(swoole_server $server, int $fd, int $reactorId); +``` + +* `$server`是`Swoole\Server`对象 +* `$fd`是连接的文件描述符,发送数据/关闭连接时需要此参数 +* `$reactorId`来自哪个`Reactor`线程 + +> 关于`$fd`和`$reactorId` [详细的解释](/wiki/page/56.html) +> onConnect/onClose这2个回调发生在worker进程内,而不是主进程。 +> UDP协议下只有onReceive事件,没有onConnect/onClose事件 + +dispatch_mode = 1/3 +---- +在`1.7.15`以上版本中,当设置`dispatch_mode = 1/3`时会自动去掉`onConnect/onClose`事件回调。原因是: + +* 在此模式下`onConnect/onReceive/onClose`可能会被投递到不同的进程。连接相关的`PHP`对象数据,无法实现在`onConnect`回调初始化数据,`onClose`清理数据 +* `onConnect/onReceive/onClose` 3种事件可能会并发执行,可能会带来异常 + diff --git a/doc/2.6.7 - onReceive.md b/doc/2.6.7 - onReceive.md new file mode 100644 index 0000000..5ce236c --- /dev/null +++ b/doc/2.6.7 - onReceive.md @@ -0,0 +1,54 @@ +#onReceive + +接收到数据时回调此函数,发生在worker进程中。函数原型: +```php +function onReceive(swoole_server $server, int $fd, int $reactor_id, string $data); +``` +* `$server`,swoole_server对象 +* `$fd`,TCP客户端连接的唯一标识符 +* `$reactor_id`,TCP连接所在的Reactor线程ID +* `$data`,收到的数据内容,可能是文本或者二进制内容 + +> 关于$fd和$reactor_id [详细的解释](/wiki/page/56.html) +> 未开启swoole的自动协议选项,`onReceive`回调函数单次收到的数据最大为64K +> Swoole支持二进制格式,$data可能是二进制数据 + +协议相关说明 +----- +* UDP协议,onReceive可以保证总是收到一个完整的包,最大长度不超过64K +* UDP协议下,`$fd`参数是对应客户端的IP,`$reactor_id`是客户端的端口和来源端口; +客户端ip等于long2ip(unpack('N',pack('L',$fd))[1]); +* TCP协议是流式的,`onReceive`无法保证数据包的完整性,可能会同时收到多个请求包,也可能只收到一个请求包的一部分数据 +* swoole只负责底层通信,$data是通过网络接收到的原始数据。对数据解包打包需要在PHP代码中自行实现 + +> 如果开启了eof_check/length_check/http_protocol,$data的长度可能会超过64K,但最大不超过$server->setting['package_max_length'] + +> 注意,onReceive回调不再支持UDP Server + +关于TCP协议下包完整性 +------- +* 使用swoole提供的open_eof_check/open_length_check/open_http_protocol,可以保证数据包的完整性 +* 不使用swoole的协议处理,在onReceive后PHP代码中自行对数据分析,合并/拆分数据包。 + +例如:代码中可以增加一个 $buffer = array(),使用$fd作为key,来保存上下文数据。 每次收到数据进行字符串拼接,$buffer[$fd] .= $data,然后在判断$buffer[$fd]字符串是否为一个完整的数据包。 + +默认情况下,同一个fd会被分配到同一个worker中,所以数据可以拼接起来。__使用dispatch_mode = 3时。 +请求数据是抢占式的,同一个fd发来的数据可能会被分到不同的进程。所以无法使用上述的数据包拼接方法__ + +关于粘包问题,如SMTP协议,客户端可能会同时发出2条指令。在swoole中可能是一次性收到的,这时应用层需要自行拆包。smtp是通过\r\n来分包的,所以业务代码中需要 explode("\r\n", $data)来拆分数据包。 + +如果是请求应答式的服务,无需考虑粘包问题。原因是客户端在发起一次请求后,必须等到服务器端返回当前请求的响应数据,才会发起第二次请求,不会同时发送2个请求。 + +多端口监听 +---- +当主服务器设置了协议后,额外监听的端口默认会继承主服务器的设置。需要显式调用`set`方法来重新设置端口的协议。 + +```php +$serv = new swoole_http_server("127.0.0.1", 9501); +$port2 = $serv->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); +$port2->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) { + echo "[#".$serv->worker_id."]\tClient[$fd]: $data\n"; +}); +``` +这里虽然调用了`on`方法注册了`onReceive`回调函数,但由于没有调用`set`方法覆盖主服务器的协议,新监听的`9502`端口依然使用`Http`协议。使用`telnet`客户端连接`9502`端口发送字符串时服务器不会触发`onReceive`。 + diff --git a/doc/2.6.8 - onPacket.md b/doc/2.6.8 - onPacket.md new file mode 100644 index 0000000..bf22991 --- /dev/null +++ b/doc/2.6.8 - onPacket.md @@ -0,0 +1,23 @@ +#onPacket + +接收到UDP数据包时回调此函数,发生在worker进程中。函数原型: +```php +function onPacket(swoole_server $server, string $data, array $client_info); +``` +* $server,swoole_server对象 +* $data,收到的数据内容,可能是文本或者二进制内容 +* $client_info,客户端信息包括address/port/server_socket 3项数据 + +服务器同时监听TCP/UDP端口时,收到TCP协议的数据会回调`onReceive`,收到UDP数据包回调`onPacket`,服务器设置的`EOF`或`Length`协议对UDP端口是不生效的,因为UDP包本身存在消息边界,不需要额外的协议处理。 + +> onPacket事件回调在1.7.18以上版本可用 +> 如果未设置onPacket回调函数,收到UDP数据包默认会回调onReceive函数 + +数据转换 +---- +`onPacket`回调可以通过计算得到`onReceive`的`$fd`和`$reactor_id`参数值。计算方法如下: + +```php +$fd = unpack('L', pack('N', ip2long($addr['address'])))[1]; +$reactor_id = ($addr['server_socket'] << 16) + $addr['port']; +``` diff --git a/doc/2.6.9 - onClose.md b/doc/2.6.9 - onClose.md new file mode 100644 index 0000000..befdcb1 --- /dev/null +++ b/doc/2.6.9 - onClose.md @@ -0,0 +1,22 @@ +#onClose + +TCP客户端连接关闭后,在worker进程中回调此函数。函数原型: +```php +function onClose(swoole_server $server, int $fd, int $reactorId); +``` +* `$server` 是swoole_server对象 +* `$fd` 是连接的文件描述符 +* `$reactorId` 来自那个reactor线程 + +``` +onClose回调函数如果发生了致命错误,会导致连接泄漏。通过netstat命令会看到大量CLOSE_WAIT状态的TCP连接 +``` + +> 无论由客户端发起close还是服务器端主动调用$serv->close()关闭连接,都会触发此事件。因此只要连接关闭,就一定会回调此函数 +> 1.7.7+版本以后onClose中依然可以调用connection_info方法获取到连接信息,在onClose回调函数执行完毕后才会调用close关闭TCP连接 + +注意:这里回调onClose时表示客户端连接已经关闭,所以无需执行`$server->close($fd)`。代码中执行`$serv->close($fd)`会抛出PHP错误告警。 + +主动关闭 +--- +swoole-1.9.7版本修改了`$reactorId`参数,当服务器主动关闭连接时,底层会设置此参数为`-1`,可以通过判断`$reactorId < 0`来分辨关闭是由服务器端还是客户端发起的。 \ No newline at end of file diff --git "a/doc/2.7 - \351\253\230\347\272\247\347\211\271\346\200\247.md" "b/doc/2.7 - \351\253\230\347\272\247\347\211\271\346\200\247.md" new file mode 100644 index 0000000..a37e68f --- /dev/null +++ "b/doc/2.7 - \351\253\230\347\272\247\347\211\271\346\200\247.md" @@ -0,0 +1,7 @@ +#高级特性 + +这里介绍下Swoole提供的高级特性,包括 + +* 调整IPC模式 +* TCP服务器心跳heartbeat方案 +* 数据缓冲 \ No newline at end of file diff --git "a/doc/2.7.10 - swoole_server\344\270\255\345\257\271\350\261\241\347\232\2044\345\261\202\347\224\237\345\221\275\345\221\250\346\234\237.md" "b/doc/2.7.10 - swoole_server\344\270\255\345\257\271\350\261\241\347\232\2044\345\261\202\347\224\237\345\221\275\345\221\250\346\234\237.md" new file mode 100644 index 0000000..b05bfef --- /dev/null +++ "b/doc/2.7.10 - swoole_server\344\270\255\345\257\271\350\261\241\347\232\2044\345\261\202\347\224\237\345\221\275\345\221\250\346\234\237.md" @@ -0,0 +1,40 @@ +#swoole_server中对象的4层生命周期 + +开发swoole程序与普通LAMP下编程有本质区别。在传统的Web编程中,PHP程序员只需要关注request到达,request结束即可。而在swoole程序中程序员可以操控更大范围,变量/对象可以有四种生存周期。 + +> 变量、对象、资源、require/include的文件等下面统称为对象 + + +程序全局期 +----- +在`swoole_server->start`之前就创建好的对象,我们称之为程序全局生命周期。这些变量在程序启动后就会一直存在,直到整个程序结束运行才会销毁。 + +有一些服务器程序可能会连续运行数月甚至数年才会关闭/重启,那么程序全局期的对象在这段时间持续驻留在内存中的。程序全局对象所占用的内存是`Worker`进程间共享的,不会额外占用内存。 + +这部分内存会在写时分离(`COW`),在`Worker`进程内对这些对象进行写操作时,会自动从共享内存中分离,变为**进程全局**对象。 + +> 程序全局期`include`/`require`的代码,必须在整个程序`shutdown`时才会释放,`reload`无效 + + +进程全局期 +----- +swoole拥有进程生命周期控制的机制,一个`Worker`子进程处理的请求数超过max_request配置后,就会自动销毁。`Worker`进程启动后创建的对象(onWorkerStart中创建的对象),在这个子进程存活周期之内,是常驻内存的。onConnect/onReceive/onClose 中都可以去访问它。 + +> 进程全局对象所占用的内存是在当前子进程内存堆的,并非共享内存。对此对象的修改仅在当前`Worker`进程中有效 +> 进程期include/require的文件,在`reload`后就会重新加载 + +会话期 +----- +会话期是在`onConnect`后创建,或者在第一次`onReceive`时创建,`onClose`时销毁。一个客户端连接进入后,创建的对象会常驻内存,直到此客户端离开才会销毁。 + +在LAMP中,一个客户端浏览器访问多次网站,就可以理解为会话期。但传统PHP程序,并不能感知到。只有单次访问时使用session_start,访问$_SESSION全局变量才能得到会话期的一些信息。 + +swoole中会话期的对象直接是常驻内存,不需要session_start之类操作。可以直接访问对象,并执行对象的方法。 + +请求期 +---- +请求期就是指一个完整的请求发来,也就是`onReceive`收到请求开始处理,直到返回结果发送`response`。这个周期所创建的对象,会在请求完成后销毁。 + +swoole中请求期对象与普通PHP程序中的对象就是一样的。请求到来时创建,请求结束后销毁。 + + diff --git "a/doc/2.7.11 - \345\234\250worker\350\277\233\347\250\213\345\206\205\347\233\221\345\220\254\344\270\200\344\270\252Server\347\253\257\345\217\243.md" "b/doc/2.7.11 - \345\234\250worker\350\277\233\347\250\213\345\206\205\347\233\221\345\220\254\344\270\200\344\270\252Server\347\253\257\345\217\243.md" new file mode 100644 index 0000000..faeced9 --- /dev/null +++ "b/doc/2.7.11 - \345\234\250worker\350\277\233\347\250\213\345\206\205\347\233\221\345\220\254\344\270\200\344\270\252Server\347\253\257\345\217\243.md" @@ -0,0 +1,22 @@ +#在worker进程内监听一个Server端口 + +在一些场景下,需要监听额外的端口提供特殊协议处理。如在HttpServer中需要监听8081端口,提供管理Server的功能。在Swoole扩展内置的服务中不支持同时处理2种协议,即使是使用了addlistener添加了多个端口也不能接受2种协议的请求包。 + +这时候可以使用本地监听来解决此问题,原理是在某一个worker进程内,创建stream_socket_server,并加入到swoole_event中。 + +```php +$serv = new swoole_server("0.0.0.0", 9502); + +$serv->on('workerstart', function($server, $id) { + //仅在worker-0中监听管理端口 + if ($id != 0) return; + $local_listener = stream_socket_server("tcp://127.0.0.1:8081", $errno, $errstr); + swoole_event_add($local_listener, function($server) { + $local_client = stream_socket_accept($server, 0); + swoole_event_add($local_client, function($client) { + echo fread($client, 8192); + fwrite($client, "hello"); + }); + }); +}); +``` \ No newline at end of file diff --git "a/doc/2.7.2 - \345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\347\232\204 reactor_id \345\222\214 fd.md" "b/doc/2.7.2 - \345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\347\232\204 reactor_id \345\222\214 fd.md" new file mode 100644 index 0000000..6dbc684 --- /dev/null +++ "b/doc/2.7.2 - \345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\347\232\204 reactor_id \345\222\214 fd.md" @@ -0,0 +1,31 @@ +#回调函数中的 reactor_id 和 fd + +服务器的`onConnect`、`onReceive`、`onClose`回调函数中会携带`reactor_id`和`fd`两个参数。 + +* `$reactor_id`是来自于哪个reactor线程 +* `$fd`是`TCP`客户端连接的标识符,在Server程序中是唯一的 +* `fd` 是一个自增数字,范围是`1 ~ 1600万`,fd超过`1600万`后会自动从`1`开始进行复用 +* `$fd`是复用的,当连接关闭后`fd`会被新进入的连接复用 +* 正在维持的TCP连接`fd`不会被复用 + +调用`swoole_server->send`/`swoole_server->close`函数需要传入`$fd`参数才能被正确的处理。如果业务中需要发送广播,需要用`apc`、`redis`、`MySQL`、`memcache`、`swoole_table`将`fd`的值保存起来。 + +```php +function my_onReceive($serv, $fd, $reactor_id, $data) { + //向Connection发送数据 + $serv->send($fd, 'Swoole: '.$data); + + //关闭Connection + $serv->close($fd); +} +``` + +fd 为什么使用整型 +------ +`$fd`使用整型而不是使用对象,主要原因是swoole是多进程的模型,在`Worker`进程/Task进程中随时可能要访问某一个客户端连接,如果使用对象,那就需要进行`Serialize/Unserialize`。增加了额外的性能开销。`$fd` 如果是整数那就可以直接存储传输被使用。 + +在PHP层可以也客户端连接可以封装成对象。面向对象的好处是可读性更好,对连接的操作可以封装到方法中。如 +```php +$connection->send($data); +$connection->close(); +``` diff --git "a/doc/2.7.3 - Length_Check \345\222\214 EOF_Check \347\232\204\344\275\277\347\224\250.md" "b/doc/2.7.3 - Length_Check \345\222\214 EOF_Check \347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..cb63957 --- /dev/null +++ "b/doc/2.7.3 - Length_Check \345\222\214 EOF_Check \347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,29 @@ +#Length_Check 和 EOF_Check 的使用 + +在外网通信时,有些客户端发送数据的速度较慢,每次只能发送一小段数据。这样`onReceive`到的数据就不是一个完整的包。 +还有些客户端是逐字节发送数据的,如果每次回调`onReceive`会拖慢整个系统。 + +`Swoole`提供了`length_check`和`eof_check`的功能,在扩展底层检测到如果不是完整的请求,会等待新的数据到达,组成完整的请求后再回调`onReceive`。 + +EOF检测 +----- +在`swoole_server::set`中增加`open_eof_check`和`package_eof`来开启此功能。`open_eof_check => true`表示启用结束符检查,`package_eof`设置数据包结束符。[查看详细说明](/wiki/page/224.html) + +Length检测 +---- +在`swoole_server::set`中增加`open_length_check`来开启此功能。[查看详细说明](/wiki/page/287.html) + +> `buffer`功能会将所有收到的数据放到内存中,会占用较多内存 +> 通过设置 `package_max_length` 来设定每个连接最大缓存多少数据,超过此大小的连接将会被关闭 + +示例: +```php +$server->set(array( + 'worker_num' => 4, //worker process num + 'backlog' => 128, //listen backlog + 'max_request' => 50, + 'dispatch_mode'=>1, + 'package_eof' => "\r\n\r\n", //http协议就是以\r\n\r\n作为结束符的,这里也可以使用二进制内容 + 'open_eof_check' => 1, +)); +``` diff --git "a/doc/2.7.4 - Worker\344\270\216Reactor\351\200\232\344\277\241\346\250\241\345\274\217.md" "b/doc/2.7.4 - Worker\344\270\216Reactor\351\200\232\344\277\241\346\250\241\345\274\217.md" new file mode 100644 index 0000000..7f6978a --- /dev/null +++ "b/doc/2.7.4 - Worker\344\270\216Reactor\351\200\232\344\277\241\346\250\241\345\274\217.md" @@ -0,0 +1,34 @@ +#Worker与Reactor通信模式 + + `Worker`进程如何与`Reactor`线程通信,Swoole提供了`5`种方式。通过`swoole_server::set`方法设置`dispatch_mode`来配置。 + +轮询模式 +----- +`dispatch_mode = 1` +收到的请求数据包会轮询发到每个Worker进程。 + +FD取模 +----- +`dispatch_mode = 2` + +数据包根据`fd`的值`%worker_num`来分配,这个模式可以保证一个`TCP`客户端连接发送的数据总是会被分配给同一个worker进程。 +这种模式可能会存在性能问题,作为SOA服务器时,不应当使用此模式。因为客户端很可能用了连接池,客户端100个进程复用10个连接,也就是同时只有10个swoole worker进程在处理请求。这种模式的业务系统可以使用`dispatch_mode = 3`,抢占式分配。 + +忙闲分配 +----- +`dispatch_mode = 3` +此模式下,`Reactor`只会给空闲的`Worker`进程投递数据。 +这个模式的缺点是,客户端连接对应的`Worker`是随机的。不确定哪个`Worker`会处理请求。无法保存连接状态。 +当然也可以借助第三方库来实现保存连接状态和会话内容,比如`apc/redis/memcache`。 + +IP取模 +---- +`dispatch_mode = 4` + +如果客户端的连接不稳定,经常发生断线重连,`fd`的值不是固定的,使用`IP`进行取模分配可以解决此问题。同一个`IP`地址会被分配到同一个`Worker`进程。 + +UID取模 +---- +`dispatch_mode = 5` + +与`fd`或`IP`取模分配一致,`dispatch_mode = 5` 需要应用层调用`bind`方法设置一个`UID` \ No newline at end of file diff --git "a/doc/2.7.5 - TCP-Keepalive\346\255\273\350\277\236\346\216\245\346\243\200\346\265\213.md" "b/doc/2.7.5 - TCP-Keepalive\346\255\273\350\277\236\346\216\245\346\243\200\346\265\213.md" new file mode 100644 index 0000000..538bb63 --- /dev/null +++ "b/doc/2.7.5 - TCP-Keepalive\346\255\273\350\277\236\346\216\245\346\243\200\346\265\213.md" @@ -0,0 +1,17 @@ +#TCP-Keepalive死连接检测 + +在TCP中有一个Keep-Alive的机制可以检测死连接,应用层如果对于死链接周期不敏感或者没有实现心跳机制,可以使用操作系统提供的keepalive机制来踢掉死链接。 +在server_swoole_set中增加open_tcp_keepalive=>1表示启用tcp keepalive。 +另外,有3个选项可以对keepalive的细节进行调整。 + +tcp_keepidle +----- +单位秒,连接在n秒内没有数据请求,将开始对此连接进行探测。 + +tcp_keepcount +----- +探测的次数,超过次数后将close此连接。 + +tcp_keepinterval +----- +探测的间隔时间,单位秒。 diff --git "a/doc/2.7.6 - TCP\346\234\215\345\212\241\345\231\250\345\277\203\350\267\263\347\273\264\346\214\201\346\226\271\346\241\210.md" "b/doc/2.7.6 - TCP\346\234\215\345\212\241\345\231\250\345\277\203\350\267\263\347\273\264\346\214\201\346\226\271\346\241\210.md" new file mode 100644 index 0000000..a233148 --- /dev/null +++ "b/doc/2.7.6 - TCP\346\234\215\345\212\241\345\231\250\345\277\203\350\267\263\347\273\264\346\214\201\346\226\271\346\241\210.md" @@ -0,0 +1,23 @@ +#TCP服务器心跳维持方案 + +正常情况下客户端中断TCP连接时,会发送一个FIN包,进行4次断开握手来通知服务器。但一些异常情况下,如客户端突然断电断网或者网络异常,服务器可能无法得知客户端已断开连接。 + +尤其是移动网络,TCP连接非常不稳定,所以需要一套机制来保证服务器和客户端之间连接的有效性。 + +Swoole扩展本身内置了这种机制,开发者只需要配置一个参数即可启用。Swoole在每次收到客户端数据会记录一个时间戳,当客户端在一定时间内未向服务器端发送数据,那服务器会自动切断连接。 + + +使用方法: +----- +```php +$serv->set(array( + 'heartbeat_check_interval' => 5, + 'heartbeat_idle_time' => 10, +)); +``` +上面的设置就是每5秒侦测一次心跳,一个TCP连接如果在10秒内未向服务器端发送数据,将会被切断。 + +高级用法: +----- +使用swoole_server::heartbeat()函数手工检测心跳是否到期。此函数会返回闲置时间超过heartbeat_idle_time的所有TCP连接。程序中可以将这些连接做一些操作,如发送数据或关闭连接。 + diff --git "a/doc/2.7.7 - \345\244\232\347\253\257\345\217\243\347\233\221\345\220\254\347\232\204\344\275\277\347\224\250.md" "b/doc/2.7.7 - \345\244\232\347\253\257\345\217\243\347\233\221\345\220\254\347\232\204\344\275\277\347\224\250.md" new file mode 100644 index 0000000..3b5cfd4 --- /dev/null +++ "b/doc/2.7.7 - \345\244\232\347\253\257\345\217\243\347\233\221\345\220\254\347\232\204\344\275\277\347\224\250.md" @@ -0,0 +1,35 @@ +#多端口监听的使用 + +Swoole提供了多端口监听的机制,这样可以同时监听UDP和TCP,同时监听内网地址和外网地址。内网地址和端口用于管理,外网地址用于对外服务。 + +```php +$serv = new swoole_server("0.0.0.0", 9501); +//这里监听了一个UDP端口用来做内网管理 +$serv->addlistener('127.0.0.1', 9502, SWOOLE_SOCK_UDP); +$serv->on('connect', function ($serv, $fd) { + echo "Client:Connect.\n"; +}); +$serv->on('receive', function ($serv, $fd, $from_id, $data) { + $info = $serv->connection_info($fd, $from_id); + //来自9502的内网管理端口 + if($info['server_port'] == 9502) { + $serv->send($fd, "welcome admin\n"); + } + //来自外网 + else { + $serv->send($fd, 'Swoole: '.$data); + } +}); +$serv->on('close', function ($serv, $fd) { + echo "Client: Close.\n"; +}); +$serv->start(); +``` + +Web层只需向此UDP端口发送管理的指令即可 +```php +$client = new swoole_client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC); +$client->connect('127.0.0.1', 9502); +$client->send("admin"); +echo $client->recv(); +``` \ No newline at end of file diff --git "a/doc/2.7.8 - \346\215\225\350\216\267Server\350\277\220\350\241\214\346\234\237\350\207\264\345\221\275\351\224\231\350\257\257.md" "b/doc/2.7.8 - \346\215\225\350\216\267Server\350\277\220\350\241\214\346\234\237\350\207\264\345\221\275\351\224\231\350\257\257.md" new file mode 100644 index 0000000..f4834a3 --- /dev/null +++ "b/doc/2.7.8 - \346\215\225\350\216\267Server\350\277\220\350\241\214\346\234\237\350\207\264\345\221\275\351\224\231\350\257\257.md" @@ -0,0 +1,57 @@ +#捕获Server运行期致命错误 + +Server运行期一旦发生致命错误,那客户端连接将无法得到回应。如Web服务器,如果有致命错误应当向客户端发送Http 500 错误信息。 + +在PHP中可以通过register_shutdown_function + error_get_last 2个函数来捕获致命错误,并将错误信息发送给客户端连接。具体代码示例如下: + +```php +register_shutdown_function('handleFatal'); +function handleFatal() +{ + $error = error_get_last(); + if (isset($error['type'])) + { + switch ($error['type']) + { + case E_ERROR : + case E_PARSE : + case E_CORE_ERROR : + case E_COMPILE_ERROR : + $message = $error['message']; + $file = $error['file']; + $line = $error['line']; + $log = "$message ($file:$line)\nStack trace:\n"; + $trace = debug_backtrace(); + foreach ($trace as $i => $t) + { + if (!isset($t['file'])) + { + $t['file'] = 'unknown'; + } + if (!isset($t['line'])) + { + $t['line'] = 0; + } + if (!isset($t['function'])) + { + $t['function'] = 'unknown'; + } + $log .= "#$i {$t['file']}({$t['line']}): "; + if (isset($t['object']) and is_object($t['object'])) + { + $log .= get_class($t['object']) . '->'; + } + $log .= "{$t['function']}()\n"; + } + if (isset($_SERVER['REQUEST_URI'])) + { + $log .= '[QUERY] ' . $_SERVER['REQUEST_URI']; + } + error_log($log); + $serv->send($this->currentFd, $log); + default: + break; + } + } +} +``` \ No newline at end of file diff --git "a/doc/2.7.9 - swoole_server\347\232\204\344\270\244\347\247\215\350\277\220\350\241\214\346\250\241\345\274\217\344\273\213\347\273\215.md" "b/doc/2.7.9 - swoole_server\347\232\204\344\270\244\347\247\215\350\277\220\350\241\214\346\250\241\345\274\217\344\273\213\347\273\215.md" new file mode 100644 index 0000000..581f224 --- /dev/null +++ "b/doc/2.7.9 - swoole_server\347\232\204\344\270\244\347\247\215\350\277\220\350\241\214\346\250\241\345\274\217\344\273\213\347\273\215.md" @@ -0,0 +1,42 @@ +#swoole_server的两种运行模式介绍 + +单线程模式(SWOOLE_BASE) +----- +这种模式就是传统的异步非阻塞`Server`。在`Reactor`内直接回调`PHP`的函数。如果回调函数中有阻塞操作会导致`Server`退化为同步模式。`worker_num`参数对与`BASE`模式仍然有效,`swoole`会启动多个`Reactor`进程。 + +> `BASE`模式下`Reactor`和`Worker`是同一个角色 + +__BASE模式的优点:__ + +* BASE模式没有IPC开销,性能更好 +* BASE模式代码更简单,不容易出错 + +__BASE模式的缺点:__ + +* `TCP`连接是在`Worker`进程中维持的,所以当某个`Worker`进程挂掉时,此`Worker`内的所有连接都将被关闭 +* 少量`TCP`长连接无法利用到所有`Worker`进程 +* `TCP`连接与`Worker`是绑定的,长连接应用中某些连接的数据量大,这些连接所在的`Worker`进程负载会非常高。但某些连接数据量小,所以在`Worker`进程的负载会非常低,不同的`Worker`进程无法实现均衡。 + +__BASE模式的适用场景:__ + +如果客户端连接之间不需要交互,可以使用BASE模式。如Memcache、Http服务器等。 + + +进程模式(SWOOLE_PROCESS) +----- +多进程模式是最复杂的方式,用了大量的进程间通信、进程管理机制。适合业务逻辑非常复杂的场景。`Swoole`提供了完善的进程管理、内存保护机制。 +在业务逻辑非常复杂的情况下,也可以长期稳定运行。 + +`Swoole`在`Reactor`线程中提供了`Buffer`的功能,可以应对大量慢速连接和逐字节的恶意客户端。另外也提供了`CPU`亲和设置选项,使程序运行的效率更好。 + +__进程模式的优点:__ + +* 连接与数据请求发送是分离的,不会因为某些连接数据量大某些连接数据量小导致`Worker`进程不均衡 +* `Worker`进程发送致命错误时,连接并不会被切断 +* 可实现单连接并发,仅保持少量`TCP`连接,请求可以并发地在多个`Worker`进程中处理 + +__进程模式的缺点:__ + +* 存在`2`次`IPC`的开销,`master`进程与`worker`进程需要使用`UnixSocket`进行通信 +* 不支持某些高级功能,如`sendwait`、`pause`、`resume`等操作 + diff --git "a/doc/2.8 - \345\270\270\350\247\201\351\227\256\351\242\230.md" "b/doc/2.8 - \345\270\270\350\247\201\351\227\256\351\242\230.md" new file mode 100644 index 0000000..d2003d5 --- /dev/null +++ "b/doc/2.8 - \345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -0,0 +1,20 @@ +#常见问题 + +连接超时 +----- +* Chrome错误信息:Error in connection establishment: net::ERR_TIMED_OUT +* Socket客户端连接错误码:110, 114, 115 + +此类错误可能是网络通信存在问题,如主机IP不可达、防火墙等原因。TCP的三次握手是由Linux内核完成的,与应用层软件无关,只要Server监听此端口,服务器就会自动对客户端连接完成握手,无需Server程序参与。 + +连接被拒绝 +--------- +* Socket客户端连接错误码:111 + +服务器未监听此端口或者监听端口的listen队列已满。 + +接收超时 +--- +* Socket客户端连接错误码:`11` + +数据接收超时,表示服务器端在规定的时间内未向客户端发送数据。一般出现在同步客户端中,调用`$client->recv`接收`Response`,服务器处理的时间过长,超过了`$client->connect`设置的超时时间(默认`500ms`)。 diff --git "a/doc/2.8.1 - \344\270\272\344\273\200\344\271\210\344\270\215\350\246\201send\345\256\214\345\220\216\347\253\213\345\215\263close.md" "b/doc/2.8.1 - \344\270\272\344\273\200\344\271\210\344\270\215\350\246\201send\345\256\214\345\220\216\347\253\213\345\215\263close.md" new file mode 100644 index 0000000..1f990b9 --- /dev/null +++ "b/doc/2.8.1 - \344\270\272\344\273\200\344\271\210\344\270\215\350\246\201send\345\256\214\345\220\216\347\253\213\345\215\263close.md" @@ -0,0 +1,23 @@ +#为什么不要send完后立即close + +send完后立即close就是不安全的,无论是服务器端还是客户端。 + +send操作成功只是表示数据成功地写入到操作系统socket缓存区,不代表对端真的接收到了数据。究竟操作系统有没有发送成功,对方服务器是否收到,服务器端程序是否处理,都不没办法确切保证。 + +> close后的逻辑请看下面的linger设置相关 + +这个逻辑和电话沟通是一个道理,A告诉B一个事情,A说完了就挂掉电话。那么B听到没有,A是不知道的。如果A说完事情,B说好,然后B挂掉电话,就绝对是安全的。 + +linger设置 +---- +一个`socket`在close时,如果发送缓冲区仍然有数据,操作系统底层会根据`linger`设置决定如何处理 +```c +struct linger +{ + int l_onoff; + int l_linger; +}; +``` +* l_onoff = 0,close时立刻返回,底层会将未发送完的数据发送完成后再释放资源,也就是优雅的退出。 +* l_onoff != 0,l_linger = 0,close时会立刻返回,但不会发送未发送完成的数据,而是通过一个RST包强制的关闭socket描述符,也就是强制的退出。 +* l_onoff !=0,l_linger > 0, closes时不会立刻返回,内核会延迟一段时间,这个时间就由l_linger的值来决定。如果超时时间到达之前,发送完未发送的数据(包括FIN包)并得到另一端的确认,close会返回正确,socket描述符优雅性退出。否则close会直接返回错误值,未发送数据丢失,socket描述符被强制性退出。如果socket描述符被设置为非堵塞型,则close会直接返回值。 diff --git "a/doc/2.8.2 - \345\246\202\344\275\225\345\234\250\345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\350\256\277\351\227\256\345\244\226\351\203\250\347\232\204\345\217\230\351\207\217.md" "b/doc/2.8.2 - \345\246\202\344\275\225\345\234\250\345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\350\256\277\351\227\256\345\244\226\351\203\250\347\232\204\345\217\230\351\207\217.md" new file mode 100644 index 0000000..9d724e1 --- /dev/null +++ "b/doc/2.8.2 - \345\246\202\344\275\225\345\234\250\345\233\236\350\260\203\345\207\275\346\225\260\344\270\255\350\256\277\351\227\256\345\244\226\351\203\250\347\232\204\345\217\230\351\207\217.md" @@ -0,0 +1,81 @@ +#如何在回调函数中访问外部的变量 + +在swoole_server/swoole_client的事件回调函数中,需要读取调用外部的变量和对象,可以通过下面的几种方法实现。 + +一、事件回调函数改用对象+属性 +---- + +```php +class Server +{ + public $buffer; + public $serv; + + function onReceive($serv, $fd, $from_id, $data) + { + //在这里可以读取到EventCallback对象上的属性和方法 + $this->buffer[$fd] = $data; + $this->hello(); + } + + function hello() + { + + } + + function run() + { + $serv = new swoole_server('127.0.0.1', 9501); + $this->serv = $serv; + $serv->on('receive', array($this, 'onReceive')); + $serv->start(); + } +} + +$server= new Server; +$server->run(); +``` + +二、使用全局变量 +----- +```php +$buffer = array(); +$serv->on('receive', function($serv, $fd, $from_id, $data) { + global $buffer; + $buffer[$fd] = $data; +}); +``` + +三、使用类静态变量 +----- +```php +class Test +{ + static $buffer; +} + +$serv->on('receive', function($serv, $fd, $from_id, $data) { + Test::$buffer[$fd] = $data; +}); +``` + +四、匿名函数中使用use语法 +----- +```php +$buffer = array(); +$serv->on('receive', function($serv, $fd, $from_id, $data) use ($buffer) { + $buffer[$fd] = $data; +}); +``` +> 需要PHP5.4或更高的版本 +> 多个参数可以用逗号隔开,如use ($buffer, $users, $config) + +五、直接保存在swoole_server/swoole_client对象上 +---- +```php +$serv->buffer = array(); +$serv->on('receive', function($serv, $fd, $from_id, $data) use ($buffer) { + $serv->buffer[$fd] = $data; +}); +``` + diff --git "a/doc/2.8.3 - swoole_server\344\270\255\345\206\205\345\255\230\347\256\241\347\220\206\346\234\272\345\210\266.md" "b/doc/2.8.3 - swoole_server\344\270\255\345\206\205\345\255\230\347\256\241\347\220\206\346\234\272\345\210\266.md" new file mode 100644 index 0000000..4efab01 --- /dev/null +++ "b/doc/2.8.3 - swoole_server\344\270\255\345\206\205\345\255\230\347\256\241\347\220\206\346\234\272\345\210\266.md" @@ -0,0 +1,87 @@ +#swoole_server中内存管理机制 + + `swoole_server`启动后内存管理的底层原理与普通php-cli程序一致。具体请参考`Zend VM`内存管理方面的文章。 + +局部变量 +---- +在事件回调函数返回后,所有局部对象和变量会全部回收,不需要`unset`。如果变量是一个资源类型,那么对应的资源也会被PHP底层释放。 + +```php +function test() +{ + $a = new Object; + $b = fopen('/data/t.log', 'r+'); + $c = new swoole_client(SWOOLE_SYNC); + $d = new swoole_client(SWOOLE_SYNC); + global $e; + $e['client'] = $d; +} +``` +* $a, $b, $c 都是局部变量,当此函数`return`时,这3个变量会立即释放,对应的内存会立即释放,打开的IO资源文件句柄会立即关闭。 +* $d 也是局部变量,但是`return`前将它保存到了全局变量$e,所以不会释放。当执行`unset($e['client'])`时,并且没有任何其他PHP变量仍然在引用$d变量,那么$d 就会被释放。 + +全局变量 +---- +在PHP中,有3类全局变量。 + +* 使用`global`关键词声明的变量 +* 使用`static`关键词声明的类静态变量、函数静态变量 +* PHP的超全局变量,包括`$_GET`、`$_POST`、`$GLOBALS`等 + +全局变量和对象,类静态变量,保存在swoole_server对象上的变量不会被释放。需要程序员自行处理这些变量和对象的销毁工作。 + +```php +class Test +{ + static $array = array(); + static $string = ''; +} + +function onReceive($serv, $fd, $reactorId, $data) +{ + Test::$array[] = $fd; + Test::$string .= $data; +} +``` + +* 在事件回调函数中需要特别注意非局部变量的array类型值,某些操作如 TestClass::$array[] = "string" 可能会造成内存泄漏,严重时可能发生爆内存,必要时应当注意清理大数组。 + +* 在事件回调函数中,非局部变量的字符串进行拼接操作是必须小心内存泄漏,如 TestClass::$string .= $data,可能会有内存泄漏,严重时可能发生爆内存。 + +解决方法 +---- +* 同步阻塞并且请求响应式无状态的Server程序可以设置`max_request`,当Worker进程/Task进程结束运行时或达到任务上限后进程自动退出。该进程的所有变量/对象/资源均会被释放回收。 +* 程序内在`onClose`或设置`定时器`及时使用`unset`清理变量,回收资源 + +异步客户端 +---- +Swoole提供的异步客户端与普通的PHP变量不同,异步客户端在发起`connect`时底层会增加一次引用计数,在连接`close`时会减少引用计数。 + +> 包括`swoole_client`、`swoole_mysql`、`swoole_redis`、`swoole_http_client` + +```php +function test() +{ + $client = new swoole_client(SWOOLE_TCP | SWOOLE_ASYNC); + $client->on("connect", function($cli) { + $cli->send("hello world\n"); + }); + $client->on("receive", function($cli, $data){ + echo "Received: ".$data."\n"; + $cli->close(); + }); + $client->on("error", function($cli){ + echo "Connect failed\n"; + }); + $client->on("close", function($cli){ + echo "Connection close\n"; + }); + $client->connect('127.0.0.1', 9501); + return; +} +``` + +* `$client`是局部变量,常规情况下return时会销毁。 +* 但这个`$client`是异步客户端在执行`connect`时swoole引擎底层会增加一次引用计数,因此return时并不会销毁。 +* 该客户端执行`onReceive`回调函数时进行了`close`或者服务器端主动关闭连接触发`onClose`,这时底层会减少引用计数,`$client`才会被销毁。 + diff --git "a/doc/2.8.4 - \346\230\257\345\220\246\345\217\257\344\273\245\345\205\261\347\224\2501\344\270\252redis\346\210\226mysql\350\277\236\346\216\245.md" "b/doc/2.8.4 - \346\230\257\345\220\246\345\217\257\344\273\245\345\205\261\347\224\2501\344\270\252redis\346\210\226mysql\350\277\236\346\216\245.md" new file mode 100644 index 0000000..f4c7dd1 --- /dev/null +++ "b/doc/2.8.4 - \346\230\257\345\220\246\345\217\257\344\273\245\345\205\261\347\224\2501\344\270\252redis\346\210\226mysql\350\277\236\346\216\245.md" @@ -0,0 +1,29 @@ +#是否可以共用1个redis或mysql连接 + +绝对不可以。必须每个进程单独创建`Redis`、`MySQL`、`PDO`连接,其他的存储客户端同样也是如此。原因是如果共用1个连接,那么返回的结果无法保证被哪个进程处理。持有连接的进程理论上都可以对这个连接进行读写,这样数据就发生错乱了。 + +__所以在多个进程之间,一定不能共用连接__ + +* 在`swoole_server`中,应当在`onWorkerStart`中创建连接对象 +* 在`swoole_process`中,应当在`swoole_process->start`后,子进程的回调函数中创建连接对象 +* 本页面所述信息对使用`pcntl_fork`的程序同样有效 + +示例 +---- +```php +$serv = new swoole_server("0.0.0.0", 9502); + +//必须在onWorkerStart回调中创建redis/mysql连接 +$serv->on('workerstart', function($serv, $id) { + $redis = new redis; + $redis->connect('127.0.0.1', 6379); + $serv->redis = $redis; +}); + +$serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) { + $value = $serv->redis->get("key"); + $serv->send($fd, "Swoole: ".$value); +}); + +$serv->start(); +``` \ No newline at end of file diff --git "a/doc/2.8.6 - 4\347\247\215PHP\345\233\236\350\260\203\345\207\275\346\225\260\351\243\216\346\240\274.md" "b/doc/2.8.6 - 4\347\247\215PHP\345\233\236\350\260\203\345\207\275\346\225\260\351\243\216\346\240\274.md" new file mode 100644 index 0000000..dd6f003 --- /dev/null +++ "b/doc/2.8.6 - 4\347\247\215PHP\345\233\236\350\260\203\345\207\275\346\225\260\351\243\216\346\240\274.md" @@ -0,0 +1,48 @@ +#4种PHP回调函数风格 + +匿名函数 +---- +```php +$server->on('Request', function ($req, $resp) { + echo "hello world"; +}); +``` + +类静态方法 +--- +```php +class A +{ + static function test($req, $resp) + { + echo "hello world"; + } +} +$server->on('Request', 'A::Test'); +$server->on('Request', array('A', 'Test')); +``` + +函数 +---- +```php +function my_onRequest($req, $resp) +{ + echo "hello world"; +} +$server->on('Request', 'my_onRequest'); +``` + +对象方法 +---- +```php +class A +{ + function test($req, $resp) + { + echo "hello world"; + } +} + +$object = new A(); +$server->on('Request', array($object, 'test')); +``` \ No newline at end of file diff --git "a/doc/2.8.7 - \344\270\215\345\220\214\347\232\204Server\347\250\213\345\272\217\345\256\236\344\276\213\351\227\264\345\246\202\344\275\225\351\200\232\344\277\241.md" "b/doc/2.8.7 - \344\270\215\345\220\214\347\232\204Server\347\250\213\345\272\217\345\256\236\344\276\213\351\227\264\345\246\202\344\275\225\351\200\232\344\277\241.md" new file mode 100644 index 0000000..fc2b7eb --- /dev/null +++ "b/doc/2.8.7 - \344\270\215\345\220\214\347\232\204Server\347\250\213\345\272\217\345\256\236\344\276\213\351\227\264\345\246\202\344\275\225\351\200\232\344\277\241.md" @@ -0,0 +1,16 @@ +#不同的Server程序实例间如何通信 + +有2种方法可以实现不同的Server程序实例间通信。 + +额外监听一个UDP端口 +---------- +* 额外监听一个UDP端口并设置`onPacket`回调,接收来自其他Server发来的消息 +* `UDP`通信不是可靠的,消息可能会丢失。需要应用层发送消息接收回执 +* `UDP`通信不存在阻塞IO + +使用swoole_client作为客户端访问Server +----- +在`Server`程序中创建一个`TCP`客户端,连接到另外一个`Server`程序。实现`Server`与`Server`之间通信。`TCP`客户端需要实现对端`Server`约定的通信协议。可使用`Server::getClientInfo`得到客户端连接的来源`IP`实现安全防护机制。 + +* `TCP`通信时可靠的可以保证安全性 +* 创建`TCP`客户端连接是推荐的做法,异步`Server`中创建一个异步的TCP客户端可以保证整个Server程序是纯异步的 diff --git "a/doc/2.8.8 - \351\224\231\350\257\257\344\277\241\346\201\257\357\274\232ERROR (9006).md" "b/doc/2.8.8 - \351\224\231\350\257\257\344\277\241\346\201\257\357\274\232ERROR (9006).md" new file mode 100644 index 0000000..e8fb0cf --- /dev/null +++ "b/doc/2.8.8 - \351\224\231\350\257\257\344\277\241\346\201\257\357\274\232ERROR (9006).md" @@ -0,0 +1,7 @@ +#错误信息:ERROR (9006) + +错误信息:`Worker#1 pipe buffer is full, the reactor will block.` + +出现此信息表示Worker进程和Reactor线程间通信的管道缓存区已满,这时Reactor线程将阻塞1秒钟等待Worker进程读取管道中的数据。 + +如果持续报出此错误,说明服务器当前并发请求数量已经超过Worker进程处理能力。需要调大Worker进程数,或者 尽快扩容机器进行解决。 \ No newline at end of file diff --git a/doc/2.8.9 - eventLoop has already been created. unable to create swoole_server.md b/doc/2.8.9 - eventLoop has already been created. unable to create swoole_server.md new file mode 100644 index 0000000..01e3e4c --- /dev/null +++ b/doc/2.8.9 - eventLoop has already been created. unable to create swoole_server.md @@ -0,0 +1,10 @@ +#eventLoop has already been created. unable to create swoole_server + +创建`Server`出现: +``` +PHP Fatal error: swoole_server::__construct(): eventLoop has already been created. unable to create swoole_server. +``` + +这表示你的程序在`new swoole_server`之前使用了其他异步IO的API,底层已经创建了`EventLoop`,无法重复创建。 + +这是错误的用法,如果要在`Server`中使用异步的`Client`、`MySQL`、`Redis`,请在`Server`的`onWorkerStart`回调函数或其他发生在`Worker`进程内的回调函数中使用。 \ No newline at end of file diff --git "a/doc/2.9 - \345\216\213\345\212\233\346\265\213\350\257\225.md" "b/doc/2.9 - \345\216\213\345\212\233\346\265\213\350\257\225.md" new file mode 100644 index 0000000..6a672d0 --- /dev/null +++ "b/doc/2.9 - \345\216\213\345\212\233\346\265\213\350\257\225.md" @@ -0,0 +1,40 @@ +#压力测试 + +注意事项 +---- +> 不同的硬件平台和软件环境,测试出的实际数据并不相同,因此仅建议进行基准测试,在相同的环境下测试不同软件系统之间的性能差距 + +* 编译`Swoole`必须关闭`debug`,使用`gcc -O2`或更高优化级别 +* 关闭屏幕输出,否则打印屏幕的`echo`操作会使服务器阻塞 +* 在多核的机器上开启合适的进程数量,进程数量不足将无法发挥全部硬件计算能力 +* 检查程序中是否存在`PHP`错误,`PHP`错误处理会使服务器的处理能力大幅下降 + +优化选项 +---- +* 使用`SWOOLE_BASE`模式,可以减少`2`次`IPC`开销,提升性能 +* 启用端口复用,可大幅提高短连接服务的性能,需要`Linux-3.10`或更高版本内核 +* `Http`服务器请关闭`gzip`压缩,可节省服务器`CPU`的开销 +* `Http`服务器请使用`KeepAlive`长连接测试,避免短连接的`IO`开销降低性能差异比例 +* 移除没有实际逻辑的回调设置,如程序中并未使用`onConnect`和`onClose`回调,在代码中不要设置这`2`项回调 + + +```php +Swoole\Async::set(array('enable_reuse_port' => true)); +$serv = new Swoole\Server("0.0.0.0", 9502, SWOOLE_BASE); + +$serv->set(array( + 'worker_num' => 8, +)); + +$serv->on('Receive', function (swoole_server $serv, $fd, $from_id, $data) +{ + $serv->send($fd, "Swoole: " . $data); +}); + +$serv->start(); +``` + +其他提示 +---- +* 使用`ab`测试工具时,请开启`-k`长连接 +* `ab`测试工具不支持`Http-Chunk`,在`Http\Server`中请勿使用`response->write` \ No newline at end of file diff --git "a/doc/2.9.1 - \345\271\266\345\217\22110\344\270\207TCP\350\277\236\346\216\245\347\232\204\346\265\213\350\257\225.md" "b/doc/2.9.1 - \345\271\266\345\217\22110\344\270\207TCP\350\277\236\346\216\245\347\232\204\346\265\213\350\257\225.md" new file mode 100644 index 0000000..32ae037 --- /dev/null +++ "b/doc/2.9.1 - \345\271\266\345\217\22110\344\270\207TCP\350\277\236\346\216\245\347\232\204\346\265\213\350\257\225.md" @@ -0,0 +1,13 @@ +#并发10万TCP连接的测试 + +代码在`examples/c10k.php`中,本测试启动`10`个子进程,发起长连接到`Swoole`的`TCP Server.` + +由于单台机器的原因,`ip_local_port_range`的范围是`32000-60000`。 +运行到`28000`个长连接时,由于`local port`不够用,无法再继续连接。 + +并发测试中使用`4`台客户端机器同时连接服务器,每台机器与服务器建立`2.8W`个`TCP`连接。`TCP Server`共维持了`11.2W`长连接。 + +`Swoole`使用`epoll`作为事件轮询,可维持大量`TCP`连接。只要操作系统的内存足够,就一直可以增加维持的`TCP`长连接。 + +`swoole_server`每个连接所占用的内存为`220`字节,使用数据缓存,如`EOF_CHECK`/`LENGTH_CHECK`后可能会增加到每连接`8K`。 + diff --git a/doc/3 - Client.md b/doc/3 - Client.md new file mode 100644 index 0000000..f9f5192 --- /dev/null +++ b/doc/3 - Client.md @@ -0,0 +1,50 @@ +#Client + + `swoole_client`提供了`tcp/udp socket`的客户端的封装代码,使用时仅需 `new swoole_client` 即可。 +`swoole`的`socket client`对比PHP提供的`stream`族函数有哪些好处: + +* `stream`函数存在超时设置的陷阱和Bug,一旦没处理好会导致Server端长时间阻塞 +* `fread`有8192长度限制,无法支持UDP的大包 +* `swoole_client`支持`waitall`,在知道包长度的情况下可以一次取完,不必循环取。 +* `swoole_client`支持UDP connect,解决了UDP串包问题 +* `swoole_client`是纯C的代码,专门处理socket,`stream`函数非常复杂。`swoole_client`性能更好 + +除了普通的同步阻塞+select的使用方法外,swoole_client还支持异步非阻塞回调。 + +同步阻塞客户端 +----- +```php +$client = new swoole_client(SWOOLE_SOCK_TCP); +if (!$client->connect('127.0.0.1', 9501, -1)) +{ + exit("connect failed. Error: {$client->errCode}\n"); +} +$client->send("hello world\n"); +echo $client->recv(); +$client->close(); +``` +> php-fpm/apache环境下只能使用同步客户端 +> apache环境下仅支持`prefork`多进程模式,不支持`prework`多线程 + +异步非阻塞客户端 +---- +```php +$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$client->on("connect", function(swoole_client $cli) { + $cli->send("GET / HTTP/1.1\r\n\r\n"); +}); +$client->on("receive", function(swoole_client $cli, $data){ + echo "Receive: $data"; + $cli->send(str_repeat('A', 100)."\n"); + sleep(1); +}); +$client->on("error", function(swoole_client $cli){ + echo "error\n"; +}); +$client->on("close", function(swoole_client $cli){ + echo "Connection close\n"; +}); +$client->connect('127.0.0.1', 9501); +``` +> 异步客户端只能使用在cli命令行环境 +异步的swoole client的使用场景对于新手同学来说可能比较陌生,因为异步客户端是不可以应用在apache或fpm中的,而且仅能用于cli环境。一种比较典型的使用场景就是你的后端服务器前面挡了一个网关服务器,网关和后端之间是通过内网TCP长链接方式通信,网关对所有前端实现http协议,那么,异步的swoole client此时就可以在网关服务器上得到价值实现。具体来说,就是使用swoole http server实现一个常驻内存级的http服务器,然后在swoole http server中使用异步client连接后端服务器。 \ No newline at end of file diff --git "a/doc/3.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" "b/doc/3.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" new file mode 100644 index 0000000..c28bb81 --- /dev/null +++ "b/doc/3.1 - \346\226\271\346\263\225\345\210\227\350\241\250.md" @@ -0,0 +1,13 @@ +#方法列表 + +SSL/TLS +---- +* 依赖`openssl`库,需要在编译swoole时增加`enable-openssl`或`with-openssl-dir` +* 必须在定义`Client`时增加`SWOOLE_SSL` + +> 低于1.9.5版本在设置`ssl_key_file`后会自动启用SSL + +```php +$client = new Swoole\Client(SWOOLE_TCP | SWOOLE_ASYNC | SWOOLE_SSL); +``` + diff --git a/doc/3.1.1 - swoole_client::__construct.md b/doc/3.1.1 - swoole_client::__construct.md new file mode 100644 index 0000000..9ae1cff --- /dev/null +++ b/doc/3.1.1 - swoole_client::__construct.md @@ -0,0 +1,30 @@ +#swoole_client::__construct + +函数原型: +```php +swoole_client->__construct(int $sock_type, int $is_sync = SWOOLE_SOCK_SYNC, string $key); +``` +可以使用swoole提供的宏来之指定类型,请参考 [swoole常量定义](/wiki/page/26.html) + +* `$sock_type`表示`socket`的类型,如`TCP/UDP` +* 使用`$sock_type | SWOOLE_SSL`可以启用`SSL`加密 +* `$is_sync`表示同步阻塞还是异步非阻塞,默认为同步阻塞 +* `$key`用于长连接的`Key`,默认使用IP:PORT作为key。相同key的连接会被复用 + +在php-fpm/apache中创建长连接 +---- +```php +$cli = new swoole_client(SWOOLE_TCP | SWOOLE_KEEP); +``` +加入SWOOLE_KEEP标志后,创建的TCP连接在PHP请求结束或者调用$cli->close时并不会关闭。下一次执行connect调用时会复用上一次创建的连接。长连接保存的方式默认是以ServerHost:ServerPort为key的。可以再第3个参数内指定key。 + +* `SWOOLE_KEEP`只允许用于同步客户端 + +> swoole_client在unset时会自动调用close方法关闭socket +> 异步模式unset时会自动关闭socket并从epoll事件轮询中移除 +> SWOOLE_KEEP长连接模式在1.6.12后可用,长连接的$key参数在1.7.5后增加 + +在swoole_server中使用swoole_client +------ +* 必须在事件回调函数中使用swoole_client,不能在`swoole_server->start`前创建 +* swoole_server可以用任何语言编写的 socket client来连接。同样swoole_client也可以去连接任何语言编写的socket server diff --git a/doc/3.1.10 - swoole_client->send.md b/doc/3.1.10 - swoole_client->send.md new file mode 100644 index 0000000..7a1d317 --- /dev/null +++ b/doc/3.1.10 - swoole_client->send.md @@ -0,0 +1,23 @@ +#swoole_client->send + +发送数据到远程服务器,必须在建立连接后,才可向Server发送数据。函数原型: +```php +int $swoole_client->send(string $data); +``` + +* $data参数为字符串,支持二进制数据 +* 成功发送返回的已发数据长度 +* 失败返回**false**,并设置$swoole_client->errCode + +异步模式下如果SOCKET缓存区已满,Swoole的处理逻辑请参考 [swoole_event_write](/wiki/page/372.html) + +> 如果未执行connect,调用send会触发PHP警告 + +同步客户端 +------ +* 发送的数据没有长度限制 +* 发送的数据太大Socket缓存区塞满,底层会阻塞等待可写 + +异步客户端 +---- +* 发送数据长度受到`socket_buffer_size`限制 \ No newline at end of file diff --git a/doc/3.1.11 - swoole_client->sendto.md b/doc/3.1.11 - swoole_client->sendto.md new file mode 100644 index 0000000..edf2241 --- /dev/null +++ b/doc/3.1.11 - swoole_client->sendto.md @@ -0,0 +1,12 @@ +#swoole_client->sendto + +向任意IP:PORT的主机发送UDP数据包,仅支持SWOOLE_SOCK_UDP/SWOOLE_SOCK_UDP6类型的swoole_client对象。 + +```php +bool swoole_client->sendto(string $ip, int $port, string $data); +``` + +* $ip,目标主机的IP地址,支持IPv4/IPv6 +* $port,目标主机端口 +* $data,要发送的数据内容,不得超过64K + diff --git a/doc/3.1.12 - swoole_client->sendfile.md b/doc/3.1.12 - swoole_client->sendfile.md new file mode 100644 index 0000000..7096f2c --- /dev/null +++ b/doc/3.1.12 - swoole_client->sendfile.md @@ -0,0 +1,32 @@ +#swoole_client->sendfile + +发送文件到服务器,本函数是基于`sendfile`操作系统调用实现,在`1.7.5`以上版本可用。 +```php +bool swoole_client->sendfile(string $filename, int $offset = 0, int $length = 0) +``` + +> `sendfile`不能用于`UDP`客户端和`SSL`隧道加密连接 + +参数 +---- +* `$filename`指定要发送文件的路径 +* `$offset` 上传文件的偏移量,可以指定从文件的中间部分开始传输数据。此特性可用于支持断点续传。 +* `$length` 发送数据的尺寸,默认为整个文件的尺寸 + +返回值 +--- +* 如果传入的文件不存在,将返回`false` +* 执行成功返回`true` + +注意事项 +---- +* `$length`, `$offse`t参数在`1.9.11`或更高版本可用 +* 如果是同步`client`,`sendfile`会一直阻塞直到整个文件发送完毕或者发生致命错误 +* 如果是异步`client`,`sendfile`会异步发送,当发生致命错误时会回调`onError` + + + + + + + diff --git a/doc/3.1.13 - swoole_client->recv.md b/doc/3.1.13 - swoole_client->recv.md new file mode 100644 index 0000000..2f35726 --- /dev/null +++ b/doc/3.1.13 - swoole_client->recv.md @@ -0,0 +1,34 @@ +#swoole_client->recv + +recv方法用于从服务器端接收数据。接受2个参数。函数原型为: +```php +//低于1.7.22 +string $swoole_client->recv(int $size = 65535, bool $waitall = 0); +//1.7.22或更高 +string $swoole_client->recv(int $size = 65535, int $flags = 0); +``` +* $size,接收数据的缓存区最大长度,此参数不要设置过大,否则会占用较大内存 +* $waitall,是否等待所有数据到达后返回 + +> 如果设定了$waitall就必须设定准确的$size,否则会一直等待,直到接收的数据长度达到`$size` +> 未设置`$waitall=true`时,$size最大为`64K` +> 如果设置了错误的$size,会导致`recv`超时,返回 **false** + +* 成功收到数据返回字符串 +* 连接关闭返回空字符串 +* 失败返回 **false**,并设置`$client->errCode`属性 + +EOF/Length +----- +客户端启用了EOF/Length检测后,无需设置$size和$waitall参数。扩展层会返回完整的数据包或者返回false。 + +* 当收到错误的包头或包头中长度值超过`package_max_length`设置时,`recv`会返回空字符串,PHP代码中应当关闭此连接 + +Flags +---- +1.7.22版本后,第二个`$waitall`参数修改为`$flags`,可以接收一些特殊的SOCKET接收设置。为了兼容旧的接口,如果$flags=1则表示 $flags = swoole_client::MSG_WAITALL + +```php +$client->recv(8192, swoole_client::MSG_PEEK | swoole_client::MSG_WAITALL); +``` + diff --git a/doc/3.1.14 - swoole_client->close.md b/doc/3.1.14 - swoole_client->close.md new file mode 100644 index 0000000..d9736eb --- /dev/null +++ b/doc/3.1.14 - swoole_client->close.md @@ -0,0 +1,61 @@ +#swoole_client->close + +关闭连接,函数原型为: +```php +bool $swoole_client->close(bool $force = false); +``` +操作成功返回 **true**。当一个swoole_client连接被close后不要再次发起connect。正确的做法是销毁当前的swoole_client,重新创建一个swoole_client并发起新的连接。 + +* 第一个参数设置为`true`表示强制关闭连接,可用于关闭`SWOOLE_KEEP`长连接 + +> swoole_client对象在析构时会自动close + +异步客户端 +---- +客户端`close`会立即关闭连接,如果发送队列中仍然有待数据,底层会丢弃。请勿在大量发送数据后,立即`close`,否则发送的数据未必能真正到达服务器端。 + +#### 错误实例 + +```php +$client = new swoole_client(SWOOLE_TCP | SWOOLE_ASYNC); +$client->on("connect", function(swoole_client $cli) { + +}); +$client->on("receive", function(swoole_client $cli, $data){ + $cli->send(str_repeat('A', 1024*1024*4)."\n"); + $cli->close(); +}); +$client->on("error", function(swoole_client $cli){ + echo "error\n"; +}); +$client->on("close", function(swoole_client $cli){ + echo "Connection close\n"; +}); +$client->connect('127.0.0.1', 9501); +``` + +客户端发送了`4M`数据,实际传输可能需要一段时间。这是立即进行了`close`操作,可能只有小部分数据传输成功。大部分数据在发送队列中排队等待发送,`close`时会丢弃这些数据。 + +#### 解决方案 +* 配合使用`onBufferEmpty`,等待发送队列为空时进行`close`操作 +* 协议设计为`onReceive`收到数据后主动关闭连接,发送数据时对端主动关闭连接 + +```php +$client = new swoole_client(SWOOLE_TCP | SWOOLE_ASYNC); +$client->on("connect", function(swoole_client $cli) { + +}); +$client->on("receive", function(swoole_client $cli, $data){ + $cli->send(str_repeat('A', 1024*1024*4)."\n"); +}); +$client->on("error", function(swoole_client $cli){ + echo "error\n"; +}); +$client->on("close", function(swoole_client $cli){ + echo "Connection close\n"; +}); +$client->on("bufferEmpty", function(swoole_client $cli){ + $cli->close(); +}); +$client->connect('127.0.0.1', 9501); +``` diff --git a/doc/3.1.15 - swoole_client->sleep.md b/doc/3.1.15 - swoole_client->sleep.md new file mode 100644 index 0000000..1f04f98 --- /dev/null +++ b/doc/3.1.15 - swoole_client->sleep.md @@ -0,0 +1,23 @@ +#swoole_client->sleep + +调用此方法会从事件循环中移除当前socket的可读监听,停止接收数据。 +```php +function swoole_client->sleep() +``` +* 此方法仅停止从socket中接收数据,但不会移除可写事件,所以不会影响发送队列 +* sleep操作与wakeup作用相反,使用wakeup方法可以重新监听可读事件 + +> sleep方法在swoole-1.7.21或更高版本可用 + +使用示例 +---- +```php +$client->on("receive", function(swoole_client $cli, $data){ + //睡眠模式,不再接收新的数据 + $cli->sleep(); + swoole_timer_after(5000, function() use ($cli) { + //唤醒,重新接收数据 + $cli->wakeup(); + }); +}); +``` diff --git a/doc/3.1.16 - swoole_client->wakeup.md b/doc/3.1.16 - swoole_client->wakeup.md new file mode 100644 index 0000000..e28e807 --- /dev/null +++ b/doc/3.1.16 - swoole_client->wakeup.md @@ -0,0 +1,9 @@ +#swoole_client->wakeup + +调用此方法会重新监听可读事件,将socket连接从睡眠中唤醒。 +```php +function swoole_client->wakeup() +``` +* 如果socket并未进入sleep模式,wakeup操作没有任何作用 + +> sleep方法在swoole-1.7.21或更高版本可用 diff --git a/doc/3.1.17 - swoole_client->enableSSL.md b/doc/3.1.17 - swoole_client->enableSSL.md new file mode 100644 index 0000000..9934a91 --- /dev/null +++ b/doc/3.1.17 - swoole_client->enableSSL.md @@ -0,0 +1,53 @@ +#swoole_client->enableSSL + +动态开启SSL隧道加密。客户端在建立连接时使用明文通信,中途希望改为SSL隧道加密通信,可以使用`enableSSL`方法来实现。使用`enableSSL`动态开启SSL隧道加密,需要满足两个条件: + +* 客户端创建时类型必须为非SSL +* 客户端已与服务器建立了连接 + +`enableSSL`方法可同时用于同步和异步客户端,异步客户端需要传入一个回调函数,当SSL握手完成后会回调此函数。同步客户端调用`enableSSL`会阻塞等待SSL握手完成。 + +同步客户端 +---- +```php +$client = new swoole_client(SWOOLE_SOCK_TCP); +if (!$client->connect('127.0.0.1', 9501, -1)) +{ + exit("connect failed. Error: {$client->errCode}\n"); +} +$client->send("hello world\n"); +echo $client->recv(); +//启用SSL隧道加密 +if ($client->enableSSL()) +{ + //握手完成,此时发送和接收的数据是加密的 + $client->send("hello world\n"); + echo $client->recv(); +} +$client->close(); +``` + +异步客户端 +---- +```php +$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); +$client->on("connect", function(swoole_client $cli) { + $cli->send("hello world\n"); +}); +$client->on("receive", function(swoole_client $cli, $data) { + echo "Receive: $data"; + $cli->send(str_repeat('A', 10)."\n"); + //启用SSL加密 + $cli->enableSSL(function($client) { + //握手完成,此时发送和接收的数据是加密的 + $client->send("hello"); + }) +}); +$client->on("error", function(swoole_client $cli){ + echo "error\n"; +}); +$client->on("close", function(swoole_client $cli){ + echo "Connection close\n"; +}); +$client->connect('127.0.0.1', 9501); +``` \ No newline at end of file diff --git a/doc/3.1.2 - swoole_client->set.md b/doc/3.1.2 - swoole_client->set.md new file mode 100644 index 0000000..799220e --- /dev/null +++ b/doc/3.1.2 - swoole_client->set.md @@ -0,0 +1,9 @@ +#swoole_client->set + +设置客户端参数,必须在connect前执行。swoole-1.7.17为客户端提供了类似swoole_server的自动协议处理功能。通过设置一个参数即可完成TCP的自动 + +```php +function swoole_client->set(array $settings); +``` + +可用的配置选项参考 [Client - 配置选项](/wiki/page/p-client_setting.html) diff --git a/doc/3.1.3 - swoole_client->on.md b/doc/3.1.3 - swoole_client->on.md new file mode 100644 index 0000000..42f3af9 --- /dev/null +++ b/doc/3.1.3 - swoole_client->on.md @@ -0,0 +1,51 @@ +#swoole_client->on + +注册异步事件回调函数,调用on方法会使当前的socket变成非阻塞的。 +```php +int swoole_client::on(string $event, mixed $callback); +``` +* 参数1为事件类型,支持connect/error/receive/close 4种。 +* 参数2为回调函数,可以是函数名字符串、匿名函数、类静态方法、对象方法。 +* __同步阻塞客户端一定不要使用on方法__ + +> 调用swoole_client->close()时会自动退出事件循环 +> on方法也可以用在UDP协议上,需要v1.6.3以上版本,UDP协议的connect事件在执行完connect方法后立即被回调 +> udp没有close事件 + +v1.6.10 +---- +从1.6.10开始,onReceive不再需要调用一次$client->recv()来接收数据,onReceive回调函数的第二个参数就是 收到的数据了。 +另外onClose事件,也无需调用$client->close(),swoole内核会自动执行close。 + + +示例: +```php +$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //异步非阻塞 + +$client->on("connect", function($cli) { + $cli->send("hello world\n"); +}); + +$client->on("receive", function($cli, $data = ""){ + $data = $cli->recv(); //1.6.10+ 不需要 + if(empty($data)){ + $cli->close(); + echo "closed\n"; + } else { + echo "received: $data\n"; + sleep(1); + $cli->send("hello\n"); + } +}); + +$client->on("close", function($cli){ + $cli->close(); // 1.6.10+ 不需要 + echo "close\n"; +}); + +$client->on("error", function($cli){ + exit("error\n"); +}); + +$client->connect('127.0.0.1', 9501, 0.5); +``` \ No newline at end of file diff --git a/doc/3.1.4 - swoole_client->connect.md b/doc/3.1.4 - swoole_client->connect.md new file mode 100644 index 0000000..414bd10 --- /dev/null +++ b/doc/3.1.4 - swoole_client->connect.md @@ -0,0 +1,62 @@ +#swoole_client->connect + +连接到远程服务器,函数原型: +```php +bool $swoole_client->connect(string $host, int $port, float $timeout = 0.5, int $flag = 0) +``` +connect方法接受4个参数: + +* `$host`是远程服务器的地址,`1.10.0`或更高版本已支持自动异步解析域名,`$host`可直接传入域名 +* `$port`是远程服务器端口 +* `$timeout`是网络IO的超时,包括`connect/send/recv`,单位是s,支持浮点数。默认为`0.5s`,即`500ms` +* $flag参数在`UDP`类型时表示是否启用`udp_connect ` +设定此选项后将绑定$host与$port,此UDP将会丢弃非指定host/port的数据包。 +* $flag参数在TCP类型,$flag=1表示设置为非阻塞socket,connect会立即返回。如果将$flag设置为1,那么在send/recv前必须使用swoole_client_select来检测是否完成了连接 + +> $timeout超时设置基于底层操作系统SOCKET参数,对异步客户端无效 + +同步模式 +----- +connect方法会阻塞,直到连接成功并返回true。这时候就可以向服务器端发送数据或者收取数据了。 + +```php +if ($cli->connect('127.0.0.1', 9501)) { + $cli->send("data"); +} else { + echo "connect failed."; +} +``` +如果连接失败,会返回false +> 同步TCP客户端在执行close后,可以再次发起Connect创建新连接到服务器 + +异步模式 +---- +`connect`会立即返回true。但实际上连接并未建立。所以不能在connect后使用`send`。通过`isConnected()`判断也是false。当连接成功后,系统会自动回调onConnect。这时才可以使用`send`向服务器发送数据。 + + +> 异步客户端执行connect时会增加一次引用计数,当连接关闭时会减少引用计数 +> 低于`1.9.11`的版本,`$timeout`超时设置`$timeout`在异步客户端中是无效的,应用层需要用`Timer::after`自行添加定时器来实现异步客户端的链接超时控制 +> `1.9.11`或更高版本,底层会自动添加定时器,在规定的时间内未连接成功,底层会触发`onError`连接失败事件,错误码为`ETIMEOUT(110)` + +失败重连 +---- +`connect`失败后如果希望重连一次,必须先进行`close`关闭旧的`socket`,否则会返回`EINPROCESS`错误,因为当前的socket正在连接服务器,客户端并不知道是否连接成功,所以无法再次执行`connect`。调用`close`会关闭当前的`socket`,底层重新创建新的`socket`来进行连接。 + +> 启用`SWOOLE_KEEP`长连接后,`close`调用的第一个参数要设置为`true`表示强行销毁长连接socket + +```php +if ($socket->connect('127.0.0.1', 9502) === false) { + $socket->close(true); + $socket->connect('127.0.0.1', 9502); +} +``` + +UDP Connect +---- +默认底层并不会启用`udp connect`,一个`UDP`客户端执行`connect`时,底层在创建`socket`后会立即返回成功。这时此`socket`绑定的地址是`0.0.0.0`,任何其他对端均可向此端口发送数据包。 + +如`$client->connect('192.168.1.100', 9502)`,这时操作系统为客户端`socket`随机分配了一个端口`58232`,其他机器,如`192.168.1.101`也可以向这个端口发送数据包。 + +> 未开启`udp connect`,调用`getsockname`返回的`host`项为`0.0.0.0` + +将第`4`项参数设置为`1`,启用`udp connect`,`$client->connect('192.168.1.100', 9502, 1, 1)`。这时将会绑定客户端和服务器端,底层会根据服务器端的地址来绑定`socket`绑定的地址。如连接了`192.168.1.100`,当前`socket`会被绑定到`192.168.1.*`的本机地址上。启用`udp connect`后,客户端将不再接收其他主机向此端口发送的数据包。 \ No newline at end of file diff --git a/doc/3.1.5 - swoole_client->isConnected.md b/doc/3.1.5 - swoole_client->isConnected.md new file mode 100644 index 0000000..b8523d6 --- /dev/null +++ b/doc/3.1.5 - swoole_client->isConnected.md @@ -0,0 +1,17 @@ +#swoole_client->isConnected + +返回`swoole_client`的连接状态 +```php +bool swoole_client->isConnected() +``` + +* 返回false,表示当前未连接到服务器 +* 返回true,表示当前已连接到服务器 + +> 此函数在1.7.5以上版本可用 + +注意事项 +---- +`isConnected`方法返回的是应用层状态,只表示Client执行了`connect`并成功连接到了Server,并且没有执行`close`关闭连接。Client可以执行`send`、`recv`、`close`等操作,但不能再次执行`connect`。 + +这不代表连接一定是可用的,当执行`send`或`recv`时仍然有可能返回错误,因为应用层无法获得底层TCP连接的状态,执行`send`或`recv`时应用层与内核发生交互,才能得到真实的连接可用状态。 \ No newline at end of file diff --git a/doc/3.1.6 - swoole_client->getSocket.md b/doc/3.1.6 - swoole_client->getSocket.md new file mode 100644 index 0000000..561731a --- /dev/null +++ b/doc/3.1.6 - swoole_client->getSocket.md @@ -0,0 +1,14 @@ +#swoole_client->getSocket + +调用此方法可以得到底层的socket句柄,返回的对象为`sockets`资源句柄。 + +> 此方法需要依赖PHP的`sockets`扩展,并且编译swoole时需要开启`--enable-sockets`选项 + +使用`socket_set_option`函数可以设置更底层的一些socket参数。 + +```php +$socket = $client->getSocket(); +if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { + echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL; +} +``` \ No newline at end of file diff --git a/doc/3.1.7 - swoole_client->getSockName.md b/doc/3.1.7 - swoole_client->getSockName.md new file mode 100644 index 0000000..60d4a97 --- /dev/null +++ b/doc/3.1.7 - swoole_client->getSockName.md @@ -0,0 +1,11 @@ +#swoole_client->getSockName + +用于获取客户端socket的本地host:port,必须在连接之后才可以使用。 + +```php +array swoole_client->getsockname(); +``` + +* 调用成功返回一个数组,如:array('host' => '127.0.0.1', 'port' => 53652) + +> 此函数在1.7.13以上版本可用 \ No newline at end of file diff --git a/doc/3.1.8 - swoole_client->getPeerName.md b/doc/3.1.8 - swoole_client->getPeerName.md new file mode 100644 index 0000000..ca83ca6 --- /dev/null +++ b/doc/3.1.8 - swoole_client->getPeerName.md @@ -0,0 +1,11 @@ +#swoole_client->getPeerName + +获取对端socket的IP地址和端口,仅支持SWOOLE_SOCK_UDP/SWOOLE_SOCK_UDP6类型的swoole_client对象。 + +```php +bool swoole_client->getpeername(); +``` + +UDP协议通信客户端向一台服务器发送数据包后,可能并非由此服务器向客户端发送响应。可以使用getpeername方法获取实际响应的服务器IP:PORT。 + +> 此函数必须在$client->recv() 之后调用 \ No newline at end of file diff --git a/doc/3.1.9 - swoole_client->getPeerCert.md b/doc/3.1.9 - swoole_client->getPeerCert.md new file mode 100644 index 0000000..5f0f56c --- /dev/null +++ b/doc/3.1.9 - swoole_client->getPeerCert.md @@ -0,0 +1,13 @@ +#swoole_client->getPeerCert + +获取服务器端证书信息。 +```php +function swoole_client->getPeerCert() +``` +* 执行成功返回一个X509证书字符串信息 +* 执行失败返回false +* 必须在SSL握手完成后才可以调用此方法 +* 可以使用`openssl`扩展提供的`openssl_x509_parse`函数解析证书的信息 + +> 需要在编译swoole时启用`--enable-openssl` +> 此方法在1.8.8或更高版本可用 diff --git "a/doc/3.2 - \345\233\236\350\260\203\345\207\275\346\225\260.md" "b/doc/3.2 - \345\233\236\350\260\203\345\207\275\346\225\260.md" new file mode 100644 index 0000000..83cd045 --- /dev/null +++ "b/doc/3.2 - \345\233\236\350\260\203\345\207\275\346\225\260.md" @@ -0,0 +1,9 @@ +#回调函数 + +UDP客户端 +---- +UDP没有连接和关闭的概念,因此 + +* `onConnect`事件会在UDP客户端创建后立即执行 +* `onClose`事件会在UDP客户端`close`时立即执行 + diff --git a/doc/3.2.1 - onConnect.md b/doc/3.2.1 - onConnect.md new file mode 100644 index 0000000..ee61547 --- /dev/null +++ b/doc/3.2.1 - onConnect.md @@ -0,0 +1,11 @@ +#onConnect + +客户端连接服务器成功后会回调此函数。 + +```php +function onConnect(swoole_client $client) +``` + +* TCP客户端必须设置onConnect回调 +* UDP客户端可选设置onConnect,socket创建成功会立即回调onConnect + diff --git a/doc/3.2.2 - onError.md b/doc/3.2.2 - onError.md new file mode 100644 index 0000000..c6d7e98 --- /dev/null +++ b/doc/3.2.2 - onError.md @@ -0,0 +1,9 @@ +#onError + +连接服务器失败时会回调此函数。 + +```php +function onError(swoole_client $client) +``` + +* UDP客户端没有onError回调 \ No newline at end of file diff --git a/doc/3.2.3 - onReceive.md b/doc/3.2.3 - onReceive.md new file mode 100644 index 0000000..b2a7033 --- /dev/null +++ b/doc/3.2.3 - onReceive.md @@ -0,0 +1,11 @@ +#onReceive + +客户端收到来自于服务器端的数据时会回调此函数 + +```php +function onReceive(swoole_client $client, string $data) +``` + +* $data 是服务器端发送的数据,可以为文本或者二进制内容 +* swoole_client启用了eof/length检测后,onReceive一定会收到一个完整的数据包 + diff --git a/doc/3.2.4 - onClose.md b/doc/3.2.4 - onClose.md new file mode 100644 index 0000000..8ad2c7c --- /dev/null +++ b/doc/3.2.4 - onClose.md @@ -0,0 +1,8 @@ +#onClose + +连接被关闭时回调此函数。 +```php +function onClose(swoole_client $client) +``` + +* Server端关闭或Client端主动关闭,都会触发onClose事件 diff --git a/doc/3.2.5 - onBufferFull.md b/doc/3.2.5 - onBufferFull.md new file mode 100644 index 0000000..6eabff0 --- /dev/null +++ b/doc/3.2.5 - onBufferFull.md @@ -0,0 +1,10 @@ +#onBufferFull + +当缓存区达到最高水位时触发此事件。 + +```php +function onBufferFull(Swoole\Client $cli); +``` + +* 设置`client->buffer_high_watermark`选项来控制缓存区高水位线 +* 触发`onBufferFull`表明发送队列已触顶即将塞满,不能再向服务器端发送数据 diff --git a/doc/3.2.6 - onBufferEmpty.md b/doc/3.2.6 - onBufferEmpty.md new file mode 100644 index 0000000..6818a60 --- /dev/null +++ b/doc/3.2.6 - onBufferEmpty.md @@ -0,0 +1,10 @@ +#onBufferEmpty + +当缓存区低于最低水位线时触发此事件。 + +```php +function onBufferEmpty(Swoole\Client $cli); +``` + +* 设置`client->buffer_low_watermark`来控制缓存区低水位线 +* 触发此事件后,表明当前发送队列中的数据已被发出,可以继续向服务器端发送数据 \ No newline at end of file diff --git "a/doc/3.3 - \345\261\236\346\200\247\345\210\227\350\241\250.md" "b/doc/3.3 - \345\261\236\346\200\247\345\210\227\350\241\250.md" new file mode 100644 index 0000000..016c1f7 --- /dev/null +++ "b/doc/3.3 - \345\261\236\346\200\247\345\210\227\350\241\250.md" @@ -0,0 +1,2 @@ +#属性列表 + diff --git a/doc/3.3.1 - swoole_client->errCode.md b/doc/3.3.1 - swoole_client->errCode.md new file mode 100644 index 0000000..e1d2cae --- /dev/null +++ b/doc/3.3.1 - swoole_client->errCode.md @@ -0,0 +1,10 @@ +#swoole_client->errCode + +类型为int型。当connect/send/recv/close失败时,会自动设置$swoole_client->errCode的值。 +errCode的值等于Linux errno。可使用socket_strerror将错误码转为错误信息。 + +```php +echo socket_strerror($client->errCode); +``` + +> [附录:Linux的errno定义](/wiki/page/172.html) \ No newline at end of file diff --git a/doc/3.3.2 - swoole_client->sock.md b/doc/3.3.2 - swoole_client->sock.md new file mode 100644 index 0000000..004dc89 --- /dev/null +++ b/doc/3.3.2 - swoole_client->sock.md @@ -0,0 +1,13 @@ +#swoole_client->sock + +类型为int。sock属性是此socket的文件描述符。在PHP代码中可以使用 +```php +$sock = fopen("php://fd/".$swoole_client->sock); +``` +__注意:$client->sock属性值,仅在$client->connect后才能取到。在未连接服务器之前,此属性的值为null。__ + +将swoole_client的socket转换成一个stream socket。可以调用fread/fwrite/fclose等函数进程操作。 +> swoole_server中的$fd不能用此方法转换,因为$fd只是一个数字,$fd文件描述符属于主进程 + +$swoole_client->sock可以转换成int作为数组的key. + diff --git a/doc/3.3.3 - swoole_client->reuse.md b/doc/3.3.3 - swoole_client->reuse.md new file mode 100644 index 0000000..ae804d8 --- /dev/null +++ b/doc/3.3.3 - swoole_client->reuse.md @@ -0,0 +1,21 @@ +#swoole_client->reuse + +类型: boolean,表示此连接是新创建的还是复用已存在的。与`SWOOLE_KEEP`配合使用。 + +> 1.8.0或更高版本可用 + +使用场景 +---- +`WebSocket`客户端与服务器建立连接后需要进行握手,如果连接是复用的,那就不需要再次进行握手,直接发送`WebSocket`数据帧即可。 + +```php +if ($client->reuse) +{ + $client->send($data); +} +else +{ + $client->doHandShake(); + $client->send($data); +} +``` diff --git "a/doc/3.4 - \345\271\266\350\241\214.md" "b/doc/3.4 - \345\271\266\350\241\214.md" new file mode 100644 index 0000000..1aebb65 --- /dev/null +++ "b/doc/3.4 - \345\271\266\350\241\214.md" @@ -0,0 +1,2 @@ +#并行 + diff --git a/doc/3.4.1 - swoole_client_select.md b/doc/3.4.1 - swoole_client_select.md new file mode 100644 index 0000000..26a2b50 --- /dev/null +++ b/doc/3.4.1 - swoole_client_select.md @@ -0,0 +1,74 @@ +#swoole_client_select + +swoole_client的并行处理中用了select来做IO事件循环。 + +函数原型: +```php +int swoole_client_select(array &$read, array &$write, array &$error, float $timeout); +``` +* `swoole_client_select`接受4个参数,`$read`, `$write`, `$error` 分别是可读/可写/错误的文件描述符。 +* 这3个参数必须是数组变量的引用。数组的元素必须`为swoole_client`对象。 `1.8.6`或更高版本可以支持`swoole_process`对象 +* 此方法基于`select`系统调用,最大支持`1024`个`socket` +* `$timeout`参数是`select`系统调用的超时时间,单位为秒,接受浮点数 + +调用成功后,会返回事件的数量,并修改`$read`/`$write`/`$error`数组。使用foreach遍历数组,然后执行`$item->recv`/`$item->send`来收发数据。或者调用`$item->close()`或`unset($item)`来关闭`socket`。 + +`swoole_client_select`返回`0`表示在规定的时间内,没有任何IO可用,`select`调用已超时。 + +> 此函数可以用于`Apache/PHP-fpm`环境 + +swoole_client用法 +---- +```php +$clients = array(); + +for($i=0; $i< 20; $i++) +{ + $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $ret = $client->connect('127.0.0.1', 9501, 0.5, 0); + if(!$ret) + { + echo "Connect Server fail.errCode=".$client->errCode; + } + else + { + $client->send("HELLO WORLD\n"); + $clients[$client->sock] = $client; + } +} + +while (!empty($clients)) +{ + $write = $error = array(); + $read = array_values($clients); + $n = swoole_client_select($read, $write, $error, 0.6); + if ($n > 0) + { + foreach ($read as $index => $c) + { + echo "Recv #{$c->sock}: " . $c->recv() . "\n"; + unset($clients[$c->sock]); + } + } +} +``` + +swoole_process用法 +--- +```php +pid . "\n"; + sleep(2); + $worker->write("hello master\n"); + $worker->exit(0); +}, false); + +$pid = $process->start(); +$r = array($process); +$write = $error = array(); +$ret = swoole_select($r, $write, $error, 1.0);//swoole_select是swoole_client_select的别名 +var_dump($ret); +var_dump($process->read()); +``` \ No newline at end of file diff --git "a/doc/3.4.2 - TCP\345\256\242\346\210\267\347\253\257\345\274\202\346\255\245\350\277\236\346\216\245.md" "b/doc/3.4.2 - TCP\345\256\242\346\210\267\347\253\257\345\274\202\346\255\245\350\277\236\346\216\245.md" new file mode 100644 index 0000000..841846c --- /dev/null +++ "b/doc/3.4.2 - TCP\345\256\242\346\210\267\347\253\257\345\274\202\346\255\245\350\277\236\346\216\245.md" @@ -0,0 +1,37 @@ +#TCP客户端异步连接 + +通过使用on方法注册异步回调函数。多个swoole_client可以嵌套回调。异步模式仅可用于cli模式,如在swoole_process或swoole_server中。 + +示例: +```php +on("connect", function($cli) { + echo "connected\n"; + $cli->send("hello world\n"); +}); + +$client->on("receive", function($cli, $data) { + if(empty($data)){ + $cli->close(); + echo "closed\n"; + } else { + echo "received: $data\n"; + sleep(1); + $cli->send("hello\n"); + } +}); + +$client->on("error", function($cli){ + exit("error\n"); +}); + +$client->on("close", function($cli){ + echo "connection is closed\n"; +}); + +$client->connect('127.0.0.1', 9501, 0.5); +``` + + diff --git "a/doc/3.4.3 - SWOOLE_KEEP\345\273\272\347\253\213TCP\351\225\277\350\277\236\346\216\245.md" "b/doc/3.4.3 - SWOOLE_KEEP\345\273\272\347\253\213TCP\351\225\277\350\277\236\346\216\245.md" new file mode 100644 index 0000000..2693c6d --- /dev/null +++ "b/doc/3.4.3 - SWOOLE_KEEP\345\273\272\347\253\213TCP\351\225\277\350\277\236\346\216\245.md" @@ -0,0 +1,17 @@ +#SWOOLE_KEEP建立TCP长连接 + +swoole_client支持在`php-fpm/apache`中创建一个TCP长连接到服务器端。使用方法: + +```php +$client = new swoole_client(SWOOLE_SOCK_TCP | SWOOLE_KEEP); +$client->connect('127.0.0.1', 9501); +``` + +启用`SWOOLE_KEEP`选项后,一个请求结束不会关闭`socket`,下一次再进行`connect`时会自动复用上次创建的连接。如果执行`connect`发现连接已经被服务器关闭,那么`connect`会创建新的连接。 + +SWOOLE_KEEP的优势 +---- +* `TCP`长连接可以减少`connect` `3`次握手/`close` `4`次挥手带来的额外IO消耗 +* 降低服务器端`close`/`connect`次数 + + diff --git "a/doc/3.5 - \345\270\270\351\207\217.md" "b/doc/3.5 - \345\270\270\351\207\217.md" new file mode 100644 index 0000000..7edc715 --- /dev/null +++ "b/doc/3.5 - \345\270\270\351\207\217.md" @@ -0,0 +1,20 @@ +#常量 + +swoole_client::MSG_WAITALL +---- +用于swoole_client->recv方法的第二个参数,阻塞等待直到收到指定长度的数据后返回。 + +```php +$client->recv(8192, swoole_client::MSG_PEEK | swoole_client::MSG_DONTWAIT); +``` +swoole_client::MSG_DONTWAIT +---- +非阻塞接收数据,无论是否有数据都会立即返回。 + +swoole_client::MSG_PEEK +---- +窥视socket缓存区中的数据。设置MSG_PEEK参数后,recv读取数据不会修改指针,因此下一次调用recv仍然会从上一次的位置起返回数据。 + +swoole_client::MSG_OOB +---- +读取带外数据。 diff --git "a/doc/3.6 - \351\205\215\347\275\256\351\200\211\351\241\271.md" "b/doc/3.6 - \351\205\215\347\275\256\351\200\211\351\241\271.md" new file mode 100644 index 0000000..db04645 --- /dev/null +++ "b/doc/3.6 - \351\205\215\347\275\256\351\200\211\351\241\271.md" @@ -0,0 +1,105 @@ +#配置选项 + + `Swoole\Client`和`Swoole\Http\Client`可以使用`set`方法设置一些选项,启用某些特性。 + + +结束符检测 +------- +```php +$client->set(array( + 'open_eof_check' => true, + 'package_eof' => "\r\n\r\n", + 'package_max_length' => 1024 * 1024 * 2, +)) +``` + +长度检测 +----- +```php +$client->set(array( + 'open_length_check' => 1, + 'package_length_type' => 'N', + 'package_length_offset' => 0, //第N个字节是包长度的值 + 'package_body_offset' => 4, //第几个字节开始计算长度 + 'package_max_length' => 2000000, //协议最大长度 +)); +``` + + +MQTT协议 +---- +启用`MQTT`协议解析,`onReceive`回调将收到完整的`MQTT`数据包。 +```php +$client->set(array( + 'open_mqtt_protocol' => true, +)); +``` + +Socket缓存区尺寸 +---- +```php +$client->set(array( + 'socket_buffer_size' => 1024*1024*2, //2M缓存区 +)); +``` +> 包括socket底层操作系统缓存区、应用层接收数据内存缓存区、应用层发送数据内存缓冲区 + + +关闭Nagle合并算法 +---- +```php +$client->set(array( + 'open_tcp_nodelay' => true, +)); +``` + +SSL/TLS证书 +---- +```php +$client->set(array( + 'ssl_cert_file' => $your_ssl_cert_file_path, + 'ssl_key_file' => $your_ssl_key_file_path, +)); +``` +> swoole-1.7.21或更高版本可用 + +绑定IP和端口 +---- +* 机器有多个网卡的情况下,设置`bind_address`参数可以强制客户端Socket绑定某个网络地址。 +* 设置`bind_port`可以使客户端Socket使用固定的端口连接到外网服务器 + +```php +$client->set(array( + 'bind_address' => '192.168.1.100', + 'bind_port' => 36002, +)); +``` +> swoole-1.8.5或更高版本可用 + +Socks5代理设置 +---- +```php +$client->set(array( + 'socks5_host' => '192.168.1.100', + 'socks5_port' => 1080, + 'socks5_username' => 'username', + 'socks5_password' => 'password', +)); +``` +* `socks5_username`、`socks5_password`为可选参数 + +Http代理设置 +---- +```php +$client->set(array( + 'http_proxy_host' => '192.168.1.100', + 'http_proxy_port' => 1080, +)); +``` + +使用说明 +---- + +* 目前支持open_length_check和open_eof_check2种自动协议处理功能,参考swoole_server中的配置选项 +* 启用了自动协议后,同步阻塞客户端recv方法将不接受长度参数,每次必然返回一个完整的数据包 +* 启用了自动协议后,异步非阻塞客户端onReceive每次必然返回一个完整的数据包 \ No newline at end of file diff --git a/doc/3.6.1 - ssl_verify_peer.md b/doc/3.6.1 - ssl_verify_peer.md new file mode 100644 index 0000000..d1ed6c8 --- /dev/null +++ b/doc/3.6.1 - ssl_verify_peer.md @@ -0,0 +1,22 @@ +#ssl_verify_peer + +验证服务器端证书。 + +```php +$client->set([ + 'ssl_verify_peer' => true, +]) +``` + +* 启用后会验证证书和主机域名是否对应,如果为否将自动关闭连接 + +自签名证书 +---- +可设置`ssl_allow_self_signed`为`true`,允许自签名证书。 + +```php +$client->set([ + 'ssl_verify_peer' => true, + 'ssl_allow_self_signed' => true, +]) +``` \ No newline at end of file diff --git a/doc/3.6.2 - ssl_host_name.md b/doc/3.6.2 - ssl_host_name.md new file mode 100644 index 0000000..70ecc61 --- /dev/null +++ b/doc/3.6.2 - ssl_host_name.md @@ -0,0 +1,9 @@ +#ssl_host_name + +设置服务器主机名称,与`ssl_verify_peer`配置或`Client::verifyPeerCert`配合使用。 + +```php +$client->set([ + 'ssl_host_name' => 'www.google.com', +]) +``` diff --git a/doc/3.6.3 - ssl_cafile.md b/doc/3.6.3 - ssl_cafile.md new file mode 100644 index 0000000..0ba41c8 --- /dev/null +++ b/doc/3.6.3 - ssl_cafile.md @@ -0,0 +1,11 @@ +#ssl_cafile + +当设置`ssl_verify_peer`为`true`时, 用来验证远端证书所用到的`CA`证书。 本选项值为`CA`证书在本地文件系统的全路径及文件名。 + +* 类型:`string` + +```php +$client->set([ + 'ssl_cafile' => '/etc/CA', +]) +``` \ No newline at end of file diff --git a/doc/3.6.4 - ssl_capath.md b/doc/3.6.4 - ssl_capath.md new file mode 100644 index 0000000..4e6cfd2 --- /dev/null +++ b/doc/3.6.4 - ssl_capath.md @@ -0,0 +1,11 @@ +#ssl_capath + +如果未设置`ssl_cafile`,或者`ssl_cafile`所指的文件不存在时, 会在`ssl_capath`所指定的目录搜索适用的证书。 该目录必须是已经经过哈希处理的证书目录。 + +* 类型:`string` + +```php +$client->set([ + 'ssl_capath' => '/etc/capath/', +]) +``` \ No newline at end of file diff --git a/doc/3.6.5 - package_length_func.md b/doc/3.6.5 - package_length_func.md new file mode 100644 index 0000000..61a8d36 --- /dev/null +++ b/doc/3.6.5 - package_length_func.md @@ -0,0 +1,35 @@ +#package_length_func + +设置长度计算函数,与`Server`的使用方法完全一致。与`open_length_check`配合使用。长度函数必须返回一个整数。此配置对同步客户端、异步客户端均有效。 + +* 返回`0`,数据不足,需要接收更多数据 +* 返回`-1`,数据错误,底层会自动关闭连接 +* 返回包长度值(包括包头和包体的总长度),底层会自动将包拼好后返回给回调函数 + +默认底层最大会读取`8K`的数据,如果包头的长度较小可能会存在内存复制的消耗。可设置`package_body_offset`参数,底层只读取包头进行长度解析。 + +实例 +---- +```php +$client = new swoole_client(SWOOLE_SOCK_TCP); +$client->set(array( + 'open_length_check' => true, + 'package_length_func' => function ($data) { + if (strlen($data) < 8) { + return 0; + } + $length = intval(trim(substr($data, 0, 8))); + if ($length <= 0) { + return -1; + } + return $length + 8; + }, +)); +if (!$client->connect('127.0.0.1', 9501, -1)) +{ + exit("connect failed. Error: {$client->errCode}\n"); +} +$client->send("hello world\n"); +echo $client->recv(); +$client->close(); +``` diff --git "a/doc/3.7 - \345\270\270\350\247\201\351\227\256\351\242\230.md" "b/doc/3.7 - \345\270\270\350\247\201\351\227\256\351\242\230.md" new file mode 100644 index 0000000..861ed5d --- /dev/null +++ "b/doc/3.7 - \345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -0,0 +1,4 @@ +#常见问题 + +### object is not instanceof swoole_client +出现这个错误,表明代码中在客户端连接关闭后,仍然调用了`recv`、`send`等方法操作socket。 \ No newline at end of file diff --git a/doc/4 - Process.md b/doc/4 - Process.md new file mode 100644 index 0000000..c94908c --- /dev/null +++ b/doc/4 - Process.md @@ -0,0 +1,111 @@ +#Process + +swoole-1.7.2增加了一个进程管理模块,用来替代PHP的pcntl扩展。 + +需要注意Process进程在系统是非常昂贵的资源,创建进程消耗很大。另外创建的进程过多会导致进程切换开销大幅上升。可以使用`vmstat`指令查看操作系统每秒进程切换的次数。 + +```shell +vmstat 1 1000 +procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- + r b swpd free buff cache si so bi bo in cs us sy id wa st + 0 0 0 8250028 509872 4061168 0 0 10 13 88 86 1 0 99 0 0 + 0 0 0 8249532 509872 4061936 0 0 0 0 451 1108 0 0 100 0 0 + 0 0 0 8249532 509872 4061884 0 0 0 0 684 1855 1 3 95 0 0 + 0 0 0 8249532 509880 4061876 0 0 0 16 492 1332 0 0 99 0 0 + 0 0 0 8249532 509880 4061844 0 0 0 0 379 893 0 0 100 0 0 + 0 0 0 8249532 509880 4061844 0 0 0 0 440 1116 0 0 99 0 0 +``` + +PHP自带的pcntl,存在很多不足,如 +---- +* pcntl没有提供进程间通信的功能 +* pcntl不支持重定向标准输入和输出 +* pcntl只提供了fork这样原始的接口,容易使用错误 +* swoole_process提供了比pcntl更强大的功能,更易用的API,使PHP在多进程编程方面更加轻松。 + +swoole_process提供了如下特性: +---- +* swoole_process提供了基于unixsock的进程间通信,使用很简单只需调用write/read或者push/pop即可 +* swoole_process支持重定向标准输入和输出,在子进程内echo不会打印屏幕,而是写入管道,读键盘输入可以重定向为管道读取数据 +* 配合swoole_event模块,创建的PHP子进程可以异步的事件驱动模式 +* swoole_process提供了exec接口,创建的进程可以执行其他程序,与原PHP父进程之间可以方便的通信 + +一个同步实例: +---- +* 子进程异常退出时,自动重启 +* 主进程异常退出时,子进程在干完手头活后退出 +```php +(new class{ + public $mpid=0; + public $works=[]; + public $max_precess=1; + public $new_index=0; + + public function __construct(){ + try { + swoole_set_process_name(sprintf('php-ps:%s', 'master')); + $this->mpid = posix_getpid(); + $this->run(); + $this->processWait(); + }catch (\Exception $e){ + die('ALL ERROR: '.$e->getMessage()); + } + } + + public function run(){ + for ($i=0; $i < $this->max_precess; $i++) { + $this->CreateProcess(); + } + } + + public function CreateProcess($index=null){ + $process = new swoole_process(function(swoole_process $worker)use($index){ + if(is_null($index)){ + $index=$this->new_index; + $this->new_index++; + } + swoole_set_process_name(sprintf('php-ps:%s',$index)); + for ($j = 0; $j < 16000; $j++) { + $this->checkMpid($worker); + echo "msg: {$j}\n"; + sleep(1); + } + }, false, false); + $pid=$process->start(); + $this->works[$index]=$pid; + return $pid; + } + public function checkMpid(&$worker){ + if(!swoole_process::kill($this->mpid,0)){ + $worker->exit(); + // 这句提示,实际是看不到的.需要写到日志中 + echo "Master process exited, I [{$worker['pid']}] also quit\n"; + } + } + + public function rebootProcess($ret){ + $pid=$ret['pid']; + $index=array_search($pid, $this->works); + if($index!==false){ + $index=intval($index); + $new_pid=$this->CreateProcess($index); + echo "rebootProcess: {$index}={$new_pid} Done\n"; + return; + } + throw new \Exception('rebootProcess Error: no pid'); + } + + public function processWait(){ + while(1) { + if(count($this->works)){ + $ret = swoole_process::wait(); + if ($ret) { + $this->rebootProcess($ret); + } + }else{ + break; + } + } + } +}); +``` \ No newline at end of file diff --git a/doc/4.1 - swoole_process::__construct.md b/doc/4.1 - swoole_process::__construct.md new file mode 100644 index 0000000..7626270 --- /dev/null +++ b/doc/4.1 - swoole_process::__construct.md @@ -0,0 +1,73 @@ +#swoole_process::__construct + +创建子进程 +--- + +```php +swoole_process::__construct(callable $function, $redirect_stdin_stdout = false, $create_pipe = true); + +// 启用命名空间 +Swoole\Process::__construct(callable $function, $redirect_stdin_stdout = false, $create_pipe = true) +``` + +* `$function`,子进程创建成功后要执行的函数,底层会自动将函数保存到对象的`callback`属性上。如果希望更改执行的函数,可赋值新的函数到对象的`callback`属性 +* `$redirect_stdin_stdout`,重定向子进程的标准输入和输出。启用此选项后,在子进程内输出内容将不是打印屏幕,而是写入到主进程管道。读取键盘输入将变为从管道中读取数据。默认为阻塞读取。 +* `$create_pipe`,是否创建管道,启用`$redirect_stdin_stdout`后,此选项将忽略用户参数,强制为`true`。如果子进程内没有进程间通信,可以设置为 `false` + +create_pipe参数 +---- + +自 `1.7.22` 版本起参数`$create_pipe`为int类型且允许设置管道的类型,其默认值为`2`,默认使用`DGRAM`管道。 + +* 参数 `$create_pipe` 小于等于`0`或为 `false` 时,不创建管道 +* 参数 `$create_pipe` 为`1`或为 `true` 时,管道类型将设置为 `SOCK_STREAM` +* 参数`$create_pipe`为`2`时,管道类型将设置为`SOCK_DGRAM` +* 启用`$redirect_stdin_stdout` 后,此选项将忽略用户参数,强制为`1` + +> 自 `1.9.6` 版本以后,参数 `$create_pipe` 默认值为 `2`,启用`$redirect_stdin_and_stdout` (即 `redirect_stdin_and_stdout` 为 `true`)后强制为 `1` +> `1.8.3 ~ 1.9.5` 版本,参数 `$create_pipe` 默认值为 `2`,启用 `$redirect_stdin_and_stdout` (即 `redirect_stdin_and_stdout` 为 `true`)后强制为 `2` +> `1.7.22 ~ 1.8.2` 版本,参数`$create_pipe` 默认值为`1`,启用 `$redirect_stdin_and_stdout` (即 `redirect_stdin_and_stdout` 为 `true`)后强制为 `1` +> `swoole_process` ( 或 `Swoole\Process`) 对象在销毁时会自动关闭管道,子进程内如果监听了管道会收到CLOSE事件 +> 使用swoole_process作为监控父进程,创建管理子process时,父类必须注册信号SIGCHLD对退出的进程执行wait,否则子process一旦被kill会引起父process exit + + +在子进程中创建swoole_server +---- + +例 1: + +可以在 `swoole_process` 创建的子进程中使用 `swoole_server`,但为了安全必须在`$process->start` 创建进程后,调用 `$worker->exec()` 执行。代码如下: + +```php +start(); + +function callback_function(swoole_process $worker) +{ + $worker->exec('/usr/local/bin/php', array(__DIR__.'/swoole_server.php')); +} + +swoole_process::wait(); +``` + +例 2:使用匿名函数作为进程逻辑,并实现了一个简单的父子进程通讯 + +```php +write('Hello'); +}, true); + +$process->start(); +usleep(100); + +echo $process->read(); // 输出 Hello +``` + +IO 线程池问题 +---- +由于`Swoole`的异步文件`IO`使用了线程池,在使用了这些`API`之后再创建`Process`可能会出现非常复杂的带线程`fork`问题。因此请勿在使用异步文件`IO`函数后创建`Process`。 + +> `2.1.4`/`1.10.4`或更高版本已经禁止了这种行为,底层检测到已创建线程池再执行`new Process`会抛出致命错误 diff --git a/doc/4.10 - swoole_process->statQueue.md b/doc/4.10 - swoole_process->statQueue.md new file mode 100644 index 0000000..144f242 --- /dev/null +++ b/doc/4.10 - swoole_process->statQueue.md @@ -0,0 +1,19 @@ +#swoole_process->statQueue + +查看消息队列状态。 + +```php +array swoole_process->statQueue(); +``` + +* 返回一个数组,包括2项信息 +* queue_num 队列中的任务数量 +* queue_bytes 队列数据的总字节数 + +```php +array( + "queue_num" => 10, + "queue_bytes" => 161, +); +``` +> 需要`1.8.6`或更高版本 \ No newline at end of file diff --git a/doc/4.11 - swoole_process->freeQueue.md b/doc/4.11 - swoole_process->freeQueue.md new file mode 100644 index 0000000..54daf87 --- /dev/null +++ b/doc/4.11 - swoole_process->freeQueue.md @@ -0,0 +1,11 @@ +#swoole_process->freeQueue + +删除队列。此方法与`useQueue`成对使用,`useQueue`创建队列,使用`freeQueue`销毁队列。销毁队列后队列中的数据会被清空。 + +如果程序中只调用了`useQueue`方法,未调用`freeQueue`在程序结束时并不会清除数据。重新运行程序时可以继续读取上次运行时留下的数据。 + +系统重启时消息队列中的数据会被丢弃。 + +```php +function swoole_process->freeQueue(); +``` diff --git a/doc/4.12 - swoole_process->push.md b/doc/4.12 - swoole_process->push.md new file mode 100644 index 0000000..da3fc6b --- /dev/null +++ b/doc/4.12 - swoole_process->push.md @@ -0,0 +1,53 @@ +#swoole_process->push + +投递数据到消息队列中。 + +```php +bool swoole_process->push(string $data); +``` + +* $data要投递的数据,长度受限与操作系统内核参数的限制。默认为8192,最大不超过65536 +* 操作失败会返回false,成功返回true +* 默认模式下(阻塞模式),如果队列已满,`push`方法会阻塞等待 +* 非阻塞模式下,如果队列已满,`push`方法会立即返回false + +示例 + +```php +$workers = []; +$worker_num = 2; + +for($i = 0; $i < $worker_num; $i++) +{ + $process = new swoole_process('callback_function', false, false); + $process->useQueue(); + $pid = $process->start(); + $workers[$pid] = $process; + //echo "Master: new worker, PID=".$pid."\n"; +} + +function callback_function(swoole_process $worker) +{ + //echo "Worker: start. PID=".$worker->pid."\n"; + //recv data from master + $recv = $worker->pop(); + + echo "From Master: $recv\n"; + + sleep(2); + $worker->exit(0); +} + +foreach($workers as $pid => $process) +{ + $process->push("hello worker[$pid]\n"); +} + +for($i = 0; $i < $worker_num; $i++) +{ + $ret = swoole_process::wait(); + $pid = $ret['pid']; + unset($workers[$pid]); + echo "Worker Exit, PID=".$pid.PHP_EOL; +} +``` diff --git a/doc/4.13 - swoole_process->pop.md b/doc/4.13 - swoole_process->pop.md new file mode 100644 index 0000000..32a8af8 --- /dev/null +++ b/doc/4.13 - swoole_process->pop.md @@ -0,0 +1,11 @@ +#swoole_process->pop + +从队列中提取数据。 + +```php +string swoole_process->pop(int $maxsize = 8192); +``` +* $maxsize表示获取数据的最大尺寸,默认为8192 +* 操作成功会返回提取到的数据内容,失败返回false +* 默认模式下,如果队列中没有数据,`pop`方法会阻塞等待 +* 非阻塞模式下,如果队列中没有数据,`pop`方法会立即返回false,并设置错误码为`ENOMSG` diff --git a/doc/4.14 - swoole_process->close.md b/doc/4.14 - swoole_process->close.md new file mode 100644 index 0000000..9e4539f --- /dev/null +++ b/doc/4.14 - swoole_process->close.md @@ -0,0 +1,14 @@ +#swoole_process->close + +用于关闭创建的好的管道。 + +```php +bool swoole_process->close(int $which = 0); +``` + +* `$which` 指定关闭哪一个管道,默认为`0`表示同时关闭读和写,`1`:关闭写,`2`关闭读 + +有一些特殊的情况`swoole_process`对象无法释放,如果持续创建进程会导致连接泄漏。调用此函数就可以直接关闭管道,释放资源。 + + + diff --git a/doc/4.15 - swoole_process->exit.md b/doc/4.15 - swoole_process->exit.md new file mode 100644 index 0000000..3931c08 --- /dev/null +++ b/doc/4.15 - swoole_process->exit.md @@ -0,0 +1,17 @@ +#swoole_process->exit + +退出子进程 +```php +int swoole_process->exit(int $status=0); +``` + +$status是退出进程的状态码,如果为0表示正常结束,会继续执行PHP的shutdown_function,其他扩展的清理工作。 + +如果$status不为0,表示异常退出,会立即终止进程。不再执行PHP的shutdown_function,其他扩展的清理工作。 + +在父进程中,执行swoole_process::wait可以得到子进程退出的事件和状态码。 + + + + + diff --git a/doc/4.16 - swoole_process::kill.md b/doc/4.16 - swoole_process::kill.md new file mode 100644 index 0000000..0267815 --- /dev/null +++ b/doc/4.16 - swoole_process::kill.md @@ -0,0 +1,19 @@ +#swoole_process::kill + +向指定pid进程发送信号 +```php +bool swoole_process::kill($pid, $signo = SIGTERM); +``` + +* 默认的信号为`SIGTERM`,表示终止进程 +* `$signo=0`,可以检测进程是否存在,不会发送信号 + +僵尸进程 +------- +子进程退出后,父进程务必要执行swoole_process::wait进行回收,否则这个子进程就会变为僵尸进程。会浪费操作系统的进程资源。 + +父进程可以设置监听SIGCHLD信号,收到信号后执行swoole_process::wait回收退出的子进程。 + + + + diff --git a/doc/4.17 - swoole_process::wait.md b/doc/4.17 - swoole_process::wait.md new file mode 100644 index 0000000..309af81 --- /dev/null +++ b/doc/4.17 - swoole_process::wait.md @@ -0,0 +1,33 @@ +#swoole_process::wait + +回收结束运行的子进程。 + +```php +array swoole_process::wait(bool $blocking = true); +$result = array('code' => 0, 'pid' => 15001, 'signal' => 15); +``` + +* $blocking 参数可以指定是否阻塞等待,默认为阻塞 +* 操作成功会返回一个数组包含子进程的PID、退出状态码、被哪种信号KILL +* 失败返回false + +> 子进程结束必须要执行wait进行回收,否则子进程会变成僵尸进程 + +> $blocking 仅在1.7.10以上版本可用 + +>使用swoole_process作为监控父进程,创建管理子process时,父类必须注册信号SIGCHLD对退出的进程执行wait,否则子process一旦被kill会引起父process exit + +在异步信号回调中执行wait +----- +```php +swoole_process::signal(SIGCHLD, function($sig) { + //必须为false,非阻塞模式 + while($ret = swoole_process::wait(false)) { + echo "PID={$ret['pid']}\n"; + } +}); +``` +* 信号发生时可能同时有多个子进程退出 +* 必须循环执行wait直到返回false + + diff --git a/doc/4.18 - swoole_process::daemon.md b/doc/4.18 - swoole_process::daemon.md new file mode 100644 index 0000000..89f7c68 --- /dev/null +++ b/doc/4.18 - swoole_process::daemon.md @@ -0,0 +1,15 @@ +#swoole_process::daemon + +使当前进程蜕变为一个守护进程。 +```php +//低于1.9.1的版本 +bool swoole_process::daemon(bool $nochdir = false, bool $noclose = false); +//1.9.1或更高版本 +bool swoole_process::daemon(bool $nochdir = true, bool $noclose = true); +``` +* $nochdir,为true表示不要切换当前目录到根目录。 +* $noclose,为true表示不要关闭标准输入输出文件描述符。 + +> 此函数在1.7.5-stable版本后可用 +> 1.9.1或更高版本修改了默认值,现在默认nochir和noclose均为true +> 蜕变为守护进程时,该进程的PID将重新fork,可以使用getmypid()来获取当前的PID diff --git a/doc/4.19 - swoole_process::signal.md b/doc/4.19 - swoole_process::signal.md new file mode 100644 index 0000000..fd11896 --- /dev/null +++ b/doc/4.19 - swoole_process::signal.md @@ -0,0 +1,21 @@ +#swoole_process::signal + +设置异步信号监听。 +```php +bool swoole_process::signal(int $signo, callable $callback); +``` +* 此方法基于`signalfd`和`eventloop`是异步IO,不能用于同步程序中 +* 同步阻塞的程序可以使用pcntl扩展提供的`pcntl_signal` +* `$callback`如果为`null`,表示移除信号监听 +* 如果已设置了此信号的回调函数,重新设置时会覆盖历史设置 + +使用举例: +```php +swoole_process::signal(SIGTERM, function($signo) { + echo "shutdown."; +}); +``` + +> swoole_server中不能设置SIGTERM和SIGALAM信号 +> swoole_process::signal在swoole-1.7.9以上版本可用 +> 信号移除特性仅在1.7.21或更高版本可用 diff --git a/doc/4.2 - swoole_process->start.md b/doc/4.2 - swoole_process->start.md new file mode 100644 index 0000000..93e6af9 --- /dev/null +++ b/doc/4.2 - swoole_process->start.md @@ -0,0 +1,54 @@ +#swoole_process->start + +执行fork系统调用,启动进程。 +```php +function swoole_process->start() : int +``` + +创建成功返回子进程的`PID`,创建失败返回`false`。可使用`swoole_errno`和`swoole_strerror`得到错误码和错误信息。 + +* `$process->pid` 属性为子进程的`PID` +* `$process->pipe` 属性为管道的文件描述符 +* 子进程会继承父进程的内存和文件句柄 +* 子进程在启动时会清除从父进程继承的`EventLoop`、`Signal`、`Timer` + +> 执行后子进程会保持父进程的内存和资源,如父进程内创建了一个redis连接,那么在子进程会保留此对象,所有操作都是对同一个连接进行的。 + + +实例 +----- +```php +$redis = new $redis; +$redis->connect('127.0.0.1', 6379); + +function callback_function () { + swoole_timer_after(10000, function () { + echo "hello world"; + }); + global $redis; +}; + +swoole_timer_tick(1000, function () { + echo "parent timer\n"; +}); + +swoole_process::signal(SIGCHLD, function ($sig) { + while ($ret = Swoole\Process::wait(false)) { + // create a new child process + $p = new Swoole\Process('callback_function'); + $p->start(); + } +}); + +// create a new child process +$p = new Swoole\Process('callback_function'); + +swoole_event_add($p->pipe, function ($pipe) use ($p) { + echo $p->read(); +}); + +$p->start(); +``` + +* 子进程启动后会自动清除父进程中`swoole_timer_tick`创建的定时器、`swoole_process::signal`监听的信号和`swoole_event_add`添加的事件监听 +* 子进程会继承父进程创建的`$redis`连接对象,父子进程使用的连接是同一个 diff --git a/doc/4.20 - swoole_process::alarm.md b/doc/4.20 - swoole_process::alarm.md new file mode 100644 index 0000000..0764ff4 --- /dev/null +++ b/doc/4.20 - swoole_process::alarm.md @@ -0,0 +1,31 @@ +#swoole_process::alarm + +高精度定时器,是操作系统`setitimer`系统调用的封装,可以设置微秒级别的定时器。定时器会触发信号,需要与`swoole_process::signal`或`pcntl_signal`配合使用。 + +```php +function swoole_process::alarm(int $interval_usec, int $type = ITIMER_REAL) : bool +``` + +* $interval_usec 定时器间隔时间,单位为微秒。如果为负数表示清除定时器 +* $type 定时器类型,0 表示为真实时间,触发`SIGALAM`信号,1 表示用户态CPU时间,触发`SIGVTALAM`信号,2 表示用户态+内核态时间,触发`SIGPROF`信号 +* 设置成功返回true,失败返回false,可以使用`swoole_errno`得到错误码 + +> alarm不能和`Swoole\Timer`同时使用 +> alarm在1.8.13或更高版本可用 + +使用实例 +---- + +```php +swoole_process::signal(SIGALRM, function () { + static $i = 0; + echo "#{$i}\talarm\n"; + $i++; + if ($i > 20) { + swoole_process::alarm(-1); + } +}); + +//100ms +swoole_process::alarm(100 * 1000); +``` \ No newline at end of file diff --git a/doc/4.21 - swoole_process::setAffinity.md b/doc/4.21 - swoole_process::setAffinity.md new file mode 100644 index 0000000..c739ba9 --- /dev/null +++ b/doc/4.21 - swoole_process::setAffinity.md @@ -0,0 +1,16 @@ +#swoole_process::setAffinity + +设置CPU亲和性,可以将进程绑定到特定的CPU核上。 +```php +function swoole_process::setAffinity(array $cpu_set); +``` + +* 接受一个数组参数表示绑定哪些`CPU`核,如`array(0,2,3)`表示绑定`CPU0/CPU2/CPU3` +* 成功返回`true`,失败返回`false` + +> `$cpu_set`内的元素不能超过`CPU`核数 +> `CPU-ID`不得超过(CPU核数 - 1) +> 使用 `swoole_cpu_num()` 可以得到当前服务器的`CPU`核数 +> `setAffinity`函数在`1.7.18`以上版本可用 + +此函数的作用是让进程只在某几个`CPU`核上运行,让出某些`CPU`资源执行更重要的程序。 diff --git a/doc/4.3 - swoole_process->name.md b/doc/4.3 - swoole_process->name.md new file mode 100644 index 0000000..8e7e042 --- /dev/null +++ b/doc/4.3 - swoole_process->name.md @@ -0,0 +1,12 @@ +#swoole_process->name + +修改进程名称。此函数是`swoole_set_process_name`的别名。 + +```php +$process->name("php server.php: worker"); +``` + +* 在执行`exec`后,进程名称会被新的程序重新设置 + +> 此方法在swoole-1.7.9以上版本可用 +> name方法应当在start之后的子进程回调函数中使用 \ No newline at end of file diff --git a/doc/4.4 - swoole_process->exec.md b/doc/4.4 - swoole_process->exec.md new file mode 100644 index 0000000..004b127 --- /dev/null +++ b/doc/4.4 - swoole_process->exec.md @@ -0,0 +1,47 @@ +#swoole_process->exec + +执行一个外部程序,此函数是exec系统调用的封装。 +```php +bool swoole_process->exec(string $execfile, array $args) +``` + +* $execfile指定可执行文件的绝对路径,如 "/usr/bin/python" +* $args是一个数组,是exec的参数列表,如 array('test.py', 123),相当与python test.py 123 + +执行成功后,当前进程的代码段将会被新程序替换。子进程蜕变成另外一套程序。父进程与当前进程仍然是父子进程关系。 + +父进程与新进程之间可以通过可以通过标准输入输出进行通信,必须启用标准输入输出重定向。 + +> $execfile必须使用绝对路径,否则会报文件不存在错误 +> 由于exec系统调用会使用指定的程序覆盖当前程序,子进程需要读写标准输出与父进程进行通信 +> 如果未指定redirect_stdin_stdout = true,执行exec后子进程与父进程无法通信 + +调用示例: + +```php +$process = new \Swoole\Process(function (\Swoole\Process $childProcess) { + // 不支持这种写法 + // $childProcess->exec('/usr/local/bin/php /var/www/project/yii-best-practice/cli/yii + t/index -m=123 abc xyz'); + + // 封装 exec 系统调用 + // 绝对路径 + // 参数必须分开放到数组中 + $childProcess->exec('/usr/local/bin/php', ['/var/www/project/yii-best-practice/cli/yii', + 't/index', '-m=123', 'abc', 'xyz']); // exec 系统调用 +}); +$process->start(); // 启动子进程 +``` + +父进程与exec进程使用管道进行通信: + +```php +// exec - 与exec进程进行管道通信 +use Swoole\Process; +$process = new Process(function (Process $worker) { + $worker->exec('/bin/echo', ['hello']); + $worker->write('hello'); +}, true); // 需要启用标准输入输出重定向 +$process->start(); +echo "from exec: ". $process->read(). "\n"; +``` diff --git a/doc/4.5 - swoole_process->write.md b/doc/4.5 - swoole_process->write.md new file mode 100644 index 0000000..f8b5008 --- /dev/null +++ b/doc/4.5 - swoole_process->write.md @@ -0,0 +1,32 @@ +#swoole_process->write + +向管道内写入数据。 +```php +int swoole_process->write(string $data); +``` +* `$data`的长度在`Linux`系统下最大不超过`8K`,`MacOS/FreeBSD`下最大不超过`2K` +* 在子进程内调用`write`,父进程可以调用`read`接收此数据 +* 在父进程内调用`write`,子进程可以调用`read`接收此数据 + +`Swoole`底层使用`Unix Socket`实现通信,`Unix Socket`是内核实现的全内存通信,无任何IO消耗。在1进程write,1进程read,每次读写`1024`字节数据的测试中,`100`万次通信仅需`1.02`秒。 + +管道通信默认的方式是流式,`write`写入的数据在`read`可能会被底层合并。可以设置`swoole_process`构造函数的第三个参数为`2`改变为数据报式。 + +> MacOS/FreeBSD可以设置`net.local.dgram.maxdgram`内核参数修改最大长度 + +异步模式 +---- +如果进程内使用了异步IO,比如`swoole_event_add`,进程内执行write操作将变为异步模式。swoole底层会监听可写事件,自动完成管道写入。 + +异步模式下如果SOCKET缓存区已满,Swoole的处理逻辑请参考 [swoole_event_write](/wiki/page/372.html) + +同步模式 +--- +进程内未使用任何异步IO,当前管道为同步阻塞模式,如果缓存区已满,将阻塞等待直到write操作完成。 + +* Task进程就是同步阻塞的模式,如果管道的缓存区已满,调用`write`时会发生阻塞 + +乱序丢包 +--- +很多网络文章提到`DGRAM`模式下会出现丢包、乱序问题,实际上这些问题仅存在于`Internet`网络的`UDP`通信。`UnixSocket`是Linux内核实现的内存数据队列,不会出现丢包乱序问题。`write`写入和`read`读取的顺序是完全一致的。`write`返回成功后一定是可以`read`到的。 + diff --git a/doc/4.6 - swoole_process->read.md b/doc/4.6 - swoole_process->read.md new file mode 100644 index 0000000..14b47da --- /dev/null +++ b/doc/4.6 - swoole_process->read.md @@ -0,0 +1,41 @@ +#swoole_process->read + +从管道中读取数据。 +```php +function swoole_process->read(int $buffer_size=8192) : string | bool; +``` + +* `$buffer_size`是缓冲区的大小,默认为`8192`,最大不超过`64K` +* 管道类型为`DGRAM`数据报时,`read`可以读取完整的一个数据包 +* 管道类型为`STREAM`时,`read`是流式的,需要自行处理包完整性问题 +* 读取成功返回二进制数据字符串,读取失败返回`false` + +> 这里是同步阻塞读取的,可以使用[swoole_event_add](/wiki/page/119.html)将管道加入到事件循环中,变为异步模式 + + +示例: +```php +function callback_function_async(swoole_process $worker) +{ + $GLOBALS['worker'] = $worker; + swoole_event_add($worker->pipe, function($pipe) { + $worker = $GLOBALS['worker']; + $recv = $worker->read(); + + echo "From Master: $recv\n"; + + //send data to master + $worker->write("hello master\n"); + + sleep(2); + + $worker->exit(0); + }); +} +``` + +注意事项 +---- +由于`Swoole`底层使用了`epoll`的`LT`模式,因此`swoole_event_add`添加的事件监听,在事件发生后回调函数中必须调用`read`方法读取`socket`中的数据,否则底层会持续触发事件回调。 + + diff --git a/doc/4.7 - swoole_process->setTimeout.md b/doc/4.7 - swoole_process->setTimeout.md new file mode 100644 index 0000000..cec257d --- /dev/null +++ b/doc/4.7 - swoole_process->setTimeout.md @@ -0,0 +1,25 @@ +#swoole_process->setTimeout + +设置管道读写操作的超时时间。 +```php +function swoole_process->setTimeout(double $timeout) +``` +* ` $timeout`单位为秒,支持浮点型,如`1.5`表示`1s`+`500ms` +* 设置成功返回`true` +* 设置失败返回`false`,可使用`swoole_errno`获取错误码 + +设置成功后,调用`recv`和`write`在规定时间内未读取或写入成功,将返回`false`,可使用`swoole_errno`获取错误码。 + +> 在`1.9.21`或更高版本可用 + +使用实例 +---- +```php +$process = new \swoole_process(function(\swoole_process $process) { + sleep(5); +}); +$process->start(); + +$process->setTimeout(0.5); +$ret = $process->read(); +``` \ No newline at end of file diff --git a/doc/4.8 - swoole_process->setBlocking.md b/doc/4.8 - swoole_process->setBlocking.md new file mode 100644 index 0000000..2d6a3f2 --- /dev/null +++ b/doc/4.8 - swoole_process->setBlocking.md @@ -0,0 +1,30 @@ +#swoole_process->setBlocking + +设置管道是否为阻塞模式。默认`Process`的管道为同步阻塞。 + +```php +function swoole_process->setBlocking(bool $blocking = true); +``` + +* `$blocking` 布尔型,默认为`true`,设置为`false`时管道为非阻塞模式 + +> 需要`1.10.3`/`2.1.2`或更高版本 + +非阻塞模式 +---- +* 在异步程序中使用`swoole_event_add`添加管道事件监听时底层会自动将管道设置为非阻塞 +* 在异步程序中使用`swoole_event_write`异步写入数据时底层会自动将管道设置为非阻塞 + + +使用实例 +---- +```php +$serv->on("WorkerStart", function () use ($process) { + //设置为阻塞模式 + $process->setBlocking(true); + while(true) { + $process->write("hello"); + $msg = $process->reqd(); + } +}); +``` \ No newline at end of file diff --git a/doc/4.9 - swoole_process->useQueue.md b/doc/4.9 - swoole_process->useQueue.md new file mode 100644 index 0000000..895bf00 --- /dev/null +++ b/doc/4.9 - swoole_process->useQueue.md @@ -0,0 +1,24 @@ +#swoole_process->useQueue + +启用消息队列作为进程间通信。 +```php +bool swoole_process->useQueue(int $msgkey = 0, int $mode = 2); +``` +useQueue方法接受2个可选参数。 + +* `$msgkey`是消息队列的key,默认会使用`ftok(__FILE__, 1)`作为KEY +* `$mode`通信模式,默认为`2`,表示争抢模式,所有创建的子进程都会从队列中取数据 +* 如果创建消息队列失败,会返回`false`。可使用`swoole_strerror(swoole_errno())` 得到错误码和错误信息。 + +> 使用模式`2`后,创建的子进程无法进行单独通信,比如发给特定子进程。 +> `$process`对象并未执行`start`,也可以执行`push`/`pop`向队列推送/提取数据 +> 消息队列通信方式与管道不可共用。消息队列不支持`EventLoop`,使用消息队列后只能使用同步阻塞模式 + +非阻塞 +----- +在`1.9.2`或更高版本中增加了`swoole_process::IPC_NOWAIT`的支持,可将队列设置为非阻塞。在非阻塞模式下,队列已满调用`push`方法、队列已空调用`pop`方法时将不再阻塞立即返回。 + +```php +//设置为非阻塞模式 +$process->useQueue($key, $mode | swoole_process::IPC_NOWAIT); +``` diff --git a/doc/6 - AsyncIO.md b/doc/6 - AsyncIO.md new file mode 100644 index 0000000..a236400 --- /dev/null +++ b/doc/6 - AsyncIO.md @@ -0,0 +1,24 @@ +#AsyncIO + + `1.6.12`版本增加了异步文件读写,异步DNS,异步Http/WebSocket客户端等特性。开发纯异步非阻塞IO的程序时,不能使用`PHP`自带的网络客户端,如`curl`、`file_get_contents`、`stream`、`sockets`、`mysql`、`redis`。 + +* `Swoole\Server`的`Task进程`是同步阻塞的,没有`EventLoop`,因此无法使用除定时器之外的任何异步`API` +* `signalfd`是`Linux-2.6.27`提供文件句柄方式处理信号特性,优点是可以将信号加入到`EventLoop`中,`Reactor`操作不会被信号打断提高了性能。缺点是有些同步阻塞的程序可能会出现问题,无法从阻塞中中断,可以使用`swoole_async_set`关闭`signalfd`特性 + +swoole_async_set +---- +此函数可以设置异步IO相关的选项。 + +```php +swoole_async_set(array $setting); +``` + +* `thread_num` 设置异步文件IO线程的数量 +* `aio_mode` 设置异步文件IO的操作模式,目前支持`SWOOLE_AIO_BASE`(使用类似于Node.js的线程池同步阻塞模拟异步)、`SWOOLE_AIO_LINUX`(Linux Native AIO) 2种模式 +* `enable_signalfd` 开启和关闭`signalfd`特性的使用 +* `socket_buffer_size` 设置SOCKET内存缓存区尺寸 +* `socket_dontwait` 在内存缓存区已满的情况下禁止底层阻塞等待 +* `log_file` 设置日志文件路径 +* `log_level` 设置错误日志等级 + +> Linux Native AIO的优点是由内核支持是真正的异步文件IO,缺点是只支持DirectIO,无法利用到系统的PageCache diff --git "a/doc/6.1 - \345\274\202\346\255\245\346\226\207\344\273\266\347\263\273\347\273\237IO.md" "b/doc/6.1 - \345\274\202\346\255\245\346\226\207\344\273\266\347\263\273\347\273\237IO.md" new file mode 100644 index 0000000..a8205b1 --- /dev/null +++ "b/doc/6.1 - \345\274\202\346\255\245\346\226\207\344\273\266\347\263\273\347\273\237IO.md" @@ -0,0 +1,37 @@ +#异步文件系统IO + +Swoole支持2种类型的异步文件读写IO,可以使用`swoole_async_set`来设置AIO模式:. + +Linux原生异步IO (AIO模式:SWOOLE_AIO_LINUX) +----- +基于Linux Native AIO系统调用,是真正的异步IO,并非阻塞模拟。 + +__优点:__ + +* 所有操作均在一个线程内完成,不需要开线程池 +* 不依赖线程执行IO,所以并发可以非常大 + +__缺点:__ + +* 只支持DriectIO,无法利用PageCache,所有对文件读写都会直接操作磁盘 +* 写入数据的size必须为`512`整数倍数 +* 写入数据的offset必须为`512`整数倍数 + + +线程池模式异步IO (AIO模式: SWOOLE_AIO_BASE) +----- +基于线程池模拟实现,文件读写请求投递到任务队列,然后由AIO线程读写文件,完成后通知主线程。AIO线程本身是同步阻塞的。所以并非真正的异步IO。 + +__优点:__ + +* 可以利用操作系统PageCache,读写热数据性能非常高,等于读内存 + +> 可修改`thread_num`项设置启用的AIO线程数量 + +__缺点:__ + +* 并发较差,不支持同时读写大量文件,最大并发受限与AIO的线程数量 + +冲突问题 +---- +请注意异步文件`IO`函数与`Swoole\Process`存在冲突,在创建线程池后如果调用`new Process`可能会导致多线程`fork`。 diff --git a/doc/6.1.1 - swoole_async_readfile.md b/doc/6.1.1 - swoole_async_readfile.md new file mode 100644 index 0000000..4501a83 --- /dev/null +++ b/doc/6.1.1 - swoole_async_readfile.md @@ -0,0 +1,25 @@ +#swoole_async_readfile + +异步读取文件内容,函数原型 +```php +//函数风格 +swoole_async_readfile(string $filename, mixed $callback); +//命名空间风格 +Swoole\Async::readFile(string $filename, mixed $callback); +``` +* 文件不存在会返回`false` +* 成功打开文件立即返回`true` +* 数据读取完毕后会回调指定的`callback`函数。 + + +使用示例: +---------- +```php +swoole_async_readfile(__DIR__."/server.php", function($filename, $content) { + echo "$filename: $content"; +}); +``` + +> `swoole_async_readfile`会将文件内容全部复制到内存,所以不能用于大文件的读取 +> 如果要读取超大文件,请使用`swoole_async_read`函数 +> `swoole_async_readfile`最大可读取`4M`的文件,受限于`SW_AIO_MAX_FILESIZE`宏 \ No newline at end of file diff --git a/doc/6.1.2 - swoole_async_writefile.md b/doc/6.1.2 - swoole_async_writefile.md new file mode 100644 index 0000000..4375ec1 --- /dev/null +++ b/doc/6.1.2 - swoole_async_writefile.md @@ -0,0 +1,21 @@ +#swoole_async_writefile + +异步写文件,调用此函数后会立即返回。当写入完成时会自动回调指定的callback函数。 + +```php +Swoole\Async::writeFile(string $filename, string $fileContent, callable $callback = null, int $flags = 0) +swoole_async_writefile('test.log', $file_content, function($filename) { + echo "wirte ok.\n"; +}, $flags = 0); +``` + +* 参数1为文件的名称,必须有可写权限,文件不存在会自动创建。打开文件失败会立即返回`false` +* 参数2为要写入到文件的内容,最大可写入`4M` +* 参数3为写入成功后的回调函数,可选 +* 参数4为写入的选项,可以使用`FILE_APPEND`表示追加到文件末尾 +* 如果文件已存在,底层会覆盖旧的文件内容 + + +> `FILE_APPEND`在`1.9.1`或更高版本可用 +> `Linux`原生异步`IO`不支持`FILE_APPEND`,并且写入的内容长度必须为`4096`的整数倍,否则底层会自动在末尾填充`0` + diff --git a/doc/6.1.3 - swoole_async_read.md b/doc/6.1.3 - swoole_async_read.md new file mode 100644 index 0000000..0e7bc34 --- /dev/null +++ b/doc/6.1.3 - swoole_async_read.md @@ -0,0 +1,26 @@ +#swoole_async_read + +异步读文件,使用此函数读取文件是非阻塞的,当读操作完成时会自动回调指定的函数。 + +```php +bool swoole_async_read(string $filename, mixed $callback, int $size = 8192, int $offset = 0); +``` +此函数与`swoole_async_readfile`不同,它是分段读取,可以用于读取超大文件。每次只读`$size`个字节,不会占用太多内存。 + +在读完后会自动回调`$callback`函数,回调函数接受2个参数: + +```php +bool callback(string $filename, string $content); +``` + +* $filename,文件名称 +* $content,读取到的分段内容,如果内容为空,表明文件已读完 + +> $offset参数在1.7.13以上版本可用 + +$callback函数,可以通过return true/false,来控制是否继续读下一段内容。 + + * return true,继续读取 + * return false,停止读取并关闭文件 + + diff --git a/doc/6.1.4 - swoole_async_write.md b/doc/6.1.4 - swoole_async_write.md new file mode 100644 index 0000000..25363db --- /dev/null +++ b/doc/6.1.4 - swoole_async_write.md @@ -0,0 +1,11 @@ +#swoole_async_write + +异步写文件,与`swoole_async_writefile`不同,`swoole_async_write`是分段写的。不需要一次性将要写的内容放到内存里,所以只占用少量内存。`swoole_async_write`通过传入的offset参数来确定写入的位置。 + +```php +bool swoole_async_write(string $filename, string $content, int $offset = -1, mixed $callback = NULL); +``` + +* 当offset为-1时表示追加写入到文件的末尾 +* Linux原生异步IO不支持追加模式,并且`$content`的长度和`$offset`必须为512的整数倍。如果传入错误的字符串长度或者`$offset`写入会失败,并且错误码为`EINVAL` + diff --git a/doc/6.1.5 - swoole_async_dns_lookup.md b/doc/6.1.5 - swoole_async_dns_lookup.md new file mode 100644 index 0000000..c51cc87 --- /dev/null +++ b/doc/6.1.5 - swoole_async_dns_lookup.md @@ -0,0 +1,37 @@ +#swoole_async_dns_lookup + +将域名解析为IP地址。调用此函数是非阻塞的,调用会立即返回。将向下执行后面的代码。 + +* 当DNS查询完成时,自动回调指定的callback函数。 +* 当DNS查询失败时,比如域名不存在,回调函数传入的$ip为空 + + +```php +swoole_async_dns_lookup("www.baidu.com", function($host, $ip){ + echo "{$host} : {$ip}\n"; +}); +``` + +关闭DNS缓存 +---- +```php +swoole_async_set(array( + 'disable_dns_cache' => true, +)); +``` + +DNS随机 +--- +```php +swoole_async_set(array( + 'dns_lookup_random' => true, +)); +``` + +指定DNS服务器 +--- +```php +swoole_async_set(array( + 'dns_server' => '114.114.114.114', +)); +``` \ No newline at end of file diff --git a/doc/6.1.6 - swoole_async::exec.md b/doc/6.1.6 - swoole_async::exec.md new file mode 100644 index 0000000..051cdb6 --- /dev/null +++ b/doc/6.1.6 - swoole_async::exec.md @@ -0,0 +1,28 @@ +#swoole_async::exec + +异步执行`Shell`命令。相当于`shell_exec`函数,执行后底层会`fork`一个子进程,并执行对应的`command`命令。 + +```php +function swoole_async::exec(string $command, callable $callback); +``` + +* `$command`为执行的终端指令,如`ls` +* 执行成功后返回子进程的`PID` +* 命令执行完毕子进程退出后会回调指定的`$callback`函数,回调函数接收`2`个参数,第一个参数为命令执行后的屏幕输出内容`$result`,第二个参数为进程退出的状态信息`$status` + +注意事项 +----- +* **`fork`创建子进程的操作代价是非常昂贵的,系统无法支撑过大的并发量** +* 使用`exec`时,请勿使用`pcntl_signal`或`swoole_process::signal`注册`SIGCHLD`函数,执行`wait`操作,否则在命令回调函数中,状态信息`$status`将为`false` + +> 此函数在`1.9.22`或更高版本可用 + + +使用实例 +----- +```php +$pid = Swoole\Async::exec("ps aux", function ($result, $status) { + var_dump(strlen($result), $status); +}); +var_dump($pid); +``` \ No newline at end of file diff --git a/doc/6.2 - EventLoop.md b/doc/6.2 - EventLoop.md new file mode 100644 index 0000000..349d0e0 --- /dev/null +++ b/doc/6.2 - EventLoop.md @@ -0,0 +1,19 @@ +#EventLoop + +除了异步`Server`和`Client`库之外,`Swoole`扩展还提供了直接操作底层`epoll/kqueue`事件循环的接口。可将其他扩展创建的`socket`,`PHP`代码中`stream/socket`扩展创建的`socket`等加入到`Swoole`的`EventLoop`中。 + +事件优先级 +---- +1. 通过`Process::signal`设置的信号处理回调函数 +2. 通过`Event::defer`设置的延迟执行函数 +2. 通过`Timer::tick`和`Timer::after`设置的定时器回调函数 +3. 通过`Event::cycle`设置的周期回调函数 + +新版本 +--- +在`2.1.2`或`1.10.3`版本中调整了`2`和`3`的顺序,优先执行定时器。 + +1. 通过`Process::signal`设置的信号处理回调函数 +2. 通过`Timer::tick`和`Timer::after`设置的定时器回调函数 +3. 通过`Event::defer`设置的延迟执行函数 +3. 通过`Event::cycle`设置的周期回调函数 diff --git a/doc/6.2.1 - swoole_event_add.md b/doc/6.2.1 - swoole_event_add.md new file mode 100644 index 0000000..f91dc38 --- /dev/null +++ b/doc/6.2.1 - swoole_event_add.md @@ -0,0 +1,58 @@ +#swoole_event_add + + `swoole_event_add`函数用于将一个socket加入到底层的`reactor`事件监听中。此函数可以用在`Server`或`Client`模式下。 +函数原型: + +```php +bool swoole_event_add(mixed $sock, mixed $read_callback, mixed $write_callback = null, + int $flags = null); +``` + +参数 +--- + +参数1可以为以下四种类型: + +* `int`,就是文件描述符,包括`swoole_client->$sock`、`swoole_process->$pipe`或者其他`fd` +* `stream`资源,就是`stream_socket_client/fsockopen`创建的资源 +* `sockets`资源,就是`sockets`扩展中`socket_create`创建的资源,需要在编译时加入 `./configure --enable-sockets` +* `object`,`swoole_process`或`swoole_client`,底层自动转换为管道或客户端连接的`socket` + +参数`2`为可读回调函数,参数`3`为可写事件回调,可以是字符串函数名、对象+方法、类静态方法或匿名函数,当此`socket`可读时回调指定的函数。 + +参数`4`为事件类型的掩码,可选择关闭/开启可读可写事件,如`SWOOLE_EVENT_READ`,`SWOOLE_EVENT_WRITE`,或者`SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE` + + +``` +在 Server 程序中使用时,必须在 Worker 进程启动后使用。在 Server::start 之前不得调用任何异步 IO 接口 +``` + +返回值 +---- +* 添加事件监听成功成功返回`true` +* 添加失败返回`false`,请使用`swoole_last_error`获取错误码 +* 已添加过的`socket`不能重复添加,可以使用`swoole_event_set`修改`socket`对应的回调函数和事件类型 + +> 使用`swoole_event_add`将`socket`加入到事件监听后,底层会自动将该`socket`设置为非阻塞模式 + +使用实例 +---- +```php +$fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30); +fwrite($fp,"GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); + +swoole_event_add($fp, function($fp) { + $resp = fread($fp, 8192); + //socket处理完成后,从epoll事件中移除socket + swoole_event_del($fp); + fclose($fp); +}); +echo "Finish\n"; //swoole_event_add不会阻塞进程,这行代码会顺序执行 +``` + +回调函数 +---- +* 在可读事件回调函数中必须使用`fread`、`recv`等函数读取`socket`缓存区中的数据,否则事件会持续触发,如果不希望继续读取必须使用`Swoole\Event::del`移除事件监听 +* 在可写事件回调函数中,写入`socket`之后必须调用`Swoole\Event::del`移除事件监听,否则可写事件会持续触发 +* 执行`fread`、`socekt_recv`、`socket_read`、`Swoole\Client::recv`返回`false`,并且错误码为`EAGAIN`时表示当前`socket`接收缓存区内没有任何数据,这时需要加入可读监听等待`EventLoop`通知 +* 执行`fwrite`、`socket_write`、`socket_send`、`Swoole\Client::send`操作返回`false`,并且错误码为`EAGAIN`时表示当前`socket`发送缓存区已满,暂时不能发送数据。需要监听可写事件等待`EventLoop`通知 diff --git a/doc/6.2.10 - swoole_event_dispatch.md b/doc/6.2.10 - swoole_event_dispatch.md new file mode 100644 index 0000000..8698982 --- /dev/null +++ b/doc/6.2.10 - swoole_event_dispatch.md @@ -0,0 +1,19 @@ +#swoole_event_dispatch + +仅执行一次`reactor->wait`操作,在`Linux`平台下相当手工调用一次`epoll_wait`。与`swoole_event_wait`不同的是,`swoole_event_wait`在底层内部维持了循环。 + +```c +void swoole_event_dispatch(void); +``` + +此函数的目的是兼容一些框架,如`amp`,它在框架内部自行控制`reactor`的循环,而使用`swoole_event_wait`,`swoole`底层维持了控制权,就无法让出给框架方。 + + +```php +while(true) +{ + swoole_event_dispatch(); +} +``` + +> 需要`2.1.2`/`1.10.3`或更高版本 \ No newline at end of file diff --git a/doc/6.2.2 - swoole_event_set.md b/doc/6.2.2 - swoole_event_set.md new file mode 100644 index 0000000..7314e0e --- /dev/null +++ b/doc/6.2.2 - swoole_event_set.md @@ -0,0 +1,26 @@ +#swoole_event_set + +修改事件监听的回调函数和掩码。函数原型: +```php +bool swoole_event_set($fd, mixed $read_callback, mixed $write_callback, int $flags); +``` + +参数与[swoole_event_add](/wiki/page/119.html)完全相同。如果传入`$fd`在`EventLoop`中不存在返回`false`。 + +* 当·`$read_callback`不为`null`时,将修改可读事件回调函数为指定的函数 +* 当`$write_callback`不为`null`时,将修改可写事件回调函数为指定的函数 +* `$flags`可关闭/开启,可写(`SWOOLE_EVENT_READ`)和可读(`SWOOLE_EVENT_WRITE`)事件的监听 + +注意如果监听了`SWOOLE_EVENT_READ`事件,而当前并未设置`read_callback`,底层会直接返回`false`,添加失败。`SWOOLE_EVENT_WRITE`同理。 + +状态变更 +---- +使用`swoole_event_add`或`swoole_event_set`设置了可读事件回调,但并未监听`SWOOLE_EVENT_READ`可读事件,这时底层仅保存回调函数的信息。并不会产生任何事件回调。 + +可以使用`swoole_event_set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE`,修改监听的事件类型,这时底层会触发可读事件。 + +释放回调函数 +---- +注意`swoole_event_set`只能替换回调函数,但并不能释放事件回调函数。如:`swoole_event_set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE`,参数中传入的`read_callback`和`write_callback`为`null`,表示不对`swoole_event_add`设置的回调函数进行修改,而不是将事件回调函数设为`null`。 + +只有调用`swoole_event_del`清除事件监听时,底层才会释放`read_callback`和`write_callback`事件回调函数。 \ No newline at end of file diff --git a/doc/6.2.3 - swoole_event_isset.md b/doc/6.2.3 - swoole_event_isset.md new file mode 100644 index 0000000..360a7cd --- /dev/null +++ b/doc/6.2.3 - swoole_event_isset.md @@ -0,0 +1,24 @@ +#swoole_event_isset + +检测传入的`$fd`是否已加入了事件监听。 + +```php +bool swoole_event_isset(mixed $fd, int $events = SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); +``` + +* `$fd`:任意的`socket`文件描述符,参考 `swoole_event_add` 文档 +* `$events`:检测的事件类型 + * `SWOOLE_EVENT_READ`:是否监听了可读事件 + * `SWOOLE_EVENT_WRITE`:是否监听了可写事件 + * `SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE`:监听可读或可写事件 + +> 需要`2.1.2`/`1.10.3`或更高版本 + +使用实例 +---- +```php +swoole_event_add($fd, $callback, null, SWOOLE_EVENT_READ); +var_dump(swoole_event_isset($fd, SWOOLE_EVENT_READ)); //返回 true +var_dump(swoole_event_isset($fd, SWOOLE_EVENT_WRITE)); //返回 false +var_dump(swoole_event_isset($fd, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)); //返回 true +``` \ No newline at end of file diff --git a/doc/6.2.4 - swoole_event_write.md b/doc/6.2.4 - swoole_event_write.md new file mode 100644 index 0000000..88ec600 --- /dev/null +++ b/doc/6.2.4 - swoole_event_write.md @@ -0,0 +1,35 @@ +#swoole_event_write + +用于PHP自带stream/sockets扩展创建的socket,使用fwrite/socket_send等函数向对端发送数据。当发送的数据量较大,socket写缓存区已满,就会发送阻塞等待或者返回EAGAIN错误。 + +swoole_event_write函数可以将stream/sockets资源的数据发送变成异步的,当缓冲区满了或者返回EAGAIN,swoole底层会将数据加入到发送队列,并监听可写。socket可写时swoole底层会自动写入。 + +```php +$fp = stream_socket_client('tcp://127.0.0.1:9501'); +$data = str_repeat('A', 1024 * 1024*2); + +swoole_event_add($fp, function($fp) { + echo fread($fp); +}); + +swoole_event_write($fp, $data); +``` +* `swoole_event_write`不能用于`SSL/TLS`等有隧道加密的`stream/sockets`资源 +* `swoole_event_write`操作成功后,会自动将该`$socket`设置为非阻塞模式 +* `$data` 发送数据的长度不得超过`Socket`缓存区尺寸 + +> 此函数在swoole-1.7.9以上版本可用 + +SOCKET缓存区已满后,Swoole的底层逻辑 +---- +持续写入SOCKET如果对端读取不够快,那SOCKET缓存区会塞满。swoole底层会将数据存到内存缓存区中,直到可写事件触发再写入SOCKET。 + +* 可使用 `Swoole\Async::set`(或 `swoole_async_set`)方法动态设置 `socket_buffer_size` 值,更多参数见 [AsyncIO](https://wiki.swoole.com/wiki/page/182.html) +* swoole老版本内存缓存区尺寸可以在通过修改`php.ini` 中的 `swoole.socket_buffer_size` 项进行配置,默认为8M + +如果内存缓存区也被写满了,此时swoole底层会抛出`pipe buffer overflow, reactor will block.` 错误,并进入阻塞等待。 + +如果调用端希望不要阻塞,直接返回错误,可以使用`swoole_async_set`设置`socket_dontwait`为`true`,write将不会阻塞而是直接返回`false` + +> 缓存塞满返回false是原子操作,只会出现全部写入成功或者全部失败 + diff --git a/doc/6.2.5 - swoole_event_del.md b/doc/6.2.5 - swoole_event_del.md new file mode 100644 index 0000000..e6ad5af --- /dev/null +++ b/doc/6.2.5 - swoole_event_del.md @@ -0,0 +1,8 @@ +#swoole_event_del + +swoole_event_del函数用于从reactor中移除监听的socket。swoole_event_del应当与swoole_event_add成对使用。 +函数原型: +```php +bool swoole_event_del(int $sock); +``` +参数为socket的文件描述符。 \ No newline at end of file diff --git a/doc/6.2.6 - swoole_event_exit.md b/doc/6.2.6 - swoole_event_exit.md new file mode 100644 index 0000000..59a7be0 --- /dev/null +++ b/doc/6.2.6 - swoole_event_exit.md @@ -0,0 +1,6 @@ +#swoole_event_exit + +退出事件轮询,此函数仅在Client程序中有效。 +```c +void swoole_event_exit(void) +``` \ No newline at end of file diff --git a/doc/6.2.7 - swoole_event_defer.md b/doc/6.2.7 - swoole_event_defer.md new file mode 100644 index 0000000..f338452 --- /dev/null +++ b/doc/6.2.7 - swoole_event_defer.md @@ -0,0 +1,18 @@ +#swoole_event_defer + +在下一个事件循环开始时执行函数。 +```php +swoole_event_defer(mixed $callback_function); +``` +swoole_event_defer函数会在当前EventLoop的事件循环结束、下一次事件循环启动时响应 + +* $callback_function 时间到期后所执行的函数,必须是可以调用的。回调函数不接受任何参数 +* 可以使用匿名函数的`use`语法传递参数到回调函数中 + +使用示例 +---- +```php +swoole_event_defer(function(){ + echo "After EventLoop\n"; +}); +``` \ No newline at end of file diff --git a/doc/6.2.8 - swoole_event_cycle.md b/doc/6.2.8 - swoole_event_cycle.md new file mode 100644 index 0000000..6daffc5 --- /dev/null +++ b/doc/6.2.8 - swoole_event_cycle.md @@ -0,0 +1,32 @@ +#swoole_event_cycle + +定义事件循环周期执行函数。此函数会在每一轮事件循环结束时调用。 + +```php +bool swoole_event_cycle(callable $callback, bool $before = false); +``` + +* `$callback`要设置的回调函数,必须为可执行。`$callback`为`null`时表示清除`cycle`函数 +* 已设置`cycle`函数,重新设置时会覆盖上一次的设定 +* `$before`在`EventLoop`之前调用该函数。此参数需要`2.1.2`/`1.10.3`或更高版本 +* 设置成功返回`true` + +可以同时存在`before=true`和`before=false`两个回调函数。 + +> 需要`1.9.24`或更高版本 + +使用实例 +--- +```php +Swoole\Timer::tick(2000, function ($id) { + var_dump($id); +}); + +Swoole\Event::cycle(function () { + echo "hello [1]\n"; + Swoole\Event::cycle(function () { + echo "hello [2]\n"; + Swoole\Event::cycle(null); + }); +}); +``` \ No newline at end of file diff --git a/doc/6.2.9 - swoole_event_wait.md b/doc/6.2.9 - swoole_event_wait.md new file mode 100644 index 0000000..3ee84d8 --- /dev/null +++ b/doc/6.2.9 - swoole_event_wait.md @@ -0,0 +1,10 @@ +#swoole_event_wait + +函数原型: +```php +void swoole_event_wait(void); +``` +PHP5.4之前的版本没有在ZendAPI中加入注册shutdown函数。所以swoole无法在脚本结尾处自动进行事件轮询。所以低于5.4的版本,需要在你的PHP脚本结尾处加swoole_event_wait函数。使脚本开始进行事件轮询。 + +> 5.4或更高版本不需要加此函数 +> SwooleServer下也不需要加 \ No newline at end of file diff --git "a/doc/6.3 - \345\274\202\346\255\245\346\257\253\347\247\222\345\256\232\346\227\266\345\231\250.md" "b/doc/6.3 - \345\274\202\346\255\245\346\257\253\347\247\222\345\256\232\346\227\266\345\231\250.md" new file mode 100644 index 0000000..a31c96e --- /dev/null +++ "b/doc/6.3 - \345\274\202\346\255\245\346\257\253\347\247\222\345\256\232\346\227\266\345\231\250.md" @@ -0,0 +1,13 @@ +#异步毫秒定时器 + +swoole_server中已经提供了定时器的API,如果是在客户端程序中,也想使用毫秒定时器。可以用swoole提供的swoole_timer模块。 + +swoole_timer与PHP本身的pcntl_alarm是不同的。pcntl_alarm是基于时钟信号 + PHP tick函数实现,有4个缺陷: + +* 最大仅支持到秒,而swoole_timer可以到毫秒级别 +* 不支持同时设定多个定时器程序 +* pcntl_alarm依赖declare(ticks = 1)性能很差 +* 无法用于异步IO,只支持同步方式 + +swoole_timer是基于timerfd+epoll实现的异步毫秒定时器,可完美的运行在EventLoop中,与swoole_client/swoole_event等模块可以无缝结合。 + diff --git a/doc/6.3.1 - swoole_timer_tick.md b/doc/6.3.1 - swoole_timer_tick.md new file mode 100644 index 0000000..a630321 --- /dev/null +++ b/doc/6.3.1 - swoole_timer_tick.md @@ -0,0 +1,66 @@ +#swoole_timer_tick + +设置一个间隔时钟定时器,与after定时器不同的是tick定时器会持续触发,直到调用swoole_timer_clear清除。 + +```php +int swoole_timer_tick(int $ms, callable $callback, mixed $user_param); +``` + +* $ms 指定时间,单位为毫秒 +* $callback_function 时间到期后所执行的函数,必须是可以调用的。 +* $user_param 用户参数, 该参数会被传递到`$callback_function`中. 如果有多个参数可以使用数组形式. 也可以使用匿名函数的`use`语法传递参数到回调函数中 +* 定时器仅在当前进程空间内有效 +* 定时器是纯异步实现的,不能与阻塞IO的函数一起使用,否则定时器的执行时间会发生错乱 + +> $ms 最大不得超过 86400000 +> tick定时器在1.7.14以上版本可用 +> 定时器在执行的过程中可能会产生微小的偏差,请勿基于定时器实现精确时间计算 + +回调函数 +---- +定时器触发的回调函数接受2个参数。 + +```php +function callbackFunction(int $timer_id, mixed $params = null); +``` + +* `$timer_id` 定时器的ID,可用于`swoole_timer_clear`清除此定时器 +* `$params` 由`swoole_timer_tick`传入的第三个参数 + +定时器校正 +---- +定时器回调函数的执行时间不影响下一次定时器执行的时间。实例:在`0.002ms`设置了`10ms`的`tick`定时器,第一次会在`0.012ms`执行回调函数,如果回调函数执行了`5ms`,下一次定时器仍然会在`0.022ms`时触发,而不是`0.027ms`。 + +但如果定时器回调函数的执行时间过长,甚至覆盖了下一次定时器执行的时间。底层会进行时间校正,丢弃已过期的行为,在下一时间回调。如上面例子中`0.012ms`时的回调函数执行了`15ms`,本该在`0.022ms`产生一次定时回调。实际上本次定时器在`0.027ms`才返回,这时定时早已过期。底层会在`0.032ms`时再次触发定时器回调。 + +使用示例 +---- +```php +swoole_timer_tick(1000, function(){ + echo "timeout\n"; +}); +``` +**正确示例** +```php +swoole_timer_tick(3000, function () { + echo "after 3000ms.\n"; + swoole_timer_after(14000, function () { + echo "after 14000ms.\n"; + }); + +}); +``` + +**错误示例** +```php +swoole_timer_tick(3000, function () { + echo "after 3000ms.\n"; + sleep(14); + echo "after 14000ms.\n"; +}); +``` + + +注意 +---- +如果需要在Swoole Server内使用此功能,请用swoole_server->tick \ No newline at end of file diff --git a/doc/6.3.2 - swoole_timer_after.md b/doc/6.3.2 - swoole_timer_after.md new file mode 100644 index 0000000..9cbffe5 --- /dev/null +++ b/doc/6.3.2 - swoole_timer_after.md @@ -0,0 +1,31 @@ +#swoole_timer_after + +在指定的时间后执行函数,需要`1.7.7`或更高版本。 +```php +int swoole_timer_after(int $after_time_ms, mixed $callback_function, mixed $user_param); +``` +`swoole_timer_after`函数是一个一次性定时器,执行完成后就会销毁。此函数与`PHP`标准库提供的`sleep`函数不同,`after`是非阻塞的。而`sleep`调用后会导致当前的进程进入阻塞,将无法处理新的请求。 + +执行成功返回定时器ID,若取消定时器,可调用 `swoole_timer_clear` + +* `$after_time_ms` 指定时间,单位为毫秒,最大不得超过 `86400000` +* `$callback_function` 时间到期后所执行的函数,必须是可以调用的。 +* `$user_param` 用户参数, 该参数会被传递到`$callback_function`中. 如果有多个参数可以使用数组形式. 也可以使用匿名函数的`use`语法传递参数到回调函数中 + +使用示例 +---- +```php +swoole_timer_after(1000, function(){ + echo "timeout\n"; +}); +``` + +性能测试 +---- +底层使用最小堆数据结构实现定时器,定时器的添加和删除,全部为内存操作,因此性能是非常高的。官方的基准测试脚本 中,添加或删除`10万`个随机时间的定时器耗时为`0.08s`左右。 + +```shell +~/workspace/swoole/benchmark$ php timer.php +add 100000 timer :0.091133117675781s +del 100000 timer :0.084658145904541s +``` \ No newline at end of file diff --git a/doc/6.3.3 - swoole_timer_clear.md b/doc/6.3.3 - swoole_timer_clear.md new file mode 100644 index 0000000..591329c --- /dev/null +++ b/doc/6.3.3 - swoole_timer_clear.md @@ -0,0 +1,24 @@ +#swoole_timer_clear + +使用定时器ID来删除定时器。 + +```php +bool swoole_timer_clear(int $timer_id) +``` + +* $timer_id,定时器ID,调用`swoole_timer_tick`、`swoole_timer_after`后会返回一个整数的ID +* swoole_timer_clear不能用于清除其他进程的定时器,只作用于当前进程 + +使用示例 +---- +```php +$timer = swoole_timer_after(1000, function(){ + echo "timeout\n"; +}); + +var_dump(swoole_timer_clear($timer)); +var_dump($timer); + +// 输出:bool(true) int(1) +// 不输出:timeout +``` diff --git "a/doc/6.4 - \345\274\202\346\255\245MySQL\345\256\242\346\210\267\347\253\257.md" "b/doc/6.4 - \345\274\202\346\255\245MySQL\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 0000000..0f460fe --- /dev/null +++ "b/doc/6.4 - \345\274\202\346\255\245MySQL\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,40 @@ +#异步MySQL客户端 + + `Swoole`在`1.8.6`版本提供了全新的异步MySQL客户端,底层自行实现了`MySQL`的通信协议,无需依赖其他第三方库,如`libmysqlclient`、`mysqlnd`、`mysqli`等。 + +从`1.8.6`版本开始`Swoole\MySQL`已内置到`Swoole`中,无需通过`--enable-async-mysql`编译参数开启。 + +使用实例 +--- +```php +$db = new swoole_mysql; +$server = array( + 'host' => '192.168.56.102', + 'port' => 3306, + 'user' => 'test', + 'password' => 'test', + 'database' => 'test', + 'charset' => 'utf8', //指定字符集 + 'timeout' => 2, // 可选:连接超时时间(非查询超时时间),默认为SW_MYSQL_CONNECT_TIMEOUT(1.0) +); + +$db->connect($server, function ($db, $r) { + if ($r === false) { + var_dump($db->connect_errno, $db->connect_error); + die; + } + $sql = 'show tables'; + $db->query($sql, function(swoole_mysql $db, $r) { + if ($r === false) + { + var_dump($db->error, $db->errno); + } + elseif ($r === true ) + { + var_dump($db->affected_rows, $db->insert_id); + } + var_dump($r); + $db->close(); + }); +}); +``` diff --git a/doc/6.4.1 - swoole_mysql->construct.md b/doc/6.4.1 - swoole_mysql->construct.md new file mode 100644 index 0000000..f75f893 --- /dev/null +++ b/doc/6.4.1 - swoole_mysql->construct.md @@ -0,0 +1,3 @@ +#swoole_mysql->construct + +创建异步mysql客户端。 \ No newline at end of file diff --git a/doc/6.4.2 - swoole_mysql->on.md b/doc/6.4.2 - swoole_mysql->on.md new file mode 100644 index 0000000..b4b0556 --- /dev/null +++ b/doc/6.4.2 - swoole_mysql->on.md @@ -0,0 +1,16 @@ +#swoole_mysql->on + +设置事件回调函数。目前仅支持`onClose`事件回调。 + +```php +function swoole_mysql->on($event_name, callable $callback); +``` + +onClose事件 +---- +当连接关闭时回调此函数。 +```php +$db->on('Close', function($db){ + echo "MySQL connection is closed.\n"; +}); +``` diff --git a/doc/6.4.3 - swoole_mysql->connect.md b/doc/6.4.3 - swoole_mysql->connect.md new file mode 100644 index 0000000..aff518a --- /dev/null +++ b/doc/6.4.3 - swoole_mysql->connect.md @@ -0,0 +1,38 @@ +#swoole_mysql->connect + +异步连接到MySQL服务器。 + +```php +function swoole_mysql->connect(array $serverConfig, callable $callback); +``` + +* $serverConfig为MySQL服务器的配置,必须为关联索引数组 +* $callback连接完成后回调此函数 + +服务器配置 +---- +```php +$server = array( + 'host' => '192.168.56.102', + 'user' => 'test', + 'password' => 'test', + 'database' => 'test', + 'charset' => 'utf8', +); +``` + +* `host` MySQL服务器的主机地址,支持IPv6(`::1`)和UnixSocket(`unix:/tmp/mysql.sock`) +* `port` MySQL服务器监听的端口,选填,默认为`3306` +* `user` 用户名,必填 +* `password` 密码,必填 +* `database` 连接的数据库,必填 +* `charset` 设置客户端字符集,选填,默认使用Server返回的字符集。如果字符串不存在,底层会抛出`Swoole\MySQL\Exception`异常 + +回调函数 +---- +```php +function onConnect(swoole_mysql $db, bool $result); +``` +* $db 为swoole_mysql对象 +* $result 连接是否成功,只有为true时才可以执行`query`查询 +* $result 为false,可以通过`connect_errno`和`connect_error`得到失败的错误码和错误信息 diff --git a/doc/6.4.4 - swoole_mysql->escape.md b/doc/6.4.4 - swoole_mysql->escape.md new file mode 100644 index 0000000..c1e6fee --- /dev/null +++ b/doc/6.4.4 - swoole_mysql->escape.md @@ -0,0 +1,28 @@ +#swoole_mysql->escape + +转义SQL语句中的特殊字符,避免SQL注入攻击。底层基于`mysqlnd`提供的函数实现,需要依赖PHP的`mysqlnd`扩展。 + +* 编译时需要增加`--enable-mysqlnd`来启用,如果你的PHP中没有`mysqlnd`将会出现编译错误 +* 必须在`connect`完成后才能使用 +* 客户端未设置字符集时默认使用Server返回的字符集设置,可在`connect`方法中加入`charset`修改连接字符集 + +> 此方法在1.9.6或更高版本可用 + +```php +function swoole_mysql->escape(string $str) : string +``` + +使用实例 +---- +```php +$db = new swoole_mysql; +$server = array( + 'host' => '127.0.0.1', + 'user' => 'root', + 'password' => 'root', + 'database' => 'test', +); +$db->connect($server, function ($db, $result) { + $data = $db->escape("abc'efg\r\n"); +}); +``` diff --git a/doc/6.4.5 - swoole_mysql->query.md b/doc/6.4.5 - swoole_mysql->query.md new file mode 100644 index 0000000..50ec389 --- /dev/null +++ b/doc/6.4.5 - swoole_mysql->query.md @@ -0,0 +1,29 @@ +#swoole_mysql->query + +执行SQL查询。 +```php +function swoole_mysql->query($sql, callable $callback); +``` + +* $sql为要执行的SQL语句 +* $callback执行成功后会回调此函数 +* 每个MySQLi连接只能同时执行一条SQL,必须等待返回结果后才能执行下一条SQL + +回调函数 +---- +```php +function onSQLReady(swoole_mysql $link, mixed $result); +``` +* 执行失败,`$result`为`false`,读取`$link`对象的`error`属性获得错误信息,`errno`属性获得错误码 +* 执行成功,SQL为非查询语句,`$result`为`true`,读取`$link`对象的`affected_rows`属性获得影响的行数,`insert_id`属性获得`Insert`操作的自增ID +* 执行成功,SQL为查询语句,`$result`为结果数组 + +事务处理 +---- +在`Swoole\MySQL`中执行下列SQL语句可以实现事务处理。 + +* 启动事务:`START TRANSACTION` +* 提交事务:`COMMIT` +* 回滚事务:`ROLLBACK` + + diff --git a/doc/6.4.6 - swoole_mysql->begin.md b/doc/6.4.6 - swoole_mysql->begin.md new file mode 100644 index 0000000..13b8c5d --- /dev/null +++ b/doc/6.4.6 - swoole_mysql->begin.md @@ -0,0 +1,26 @@ +#swoole_mysql->begin + +启动事务。函数原型: +```php +function swoole_mysql->begin(callable $callback); +``` + +* 启动一个MySQL事务,事务启动成功会回调指定的函数 +* 与`commit`和`rollback`结合实现MySQL事务处理 +* 同一个MySQL连接对象,同一时间只能启动一个事务 +* 必须等到上一个事务`commit`或`rollback`才能继续启动新事务 +* 否则底层会抛出`Swoole\MySQL\Exception`异常,异常`code`为`21` + +> 事务处理在`1.9.15`或更高版本可用 + +使用实例 +---- +```php +$db->begin(function( $db, $result) { + $db->query("update userinfo set level = 22 where id = 1", function($db, $result) { + $db->rollback(function($db, $result) { + echo "commit ok\n"; + }); + }); +}); +``` \ No newline at end of file diff --git a/doc/6.4.7 - swoole_mysql->commit.md b/doc/6.4.7 - swoole_mysql->commit.md new file mode 100644 index 0000000..5d39928 --- /dev/null +++ b/doc/6.4.7 - swoole_mysql->commit.md @@ -0,0 +1,25 @@ +#swoole_mysql->commit + +提交事务。 +```php +function swoole_mysql->commit(callable $callback); +``` + +* 提交事务,当服务器返回响应时回调此函数 +* 必须先调用`begin`启动事务才能调用`commit`否则底层会抛出`Swoole\MySQL\Exception`异常 +* 异常`code`为`22` + +> 在`1.9.15`或更高版本可用 + +使用实例 +----- + +```php +$db->begin(function( $db, $result) { + $db->query("update userinfo set level = 22 where id = 1", function($db, $result) { + $db->commit(function($db, $result){ + echo "commit ok\n"; + }); + }); +}); +``` \ No newline at end of file diff --git a/doc/6.4.8 - swoole_mysql->rollback.md b/doc/6.4.8 - swoole_mysql->rollback.md new file mode 100644 index 0000000..32034cc --- /dev/null +++ b/doc/6.4.8 - swoole_mysql->rollback.md @@ -0,0 +1,10 @@ +#swoole_mysql->rollback + +回滚事务。 + +```php +function swoole_mysql->rollback(callable $callback); +``` + +* 必须先调用`begin`启动事务才能调用`rollback`否则底层会抛出`Swoole\MySQL\Exception`异常 +* 异常`code`为`22` \ No newline at end of file diff --git a/doc/6.4.9 - swoole_mysql->close.md b/doc/6.4.9 - swoole_mysql->close.md new file mode 100644 index 0000000..f520187 --- /dev/null +++ b/doc/6.4.9 - swoole_mysql->close.md @@ -0,0 +1,7 @@ +#swoole_mysql->close + +关闭MySQL连接。 + +```php +function swoole_mysql->close(); +``` \ No newline at end of file diff --git "a/doc/6.5 - \345\274\202\346\255\245Redis\345\256\242\346\210\267\347\253\257.md" "b/doc/6.5 - \345\274\202\346\255\245Redis\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 0000000..bb02571 --- /dev/null +++ "b/doc/6.5 - \345\274\202\346\255\245Redis\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,33 @@ +#异步Redis客户端 + + `Swoole-1.8.0`版本增加了对异步Redis客户端的支持,基于redis官方提供的[hiredis](https://github.com/redis/hiredis)库实现。Swoole提供了`__call`魔术方法,来映射绝大部分Redis指令。 + +编译安装hiredis +---- +使用Redis客户端,需要安装hiredis库。下载`hiredis`源码后,执行 + +```shell +make -j +sudo make install +sudo ldconfig +``` +* hiredis下载地址: + +启用异步Redis客户端 +---- +编译swoole时,在`configure`指令中加入`--enable-async-redis` +```shell +./configure --enable-async-redis +make clean +make -j +sudo make install +``` +可能遇到的问题 +---- +`php-m` 发现swoole消失或者是通过`php --ri swoole`没有显示async redis client +```shell +vi ~/.bash_profile +在最后一行添加 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib +source ~/.bash_profile +``` +重新编译安装swoole即可 diff --git a/doc/6.5.1 - swoole_redis->__construct.md b/doc/6.5.1 - swoole_redis->__construct.md new file mode 100644 index 0000000..24d9131 --- /dev/null +++ b/doc/6.5.1 - swoole_redis->__construct.md @@ -0,0 +1,38 @@ +#swoole_redis->__construct + +Redis异步客户端构造方法,可以设置`Redis`连接的配置选项。 + +```php +function swoole_redis->__construct(array $options = null); +``` + +* `$options` 配置选项数组,默认为`null` +* 在`1.9.15`或更高版本可用 + +超时控制 +---- +```php +$options['timeout'] = 1.5; +``` + +* 浮点型,单位为秒,最小粒度为1毫秒。Connect后,在规定的时间内服务器没有完成握手,底层将自动关闭`socket`,设置连接为失败,触发`onConnect`事件 + +设置密码 +---- +```php +$options['password'] = 'passwd'; +``` + +* 必须为字符串类型,可以设置Redis服务器密码,等同于`auth`指令 + +设置数据库 +---- +```php +$options['database'] = 0; +``` + +* 整型,设置使用的Redis服务器的数据库编号,等同于`select`指令 + +> 设置了`password`或`database`选项后,连接就绪后底层会自动发送相关指令 +> 等待服务器响应成功后才会触发`onConnect`连接成功事件 +> 如果`password`或`database`错误,`onConnect`连接结果为失败 \ No newline at end of file diff --git a/doc/6.5.2 - swoole_redis->on.md b/doc/6.5.2 - swoole_redis->on.md new file mode 100644 index 0000000..d8887a8 --- /dev/null +++ b/doc/6.5.2 - swoole_redis->on.md @@ -0,0 +1,22 @@ +#swoole_redis->on + +注册事件回调函数。 +```php +function swoole_redis->on(string $event_name, callable $callback); +``` +目前`swoole_redis`支持2种事件回调函数。`on`方法必须在`connect`前被调用。 + +onClose +----- +当Redis服务器主动关闭连接或者客户端主动调用close关闭连接时,会触发`onClose`事件。 + +```php +function onClose(swoole_redis $redis); +``` + +onMessage +---- +当客户端收到来自服务器的订阅消息时触发onMessage事件。 +```php +function onMessage(swoole_redis $redis, array $message); +``` \ No newline at end of file diff --git a/doc/6.5.3 - swoole_redis->connect.md b/doc/6.5.3 - swoole_redis->connect.md new file mode 100644 index 0000000..94a4950 --- /dev/null +++ b/doc/6.5.3 - swoole_redis->connect.md @@ -0,0 +1,38 @@ +#swoole_redis->connect + +连接到Redis服务器 + +函数原型: +--- +```php +function swoole_redis->connect(string $host, int $port, callable $callback); +``` +* $host: Redis服务器的主机IP +* $port: Redis服务器的端口 +* $callback: 连接成功后回调的函数 + +回调函数 +---- +```php +function onConnect(swoole_redis $redis, bool $result); +``` + +* $redis: redis连接对象 +* $result: 连接成功为true,连接失败为false,可以读取`$redis->errCode`获得错误码,读取`$redis->errMsg`获得错误消息 + +连接成功后就可以执行Redis指令了。 + +使用示例 +---- +```php +$client = new swoole_redis; +$client->connect('127.0.0.1', 6379, function (swoole_redis $client, $result) { + if ($result === false) { + echo "connect to redis server failed.\n" + return; + } + $client->set('key', 'swoole', function (swoole_redis $client, $result) { + var_dump($result); + }); +}); +``` diff --git a/doc/6.5.4 - swoole_redis->__call.md b/doc/6.5.4 - swoole_redis->__call.md new file mode 100644 index 0000000..a0e3814 --- /dev/null +++ b/doc/6.5.4 - swoole_redis->__call.md @@ -0,0 +1,59 @@ +#swoole_redis->__call + +魔术方法,方法名会映射为Redis指令,参数作为Redis指令的参数。 + +函数原型 +---- +```php +function swoole_redis->__call(string $command, array $params); +``` + +* $command,必须为合法的Redis指令,详细参见[Redis指令列表](http://redis.io/commands) +* $params的最后一个参数必须为可执行的函数,其他参数必须为字符串 + +订阅/发布消息 +---- +Redis服务器除了作为内存存储之外,还可以作为一个消息通道服务器。SwooleRedis客户端也支持了Redis的订阅/发布消息指令。 + +与普通的存储指令不同,消息订阅/发布指令不是请求响应式的。 + +* 订阅/发布指令没有回调函数,不需要在最后一个参数传入callback +* 使用订阅/发布消息命名,必须设置`onMessage`事件回调函数 +* 客户端发出了`subscribe`命令后,只能执行`subscribe`, `psubscribe`,`unsubscribe`,`punsubscribe`这4条命令 + +```php +$client = new swoole_redis; +$client->on('message', function (swoole_redis $client, $result) { + var_dump($result); + static $more = false; + if (!$more and $result[0] == 'message') + { + echo "subscribe new channel\n"; + $client->subscribe('msg_1', 'msg_2'); + $client->unsubscribe('msg_0'); + $more = true; + } +}); +$client->connect('127.0.0.1', 6379, function (swoole_redis $client, $result) { + echo "connect\n"; + $client->subscribe('msg_0'); +}); +``` + +回调函数 +---- +```php +function onReceive(swoole_redis $redis, bool $result); +``` + +* $redis: redis连接对象 +* 执行失败,$result为false, 可以读取`$redis->errCode`获得错误码,读取`$redis->errMsg`获得错误消息 +* 执行成功,返回数据结果,可能是字符串、数组或true + +使用示例 +---- +```php +$client->get('key', function (swoole_redis $client, $result) { + var_dump($result); +}); +``` diff --git a/doc/6.5.5 - swoole_redis->close.md b/doc/6.5.5 - swoole_redis->close.md new file mode 100644 index 0000000..d69dfea --- /dev/null +++ b/doc/6.5.5 - swoole_redis->close.md @@ -0,0 +1,7 @@ +#swoole_redis->close + +关闭Redis连接,不接受任何参数。 + +```php +function swoole_redis->close(); +``` \ No newline at end of file diff --git a/doc/6.6.1 - swoole_http_client->__construct.md b/doc/6.6.1 - swoole_http_client->__construct.md new file mode 100644 index 0000000..c442cd6 --- /dev/null +++ b/doc/6.6.1 - swoole_http_client->__construct.md @@ -0,0 +1,31 @@ +#swoole_http_client->__construct + +构造方法,函数原型: +```php +swoole_http_client->__construct(string $host, int port, bool $ssl = false); +``` + +* `$host` 目标主机的IP地址,`$host`如果为域名底层需要进行一次DNS查询,这是阻塞IO,请使用`Swoole\Async::dnsLookup`实现异步非阻塞 +* `$port` 目标端口,`Http`一般为`80`端口,`Https`一般为`443`端口 +* `$ssl` 是否开启`TLS/SSL`隧道加密,`https`网站必须设置为`true` +* `1.9.15`/`2.0.9`或更高版本增加了超时机制,默认超时时间为`500ms`,如果你需要请求外网`URL`请修改`timeout`为更大的数值 + +> `$ssl`需要依赖`openssl`,必须在编译swoole时启用`--enable-openssl` + +使用示例 +---- +```php +Swoole\Async::dnsLookup("www.baidu.com", function ($domainName, $ip) { + $cli = new swoole_http_client($ip, 80); + $cli->setHeaders([ + 'Host' => $domainName, + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', + ]); + $cli->get('/index.html', function ($cli) { + echo "Length: " . strlen($cli->body) . "\n"; + echo $cli->body; + }); +}); +``` \ No newline at end of file diff --git a/doc/6.6.10 - swoole_http_client->upgrade.md b/doc/6.6.10 - swoole_http_client->upgrade.md new file mode 100644 index 0000000..c85cd93 --- /dev/null +++ b/doc/6.6.10 - swoole_http_client->upgrade.md @@ -0,0 +1,53 @@ +#swoole_http_client->upgrade + +发起WebSocket握手请求,并将连接升级为WebSocket。 +```php +function swoole_http_client->upgrade(string $path, callable $callback); +``` +* $path URL路径 +* $callback 握手成功或失败后回调此函数 +* 使用`Upgrade`方法必须设置`onMessage`回调函数 + +使用实例 +-------- +```php +$cli = new swoole_http_client('127.0.0.1', 9501); + +$cli->on('message', function ($_cli, $frame) { + var_dump($frame); +}); + +$cli->upgrade('/', function ($cli) { + echo $cli->body; + $cli->push("hello world"); +}); +``` + +onMessage回调 +------ +```php +function onMessage(swoole_http_client $client, swoole_websocket_frame $frame); +``` +* $client 客户端对象,可调用`push`方法向服务器发送数据 +* $frame WebSocket数据帧,可参考 [swoole_websocket_server->onMessage](/wiki/page/402.html) + +握手失败 +---- +某些`WebSocket`服务器对于客户端要求非常严格,`Client`需要非常接近`Chrome`等浏览器才可以握手成功。 + +可以通过增加参数和`Http`头,尽可能地让`Client`与`Chrome`等浏览器行为保持一致。 + +```php +$client->set([ + 'websocket_mask' => true, + 'ssl_host_name' => 'www.yourdomain.com', +]); +$client->setHeaders([ + 'Host' => 'www.yourdomain.com', + 'UserAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36' +]); +``` + +* `websocket_mask` 启用数据掩码,浏览器总是对数据进行掩码处理,因为存在较多性能消耗,默认未开启 +* `ssl_host_name` 设置`openssl`域名,`SSL`握手时可能会验证此参数 +* `Host` 设置`Http`的`Host`头,某些服务器会验证此项信息 \ No newline at end of file diff --git a/doc/6.6.11 - swoole_http_client->push.md b/doc/6.6.11 - swoole_http_client->push.md new file mode 100644 index 0000000..0a93964 --- /dev/null +++ b/doc/6.6.11 - swoole_http_client->push.md @@ -0,0 +1,27 @@ +#swoole_http_client->push + +向`WebSocket`服务器发送数据。 +```php +bool swoole_http_client->push(string $data, int $opcode = WEBSOCKET_OPCODE_TEXT, + bool $finish = true) +``` + +* `push`方法必须在`upgrade`事件回调成功触发之后才能执行 + +参数 +---- +* `$data` 要发送的数据内容,默认为`UTF-8`文本格式,如果为其他格式编码或二进制数据,请使用`WEBSOCKET_OPCODE_BINARY` +* `$opcode`操作类型,默认为`WEBSOCKET_OPCODE_TEXT`表示发送文本 +* `$opcode`必须为合法的`WebSocket OPCODE`,否则会返回失败,并打印错误信息`opcode max 10` + + +返回值 +---- +* 发送成功,返回`true` +* 连接不存在、已关闭、未完成`WebSocket`,发送失败返回`false` + +错误码 +---- +* `8502`:错误的`OPCODE` +* `8503`:未连接到服务器或连接已被关闭 +* `8504`:握手失败 diff --git a/doc/6.6.12 - swoole_http_client->execute.md b/doc/6.6.12 - swoole_http_client->execute.md new file mode 100644 index 0000000..36dd35a --- /dev/null +++ b/doc/6.6.12 - swoole_http_client->execute.md @@ -0,0 +1,7 @@ +#swoole_http_client->execute + +更底层的Http请求方法,需要代码中调用`setMethod`和`setData`等接口设置请求的方法和数据。 + +```php +function swoole_http_client->execute(string $path, callable $callback); +``` \ No newline at end of file diff --git a/doc/6.6.13 - swoole_http_client->download.md b/doc/6.6.13 - swoole_http_client->download.md new file mode 100644 index 0000000..4bb0ddd --- /dev/null +++ b/doc/6.6.13 - swoole_http_client->download.md @@ -0,0 +1,53 @@ +#swoole_http_client->download + +通过Http下载文件。`download`与`get`方法的不同是`download`收到数据后会写入到磁盘,而不是在内存中对Http Body进行拼接。因此`download`仅使用小量内存,就可以完成超大文件的下载。 +函数原型: + +```php +function swoole_http_client->download(string $path, string $filename, callable $callback, + int $offset = 0); +``` + +* $path URL路径 +* $filename 指定下载内容写入的文件路径,会自动写入到`downloadFile`属性 +* $callback 下载成功后的回调函数 +* $offset 指定写入文件的偏移量,此选项可用于支持断点续传,可配合Http头`Range:bytes=$offset-`实现 +* $offset为0时若文件已存在,底层会自动清空此文件 +* 执行成功返回true +* 打开文件失败或feek失败返回false + +使用示例 +---- +```php +$cli = new swoole_http_client('127.0.0.1', 80); + +$cli->setHeaders([ + 'Host' => "localhost", + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => '*', + 'Accept-Encoding' => 'gzip', +]); + +$cli->download('/video.avi', __DIR__.'/video.avi', function ($cli) { + var_dump($cli->downloadFile); +}); +``` + +断点续传 +--- +```php +$cli = new swoole_http_client('127.0.0.1', 80); +$file = __DIR__.'/video.avi'; +$offset = filesize($file); +$cli->setHeaders([ + 'Host' => "localhost", + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => '*', + 'Accept-Encoding' => 'gzip', + 'Range' => "bytes=$offset-", +]); + +$cli->download('/video.avi', $file, function ($cli) { + var_dump($cli->downloadFile); +}, $offset); +``` \ No newline at end of file diff --git a/doc/6.6.14 - swoole_http_client->close.md b/doc/6.6.14 - swoole_http_client->close.md new file mode 100644 index 0000000..5fe5695 --- /dev/null +++ b/doc/6.6.14 - swoole_http_client->close.md @@ -0,0 +1,9 @@ +#swoole_http_client->close + +关闭连接,函数原型为: +```php +function swoole_http_client->close() : bool +``` +操作成功返回 **true** + +> `swoole_http_client`与普通的`swoole_client`不同,`close`后如果再次请求`get`、`post`等方法时,底层会重新连接服务器 \ No newline at end of file diff --git a/doc/6.6.2 - swoole_http_client->set.md b/doc/6.6.2 - swoole_http_client->set.md new file mode 100644 index 0000000..ee32801 --- /dev/null +++ b/doc/6.6.2 - swoole_http_client->set.md @@ -0,0 +1,34 @@ +#swoole_http_client->set + +设置客户端参数,此方法与`Swoole\Client->set`接收的参数完全一致,可参考 [Swoole\Client->set](/wiki/page/p-client_setting.html) 方法的文档。 + +除了设置TCPSocket的参数之外,`Swoole\Http\Client` 额外增加了一些选项,来控制`Http`和`WebSocket`客户端。 + +超时控制 +---- +设置`timeout`选项,启用Http请求超时检测。单位为秒,最小粒度支持毫秒。 +```php +$http->set(['timeout' => 3.0]); +``` + +* 连接超时或被服务器关闭连接,`statusCode`将设置为`-1` +* 在约定的时间内服务器未返回响应,请求超时,`statusCode`将设置为`-2` +* 请求超时后底层会自动切断连接 +* 设置为`-1`表示永不超时,底层将不会添加超时检测的定时器 + +> 仅在`1.9.14`或更高版本可用 + +keep_alive +---- +设置`keep_alive`选项,启用或关闭Http长连接。 +```php +$http->set(['keep_alive' => false]); +``` + +websocket_mask +---- +`WebSocket`客户端启用或关闭掩码。默认为关闭。启用后会对WebSocket客户端发送的数据使用掩码进行数据转换。 +```php +$http->set(['websocket_mask' => true]); +``` + diff --git a/doc/6.6.3 - swoole_http_client->setMethod.md b/doc/6.6.3 - swoole_http_client->setMethod.md new file mode 100644 index 0000000..e6447c1 --- /dev/null +++ b/doc/6.6.3 - swoole_http_client->setMethod.md @@ -0,0 +1,10 @@ +#swoole_http_client->setMethod + +设置Http请求方法 +```php +function swoole_http_client->setMethod(string $method); +$client->setMethod("PUT"); +``` +* $method 必须为符合Http标准的方法名称,如果$method设置错误可能会被Http服务器拒绝请求 +* setMethod仅在当前请求有效,发送请求后会立刻清除method设置 + diff --git a/doc/6.6.4 - swoole_http_client->setHeaders.md b/doc/6.6.4 - swoole_http_client->setHeaders.md new file mode 100644 index 0000000..32f37be --- /dev/null +++ b/doc/6.6.4 - swoole_http_client->setHeaders.md @@ -0,0 +1,9 @@ +#swoole_http_client->setHeaders + +设置Http请求头 +```php +function swoole_http_client->setHeaders(array $headers); +``` +* $headers必须为键值对应的数组,底层会自动映射为`$key: $value`格式的Http标准头格式 +* `setHeaders`设置的Http头在`swoole_http_client`对象存活期间的每次请求永久有效 +* 重新调用`setHeaders`会覆盖上一次的设置 \ No newline at end of file diff --git a/doc/6.6.5 - swoole_http_client->setCookies.md b/doc/6.6.5 - swoole_http_client->setCookies.md new file mode 100644 index 0000000..bcd6249 --- /dev/null +++ b/doc/6.6.5 - swoole_http_client->setCookies.md @@ -0,0 +1,10 @@ +#swoole_http_client->setCookies + +设置Cookie +```php +function swoole_http_client->setCookies(array $cookies); +``` + +* $cookies 设置COOKIE,必须为键值对应数组 +* 设置COOKIE后在客户端对象存活期间会持续保存 +* 服务器端主动设置的COOKIE会合并到`cookies`数组中,可读取`$client->cookies`属性获得当前Http客户端的COOKIE信息 diff --git a/doc/6.6.6 - swoole_http_client->setData.md b/doc/6.6.6 - swoole_http_client->setData.md new file mode 100644 index 0000000..fc9a80d --- /dev/null +++ b/doc/6.6.6 - swoole_http_client->setData.md @@ -0,0 +1,11 @@ +#swoole_http_client->setData + +设置Http请求的包体 +```php +function swoole_http_client->setData(string $data); +``` + +* $data 为字符串格式 +* 设置$data后并且未设置$method,底层会自动设置为`POST` +* 未设置Http请求包体并且未设置$method,底层会自动设置为`GET` + diff --git a/doc/6.6.7 - swoole_http_client->addFile.md b/doc/6.6.7 - swoole_http_client->addFile.md new file mode 100644 index 0000000..8ba8d48 --- /dev/null +++ b/doc/6.6.7 - swoole_http_client->addFile.md @@ -0,0 +1,33 @@ +#swoole_http_client->addFile + +添加POST文件。 +```php +function swoole_http_client->addFile(string $path, string $name, string $filename = null, + string $mimeType = null, int $offset = 0, int $length = 0) +``` + +* $path 文件的路径,必选参数,不能为空文件或者不存在的文件 +* $name 表单的名称,必选参数,FILES参数中的key +* $filename 文件名称,可选参数,默认为`basename($path)` +* $mimeType 文件的MIME格式,可选参数,底层会根据文件的扩展名自动推断 +* $offset 上传文件的偏移量,可以指定从文件的中间部分开始传输数据。此特性可用于支持断点续传。 +* $length 发送数据的尺寸,默认为整个文件的尺寸 + +使用`addFile`会自动将POST的`Content-Type`将变更为`form-data`。`addFile`底层基于`sendfile`,可支持异步发送超大文件。 + +> addFile在1.8.9或更高版本可用 +> $offset, $length 参数在1.9.11或更高版本可用 + +使用示例 +---- +```php +setHeaders(['User-Agent' => "swoole"]); +$cli->addFile(__DIR__.'/post.data', 'post'); +$cli->addFile(dirname(__DIR__).'/test.jpg', 'debug'); +$cli->post('/dump2.php', array("xxx" => 'abc', 'x2' => 'rango'), function ($cli) { + echo $cli->body; +}); +``` \ No newline at end of file diff --git a/doc/6.6.8 - swoole_http_client->get.md b/doc/6.6.8 - swoole_http_client->get.md new file mode 100644 index 0000000..97936bb --- /dev/null +++ b/doc/6.6.8 - swoole_http_client->get.md @@ -0,0 +1,29 @@ +#swoole_http_client->get + +发起`GET`请求,函数原型: +```php +function swoole_http_client->get(string $path, callable $callback); +``` + +* $path 设置URL路径,如`/index.html`,注意这里不能传入`http://domain` +* $callback 调用成功或失败后回调此函数 +* 默认使用`GET`方法,可使用`setMethod`设置新的请求方法 +* Http响应内容会在内存中进行数据拼接。因此如果响应体很大可能会占用大量内存 + +使用实例 +---- +```php +$cli = new swoole_http_client('127.0.0.1', 80); + +$cli->setHeaders([ + 'Host' => "localhost", + "User-Agent" => 'Chrome/49.0.2587.3', + 'Accept' => 'text/html,application/xhtml+xml,application/xml', + 'Accept-Encoding' => 'gzip', +]); + +$cli->get('/index.php', function ($cli) { + echo "Length: " . strlen($cli->body) . "\n"; + echo $cli->body; +}); +``` \ No newline at end of file diff --git a/doc/6.6.9 - swoole_http_client->post.md b/doc/6.6.9 - swoole_http_client->post.md new file mode 100644 index 0000000..a57b436 --- /dev/null +++ b/doc/6.6.9 - swoole_http_client->post.md @@ -0,0 +1,21 @@ +#swoole_http_client->post + +发起`POST`请求,函数原型: +```php +function swoole_http_client->post(string $path, mixed $data, callable $callback); +``` + +* $path 设置URL路径,如`/index.html`,注意这里不能传入`http://domain` +* $data 请求的包体数据,如果$data为数组底层自动会打包为`x-www-form-urlencoded`格式的POST内容,并设置`Content-Type`为`application/x-www-form-urlencoded` +* $callback 调用成功或失败后回调此函数 +* 默认使用`POST`方法,可使用`setMethod`设置新的方法 + +使用实例 +---- +```php +$cli = new swoole_http_client('127.0.0.1', 80); +$cli->post('/post.php', array("a" => '1234', 'b' => '456'), function ($cli) { + echo "Length: " . strlen($cli->body) . "\n"; + echo $cli->body; +}); +``` \ No newline at end of file diff --git "a/doc/6.7 - \345\274\202\346\255\245Http2.0\345\256\242\346\210\267\347\253\257.md" "b/doc/6.7 - \345\274\202\346\255\245Http2.0\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 0000000..f773e4b --- /dev/null +++ "b/doc/6.7 - \345\274\202\346\255\245Http2.0\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,38 @@ +#异步Http2.0客户端 + +Swoole-1.9.7增加了对`Http2.0`客户端的支持。新增的客户端类名为`Swoole\Http2\Client`,继承自`Swoole\Client`,实现了`Http2.0`客户端协议的完整支持。 + +Http2.0客户端与Http1.1的最大差别是2.0支持了Stream并发机制,可以同时发起多个`GET`或`POST`请求。最大并发数量受限与服务器端规定的`max_concurrent_streams`设置。 + +编译安装 +---- +需要依赖`nghttp2`库,编译Swoole扩展时需要设置`--enable-http2`、`--enable-openssl`或`--with-openssl-dir`。 + +使用示例 +----- +```php +$array = array( + "host" => "www.jd.com", + "accept-encoding" => "gzip, deflate", + 'accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'accept-language' => 'zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,ja;q=0.2', + 'user-agent' => 'Mozilla/5.0 (X11; Linux x86_64) Chrome/58.0.3026.3 Safari/537.36', +); + +$client = new Swoole\Http2\Client("www.jd.com", 443, true); + +$client->setHeaders($array); +$client->setCookies(array("a" => "1", "b" => "2")); + +$client->get("/", function ($o) use($client) { + echo "#{$client->sock} hello world 1\n"; + echo $o->body; +}); + +$client->post("/", $array, function ($o) use($client) { + echo "{$client->sock} hello world 3\n"; + echo $o->body; + $client->close(); +}); +Swoole\Event::wait(); +``` \ No newline at end of file diff --git a/doc/6.7.1 - swoole_http2_client->__construct.md b/doc/6.7.1 - swoole_http2_client->__construct.md new file mode 100644 index 0000000..44adcde --- /dev/null +++ b/doc/6.7.1 - swoole_http2_client->__construct.md @@ -0,0 +1,11 @@ +#swoole_http2_client->__construct + +构造方法,与`swoole_http_client`的构造方法参数完全一致,共接受3个参数。 + +```php +function swoole_http2_client->__construct($host, $port, $ssl = false) +``` + +* `$host` 服务器的地址,如果未设置`host`头,将自动使用`$host`参数作为默认的`host`头 +* `$port` 端口号,SSL一般为443,非SSL一般为80 +* `$ssl` 是否启用SSL加密,需要依赖`openssl` diff --git a/doc/6.7.2 - swoole_http2_client->get.md b/doc/6.7.2 - swoole_http2_client->get.md new file mode 100644 index 0000000..ebc3c14 --- /dev/null +++ b/doc/6.7.2 - swoole_http2_client->get.md @@ -0,0 +1,30 @@ +#swoole_http2_client->get + +发起`GET`请求,函数原型: +```php +function swoole_http_client->get(string $path, callable $callback); +``` + +* `$path` 设置URL路径,如`/index.html`,注意这里不能传入`http://domain` +* `$callback` 调用成功或失败后回调此函数 +* Http响应内容会在内存中进行数据拼接。因此如果响应体很大可能会占用大量内存 + +回调函数 +---- +与`Http1.1`客户端事件回调函数不同,`Http2.0`回调函数中的参数为`Swoole\Http2\Response`对象。而不是`Client`本身。可使用`use`语法将`Client`对象传递给匿名函数。 + +```php +function callback(Swoole\Http2\Response $resp) +{ + var_dump($resp->cookie); + var_dump($resp->header); + var_dump($resp->server); + var_dump($resp->body); + var_dump($resp->statusCode); +} +``` +* `cookie` 服务器设置的COOKIE信息 +* `header` 服务器发送的Header信息 +* `server` 底层连接与协议相关的信息 +* `body` 服务器发送的响应包体 +* `statusCode` 服务器发送的Http状态码,如`200`、`502`等 diff --git a/doc/6.7.3 - swoole_http2_client->post.md b/doc/6.7.3 - swoole_http2_client->post.md new file mode 100644 index 0000000..f68707a --- /dev/null +++ b/doc/6.7.3 - swoole_http2_client->post.md @@ -0,0 +1,20 @@ +#swoole_http2_client->post + +发起`POST`请求,函数原型: +```php +function swoole_http2_client->post(string $path, mixed $data, callable $callback); +``` + +* $path 设置URL路径,如`/index.html`,注意这里不能传入`http://domain` +* $data 请求的包体数据,如果$data为数组底层自动会打包为`x-www-form-urlencoded`格式的POST内容,并设置`Content-Type`为`application/x-www-form-urlencoded` +* $callback 调用成功或失败后回调此函数 + +使用实例 +---- +```php +$cli = new swoole_http2_client('127.0.0.1', 80); +$cli->post('/post.php', array("a" => '1234', 'b' => '456'), function ($response) { + echo "Length: " . strlen($cli->body) . "\n"; + echo $cli->body; +}); +``` \ No newline at end of file diff --git a/doc/6.7.4 - swoole_http2_client->setHeaders.md b/doc/6.7.4 - swoole_http2_client->setHeaders.md new file mode 100644 index 0000000..1b0c4c3 --- /dev/null +++ b/doc/6.7.4 - swoole_http2_client->setHeaders.md @@ -0,0 +1,9 @@ +#swoole_http2_client->setHeaders + +设置Http请求头 +```php +function swoole_http2_client->setHeaders(array $headers); +``` +* `$headers`必须为键值对应的数组,底层会自动映射为`$key: $value`格式的Http标准头格式 +* `setHeaders`设置的Http头在`swoole_http2_client`对象存活期间的每次请求永久有效 +* 重新调用`setHeaders`会覆盖上一次的设置 \ No newline at end of file diff --git a/doc/6.7.5 - swoole_http2_client->setCookies.md b/doc/6.7.5 - swoole_http2_client->setCookies.md new file mode 100644 index 0000000..29f7828 --- /dev/null +++ b/doc/6.7.5 - swoole_http2_client->setCookies.md @@ -0,0 +1,11 @@ +#swoole_http2_client->setCookies + +设置Cookie +```php +function swoole_http2_client->setCookies(array $cookies); +``` + +* `$cookies` 设置COOKIE,必须为键值对应数组 +* 设置COOKIE后在客户端对象存活期间会持续保存 +* 服务器端主动设置的COOKIE会合并到`cookies`数组中,可读取`$client->cookies`属性获得当前Http2客户端的COOKIE信息 +* 重新调用`setCookies`方法会覆盖已有COOKIE diff --git a/doc/7 - Memory.md b/doc/7 - Memory.md new file mode 100644 index 0000000..2afe311 --- /dev/null +++ b/doc/7 - Memory.md @@ -0,0 +1,7 @@ +#Memory + +Swoole提供了`7`个内存操作的模块,在多进程编程中可以帮助开发者实现一些特殊的需求。 + +* `Memory`下的模块可以安全的用于异步非阻塞程序中,不存在任何`IO`消耗 +* 所有模块均为多进程安全的,无需担心数据同步问题 +* `Memory`相关模块对象为有限资源,不可大量创建 \ No newline at end of file diff --git a/doc/7.1 - Lock.md b/doc/7.1 - Lock.md new file mode 100644 index 0000000..2cde5ab --- /dev/null +++ b/doc/7.1 - Lock.md @@ -0,0 +1,36 @@ +#Lock + +swoole1.6.4版本增加了锁的实现。PHP代码中可以很方便地创建一个锁,用来实现数据同步。swoole_lock类支持5种锁的类型: + +1. 文件锁 SWOOLE_FILELOCK +2. 读写锁 SWOOLE_RWLOCK +3. 信号量 SWOOLE_SEM +4. 互斥锁 SWOOLE_MUTEX +5. 自旋锁 SWOOLE_SPINLOCK + +**注意:请勿在`onReceive`等回调函数中创建锁,否则底层的`GlobalMemory`内存会持续增长,造成内存泄漏。** + +示例: +----- +```php +$lock = new swoole_lock(SWOOLE_MUTEX); +echo "[Master]create lock\n"; +$lock->lock(); +if (pcntl_fork() > 0) +{ + sleep(1); + $lock->unlock(); +} +else +{ + echo "[Child] Wait Lock\n"; + $lock->lock(); + echo "[Child] Get Lock\n"; + $lock->unlock(); + exit("[Child] exit\n"); +} +echo "[Master]release lock\n"; +unset($lock); +sleep(1); +echo "[Master]exit\n"; +``` diff --git a/doc/7.1.1 - swoole_lock->__construct.md b/doc/7.1.1 - swoole_lock->__construct.md new file mode 100644 index 0000000..486789e --- /dev/null +++ b/doc/7.1.1 - swoole_lock->__construct.md @@ -0,0 +1,14 @@ +#swoole_lock->__construct + +函数原型: +```php +swoole_lock->__construct(int $type, [string $lockfile]) +``` + +* $type为锁的类型 +* $lockfile,当类型为SWOOLE_FILELOCK时必须传入,指定文件锁的路径 + +注意每一种类型的锁支持的方法都不一样。如读写锁、文件锁可以支持$lock->lock_read()。 +另外除文件锁外,其他类型的锁必须在父进程内创建,这样fork出的子进程之间才可以互相争抢锁。 + +> 不要循环创建/销毁锁的对象,否则会发生内存泄漏 \ No newline at end of file diff --git a/doc/7.1.2 - swoole_lock->lock.md b/doc/7.1.2 - swoole_lock->lock.md new file mode 100644 index 0000000..29e6cc3 --- /dev/null +++ b/doc/7.1.2 - swoole_lock->lock.md @@ -0,0 +1,9 @@ +#swoole_lock->lock + +函数原型: +```php +$lock->lock(); +``` +加锁操作。如果有其他进程持有锁,那这里将进入阻塞,直到持有锁的进程unlock。 + +* 加锁成功返回`true` \ No newline at end of file diff --git a/doc/7.1.3 - swoole_lock->trylock.md b/doc/7.1.3 - swoole_lock->trylock.md new file mode 100644 index 0000000..034f4bd --- /dev/null +++ b/doc/7.1.3 - swoole_lock->trylock.md @@ -0,0 +1,13 @@ +#swoole_lock->trylock + +函数原型: +```php +$lock->trylock(); +``` +加锁操作。与lock方法不同的是,trylock()不会阻塞,它会立即返回。 + +* 加锁成功返回true,此时可以修改共享变量。 +* 加锁失败返回false,表示有其他进程持有锁。 + + +> SWOOlE_SEM 信号量没有trylock方法 \ No newline at end of file diff --git a/doc/7.1.4 - swoole_lock->unlock.md b/doc/7.1.4 - swoole_lock->unlock.md new file mode 100644 index 0000000..de40d76 --- /dev/null +++ b/doc/7.1.4 - swoole_lock->unlock.md @@ -0,0 +1,8 @@ +#swoole_lock->unlock + +释放锁 +```php +$lock->unlock(); +``` + +* 解锁成功返回`true` diff --git a/doc/7.1.5 - swoole_lock->lock_read.md b/doc/7.1.5 - swoole_lock->lock_read.md new file mode 100644 index 0000000..723abb3 --- /dev/null +++ b/doc/7.1.5 - swoole_lock->lock_read.md @@ -0,0 +1,12 @@ +#swoole_lock->lock_read + +只读加锁。`lock_read`方法表示仅锁定读。 +```php +bool $lock->lock_read(); +``` + +* 在持有读锁的过程中,其他进程依然可以获得读锁,可以继续发生读操作 +* 但不能`$lock->lock()`或`$lock->trylock()`,这两个方法是获取独占锁,在独占锁加锁时,其他进程无法再进行任何加锁操作,包括读锁 +* 当另外一个进程获得了独占锁(调用`$lock->lock`/`$lock->trylock`)时,`$lock->lock_read()`会发生阻塞,直到持有独占锁的进程释放锁 + +> 只有`SWOOLE_RWLOCK`和`SWOOLE_FILELOCK`类型的锁支持只读加锁 diff --git a/doc/7.1.6 - swoole_lock->trylock_read.md b/doc/7.1.6 - swoole_lock->trylock_read.md new file mode 100644 index 0000000..b52ddac --- /dev/null +++ b/doc/7.1.6 - swoole_lock->trylock_read.md @@ -0,0 +1,7 @@ +#swoole_lock->trylock_read + +加锁。此方法与`lock_read`相同,但是非阻塞的。 +```php +$lock->trylock_read(); +``` +调用会立即返回,必须检测返回值以确定是否拿到了锁。 \ No newline at end of file diff --git a/doc/7.1.7 - swoole_lock->lockwait.md b/doc/7.1.7 - swoole_lock->lockwait.md new file mode 100644 index 0000000..c129c4e --- /dev/null +++ b/doc/7.1.7 - swoole_lock->lockwait.md @@ -0,0 +1,15 @@ +#swoole_lock->lockwait + +加锁操作,作用于`swoole_lock->lock`一致,但`lockwait`可以设置超时时间。 + +```php +function swoole_lock->lockwait(float $timeout = 1.0) : bool; +``` + +* `$timeout`传入超时时间,默认为`1秒` +* 在规定的时间内未获得锁,返回`false` +* 加锁成功返回`true` + +> 只有`Mutex`类型的锁支持`lockwait` +> 在`1.9.16`或更高版本可用 + diff --git a/doc/7.2 - Buffer.md b/doc/7.2 - Buffer.md new file mode 100644 index 0000000..e4d12f5 --- /dev/null +++ b/doc/7.2 - Buffer.md @@ -0,0 +1,12 @@ +#Buffer + +swoole1.7.5提供了一个swoole_buffer类,让PHP开发者可以像C一样直接读写内存,提升程序的性能,又不用担心内存越界。swoole_buffer会检测offset。 + +> swoole_buffer申请的内存并非共享内存,所以无法在多个进程间被共享 + +属性列表 +---- + +* swoole_buffer->$length,当前数据的长度 +* swoole_buffer->$capacity,当前缓存区的容量 + diff --git a/doc/7.2.1 - swoole_buffer->__construct.md b/doc/7.2.1 - swoole_buffer->__construct.md new file mode 100644 index 0000000..6b986d5 --- /dev/null +++ b/doc/7.2.1 - swoole_buffer->__construct.md @@ -0,0 +1,7 @@ +#swoole_buffer->__construct + +创建一个内存对象。函数原型: +```php +swoole_buffer->__construct(int $size = 128); +``` +参数$size指定了缓冲区内存的初始尺寸。当申请的内存容量不够时swoole底层会自动扩容。 \ No newline at end of file diff --git a/doc/7.2.2 - swoole_buffer->append.md b/doc/7.2.2 - swoole_buffer->append.md new file mode 100644 index 0000000..569b429 --- /dev/null +++ b/doc/7.2.2 - swoole_buffer->append.md @@ -0,0 +1,10 @@ +#swoole_buffer->append + +将一个字符串数据追加到缓存区末尾。 +```php +int swoole_buffer->append(string $data); +``` + +* $data是要写入的数据,支持二进制内容 +* 执行成功后,会返回新的长度 + diff --git a/doc/7.2.3 - swoole_buffer->substr.md b/doc/7.2.3 - swoole_buffer->substr.md new file mode 100644 index 0000000..06e4731 --- /dev/null +++ b/doc/7.2.3 - swoole_buffer->substr.md @@ -0,0 +1,13 @@ +#swoole_buffer->substr + +从缓冲区中取出内容。 +```php +string swoole_buffer->substr(int $offset, int $length = -1, bool $remove = false); +``` +* `$offset` 表示偏移量,如果为负数,表示倒数计算偏移量 +* `$length` 表示读取数据的长度,默认为从 $offset 到整个缓存区末尾 +* `$remove` 表示从缓冲区的头部将此数据移除。只有 $offset = 0 时此参数才有效 + +> `swoole_buffer->substr()` 会复制一次内存 +> `$remove` 后内存并没有释放,只是底层进行了指针偏移。当销毁此对象时才会真正释放内存 +> `1.9.4`及之后的版本里,`swoole_buffer->substr()` 能够在某些情况下触发 `swoole_buffer->recycle()` \ No newline at end of file diff --git a/doc/7.2.4 - swoole_buffer->clear.md b/doc/7.2.4 - swoole_buffer->clear.md new file mode 100644 index 0000000..6bc5edc --- /dev/null +++ b/doc/7.2.4 - swoole_buffer->clear.md @@ -0,0 +1,10 @@ +#swoole_buffer->clear + +清理缓存区数据。 +```php +swoole_buffer->clear() +``` +执行此操作后,缓存区将重置。swoole_buffer对象就可以用来处理新的请求了。 + +> swoole_buffer基于指针运算实现clear,并不会写内存 + diff --git a/doc/7.2.5 - swoole_buffer->expand.md b/doc/7.2.5 - swoole_buffer->expand.md new file mode 100644 index 0000000..74cbfdb --- /dev/null +++ b/doc/7.2.5 - swoole_buffer->expand.md @@ -0,0 +1,7 @@ +#swoole_buffer->expand + +为缓存区扩容。 +```php +swoole_buffer->expand(int $new_size); +``` +* $new_size 指定新的缓冲区尺寸,必须大于当前的尺寸 diff --git a/doc/7.2.6 - swoole_buffer->write.md b/doc/7.2.6 - swoole_buffer->write.md new file mode 100644 index 0000000..3bb074c --- /dev/null +++ b/doc/7.2.6 - swoole_buffer->write.md @@ -0,0 +1,13 @@ +#swoole_buffer->write + +向缓存区的任意内存位置写数据。read/write函数可以直接读写内存。所以使用务必要谨慎,否则可能会破坏现有数据。 + +```php +swoole_buffer->write(int $offset, string $data) +``` + +* $offset 偏移量 +* $data 写入的数据 + +> 在 1.9.3 及之前的版本中,swoole_buffer->write() 不会自动扩容,需要通过 swoole_buffer->expand() 预留足够的空间 +> swoole_buffer->write() 不能实现字符串追加,请使用 swoole_buffer->append() diff --git a/doc/7.2.7 - swoole_buffer->read.md b/doc/7.2.7 - swoole_buffer->read.md new file mode 100644 index 0000000..88ef8fd --- /dev/null +++ b/doc/7.2.7 - swoole_buffer->read.md @@ -0,0 +1,14 @@ +#swoole_buffer->read + +读取缓存区任意位置的内存。 + +```php +swoole_buffer->read(int $offset, int $length) +``` + +此接口是一个底层接口,可直接操作内存。 + +* $offset 偏移量 +* $length 要读取的数据长度 + +如果 $offset 错误或读取的长度超过实际数据的长度,返回 false diff --git a/doc/7.2.8 - swoole_buffer->recycle.md b/doc/7.2.8 - swoole_buffer->recycle.md new file mode 100644 index 0000000..b722d49 --- /dev/null +++ b/doc/7.2.8 - swoole_buffer->recycle.md @@ -0,0 +1,12 @@ +#swoole_buffer->recycle + +( swoole >= 1.9.4 ) + +回收缓冲中已经废弃的内存。 +```php +swoole_buffer->recycle(); +``` + +此方法能够在不清空缓冲区和使用 swoole_buffer->clear() 的情况下,回收通过 swoole_buffer->substr() 移除但仍存在的部分内存空间。 + +> swoole_buffer->recycle() 会进行一次内存拷贝 \ No newline at end of file diff --git a/doc/7.3 - Table.md b/doc/7.3 - Table.md new file mode 100644 index 0000000..1e46e20 --- /dev/null +++ b/doc/7.3 - Table.md @@ -0,0 +1,29 @@ +#Table + +swoole_table一个基于共享内存和锁实现的超高性能,并发数据结构。用于解决多进程/多线程数据共享和同步加锁问题。 + +> 最新版本已移除`lock`和`unlock`方法,请使用`Swoole\Lock`来实现数据同步 + +swoole_table的优势 +---- +* 性能强悍,单线程每秒可读写`200万`次 +* 应用代码无需加锁,`swoole_table`内置行锁自旋锁,所有操作均是多线程/多进程安全。用户层完全不需要考虑数据同步问题。 +* 支持多进程,`swoole_table`可以用于多进程之间共享数据 +* 使用行锁,而不是全局锁,仅当`2`个进程在同一`CPU`时间,并发读取同一条数据才会进行发生抢锁 + +> `swoole_table`不受`PHP`的`memory_limit`控制 +> `swoole_table`在`1.7.5`以上版本可用 + +遍历Table +------ +swoole_table类实现了迭代器和Countable接口,可以使用foreach进行遍历,使用count计算当前行数。 +> 遍历Table 依赖pcre 如果发现无法遍历table,检查机器是否安装pcre-devel + +```php +foreach($table as $row) +{ + var_dump($row); +} +echo count($table); +``` + diff --git a/doc/7.3.1 - swoole_table->__construct.md b/doc/7.3.1 - swoole_table->__construct.md new file mode 100644 index 0000000..31c8734 --- /dev/null +++ b/doc/7.3.1 - swoole_table->__construct.md @@ -0,0 +1,20 @@ +#swoole_table->__construct + +创建内存表。 +```php +swoole_table->__construct(int $size, float $conflict_proportion = 0.2) +``` + +* `$size`参数指定表格的最大行数,如果`$size`不是为`2`的N次方,如`1024`、`8192`,`65536`等,底层会自动调整为接近的一个数字,如果小于1024则默认成1024,即1024是最小值 +* `table`占用的内存总数为 (结构体长度 + KEY长度64字节 + 行尺寸`$size`) * (1.2预留20%作为hash冲突) * (列尺寸),如果机器内存不足table会创建失败 +* `set`操作能存储的最大行数与`$size`无关,如`$size`为1024实际可存储的行数小于`1024` + + +> swoole_table基于行锁,所以单次set/get/del在多线程/多进程的环境下是安全的 +> set/get/del是原子操作,用户代码中不需要担心数据加锁和同步的问题 + +容量计算 +----- +`Table`底层是建立在共享内存之上的`HashTable`数据结构。`$size`最大行数,决定了`HashTable`的总行数。由于`Table`是在共享内存之上,所以无法动态扩容。这个`$size`必须在创建前设置好。 + +`Table`能存储的总数据行数,取决于数据的`Key`冲突率。如果冲突率超过`20%`,预留的`hash`冲突内存块容量不足,就会报`Unable to allocate memory`错误,并返回`false`,存储失败。 diff --git "a/doc/7.3.10 - \345\270\270\351\207\217\345\210\227\350\241\250.md" "b/doc/7.3.10 - \345\270\270\351\207\217\345\210\227\350\241\250.md" new file mode 100644 index 0000000..66237d9 --- /dev/null +++ "b/doc/7.3.10 - \345\270\270\351\207\217\345\210\227\350\241\250.md" @@ -0,0 +1,6 @@ +#常量列表 + +* swoole_table::TYPE_INT 整形字段 +* swoole_table::TYPE_FLOAT 浮点字段 +* swoole_table::TYPE_STRING 字符串字段 + diff --git a/doc/7.3.2 - swoole_table->column.md b/doc/7.3.2 - swoole_table->column.md new file mode 100644 index 0000000..6282795 --- /dev/null +++ b/doc/7.3.2 - swoole_table->column.md @@ -0,0 +1,66 @@ +#swoole_table->column + +内存表增加一列 +```php +bool swoole_table->column(string $name, int $type, int $size = 0); +``` + +* $name指定字段的名称 +* $type指定字段类型,支持3种类型,swoole_table::TYPE_INT, swoole_table::TYPE_FLOAT, swoole_table::TYPE_STRING +* $size指定字符串字段的最大长度,单位为字节。字符串类型的字段必须指定$size + +> swoole_table::TYPE_INT默认为4个字节,可以设置1,2,4,8一共4种长度 +> swoole_table::TYPE_STRING设置后,设置的字符串不能超过此长度 +> swoole_table::TYPE_FLOAT会占用8个字节的内存 + + + +> 非x86环境需要内存对齐 + +```php +/* +Linux raspberrypi 4.4.34-v7+ #930 SMP Wed Nov 23 15:20:41 GMT 2016 armv7l GNU/Linux + +gcc version 4.9.2 (Raspbian 4.9.2-10) + +model name : ARMv7 Processor rev 4 (v7l) +BogoMIPS : 76.80 +Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 +CPU implementer : 0x41 +CPU architecture: 7 +CPU variant : 0x0 +CPU part : 0xd03 +CPU revision : 4 +*/ + +//bad +use Swoole\Table; +$table = new \Swoole\Table(8); +$table->column('data',Table::TYPE_STRING, 1); +$table->create(); +$table->set(1,[]); +var_dump($table->get(1)); +//result: Bus error + +//good +use Swoole\Table; +$table = new \Swoole\Table(8); + +$table->column('data',Table::TYPE_STRING, 1); +$table->column('pad',Table::TYPE_STRING, 1); +$table->create(); + +$table->set(1,[]); +var_dump($table->get(1)); +/* +result +array(2) { + ["data"]=> + string(0) "" + ["pad"]=> + string(0) "" +} +*/ +``` + + diff --git a/doc/7.3.3 - swoole_table->create.md b/doc/7.3.3 - swoole_table->create.md new file mode 100644 index 0000000..21b7a70 --- /dev/null +++ b/doc/7.3.3 - swoole_table->create.md @@ -0,0 +1,37 @@ +#swoole_table->create + +创建内存表。 +```php +function swoole_table->create() : bool; +``` + +* 定义好表的结构后,执行`create`向操作系统申请内存,创建表 +* 调用`create`之前不能使用`set`、`get`等数据读写操作方法 +* 调用`create`之后不能使用`column`方法添加新字段 +* 系统内存不足,申请失败,`create`返回`false` +* 申请内存成功,`create`返回`true` + +> `swoole_table`使用共享内存来保存数据,在创建子进程前,务必要执行`swoole_table->create()` +> `swoole_server`中使用`swoole_table`,`swoole_table->create()` 必须在`swoole_server->start()`前执行 + +```php +$table = new swoole_table(1024); +$table->column('id', swoole_table::TYPE_INT, 4); //1,2,4,8 +$table->column('name', swoole_table::TYPE_STRING, 64); +$table->column('num', swoole_table::TYPE_FLOAT); +$table->create(); + +$worker = new swoole_process('child1', false, false); +$worker->start(); + +//$serv = new swoole_server('127.0.0.1', 9501); +//$serv->start(); +``` + +内存尺寸 +---- +使用`create`方法创建表后,可以读取`$table->memorySize`属性获取实际占用内存的尺寸,单位为字节。 + +```php +echo $table->memorySize; +``` diff --git a/doc/7.3.4 - swoole_table->set.md b/doc/7.3.4 - swoole_table->set.md new file mode 100644 index 0000000..5aa51dc --- /dev/null +++ b/doc/7.3.4 - swoole_table->set.md @@ -0,0 +1,31 @@ +#swoole_table->set + +设置行的数据,swoole_table使用key-value的方式来访问数据。 +```php +swoole_table->set(string $key, array $value) +``` + +* $key,数据的key,相同的$key对应同一行数据,如果set同一个key,会覆盖上一次的数据 +* $value,必须是一个数组,必须与字段定义的$name完全相同 + +> swoole_table->set() 可以设置全部字段的值,也可以只修改部分字段 +> swoole_table->set() 未设置前,该行数据的所有字段均为空 +> set/get/del 是自带行锁,所以不需要调用lock加锁 + +```php +$table->set('1', ['id' => 1, 'name' => 'test1', 'age' => 20]); +$table->set('2', ['id' => 2, 'name' => 'test2', 'age' => 21]); +$table->set('3', ['id' => 3, 'name' => 'test3', 'age' => 19]); +``` + +设置超过最大长度字符串 +---- +如果传入字符串长度超过了列定义时设定的最大尺寸,底层会自动截断。 + +示例: +```php +$table->column('str_value', swoole_table::TYPE_STRING, 5); +$table->set('hello', array('str_value' => 'world 123456789')); +``` +* `str_value`列最大尺寸为5字节,但`set`设置了超过5字节的字符串 +* 底层会自动截取5字节的数据,最终`str_value`的值为`world` diff --git a/doc/7.3.5 - swoole_table->incr.md b/doc/7.3.5 - swoole_table->incr.md new file mode 100644 index 0000000..32f4369 --- /dev/null +++ b/doc/7.3.5 - swoole_table->incr.md @@ -0,0 +1,14 @@ +#swoole_table->incr + +原子自增操作。 + +```php +function swoole_table->incr(string $key, string $column, mixed $incrby = 1); +``` + +* $key 指定数据的key,如果$key对应的行不存在,默认列的值为0 +* $column 指定列名,仅支持浮点型和整型字段 +* $incrby 增量,默认为1。如果列为整形,$incrby必须为int型,如果列为浮点型,$incrby必须为float类型 +* 失败返回false,成功返回最终的结果数值 + +> swoole_table->incr在1.7.15以上版本可用 diff --git a/doc/7.3.6 - swoole_table->decr.md b/doc/7.3.6 - swoole_table->decr.md new file mode 100644 index 0000000..82793b3 --- /dev/null +++ b/doc/7.3.6 - swoole_table->decr.md @@ -0,0 +1,14 @@ +#swoole_table->decr + +原子自减操作。 + +```php +function swoole_table->decr(string $key, string $column, mixed $decrby = 1); +``` + +* $key 指定数据的key,如果$key对应的行不存在,默认列的值为0 +* $column 指定列名,仅支持浮点型和整型字段 +* $decrby 减量,默认为1。如果列为整形,$decrby必须为int型,如果列为浮点型,$decrby必须为float类型 +* 失败返回false,成功返回最终的结果数值 + +> swoole_table->decr在1.7.15以上版本可用 diff --git a/doc/7.3.7 - swoole_table->get.md b/doc/7.3.7 - swoole_table->get.md new file mode 100644 index 0000000..8b64f64 --- /dev/null +++ b/doc/7.3.7 - swoole_table->get.md @@ -0,0 +1,13 @@ +#swoole_table->get + +获取一行数据 +```php +array swoole_table->get(string $key, string $field = null); +``` + +* 如果$key不存在,将返回false +* 成功返回结果数组 +* 当指定了`$field`时仅返回该字段的值,而不是整个记录 + +> `$field` 参数在`1.9.9`或更高版本可用 + diff --git a/doc/7.3.8 - swoole_table->exist.md b/doc/7.3.8 - swoole_table->exist.md new file mode 100644 index 0000000..76cb828 --- /dev/null +++ b/doc/7.3.8 - swoole_table->exist.md @@ -0,0 +1,11 @@ +#swoole_table->exist + +检查table中是否存在某一个key。 +```php +bool swoole_table->exist(string $key); +``` + +* 存在返回true +* 不存在返回false + +> 此方法在1.7.17以上版本可用 \ No newline at end of file diff --git a/doc/7.3.9 - swoole_table->del.md b/doc/7.3.9 - swoole_table->del.md new file mode 100644 index 0000000..b6aea5b --- /dev/null +++ b/doc/7.3.9 - swoole_table->del.md @@ -0,0 +1,10 @@ +#swoole_table->del + +删除数据 +```php +bool swoole_table->del(string $key) +``` + +* $key对应的数据不存在,将返回false +* 成功删除返回true + diff --git a/doc/7.4 - Atomic.md b/doc/7.4 - Atomic.md new file mode 100644 index 0000000..5f1491c --- /dev/null +++ b/doc/7.4 - Atomic.md @@ -0,0 +1,30 @@ +#Atomic + +swoole_atomic是swoole扩展提供的原子计数操作类,可以方便整数的无锁原子增减。 + +* `swoole_atomic`使用共享内存,可以在不同的进程之间操作计数 +* `swoole_atomic`基于gcc提供的CPU原子指令,无需加锁 +* `swoole_atomic`在服务器程序中必须在`swoole_server->start`前创建才能在Worker进程中使用 +* `swoole_atomic`默认使用`32`位无符号类型,如需要`64`有符号整型,可使用`Swoole\Atomic\Long` + +**注意:请勿在`onReceive`等回调函数中创建原子数,否则底层的`GlobalMemory`内存会持续增长,造成内存泄漏。** + +> `swoole_atomic`在`1.7.19`以上版本可用 +> `Swoole\Atomic\Long`在`1.9.20`以上版本可用 + +64位长整型 +----- +`1.9.20`版本增加了对64位有符号长整型原子计数的支持。使用`new Swoole\Atomic\Long` 来创建。 + +* `Swoole\Atomic\Long` 不支持`wait`和`wakeup`方法 + +使用示例 +---- +```php +$atomic = new swoole_atomic(123); +echo $atomic->add(12)."\n"; +echo $atomic->sub(11)."\n"; +echo $atomic->cmpset(122, 999)."\n"; +echo $atomic->cmpset(124, 999)."\n"; +echo $atomic->get()."\n"; +``` \ No newline at end of file diff --git a/doc/7.4.1 - swoole_atomic->__construct.md b/doc/7.4.1 - swoole_atomic->__construct.md new file mode 100644 index 0000000..25bebb8 --- /dev/null +++ b/doc/7.4.1 - swoole_atomic->__construct.md @@ -0,0 +1,15 @@ +#swoole_atomic->__construct + +创建一个原子计数对象。 + +```php +function swoole_atomic->__construct(int $init_value = 0) +``` + +* `swoole_atomic`只能操作`32`位无符号整数,最大支持`42`亿,不支持负数 +* `$init_value`可以指定初始化的数值,默认为`0` + +> 在`swoole_server`中使用原子计数器,必须在`swoole_server->start`前创建 +> 在`swoole_process`中使用原子计数器,必须在`swoole_process->start`前创建 + + diff --git a/doc/7.4.2 - swoole_atomic->add.md b/doc/7.4.2 - swoole_atomic->add.md new file mode 100644 index 0000000..8364630 --- /dev/null +++ b/doc/7.4.2 - swoole_atomic->add.md @@ -0,0 +1,16 @@ +#swoole_atomic->add + +增加计数 + +```php +function swoole_atomic->add(int $add_value = 1) +``` + +* $add_value要增加的数值,默认为1 +* $add_value必须为正整数 +* $add_value与原值相加如果超过42亿,将会溢出,高位数值会被丢弃 +* add方法操作成功后返回结果数值 + + + + diff --git a/doc/7.4.3 - swoole_atomic->sub.md b/doc/7.4.3 - swoole_atomic->sub.md new file mode 100644 index 0000000..93ba3cf --- /dev/null +++ b/doc/7.4.3 - swoole_atomic->sub.md @@ -0,0 +1,13 @@ +#swoole_atomic->sub + +减少计数 + +```php +function swoole_atomic->sub(int $sub_value = 1) +``` + +* $sub_value要减少的数值,默认为1 +* $sub_value必须为正整数 +* $sub_value与原值相减如果低于0将会溢出,高位数值会被丢弃 +* sub方法操作成功后返回结果数值 + diff --git a/doc/7.4.4 - swoole_atomic->get.md b/doc/7.4.4 - swoole_atomic->get.md new file mode 100644 index 0000000..e25089c --- /dev/null +++ b/doc/7.4.4 - swoole_atomic->get.md @@ -0,0 +1,9 @@ +#swoole_atomic->get + +获取当前计数的值 + +```php +function swoole_atomic->get() +``` + +* 返回当前的数值 diff --git a/doc/7.4.5 - swoole_atomic->set.md b/doc/7.4.5 - swoole_atomic->set.md new file mode 100644 index 0000000..89f0820 --- /dev/null +++ b/doc/7.4.5 - swoole_atomic->set.md @@ -0,0 +1,9 @@ +#swoole_atomic->set + +将当前值设置为指定的数字。 +```php +function swoole_atomic->set(int $value); +``` + +* $value,指定要设置的目标数值 +* set方法没有任何返回值 diff --git a/doc/7.4.6 - swoole_atomic->cmpset.md b/doc/7.4.6 - swoole_atomic->cmpset.md new file mode 100644 index 0000000..9ff1f6b --- /dev/null +++ b/doc/7.4.6 - swoole_atomic->cmpset.md @@ -0,0 +1,10 @@ +#swoole_atomic->cmpset + +如果当前数值等于参数1,则将当前数值设置为参数2 +```php +function swoole_atomic->cmpset(int $cmp_value, int $set_value); +``` + +* 如果当前数值等于$cmp_value返回true,并将当前数值设置为$set_value +* 如果不等于返回false +* $cmp_value,$set_value 必须为小于42亿的整数 diff --git a/doc/7.4.7 - swoole_atomic->wait.md b/doc/7.4.7 - swoole_atomic->wait.md new file mode 100644 index 0000000..28e86f0 --- /dev/null +++ b/doc/7.4.7 - swoole_atomic->wait.md @@ -0,0 +1,29 @@ +#swoole_atomic->wait + +当原子计数的值为`0`时程序进入等待状态。另外一个进程调用`wakeup`可以再次唤醒程序。底层基于`Linux Futex`实现,使用此特性,可以仅用`4`字节内存实现一个等待、通知、锁的功能。 +```php +function swoole_atomic->wait(float $timeout = -1) : bool +``` +> 在不支持`Futex`的平台下,底层会使用循环`usleep(1000)`模拟实现 + +* `$timeout` 指定超时时间,默认为`-1`,表示永不超时,会持续等待直到有其他进程唤醒 +* 超时返回`false`,错误码为`EAGAIN`,可使用`swoole_errno`函数获取 +* 成功返回`true`,表示有其他进程通过`wakeup`成功唤醒了当前的锁 +* 使用`wait/wakeup`特性时,原子计数的值只能为`0`或`1`,否则会导致无法正常使用 +* 当然原子计数的值为`1`时,表示不需要进入等待状态,资源当前就是可用。`wait`函数会立即返回`true` + +使用方法 +---- +```php +$n = new swoole_atomic; +if (pcntl_fork() > 0) { + echo "master start\n"; + $n->wait(1.5); + echo "master end\n"; +} else { + echo "child start\n"; + sleep(1); + $n->wakeup(); + echo "child end\n"; +} +``` \ No newline at end of file diff --git a/doc/7.4.8 - swoole_atomic->wakeup.md b/doc/7.4.8 - swoole_atomic->wakeup.md new file mode 100644 index 0000000..48df2b7 --- /dev/null +++ b/doc/7.4.8 - swoole_atomic->wakeup.md @@ -0,0 +1,12 @@ +#swoole_atomic->wakeup + +唤醒处于`wait`状态的其他进程。 + +```php +function swoole_atomic->wakeup(int $n = 1); +``` + +* 当前原子计数如果为`0`时,表示没有进程正在`wait`,`wakeup`会立即返回`true` +* 当前原子计数如果为`1`时,表示当前有进程正在`wait`,`wakeup`会唤醒等待的进程,并返回`true` +* 如果同时有多个进程处于`wait`状态,`$n`参数可以控制唤醒的进程数量 +* 被唤醒的进程返回后,会将原子计数设置为`0`,这时可以再次调用`wakeup`唤醒其他正在`wait`的进程 diff --git a/doc/7.5 - mmap.md b/doc/7.5 - mmap.md new file mode 100644 index 0000000..995399c --- /dev/null +++ b/doc/7.5 - mmap.md @@ -0,0 +1,6 @@ +#mmap + +swoole-1.9.0增加了一个新的模块,提供了对操作系统`mmap`的封装。使用`mmap`可以很方便地将一个磁盘文件映射为内存,读写性能更高。 + +`mmap`可以减少读写磁盘操作的IO消耗、减少内存拷贝。在实现高性能的磁盘操作程序中,可以使用`mmap`来提升性能。 + diff --git a/doc/7.5.1 - swoole_mmap::open.md b/doc/7.5.1 - swoole_mmap::open.md new file mode 100644 index 0000000..238d2f2 --- /dev/null +++ b/doc/7.5.1 - swoole_mmap::open.md @@ -0,0 +1,36 @@ +#swoole_mmap::open + +创建文件内存映射,函数原型: +```php +function swoole_mmap::open($file, $size = -1, $offset = 0); +``` + +* $file 磁盘文件名称,必须是存在的文件,如果文件不存在将会创建失败。可以使用`file_put_contents`来初始化文件。 +* $size 映射操作,默认为整个文件的长度,操作系统会分配同等大小的内存。注意不要尝试将超过系统内存尺寸的文件进行映射 +* $offset 文件的映射起始位置,默认为0 +* 执行成功将返回一个PHP的`stream`资源,可使用PHP提供的流式操作函数读写数据 + +操作内存 +---- +* 读取数据,`fread`、`fgets` +* 写入数据,`fwrite`、`fputs` +* 关闭内存映射,`fclose`,底层会自动执行`fflush`将数据同步到磁盘文件 +* 同步数据,`fflush`将内存中的数据写入到磁盘 + +使用示例 +--- +```php +$file = __DIR__.'/data'; +$size = 8192; +if (!is_file($file)) { + file_put_contents($file, str_repeat("\0", $size)); +} + +$fp = swoole\mmap::open($file, 8192); + +fwrite($fp, "hello world\n"); +fwrite($fp, "hello swoole\n"); + +fflush($fp); +fclose($fp); +``` diff --git a/doc/7.6 - Channel.md b/doc/7.6 - Channel.md new file mode 100644 index 0000000..37ebf09 --- /dev/null +++ b/doc/7.6 - Channel.md @@ -0,0 +1,54 @@ +#Channel + + `Swoole-1.9.0`新增了一个新的内存数据结构`Channel`,类似于`Go`的`chan`通道,底层基于共享内存+`Mutex`互斥锁实现,可实现用户态的高性能内存队列。 + +* `Channel`可用于多进程环境下,底层在读取写入时会自动加锁,应用层不需要担心数据同步问题 +* 必须在父进程内创建才可以在子进程内使用 + +> `Channel`不受`PHP`的`memory_limit`控制 + +使用示例: +---- +```php +$chan = new Swoole\Channel(1024 * 256); +$n = 100000; +$bytes = 0; +if (pcntl_fork() > 0) +{ + echo "Father\n"; + for ($i = 0; $i < $n; $i++) + { + $data = str_repeat('A', rand(100, 200)); + if ($chan->push($data) === false) + { + echo "channel full\n"; + usleep(1000); + $i--; + continue; + } + $bytes += strlen($data); +// echo "#$i\tpush ".strlen($data)." bytes\n"; + } + echo "total push bytes: $bytes\n"; + var_dump($chan->stats()); +} +else +{ + echo "Child\n"; + for ($i = 0; $i < $n; $i++) + { + $data = $chan->pop(); + if ($data === false) + { + echo "channel empty\n"; + usleep(1000); + $i--; + continue; + } + $bytes += strlen($data); +// echo "#$i\tpop " . strlen($data) . " bytes\n"; + } + echo "total pop bytes: $bytes\n"; + var_dump($chan->stats()); +} +``` \ No newline at end of file diff --git a/doc/7.6.1 - Channel->__construct.md b/doc/7.6.1 - Channel->__construct.md new file mode 100644 index 0000000..10ef5bc --- /dev/null +++ b/doc/7.6.1 - Channel->__construct.md @@ -0,0 +1,9 @@ +#Channel->__construct + +创建通道。函数原型 +```php +function Channel->__construct(int $size); +``` + +* $size 通道占用的内存的尺寸,单位为字节。最小值为64K,最大值没有限制 +* 系统内存不足底层会抛出内存不足异常 \ No newline at end of file diff --git a/doc/7.6.2 - Channel->push.md b/doc/7.6.2 - Channel->push.md new file mode 100644 index 0000000..584a3d0 --- /dev/null +++ b/doc/7.6.2 - Channel->push.md @@ -0,0 +1,12 @@ +#Channel->push + +向通道写入数据,函数原型: +```php +bool function Channel->push(mixed $data); +``` + +* $data可以为任意PHP变量,当$data是非字符串类型时底层会自动进行串化 +* $data的尺寸超过8K时会启用临时文件存储数据 +* $data必须为非空变量,如空字符串、空数组、0、null、false +* 写入成功返回true +* 通道的空间不足时写入失败并返回false diff --git a/doc/7.6.3 - Channel->pop.md b/doc/7.6.3 - Channel->pop.md new file mode 100644 index 0000000..ea75c7c --- /dev/null +++ b/doc/7.6.3 - Channel->pop.md @@ -0,0 +1,10 @@ +#Channel->pop + +弹出数据,函数原型: +```php +function Channel->pop() : mixed; +``` + +* pop方法无需传入任何参数 +* 当通道内有数据时自动将数据弹出并还原为PHP变量 +* 当通道内没有任何数据时pop会失败并返回false diff --git a/doc/7.6.4 - Channel->stats.md b/doc/7.6.4 - Channel->stats.md new file mode 100644 index 0000000..e3c8974 --- /dev/null +++ b/doc/7.6.4 - Channel->stats.md @@ -0,0 +1,16 @@ +#Channel->stats + +获取通道的状态。函数原型: +```php +function Channel->stats() : array; +``` +- 返回一个数组,包括2项信息 +- queue_num 通道中的元素数量 +- queue_bytes 通道当前占用的内存字节数 + +```php +array( + "queue_num" => 10, + "queue_bytes" => 161, +); +``` \ No newline at end of file diff --git a/doc/7.7 - Serialize.md b/doc/7.7 - Serialize.md new file mode 100644 index 0000000..e984c5c --- /dev/null +++ b/doc/7.7 - Serialize.md @@ -0,0 +1,25 @@ +#Serialize + +1.9.6版本增加了一个新的模块`swoole_serialize`,是一个高性能的序列化库,与PHP官方提供的`serialize`和`json_encode`相比,`swoole_serialize`的不同之处是: + +* 序列化后的结果为二进制格式,只适合机器读取,不适合人读 +* 序列化性能更高,可节省大量CPU资源,基准测试中序列化和反序列化耗时为PHP官方`serialize`的40% +* 序列化后的结果数据尺寸更小,可节省内存资源,基准测试中序列化结果尺寸为PHP官方`serialize`的50% +* 基准测试脚本 [benchmark](https://github.com/swoole/swoole-src/blob/master/benchmark/seria_bench.php) + +> serialize模块仅在PHP7或更高版本中可用 + +相关配置 +--- +可修改`php.ini`配置,在swoole_server中的task功能中使用`swoole_serialize`对异步任务数据序列化。 + +```php +swoole.fast_serialize=On +``` + +独立扩展 +---- +`swoole_serialize`模块可单独编译安装,不需要依赖`swoole`扩展。 + +* GitHub主页: +* PECL主页: diff --git a/doc/7.7.1 - swoole_serialize::pack.md b/doc/7.7.1 - swoole_serialize::pack.md new file mode 100644 index 0000000..59ecf23 --- /dev/null +++ b/doc/7.7.1 - swoole_serialize::pack.md @@ -0,0 +1,10 @@ +#swoole_serialize::pack + +将PHP变量序列化,函数原型: +```php +function swoole_serialize::pack(mixed $data, int $flags = 0); +``` + +* `$data` 为要进行序列化的变量,请注意`function`和`resource`类型的变量是不支持序列化的 +* `$flags` 是否启用快速模式,`swoole_serialize`默认会使用静态表保存关联数组的Key,设置此参数为`SWOOLE_FAST_PACK`后将不再保存数组key +* 序列化成功后返回二进制字符串,失败返回`false` diff --git a/doc/7.7.2 - swoole_serialize::unpack.md b/doc/7.7.2 - swoole_serialize::unpack.md new file mode 100644 index 0000000..9d76705 --- /dev/null +++ b/doc/7.7.2 - swoole_serialize::unpack.md @@ -0,0 +1,9 @@ +#swoole_serialize::unpack + +反序列化,函数原型: +```php +function swoole_serialize::unpack(string $data); +``` + +* `$data` 序列化数据,必须是由`swoole_serialize::pack`函数生成 +* 操作成功返回PHP变量,失败返回false diff --git a/doc/8 - HttpServer.md b/doc/8 - HttpServer.md new file mode 100644 index 0000000..2b01cac --- /dev/null +++ b/doc/8 - HttpServer.md @@ -0,0 +1,58 @@ +#HttpServer + +swoole-1.7.7增加了内置Http服务器的支持,通过几行代码即可写出一个异步非阻塞多进程的Http服务器。 + +```php +$http = new swoole_http_server("127.0.0.1", 9501); +$http->on('request', function ($request, $response) { + $response->end("

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

"); +}); +$http->start(); +``` + +> swoole_http_server对Http协议的支持并不完整,建议仅作为应用服务器。并且在前端增加Nginx作为代理 + +通过使用apache bench工具进行压力测试,在Inter Core-I5 4核 + 8G内存的普通PC机器上,swoole_http_server可以达到近11万QPS。远远超过php-fpm,golang自带http服务器,node.js自带http服务器。性能几乎接近与Nginx的静态文件处理。 + +```shell +ab -c 200 -n 200000 -k http://127.0.0.1:9501 +``` + +使用Http2协议 +---- +* 需要依赖`nghttp2`库,[下载nghttp2](https://github.com/tatsuhiro-t/nghttp2)后编译安装 +* 使用`Http2`协议必须开启`openssl` +* 需要高版本`openssl`必须支持`TLS1.2`、`ALPN`、`NPN` + +```shell +./configure --enable-openssl --enable-http2 +``` + +设置http服务器的`open_http2_protocol`为`true` +```php +$serv = new swoole_http_server("127.0.0.1", 9501, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$serv->set([ + 'ssl_cert_file' => $ssl_dir . '/ssl.crt', + 'ssl_key_file' => $ssl_dir . '/ssl.key', + 'open_http2_protocol' => true, +]); +``` + +nginx+swoole配置 +----- +``` +server { + root /data/wwwroot/; + server_name local.swoole.com; + + location / { + proxy_http_version 1.1; + proxy_set_header Connection "keep-alive"; + proxy_set_header X-Real-IP $remote_addr; + if (!-e $request_filename) { + proxy_pass http://127.0.0.1:9501; + } + } +} +``` +> 在swoole中通过读取`$request->header['x-real-ip']`来获取客户端的真实IP \ No newline at end of file diff --git a/doc/8.1 - swoole_http_server.md b/doc/8.1 - swoole_http_server.md new file mode 100644 index 0000000..f22f07c --- /dev/null +++ b/doc/8.1 - swoole_http_server.md @@ -0,0 +1,27 @@ +#swoole_http_server + +swoole_http_server继承自swoole_server,是一个完整的http服务器实现。swoole_http_server支持同步和异步2种模式。 + +> http/websocket服务器都是继承自swoole_server,所以swoole_server提供的API,如task/finish/tick等都可以使用 + +无论是同步模式还是异步模式,swoole_http_server都可以维持大量TCP客户端连接。同步/异步仅仅体现在对请求的处理方式上。 + +示例: +```php +$http = new swoole_http_server("127.0.0.1", 9501); +$http->on('request', function ($request, $response) { + $response->end("

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

"); +}); +$http->start(); +``` + +同步模式 +----- +这种模式等同于nginx+php-fpm/apache,它需要设置大量worker进程来完成并发请求处理。Worker进程内可以使用同步阻塞IO,编程方式与普通PHP Web程序完全一致。 + +与php-fpm/apache不同的是,客户端连接并不会独占进程,服务器依然可以应对大量并发连接。 + +异步模式 +---- +这种模式下整个服务器是异步非阻塞的,服务器可以应对大规模的并发连接和并发请求。但编程方式需要完全使用异步API,如MySQL、redis、http_client、file_get_contents、sleep等阻塞IO操作必须切换为异步的方式,如异步swoole_client,swoole_event_add,swoole_timer,swoole_get_mysqli_sock等API。 + diff --git a/doc/8.1.1 - swoole_http_server->on.md b/doc/8.1.1 - swoole_http_server->on.md new file mode 100644 index 0000000..b349e41 --- /dev/null +++ b/doc/8.1.1 - swoole_http_server->on.md @@ -0,0 +1,24 @@ +#swoole_http_server->on + +注册事件回调函数,与swoole_server->on相同。swoole_http_server->on的不同之处是: + +* swoole_http_server->on不接受onConnect/onReceive回调设置 +* swoole_http_server->on 额外接受1种新的事件类型`onRequest` + +onRequest事件 +----- +```php +$http_server->on('request', function(swoole_http_request $request, swoole_http_response $response) { + $response->end("

hello swoole

"); +}) +``` +在收到一个完整的Http请求后,会回调此函数。回调函数共有2个参数: + +* $request,Http请求信息对象,包含了header/get/post/cookie等相关信息 +* $response,Http响应对象,支持cookie/header/status等Http操作 +* 在`onRequest`回调函数返回时底层会销毁`$request`和`$response`对象,如果未执行`$response->end()`操作,底层会自动执行一次`$response->end("")` + +> onRequest在1.7.7后可用 +> $response/$request 对象传递给其他函数时,不要加&引用符号 +> $response/$request 对象传递给其他函数后,引用计数会增加,`onRequest`退出时不会销毁 + diff --git a/doc/8.1.2 - swoole_http_server->start.md b/doc/8.1.2 - swoole_http_server->start.md new file mode 100644 index 0000000..3584f58 --- /dev/null +++ b/doc/8.1.2 - swoole_http_server->start.md @@ -0,0 +1,9 @@ +#swoole_http_server->start + +启动Http服务器。 +```php +swoole_http_server->start() +``` +启动后开始监听端口,并接收新的Http和WebSocket请求。使用on方法注册的事件回调,如onWorkerStart/onTimer/onShutdown等事件回调函数依然有效。 + + diff --git a/doc/8.2 - swoole_http_request.md b/doc/8.2 - swoole_http_request.md new file mode 100644 index 0000000..e0d9231 --- /dev/null +++ b/doc/8.2 - swoole_http_request.md @@ -0,0 +1,6 @@ +#swoole_http_request + + `Http`请求对象,保存了Http客户端请求的相关信息,包括`GET`、`POST`、`COOKIE`、`Header`等。 + +* `Request`对象销毁时会自动删除上传的临时文件 +* 请勿使用`&`符号引用`$request`对象 \ No newline at end of file diff --git a/doc/8.2.1 - swoole_http_request->$header.md b/doc/8.2.1 - swoole_http_request->$header.md new file mode 100644 index 0000000..9493653 --- /dev/null +++ b/doc/8.2.1 - swoole_http_request->$header.md @@ -0,0 +1,9 @@ +#swoole_http_request->$header + +Http请求的头部信息。类型为数组,所有key均为小写。 + +```php +echo $request->header['host']; +echo $request->header['accept-language']; +``` + diff --git a/doc/8.2.2 - swoole_http_request->$server.md b/doc/8.2.2 - swoole_http_request->$server.md new file mode 100644 index 0000000..07baa30 --- /dev/null +++ b/doc/8.2.2 - swoole_http_request->$server.md @@ -0,0 +1,15 @@ +#swoole_http_request->$server + +Http请求相关的服务器信息,相当于PHP的`$_SERVER`数组。包含了`Http`请求的方法,`URL`路径,客户端`IP`等信息。 + +```php +echo $request->server['request_time']; +``` + +* 数组的`key`全部为小写,并且与`PHP`的`$_SERVER`数组保持一致 + +request_time +---- +`request_time`是在`Worker`设置的,在`SWOOLE_PROCESS`模式下存在`dispatch`过程,因此可能会与实际收包时间存在偏差。尤其是当请求量超过服务器处理能力时,`request_time`可能远滞后于实际收包时间。 + +可以通过`$server->getClientInfo`方法获取`last_time`获得准确的收包时间。 diff --git a/doc/8.2.3 - swoole_http_request->$get.md b/doc/8.2.3 - swoole_http_request->$get.md new file mode 100644 index 0000000..88fa764 --- /dev/null +++ b/doc/8.2.3 - swoole_http_request->$get.md @@ -0,0 +1,12 @@ +#swoole_http_request->$get + +Http请求的GET参数,相当于PHP中的$_GET,格式为数组。 +```php +// 如:index.php?hello=123 +echo $request->get['hello']; + +// 获取所有GET参数 +var_dump($request->get); +``` + +> 为防止HASH攻击,GET参数最大不允许超过128个 \ No newline at end of file diff --git a/doc/8.2.4 - swoole_http_request->$post.md b/doc/8.2.4 - swoole_http_request->$post.md new file mode 100644 index 0000000..bed426e --- /dev/null +++ b/doc/8.2.4 - swoole_http_request->$post.md @@ -0,0 +1,9 @@ +#swoole_http_request->$post + +HTTP POST参数,格式为数组。 +```php +echo $request->post['hello']; +``` + +> POST与Header加起来的尺寸不得超过package_max_length的设置,否则会认为是恶意请求 +> POST参数的个数最大不超过128个 \ No newline at end of file diff --git a/doc/8.2.5 - swoole_http_request->$cookie.md b/doc/8.2.5 - swoole_http_request->$cookie.md new file mode 100644 index 0000000..1f8a63d --- /dev/null +++ b/doc/8.2.5 - swoole_http_request->$cookie.md @@ -0,0 +1,8 @@ +#swoole_http_request->$cookie + +HTTP请求携带的COOKIE信息,与PHP的$_COOKIE相同,格式为数组。 + +```php +echo $request->cookie['username']; +``` + diff --git a/doc/8.2.6 - swoole_http_request->$files.md b/doc/8.2.6 - swoole_http_request->$files.md new file mode 100644 index 0000000..ad9fcf2 --- /dev/null +++ b/doc/8.2.6 - swoole_http_request->$files.md @@ -0,0 +1,23 @@ +#swoole_http_request->$files + +文件上传信息。类型为以`form`名称为`key`的二维数组。与`PHP`的`$_FILES`相同。最大文件尺寸不得超过`package_max_length`设置的值。请勿使用`Swoole\Http\Server`处理大文件上传。 + +```php +Array +( + [name] => facepalm.jpg + [type] => image/jpeg + [tmp_name] => /tmp/swoole.upfile.n3FmFr + [error] => 0 + [size] => 15476 +) +``` + +* `name` 浏览器上传时传入的文件名称 +* `type` MIME类型 +* `tmp_name` 上传的临时文件,文件名以`/tmp/swoole.upfile`开头 +* `size` 文件尺寸 +* `1.9.10`以上版本支持`is_uploaded_file`和`move_uploaded_file`函数 + +> 当`$request`对象销毁时,会自动删除上传的临时文件 + diff --git a/doc/8.2.7 - swoole_http_request->rawContent.md b/doc/8.2.7 - swoole_http_request->rawContent.md new file mode 100644 index 0000000..26d195b --- /dev/null +++ b/doc/8.2.7 - swoole_http_request->rawContent.md @@ -0,0 +1,12 @@ +#swoole_http_request->rawContent + +获取原始的POST包体,用于非`application/x-www-form-urlencoded`格式的`Http POST`请求。 + +```php +string swoole_http_request->rawContent(); +``` + +* 返回原始`POST`数据,此函数等同于PHP的`fopen('php://input')` + +有些情况下服务器不需要解析`Http POST`请求参数,`1.7.18`以上版本增加了`http_parse_post` 配置,可以关闭`POST`数据解析。 + diff --git a/doc/8.2.8 - swoole_http_request->getData.md b/doc/8.2.8 - swoole_http_request->getData.md new file mode 100644 index 0000000..075b56a --- /dev/null +++ b/doc/8.2.8 - swoole_http_request->getData.md @@ -0,0 +1,9 @@ +#swoole_http_request->getData + +获取完整的原始`Http`请求报文。包括`Http Header`和`Http Body` + +```php +function swoole_http_request->getData() : string +``` + +> 需要`1.10.3`/`2.1.2`或更高版本 \ No newline at end of file diff --git a/doc/8.3 - swoole_http_response.md b/doc/8.3 - swoole_http_response.md new file mode 100644 index 0000000..87a2cad --- /dev/null +++ b/doc/8.3 - swoole_http_response.md @@ -0,0 +1,6 @@ +#swoole_http_response + + `Http`响应对象,通过调用此对象的方法,实现`Http`响应发送。 + +* 当`Response`对象销毁时,如果未调用`end`发送`Http`响应,底层会自动执行`end` +* 请勿使用`&`符号引用`$response`对象 \ No newline at end of file diff --git a/doc/8.3.1 - swoole_http_response->header.md b/doc/8.3.1 - swoole_http_response->header.md new file mode 100644 index 0000000..8ea7cf6 --- /dev/null +++ b/doc/8.3.1 - swoole_http_response->header.md @@ -0,0 +1,36 @@ +#swoole_http_response->header + +设置`HTTP`响应的`Header`信息。 + +```php +function swoole_http_response->header(string $key, string $value, bool $ucwords = true); +``` + +参数 +---- +* `$key`,`Http`头的`Key` +* `$value`,`Http`头的`Value` +* `$ucwords` 是否需要对Key进行Http约定格式化,默认`true`会自动格式化 + +返回值 +---- +* 设置失败,返回`false` +* 设置成功,没有任何返回值 + +注意事项 +---- +* `header`设置必须在`end`方法之前 +* `$key`必须完全符合`Http`的约定,每个单词首字母大写,不得包含中文,下划线或者其他特殊字符 +* `$value`必须填写 +* `$ucwords` 设为`true`,swoole底层会自动对`$key`进行约定格式化 + +> `Swoole`底层不允许设置相同`$key`的`Http`头 + +示例 +--- +```php +$response->header('Content-Type', 'image/jpeg',false); +$response->header('content-type', 'image/jpeg',true); +``` + + diff --git a/doc/8.3.10 - swoole_http_response::create.md b/doc/8.3.10 - swoole_http_response::create.md new file mode 100644 index 0000000..6ab7e77 --- /dev/null +++ b/doc/8.3.10 - swoole_http_response::create.md @@ -0,0 +1,24 @@ +#swoole_http_response::create + +构造新的`Http\Response`对象。使用此方法前请务必调用`detach`方法将旧的`$response`对象分离,否则可能会造成对同一个请求发送两次响应内容。 + +```php +function swoole_http_response::create(int $fd) : swoole_http_response; +``` + +* 参数为需要绑定的连接`$fd`,调用`Http\Response`对象的`end`与`write`方法时会向此连接发送数据 +* 调用成功返回一个新的`Http\Response`对象,调用失败返回`false` + +> 需要`2.2.0`或更高版本 + +```php +$http = new swoole_http_server("0.0.0.0", 9501); + +$http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + $resp->detach(); + $resp2 = Swoole\Http\Response::create($data); + $resp2->end("hello world"); +}); + +$http->start(); +``` \ No newline at end of file diff --git a/doc/8.3.2 - swoole_http_response->cookie.md b/doc/8.3.2 - swoole_http_response->cookie.md new file mode 100644 index 0000000..c7c4dd5 --- /dev/null +++ b/doc/8.3.2 - swoole_http_response->cookie.md @@ -0,0 +1,14 @@ +#swoole_http_response->cookie + +设置`HTTP`响应的`cookie`信息。此方法参数与`PHP`的`setcookie`完全一致。 + +```php +swoole_http_response->cookie(string $key, string $value = '', int $expire = 0 , string $path = '/', string $domain = '', bool $secure = false , bool $httponly = false); +``` + +> cookie设置必须在end方法之前 + +注意事项 +--- +* 底层自动会对`$value`进行`urlencode`编码,可使用`rawCookie`关闭对`$value`的编码处理 +* 底层允许设置多个相同`$key`的`COOKIE` \ No newline at end of file diff --git a/doc/8.3.3 - swoole_http_response->status.md b/doc/8.3.3 - swoole_http_response->status.md new file mode 100644 index 0000000..102df73 --- /dev/null +++ b/doc/8.3.3 - swoole_http_response->status.md @@ -0,0 +1,10 @@ +#swoole_http_response->status + +发送Http状态码。 +```php +swoole_http_response->status(int $http_status_code); +``` + +* $http_status_code必须为合法的HttpCode,如200, 502, 301, 404等,否则会报错 +* 必须在$response->end之前执行status + diff --git a/doc/8.3.4 - swoole_http_response->gzip.md b/doc/8.3.4 - swoole_http_response->gzip.md new file mode 100644 index 0000000..d2647c2 --- /dev/null +++ b/doc/8.3.4 - swoole_http_response->gzip.md @@ -0,0 +1,22 @@ +#swoole_http_response->gzip + +启用`Http GZIP`压缩。压缩可以减小`HTML`内容的尺寸,有效节省网络带宽,提高响应时间。必须在`write/end`发送内容之前执行`gzip`,否则会抛出错误。 + +```php +swoole_http_response->gzip(int $level = 1); +``` + +* `$level` 压缩等级,范围是`1-9`,等级越高压缩后的尺寸越小,但`CPU`消耗更多。默认为1 +* 调用`gzip`方法后,底层会自动添加`Http`编码头,PHP代码中不应当再行设置相关Http头 + +> `gzip`压缩在`1.7.14`以上版本可用 +> `jpg/png/gif`格式的图片已经经过压缩,无需再次压缩 + +依赖 +---- +`gzip`功能依赖`zlib`库,在编译`swoole`时底层会检测系统是否存在`zlib`,如果不存在,`gzip`方法将不可用。 + +可以使用`yum`或`apt-get`安装`zlib`库: +```shell +sudo apt-get install libz-dev +``` diff --git a/doc/8.3.5 - swoole_http_response->redirect.md b/doc/8.3.5 - swoole_http_response->redirect.md new file mode 100644 index 0000000..266cc27 --- /dev/null +++ b/doc/8.3.5 - swoole_http_response->redirect.md @@ -0,0 +1,26 @@ +#swoole_http_response->redirect + +发送`Http`跳转。调用此方法会自动`end`发送并结束响应。 + +```php +function swoole_http_response->redirect(string $url, int $http_code = 302); +``` + +> 需要`2.2.0`或更高版本 + +参数 +---- +* `$url`:跳转的新地址,作为`Location`头进行发送 +* `$http_code`:状态码,默认为`302`临时跳转,传入`301`表示永久跳转 + +实例 +---- +```php +$http = new swoole_http_server("0.0.0.0", 9501, SWOOLE_BASE); + +$http->on('request', function ($req, Swoole\Http\Response $resp) { + $resp->redirect("http://www.baidu.com/", 301); +}); + +$http->start(); +``` \ No newline at end of file diff --git a/doc/8.3.6 - swoole_http_response->write.md b/doc/8.3.6 - swoole_http_response->write.md new file mode 100644 index 0000000..5ce6105 --- /dev/null +++ b/doc/8.3.6 - swoole_http_response->write.md @@ -0,0 +1,12 @@ +#swoole_http_response->write + +启用Http Chunk分段向浏览器发送相应内容。关于Http Chunk可以参考Http协议标准文档。 + +```php +bool swoole_http_response->write(string $data); +``` + +* $data要发送的数据内容,最大长度不得超过2M +* 使用write分段发送数据后,end方法将不接受任何参数 +* 调用end方法后会发送一个长度为0的Chunk表示数据传输完毕 + diff --git a/doc/8.3.7 - swoole_http_response->sendfile.md b/doc/8.3.7 - swoole_http_response->sendfile.md new file mode 100644 index 0000000..d0d71c1 --- /dev/null +++ b/doc/8.3.7 - swoole_http_response->sendfile.md @@ -0,0 +1,23 @@ +#swoole_http_response->sendfile + +发送文件到浏览器。 +```php +function swoole_http_response->sendfile(string $filename, int $offset = 0, int $length = 0); +``` + +* `$filename` 要发送的文件名称,文件不存在或没有访问权限`sendfile`会失败 +* 底层无法推断要发送文件的MIME格式因此需要应用代码指定`Content-Type` +* 调用`sendfile`前不得使用`write`方法发送`Http-Chunk` +* 调用`sendfile`后底层会自动执行`end` +* `sendfile`不支持`gzip`压缩 +* `$offset` 上传文件的偏移量,可以指定从文件的中间部分开始传输数据。此特性可用于支持断点续传。 +* `$length` 发送数据的尺寸,默认为整个文件的尺寸 + +> `$length`、`$offset`参数在`1.9.11`或更高版本可用 + +使用示例 +----- +```php +$response->header('Content-Type', 'image/jpeg'); +$response->sendfile(__DIR__.$request->server['request_uri']); +``` diff --git a/doc/8.3.8 - swoole_http_response->end.md b/doc/8.3.8 - swoole_http_response->end.md new file mode 100644 index 0000000..80fafa1 --- /dev/null +++ b/doc/8.3.8 - swoole_http_response->end.md @@ -0,0 +1,12 @@ +#swoole_http_response->end + +发送Http响应体,并结束请求处理。 + +```php +swoole_http_response->end(string $html); +``` + +* `end`操作后将向客户端浏览器发送`HTML`内容 +* `end`只能调用一次,如果需要分多次向客户端发送数据,请使用`write`方法 +* 客户端开启了`KeepAlive`,连接将会保持,服务器会等待下一次请求 +* 客户端未开启`KeepAlive`,服务器将会切断连接 diff --git a/doc/8.3.9 - swoole_http_response->detach.md b/doc/8.3.9 - swoole_http_response->detach.md new file mode 100644 index 0000000..80bb227 --- /dev/null +++ b/doc/8.3.9 - swoole_http_response->detach.md @@ -0,0 +1,58 @@ +#swoole_http_response->detach + +分离响应对象。使用此方法后,`$response`对象销毁时不会自动`end`,与`swoole_http_response::create`和`Server::send`配合使用。 + +```php +function swoole_http_response->detach(); +``` + +* 客户端已完成响应,操作失败返回`false`,成功返回`true` + +> 需要`2.2.0`或更高版本 + + +跨进程响应 +---- +某些情况下,需要在`Task`进程中对客户端发出响应。这时可以利用`detach`使`$response`对象独立。在`Task`进程可以重新构建`$response`,发起`Http`请求响应。 + +```php +$http = new swoole_http_server("0.0.0.0", 9501); + +$http->set(['task_worker_num' => 1, 'worker_num' => 1]); + +$http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + $resp->detach(); + $http->task(strval($resp->fd)); +}); + +$http->on('finish', function () +{ + echo "task finish"; +}); + +$http->on('task', function ($serv, $task_id, $worker_id, $data) +{ + var_dump($data); + $resp = Swoole\Http\Response::create($data); + $resp->end("in task"); + echo "async task\n"; +}); + +$http->start(); +``` + +发送任意内容 +---- +某些特殊的场景下,需要对客户端发送特殊的响应内容。`Http\Response`对象自带的`end`方法无法满足需求,可以使用`detach`分离响应对象,然后自行组包并使用`Server::send`发送数据。 + + +```php +$http = new swoole_http_server("0.0.0.0", 9501); + +$http->on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + $resp->detach(); + $http->send($resp->fd, "HTTP/1.1 200 OK\r\nServer: server\r\n\r\nHello World\n"); +}); + +$http->start(); +``` \ No newline at end of file diff --git "a/doc/8.4 - \351\205\215\347\275\256\351\200\211\351\241\271.md" "b/doc/8.4 - \351\205\215\347\275\256\351\200\211\351\241\271.md" new file mode 100644 index 0000000..610f062 --- /dev/null +++ "b/doc/8.4 - \351\205\215\347\275\256\351\200\211\351\241\271.md" @@ -0,0 +1,13 @@ +#配置选项 + + `Http\Server`除了可以设置`Server`相关选项外,还可以设置一些特有的选项。 + + +文件上传 +----- +`Http\Server`支持文件上传,但由于`Swoole`底层的限制,文件内容是存放在内存中的,因此如果并发上传大量文件可能会导致内存占用过大。 + + +POST 尺寸 +---- +默认上传`2M`尺寸的文件或`POST` `2M`数据,可修改`package_max_length`调整最大`POST`尺寸限制。 \ No newline at end of file diff --git a/doc/8.4.1 - upload_tmp_dir.md b/doc/8.4.1 - upload_tmp_dir.md new file mode 100644 index 0000000..fed3091 --- /dev/null +++ b/doc/8.4.1 - upload_tmp_dir.md @@ -0,0 +1,11 @@ +#upload_tmp_dir + +设置上传文件的临时目录。 + +```php +$serv->set(array( + 'upload_tmp_dir' => '/data/uploadfiles/', +)); +``` + +* 目录最大长度不得超过220字节 \ No newline at end of file diff --git a/doc/8.4.2 - http_parse_post.md b/doc/8.4.2 - http_parse_post.md new file mode 100644 index 0000000..aa7fc07 --- /dev/null +++ b/doc/8.4.2 - http_parse_post.md @@ -0,0 +1,9 @@ +#http_parse_post + +设置POST消息解析开关,选项为`true`时自动将`Content-Type`为`x-www-form-urlencoded`的请求包体解析到POST数组。设置为`false`时将关闭POST解析。 + +```php +$serv->set(array( + 'http_parse_post' => false, +)); +``` \ No newline at end of file diff --git a/doc/8.4.3 - document_root.md b/doc/8.4.3 - document_root.md new file mode 100644 index 0000000..6681c66 --- /dev/null +++ b/doc/8.4.3 - document_root.md @@ -0,0 +1,14 @@ +#document_root + +配置静态文件根目录,与`enable_static_handler`配合使用。 +```php +$server->set([ + 'document_root' => '/data/webroot/example.com', + 'enable_static_handler' => true, +]); +``` + +设置`document_root`并设置`enable_static_handler`为`true`后,底层收到`Http`请求会先判断`document_root`路径下是否存在此文件,如果存在会直接发送文件内容给客户端,不再触发`onRequest`回调。 + +> 在`1.9.17`或更高版本可用 +> 使用静态文件处理特性时,应当将动态`PHP`代码和静态文件进行隔离,静态文件存放到特定的目录 diff --git "a/doc/8.5 - \345\270\270\350\247\201\351\227\256\351\242\230.md" "b/doc/8.5 - \345\270\270\350\247\201\351\227\256\351\242\230.md" new file mode 100644 index 0000000..47db965 --- /dev/null +++ "b/doc/8.5 - \345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -0,0 +1,2 @@ +#常见问题 + diff --git "a/doc/8.5.1 - CURL\345\217\221\351\200\201POST\350\257\267\346\261\202\346\234\215\345\212\241\345\231\250\347\253\257\350\266\205\346\227\266.md" "b/doc/8.5.1 - CURL\345\217\221\351\200\201POST\350\257\267\346\261\202\346\234\215\345\212\241\345\231\250\347\253\257\350\266\205\346\227\266.md" new file mode 100644 index 0000000..b9be754 --- /dev/null +++ "b/doc/8.5.1 - CURL\345\217\221\351\200\201POST\350\257\267\346\261\202\346\234\215\345\212\241\345\231\250\347\253\257\350\266\205\346\227\266.md" @@ -0,0 +1,27 @@ +#CURL发送POST请求服务器端超时 + +CURL在发送较大的POST请求时会先发一个100-continue的请求,如果收到服务器的回应才会发送实际的POST数据。而swoole_http_server不支持100-continue,就会导致CURL请求超时。 + +解决办法是关闭CURL的100-continue + +```php +// 创建一个新cURL资源 +$ch = curl_init(); +// 设置URL和相应的选项 +curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:9501"); +curl_setopt($ch, CURLOPT_HEADER, 0); +curl_setopt($ch, CURLOPT_POST, 1); //设置为POST方式 +curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); +curl_setopt($ch, CURLOPT_POSTFIELDS, array('test' => str_repeat('a', 800000)));//POST数据 +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +``` + +其他客户端 +----- +如果客户端是其他语言编写的,无法修改客户端去掉100-continue,那么还有2个方案可以解决此问题。 + +* 使用Nginx做前端代理,由Nginx处理100-Continue +* 重新编译Swoole启用100-Continue的支持,需要手工修改swoole_config.h,找到SW_HTTP_100_CONTINUE,去掉注释,执行make clean && make install + +> 启用100-CONTINUE后会额外消耗服务器的CPU资源 + diff --git "a/doc/8.5.2 - \344\275\277\347\224\250Chrome\350\256\277\351\227\256\346\234\215\345\212\241\345\231\250\344\274\232\344\272\247\347\224\2372\346\254\241\350\257\267\346\261\202.md" "b/doc/8.5.2 - \344\275\277\347\224\250Chrome\350\256\277\351\227\256\346\234\215\345\212\241\345\231\250\344\274\232\344\272\247\347\224\2372\346\254\241\350\257\267\346\261\202.md" new file mode 100644 index 0000000..b1c41c2 --- /dev/null +++ "b/doc/8.5.2 - \344\275\277\347\224\250Chrome\350\256\277\351\227\256\346\234\215\345\212\241\345\231\250\344\274\232\344\272\247\347\224\2372\346\254\241\350\257\267\346\261\202.md" @@ -0,0 +1,3 @@ +#使用Chrome访问服务器会产生2次请求 + +这是因为Chrome浏览器会自动请求一次favicon.ico,所以服务器会收到2个Request,通过打印$req->server['request_uri'] 能看到请求的URL路径。 diff --git a/doc/9 - WebSocket.md b/doc/9 - WebSocket.md new file mode 100644 index 0000000..6e8fa19 --- /dev/null +++ b/doc/9 - WebSocket.md @@ -0,0 +1,90 @@ +#WebSocket + + `1.7.9`增加了内置的`WebSocket`服务器支持,通过几行PHP代码就可以写出一个异步非阻塞多进程的`WebSocket`服务器。 + + +```php +$server = new swoole_websocket_server("0.0.0.0", 9501); + +$server->on('open', function (swoole_websocket_server $server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; +}); + +$server->on('message', function (swoole_websocket_server $server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); +}); + +$server->on('close', function ($ser, $fd) { + echo "client {$fd} closed\n"; +}); + +$server->start(); +``` + +onRequest回调 +---- +swoole_websocket_server 继承自 swoole_http_server + +* 设置了`onRequest`回调,websocket服务器也可以同时作为http服务器 +* 未设置`onRequest`回调,websocket服务器收到http请求后会返回`http 400`错误页面 +* 如果想通过接收http触发所有websocket的推送,需要注意作用域的问题,面向过程请使用`global`对`swoole_websocket_server`进行引用,面向对象可以把`swoole_websocket_server`设置成一个成员属性 + +1、面向过程代码 +```php +$server = new swoole_websocket_server("0.0.0.0", 9501); +$server->on('open', function (swoole_websocket_server $server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; + }); +$server->on('message', function (swoole_websocket_server $server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); + }); +$server->on('close', function ($ser, $fd) { + echo "client {$fd} closed\n"; + }); +$server->on('request', function (swoole_http_request $request, swoole_http_response $response) { + global $server;//调用外部的server + // $server->connections 遍历所有websocket连接用户的fd,给所有用户推送 + foreach ($server->connections as $fd) { + $server->push($fd, $request->get['message']); + } + }); +$server->start(); +``` +2、面向对象代码 +```php +class WebsocketTest { + public $server; + public function __construct() { + $this->server = new swoole_websocket_server("0.0.0.0", 9501); + $this->server->on('open', function (swoole_websocket_server $server, $request) { + echo "server: handshake success with fd{$request->fd}\n"; + }); + $this->server->on('message', function (swoole_websocket_server $server, $frame) { + echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; + $server->push($frame->fd, "this is server"); + }); + $this->server->on('close', function ($ser, $fd) { + echo "client {$fd} closed\n"; + }); + $this->server->on('request', function ($request, $response) { + // 接收http请求从get获取message参数的值,给用户推送 + // $this->server->connections 遍历所有websocket连接用户的fd,给所有用户推送 + foreach ($this->server->connections as $fd) { + $this->server->push($fd, $request->get['message']); + } + }); + $this->server->start(); + } +} +new WebsocketTest(); +``` +客户端 +---- +* Chrome/Firefox/高版本IE/Safari等浏览器内置了JS语言的WebSocket客户端 +* 微信小程序开发框架内置的WebSocket客户端 +* 异步的PHP程序中可以使用`Swoole\Http\Client`作为WebSocket客户端 +* apache/php-fpm或其他同步阻塞的PHP程序中可以使用swoole/framework提供的[同步WebSocket客户端](https://github.com/swoole/framework/blob/master/libs/Swoole/Client/WebSocket.php) +* 非WebSocket客户端不能与WebSocket服务器通信 + diff --git "a/doc/9.1 - \345\233\236\350\260\203\345\207\275\346\225\260.md" "b/doc/9.1 - \345\233\236\350\260\203\345\207\275\346\225\260.md" new file mode 100644 index 0000000..f3095f5 --- /dev/null +++ "b/doc/9.1 - \345\233\236\350\260\203\345\207\275\346\225\260.md" @@ -0,0 +1,6 @@ +#回调函数 + +WebSocket除了接收`Swoole\Server`和`Swoole\Http\Server`基类的回调函数外,额外增加了3个回调函数设置。其中: + +* `onMessage`回调函数为**必选** +* `onOpen`和`onHandShake`回调函数为可选 \ No newline at end of file diff --git a/doc/9.1.1 - onHandShake.md b/doc/9.1.1 - onHandShake.md new file mode 100644 index 0000000..f94cc1f --- /dev/null +++ b/doc/9.1.1 - onHandShake.md @@ -0,0 +1,63 @@ +#onHandShake + +WebSocket建立连接后进行握手。WebSocket服务器已经内置了`handshake`,如果用户希望自己进行握手处理,可以设置`onHandShake`事件回调函数。 + +```php +function onHandShake(swoole_http_request $request, swoole_http_response $response); +``` + +* `onHandShake`事件回调是可选的 +* 设置onHandShake回调函数后不会再触发`onOpen`事件,需要应用代码自行处理 +* `onHandShake`函数必须返回`true`表示握手成功,返回其他值表示握手失败 +* 内置的握手协议为`Sec-WebSocket-Version: 13`,低版本浏览器需要自行实现握手 + +> 1.8.1或更高版本可以使用`server->defer`调用`onOpen`逻辑 + +注意: +仅仅你需要自行处理handshake的时候再设置这个回调函数,如果您不需要“自定义”握手过程,那么不要设置该回调,用swoole默认的握手即可。下面是“自定义”handshake事件回调函数中必须要具备的: + +```php +$server->on('handshake', function (\swoole_http_request $request, \swoole_http_response $response) { + // print_r( $request->header ); + // if (如果不满足我某些自定义的需求条件,那么返回end输出,返回false,握手失败) { + // $response->end(); + // return false; + // } + + // websocket握手连接算法验证 + $secWebSocketKey = $request->header['sec-websocket-key']; + $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; + if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { + $response->end(); + return false; + } + echo $request->header['sec-websocket-key']; + $key = base64_encode(sha1( + $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', + true + )); + + $headers = [ + 'Upgrade' => 'websocket', + 'Connection' => 'Upgrade', + 'Sec-WebSocket-Accept' => $key, + 'Sec-WebSocket-Version' => '13', + ]; + + // WebSocket connection to 'ws://127.0.0.1:9502/' + // failed: Error during WebSocket handshake: + // Response must not include 'Sec-WebSocket-Protocol' header if not present in request: websocket + if (isset($request->header['sec-websocket-protocol'])) { + $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol']; + } + + foreach ($headers as $key => $val) { + $response->header($key, $val); + } + + $response->status(101); + $response->end(); + echo "connected!" . PHP_EOL; + return true; +}); +``` \ No newline at end of file diff --git a/doc/9.1.2 - onOpen.md b/doc/9.1.2 - onOpen.md new file mode 100644 index 0000000..025d21d --- /dev/null +++ b/doc/9.1.2 - onOpen.md @@ -0,0 +1,11 @@ +#onOpen + +当WebSocket客户端与服务器建立连接并完成握手后会回调此函数。 + +```php +function onOpen(swoole_websocket_server $svr, swoole_http_request $req); +``` + +* $req 是一个Http请求对象,包含了客户端发来的握手请求信息 +* onOpen事件函数中可以调用push向客户端发送数据或者调用close关闭连接 +* onOpen事件回调是可选的 diff --git a/doc/9.1.3 - onMessage.md b/doc/9.1.3 - onMessage.md new file mode 100644 index 0000000..ae7b5fc --- /dev/null +++ b/doc/9.1.3 - onMessage.md @@ -0,0 +1,29 @@ +#onMessage + +当服务器收到来自客户端的数据帧时会回调此函数。 + +```php +function onMessage(swoole_server $server, swoole_websocket_frame $frame) +``` + +* $frame 是swoole_websocket_frame对象,包含了客户端发来的数据帧信息 +* onMessage回调必须被设置,未设置服务器将无法启动 +* 客户端发送的`ping`帧不会触发`onMessage`,底层会自动回复`pong`包 + +swoole_websocket_frame +---- +共有4个属性,分别是 + +* $frame->fd,客户端的socket id,使用$server->push推送数据时需要用到 +* $frame->data,数据内容,可以是文本内容也可以是二进制数据,可以通过opcode的值来判断 +* $frame->opcode,WebSocket的OpCode类型,可以参考WebSocket协议标准文档 +* $frame->finish, 表示数据帧是否完整,一个WebSocket请求可能会分成多个数据帧进行发送(底层已经实现了自动合并数据帧,现在不用担心接收到的数据帧不完整) + +> $data 如果是文本类型,编码格式必然是UTF-8,这是WebSocket协议规定的 + +OpCode与数据类型 +---- +* WEBSOCKET_OPCODE_TEXT = 0x1 ,文本数据 +* WEBSOCKET_OPCODE_BINARY = 0x2 ,二进制数据 + + diff --git "a/doc/9.2 - \345\207\275\346\225\260\345\210\227\350\241\250.md" "b/doc/9.2 - \345\207\275\346\225\260\345\210\227\350\241\250.md" new file mode 100644 index 0000000..d62b852 --- /dev/null +++ "b/doc/9.2 - \345\207\275\346\225\260\345\210\227\350\241\250.md" @@ -0,0 +1,3 @@ +#函数列表 + + `Swoole\WebSocket\Server`是`Swoole\Server`的子类,因此可以调用`Swoole\Server`的全部方法。需要注意`WebSocket`服务器向客户端发送数据应当使用`Swoole\WebSocket\Server::push`方法,此方法会进行`WebSocket`协议打包。而`Swoole\Server::send`方法是原始的`TCP`发送接口。 \ No newline at end of file diff --git a/doc/9.2.1 - swoole_websocket_server->push.md b/doc/9.2.1 - swoole_websocket_server->push.md new file mode 100644 index 0000000..581b41d --- /dev/null +++ b/doc/9.2.1 - swoole_websocket_server->push.md @@ -0,0 +1,14 @@ +#swoole_websocket_server->push + +向websocket客户端连接推送数据,长度最大不得超过2M。 + +```php +function swoole_websocket_server->push(int $fd, string $data, int $opcode = 1, bool $finish = true); +``` + +* $fd 客户端连接的ID,如果指定的$fd对应的TCP连接并非websocket客户端,将会发送失败 +* $data 要发送的数据内容 +* $opcode,指定发送数据内容的格式,默认为文本。发送二进制内容$opcode参数需要设置为`WEBSOCKET_OPCODE_BINARY` +* 发送成功返回true,发送失败返回false + +> swoole_websocket_server->push在swoole-1.7.11以上版本可用 diff --git a/doc/9.2.2 - swoole_websocket_server->exist.md b/doc/9.2.2 - swoole_websocket_server->exist.md new file mode 100644 index 0000000..ca1ac18 --- /dev/null +++ b/doc/9.2.2 - swoole_websocket_server->exist.md @@ -0,0 +1,10 @@ +#swoole_websocket_server->exist + +判断`WebSocket`客户端是否存在,并且状态为`Active`状态。 + +```php +function swoole_websocket_server->exist(int $fd); +``` + +* 连接存在,并且已完成`WebSocket`握手,返回`true` +* 连接不存在或尚未完成握手,返回`false` diff --git a/doc/9.2.3 - swoole_websocket_server::pack.md b/doc/9.2.3 - swoole_websocket_server::pack.md new file mode 100644 index 0000000..6b091cd --- /dev/null +++ b/doc/9.2.3 - swoole_websocket_server::pack.md @@ -0,0 +1,13 @@ +#swoole_websocket_server::pack + +打包`WebSocket`消息。函数原型: +```php +function swoole_websocket_server::pack(string $data, int $opcode = 1, bool $finish = true, bool $mask = false) : string; +``` + +* `$data`:消息内容 +* `$opcode`:`WebSocket`的`opcode`指令类型,`1`表示文本,`2`表示二进制数据,`9`表示心跳`ping` +* `$finish`:帧是否完成 +* `$mask`:是否设置掩码 +* 返回打包好的`WebSocket`数据包,可通过`Socket`发送给对端 + diff --git a/doc/9.2.4 - swoole_websocket_server::unpack.md b/doc/9.2.4 - swoole_websocket_server::unpack.md new file mode 100644 index 0000000..87049bc --- /dev/null +++ b/doc/9.2.4 - swoole_websocket_server::unpack.md @@ -0,0 +1,8 @@ +#swoole_websocket_server::unpack + +解析`WebSocket`数据帧。函数原型: +```php +swoole_websocket_server::unpack(string $data); +``` + +* 解析失败返回`false`,解析成功返回`Swoole\WebSocket\Frame`对象 \ No newline at end of file diff --git "a/doc/9.3 - \351\242\204\345\256\232\344\271\211\345\270\270\351\207\217.md" "b/doc/9.3 - \351\242\204\345\256\232\344\271\211\345\270\270\351\207\217.md" new file mode 100644 index 0000000..802fa9a --- /dev/null +++ "b/doc/9.3 - \351\242\204\345\256\232\344\271\211\345\270\270\351\207\217.md" @@ -0,0 +1,17 @@ +#预定义常量 + +WebSocket数据帧类型 +----- +* WEBSOCKET_OPCODE_TEXT = 0x1,UTF-8文本字符数据 +* WEBSOCKET_OPCODE_BINARY = 0x2,二进制数据 + +从1.9版本起: + +* WEBSOCKET_OPCODE_PING = 0x9,ping类型数据 + +WebSocket连接状态 +----- +* WEBSOCKET_STATUS_CONNECTION = 1,连接进入等待握手 +* WEBSOCKET_STATUS_HANDSHAKE = 2,正在握手 +* WEBSOCKET_STATUS_FRAME = 3,已握手成功等待浏览器发送数据帧 + diff --git "a/doc/9.4 - \345\270\270\350\247\201\351\227\256\351\242\230.md" "b/doc/9.4 - \345\270\270\350\247\201\351\227\256\351\242\230.md" new file mode 100644 index 0000000..ae0e213 --- /dev/null +++ "b/doc/9.4 - \345\270\270\350\247\201\351\227\256\351\242\230.md" @@ -0,0 +1,9 @@ +#常见问题 + +如何判断连接是否为WebSocket客户端 +---- +通过使用$server->connection_info($fd)获取连接信息,返回的数组中有一项为 websocket_status,根据此状态可以判断是否为WebSocket客户端。 + +* WEBSOCKET_STATUS_CONNECTION = 1,连接进入等待握手 +* WEBSOCKET_STATUS_HANDSHAKE = 2,正在握手 +* WEBSOCKET_STATUS_FRAME = 3,已握手成功等待浏览器发送数据帧 diff --git "a/doc/9.5 - \351\205\215\347\275\256\351\200\211\351\241\271.md" "b/doc/9.5 - \351\205\215\347\275\256\351\200\211\351\241\271.md" new file mode 100644 index 0000000..8d47f52 --- /dev/null +++ "b/doc/9.5 - \351\205\215\347\275\256\351\200\211\351\241\271.md" @@ -0,0 +1,13 @@ +#配置选项 + + `WebSocket\Server`是`Server`的子类,可以使用`Server::set`方法传入配置选项,设置某些参数。 + + websocket_subprotocol +---- +设置`WebSocket`子协议。设置后握手响应的`Http`头会增加`Sec-WebSocket-Protocol: {$websocket_subprotocol}`。具体使用方法请参考`WebSocket`协议相关`RFC`文档。 + +```php +$server->set([ + 'websocket_subprotocol' => 'chat', +]); +``` \ No newline at end of file