{"id":13497798,"url":"https://github.com/hhxsv5/laravel-s","last_synced_at":"2025-09-26T15:26:54.880Z","repository":{"id":28330824,"uuid":"117649937","full_name":"hhxsv5/laravel-s","owner":"hhxsv5","description":"LaravelS is an out-of-the-box adapter between Laravel/Lumen and Swoole.","archived":false,"fork":false,"pushed_at":"2024-12-13T04:51:01.000Z","size":5636,"stargazers_count":3873,"open_issues_count":74,"forks_count":476,"subscribers_count":190,"default_branch":"PHP-8.x","last_synced_at":"2025-05-14T09:03:52.656Z","etag":null,"topics":["async","http","laravel","lumen","performance","process","server","swoole","task","tcp","timer","udp","websocket"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hhxsv5.png","metadata":{"files":{"readme":"README-CN.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["https://www.paypal.me/hhxsv5","https://www.blockchain.com/btc/address/367HnAzVTAEk8offesDhcsCQswnugsE54u","https://gitee.com/hhxsv5/laravel-s?donate=true"]}},"created_at":"2018-01-16T07:37:04.000Z","updated_at":"2025-05-13T05:17:13.000Z","dependencies_parsed_at":"2024-04-07T02:44:15.861Z","dependency_job_id":"8f0cc480-7413-4cca-9014-fa27b3d8ecc8","html_url":"https://github.com/hhxsv5/laravel-s","commit_stats":{"total_commits":1706,"total_committers":43,"mean_commits":"39.674418604651166","dds":"0.10961313012895657","last_synced_commit":"a02e4ab25d5589db5e002e9e238726c8affba008"},"previous_names":[],"tags_count":224,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hhxsv5%2Flaravel-s","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hhxsv5%2Flaravel-s/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hhxsv5%2Flaravel-s/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hhxsv5%2Flaravel-s/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hhxsv5","download_url":"https://codeload.github.com/hhxsv5/laravel-s/tar.gz/refs/heads/PHP-8.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254110374,"owners_count":22016391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","http","laravel","lumen","performance","process","server","swoole","task","tcp","timer","udp","websocket"],"created_at":"2024-07-31T20:00:39.973Z","updated_at":"2025-09-26T15:26:54.790Z","avatar_url":"https://github.com/hhxsv5.png","language":"PHP","funding_links":["https://www.paypal.me/hhxsv5","https://www.blockchain.com/btc/address/367HnAzVTAEk8offesDhcsCQswnugsE54u","https://gitee.com/hhxsv5/laravel-s?donate=true"],"categories":["Table of Contents","Framework Integration","目录","PHP","框架( Frameworks )","Integration"],"sub_categories":["Framework Extras","框架 frameworks","Framework"],"readme":"\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"https://github.com/hhxsv5/laravel-s/raw/PHP-8.x/logo.svg\" alt=\"LaravelS Logo\" height=\"80\"\u003e\n     \u003cp\u003e\n        \u003ca href=\"https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md\"\u003e中文文档\u003c/a\u003e |\n        \u003ca href=\"https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README.md\"\u003eEnglish Docs\u003c/a\u003e\n    \u003c/p\u003e\n    \u003cp\u003e🚀 LaravelS 是 Laravel/Lumen 和 Swoole 之间开箱即用的适配器\u003c/p\u003e\n    \u003cp\u003e\n        \u003ca href=\"https://github.com/hhxsv5/laravel-s/releases\"\u003e\n            \u003cimg src=\"https://img.shields.io/github/release/hhxsv5/laravel-s.svg\" alt=\"Latest Version\"\u003e\n        \u003c/a\u003e\n        \u003ca href=\"https://www.php.net/\"\u003e\n            \u003cimg src=\"https://img.shields.io/packagist/php-v/hhxsv5/laravel-s\" alt=\"PHP Version\"\u003e\n        \u003c/a\u003e\n        \u003ca href=\"https://github.com/swoole/swoole-src\"\u003e\n            \u003cimg src=\"https://img.shields.io/badge/swoole-%3E=5.0.0-flat.svg\" alt=\"Swoole Version\"\u003e\n        \u003c/a\u003e\n        \u003ca href=\"https://packagist.org/packages/hhxsv5/laravel-s/stats\"\u003e\n            \u003cimg src=\"https://img.shields.io/packagist/dt/hhxsv5/laravel-s\" alt=\"Total Downloads\"\u003e\n        \u003c/a\u003e\n        \u003ca href=\"https://scrutinizer-ci.com/g/hhxsv5/laravel-s/\"\u003e\n            \u003cimg src=\"https://scrutinizer-ci.com/g/hhxsv5/laravel-s/badges/build.png?b=PHP-8.x\" alt=\"Build Status\"\u003e\n        \u003c/a\u003e\n        \u003ca href=\"https://scrutinizer-ci.com/g/hhxsv5/laravel-s/\"\u003e\n            \u003cimg src=\"https://scrutinizer-ci.com/g/hhxsv5/laravel-s/badges/code-intelligence.svg?b=PHP-8.x\" alt=\"Code Intelligence Status\"\u003e\n        \u003c/a\u003e\n        \u003ca href=\"https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/LICENSE\"\u003e\n            \u003cimg src=\"https://img.shields.io/github/license/hhxsv5/laravel-s\" alt=\"License\"\u003e\n        \u003c/a\u003e\n    \u003c/p\u003e\n\u003c/div\u003e\n\n---\n\n## 持续更新\n- *请`Watch`此仓库，以获得最新的更新。*\n- **QQ交流群**：\n  - `698480528` [![点击加群](https://pub.idqqimg.com/wpa/images/group.png \"点击加群\")](//shang.qq.com/wpa/qunwpa?idkey=f949191c8f413a3ecc5fbce661e57d379740ba92172bd50b02d23a5ab36cc7d6)\n  - `62075835` [![点击加群](https://pub.idqqimg.com/wpa/images/group.png \"点击加群\")](//shang.qq.com/wpa/qunwpa?idkey=5230f8da0693a812811e21e19d5823ee802ee5d24def177663f42a32a9060e97)\n\n文档目录\n=================\n\n* [特性](#特性)\n* [Benchmark](#benchmark)\n* [要求](#要求)\n* [安装](#安装)\n* [运行](#运行)\n* [部署](#部署)\n* [与Nginx配合使用（推荐）](#与nginx配合使用推荐)\n* [与Apache配合使用](#与apache配合使用)\n* [启用WebSocket服务器](#启用websocket服务器)\n* [监听事件](#监听事件)\n    * [系统事件](#系统事件)\n    * [自定义的异步事件](#自定义的异步事件)\n* [异步的任务队列](#异步的任务队列)\n* [毫秒级定时任务](#毫秒级定时任务)\n* [修改代码后自动Reload](#修改代码后自动reload)\n* [在你的项目中使用SwooleServer实例](#在你的项目中使用swooleserver实例)\n* [使用SwooleTable](#使用swooletable)\n* [多端口混合协议](#多端口混合协议)\n* [协程](#协程)\n* [自定义进程](#自定义进程)\n* [常用组件](#常用组件)\n    * [Apollo](#apollo)\n    * [Prometheus](#prometheus)\n* [其他特性](#其他特性)\n    * [配置Swoole事件](#配置Swoole事件)\n    * [Serverless](#serverless)\n* [注意事项](#注意事项)\n* [用户与案例](#用户与案例)\n* [其他选择](#其他选择)\n* [赞助](#赞助)\n    * [感谢](#感谢)\n* [License](#license)\n\n## 特性\n\n- 内置Http/[WebSocket](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md#%E5%90%AF%E7%94%A8websocket%E6%9C%8D%E5%8A%A1%E5%99%A8)服务器\n\n- [多端口混合协议](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md#%E5%A4%9A%E7%AB%AF%E5%8F%A3%E6%B7%B7%E5%90%88%E5%8D%8F%E8%AE%AE)\n\n- [自定义进程](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E8%BF%9B%E7%A8%8B)\n\n- 常驻内存\n\n- [异步的事件监听](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84%E5%BC%82%E6%AD%A5%E4%BA%8B%E4%BB%B6)\n\n- [异步的任务队列](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md#%E5%BC%82%E6%AD%A5%E7%9A%84%E4%BB%BB%E5%8A%A1%E9%98%9F%E5%88%97)\n\n- [毫秒级定时任务](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md#%E6%AF%AB%E7%A7%92%E7%BA%A7%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1)\n\n- [常用组件](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md#%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6)\n\n- 平滑Reload\n\n- [修改代码后自动Reload](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md#%E4%BF%AE%E6%94%B9%E4%BB%A3%E7%A0%81%E5%90%8E%E8%87%AA%E5%8A%A8reload)\n\n- 同时支持Laravel与Lumen，兼容主流版本\n\n- 简单，开箱即用\n\n## Benchmark\n\n- [Which is the fastest web framework?](https://github.com/the-benchmarker/web-frameworks)\n\n- [TechEmpower Framework Benchmarks](https://www.techempower.com/benchmarks/)\n\n## 要求\n\n| 依赖 | 说明 |\n| -------- | -------- |\n| [PHP](https://www.php.net/) | `\u003e=8.2` `推荐8.2` |\n| [Swoole](https://www.swoole.com/) | `\u003e=5.0` `推荐5.1.1` |\n| [Laravel](https://laravel.com/)/[Lumen](https://lumen.laravel.com/) | `\u003e=10` `推荐10` |\n\n## 安装\n\n1.通过[Composer](https://getcomposer.org/)安装([packagist](https://packagist.org/packages/hhxsv5/laravel-s))。有可能找不到`3.0`版本，解决方案移步[#81](https://github.com/hhxsv5/laravel-s/issues/81)。\n```bash\n# PHP \u003e=8.2\ncomposer require \"hhxsv5/laravel-s:~3.8.0\"\n\n# PHP \u003e=5.5.9,\u003c=7.4.33\n# composer require \"hhxsv5/laravel-s:~3.7.0\"\n\n# 确保你的composer.lock文件是在版本控制中\n```\n\n2.注册Service Provider（以下两步二选一）。\n\n- `Laravel`: 修改文件`config/app.php`，`Laravel 5.5+支持包自动发现，你应该跳过这步`\n    ```php\n    'providers' =\u003e [\n        //...\n        Hhxsv5\\LaravelS\\Illuminate\\LaravelSServiceProvider::class,\n    ],\n    ```\n\n- `Lumen`: 修改文件`bootstrap/app.php`\n    ```php\n    $app-\u003eregister(Hhxsv5\\LaravelS\\Illuminate\\LaravelSServiceProvider::class);\n    ```\n\n3.发布配置和二进制文件。\n\u003e *每次升级LaravelS后，需重新publish；点击[Release](https://github.com/hhxsv5/laravel-s/releases)去了解各个版本的变更记录。*\n\n```bash\nphp artisan laravels publish\n# 配置文件：config/laravels.php\n# 二进制文件：bin/laravels bin/fswatch bin/inotify\n```\n\n4.修改配置`config/laravels.php`：监听的IP、端口等，请参考[配置项](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/Settings-CN.md)。\n\n5.性能调优\n\n- [调整内核参数](https://wiki.swoole.com/#/other/sysctl?id=%e5%86%85%e6%a0%b8%e5%8f%82%e6%95%b0%e8%b0%83%e6%95%b4)\n\n- [Worker数量](https://wiki.swoole.com/#/server/setting?id=worker_num)：LaravelS 使用 Swoole 的`同步IO`模式，`worker_num`设置的越大并发性能越好，但会造成更多的内存占用和进程切换开销。如果`1`个请求耗时`100ms`，为了提供`1000QPS`的并发能力，至少需要配置`100`个Worker进程，计算方法：worker_num = 1000QPS/(1s/1ms) = 100，故需进行增量压测计算出最佳的`worker_num`。\n\n- [Task Worker数量](https://wiki.swoole.com/#/server/setting?id=task_worker_num)\n\n\n## 运行\n\u003e `在运行之前，请先仔细阅读：`[注意事项](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/README-CN.md#%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9)(这非常重要)。\n\n- 操作命令：`php bin/laravels {start|stop|restart|reload|info|help}`。\n\n| 命令 | 说明 |\n| --------- | --------- |\n| start | 启动LaravelS，展示已启动的进程列表 \"*ps -ef\u0026#124;grep laravels*\" |\n| stop | 停止LaravelS，并触发自定义进程的`onStop`方法 |\n| restart | 重启LaravelS：先平滑`Stop`，然后再`Start`；在`Start`完成之前，服务是`不可用的` |\n| reload | 平滑重启所有Task/Worker/Timer进程(这些进程内包含了你的业务代码)，并触发自定义进程的`onReload`方法，不会重启Master/Manger进程；修改`config/laravels.php`后，你`只有`调用`restart`来完成重启 |\n| info | 显示组件的版本信息 |\n| help | 显示帮助信息 |\n\n- 启动选项，针对`start`和`restart`命令。\n\n| 选项 | 说明 |\n| --------- | --------- |\n| -d\u0026#124;--daemonize | 以守护进程的方式运行，此选项将覆盖`laravels.php`中`swoole.daemonize`设置 |\n| -e\u0026#124;--env | 指定运行的环境，如`--env=testing`将会优先使用配置文件`.env.testing`，这个特性要求`Laravel 5.2+` |\n| -i\u0026#124;--ignore | 忽略检查Master进程的PID文件 |\n| -x\u0026#124;--x-version | 记录当前工程的版本号(分支)，保存在`$_ENV`/`$_SERVER`中，访问方式：`$_ENV['X_VERSION']` `$_SERVER['X_VERSION']` `$request-\u003eserver-\u003eget('X_VERSION')` |\n\n- `运行时`文件：`start`时会自动执行`php artisan laravels config`并生成这些文件，开发者一般不需要关注它们，建议将它们加到`.gitignore`中。\n\n| 文件 | 说明 |\n| --------- | --------- |\n| storage/laravels.conf | LaravelS的`运行时`配置文件 |\n| storage/laravels.pid | Master进程的PID文件 |\n| storage/laravels-timer-process.pid | 定时器Timer进程的PID文件 |\n| storage/laravels-custom-processes.pid | 所有自定义进程的PID文件 |\n\n## 部署\n\u003e 建议通过[Supervisord](http://supervisord.org/)监管主进程，前提是不能加`-d`选项并且设置`swoole.daemonize`为`false`。\n\n```\n[program:laravel-s-test]\ndirectory=/var/www/laravel-s-test\ncommand=/usr/local/bin/php bin/laravels start -i\nnumprocs=1\nautostart=true\nautorestart=true\nstartretries=3\nuser=www-data\nredirect_stderr=true\nstdout_logfile=/var/log/supervisor/%(program_name)s.log\n```\n\n## 与Nginx配合使用（推荐）\n\u003e [示例](https://github.com/hhxsv5/docker/blob/master/nginx/conf.d/laravels.conf)。\n\n```nginx\ngzip on;\ngzip_min_length 1024;\ngzip_comp_level 2;\ngzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml;\ngzip_vary on;\ngzip_disable \"msie6\";\nupstream swoole {\n    # 通过 IP:Port 连接\n    server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;\n    # 通过 UnixSocket Stream 连接，小诀窍：将socket文件放在/dev/shm目录下，可获得更好的性能\n    #server unix:/yourpath/laravel-s-test/storage/laravels.sock weight=5 max_fails=3 fail_timeout=30s;\n    #server 192.168.1.1:5200 weight=3 max_fails=3 fail_timeout=30s;\n    #server 192.168.1.2:5200 backup;\n    keepalive 16;\n}\nserver {\n    listen 80;\n    # 别忘了绑Host\n    server_name laravels.com;\n    root /yourpath/laravel-s-test/public;\n    access_log /yourpath/log/nginx/$server_name.access.log  main;\n    autoindex off;\n    index index.html index.htm;\n    # Nginx处理静态资源(建议开启gzip)，LaravelS处理动态资源。\n    location / {\n        try_files $uri @laravels;\n    }\n    # 当请求PHP文件时直接响应404，防止暴露public/*.php\n    #location ~* \\.php$ {\n    #    return 404;\n    #}\n    location @laravels {\n        # proxy_connect_timeout 60s;\n        # proxy_send_timeout 60s;\n        # proxy_read_timeout 120s;\n        proxy_http_version 1.1;\n        proxy_set_header Connection \"\";\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Real-PORT $remote_port;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header Host $http_host;\n        proxy_set_header Scheme $scheme;\n        proxy_set_header Server-Protocol $server_protocol;\n        proxy_set_header Server-Name $server_name;\n        proxy_set_header Server-Addr $server_addr;\n        proxy_set_header Server-Port $server_port;\n        # “swoole”是指upstream\n        proxy_pass http://swoole;\n    }\n}\n```\n\n## 与Apache配合使用\n\n```apache\nLoadModule proxy_module /yourpath/modules/mod_proxy.so\nLoadModule proxy_balancer_module /yourpath/modules/mod_proxy_balancer.so\nLoadModule lbmethod_byrequests_module /yourpath/modules/mod_lbmethod_byrequests.so\nLoadModule proxy_http_module /yourpath/modules/mod_proxy_http.so\nLoadModule slotmem_shm_module /yourpath/modules/mod_slotmem_shm.so\nLoadModule rewrite_module /yourpath/modules/mod_rewrite.so\nLoadModule remoteip_module /yourpath/modules/mod_remoteip.so\nLoadModule deflate_module /yourpath/modules/mod_deflate.so\n\n\u003cIfModule deflate_module\u003e\n    SetOutputFilter DEFLATE\n    DeflateCompressionLevel 2\n    AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/x-httpd-php image/jpeg image/gif image/png font/ttf font/otf image/svg+xml\n\u003c/IfModule\u003e\n\n\u003cVirtualHost *:80\u003e\n    # 别忘了绑Host\n    ServerName www.laravels.com\n    ServerAdmin hhxsv5@sina.com\n\n    DocumentRoot /yourpath/laravel-s-test/public;\n    DirectoryIndex index.html index.htm\n    \u003cDirectory \"/\"\u003e\n        AllowOverride None\n        Require all granted\n    \u003c/Directory\u003e\n\n    RemoteIPHeader X-Forwarded-For\n\n    ProxyRequests Off\n    ProxyPreserveHost On\n    \u003cProxy balancer://laravels\u003e  \n        BalancerMember http://192.168.1.1:5200 loadfactor=7\n        #BalancerMember http://192.168.1.2:5200 loadfactor=3\n        #BalancerMember http://192.168.1.3:5200 loadfactor=1 status=+H\n        ProxySet lbmethod=byrequests\n    \u003c/Proxy\u003e\n    #ProxyPass / balancer://laravels/\n    #ProxyPassReverse / balancer://laravels/\n\n    # Apache处理静态资源，LaravelS处理动态资源。\n    RewriteEngine On\n    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d\n    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f\n    RewriteRule ^/(.*)$ balancer://laravels%{REQUEST_URI} [P,L]\n\n    ErrorLog ${APACHE_LOG_DIR}/www.laravels.com.error.log\n    CustomLog ${APACHE_LOG_DIR}/www.laravels.com.access.log combined\n\u003c/VirtualHost\u003e\n```\n## 启用WebSocket服务器\n\u003e WebSocket服务器监听的IP和端口与Http服务器相同。\n\n1.创建WebSocket Handler类，并实现接口`WebSocketHandlerInterface`。start时会自动实例化，不需要手动创建实例。\n\n```php\nnamespace App\\Services;\nuse Hhxsv5\\LaravelS\\Swoole\\WebSocketHandlerInterface;\nuse Swoole\\Http\\Request;\nuse Swoole\\Http\\Response;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\Server;\n/**\n * @see https://wiki.swoole.com/#/start/start_ws_server\n */\nclass WebSocketService implements WebSocketHandlerInterface\n{\n    // 声明没有参数的构造函数\n    public function __construct()\n    {\n    }\n    // public function onHandShake(Request $request, Response $response)\n    // {\n           // 自定义握手：https://wiki.swoole.com/#/websocket_server?id=onhandshake\n           // 成功握手之后会自动触发onOpen事件\n    // }\n    public function onOpen(Server $server, Request $request)\n    {\n        // 在触发onOpen事件之前，建立WebSocket的HTTP请求已经经过了Laravel的路由，\n        // 所以Laravel的Request、Auth等信息是可读的，Session是可读写的，但仅限在onOpen事件中。\n        // \\Log::info('New WebSocket connection', [$request-\u003efd, request()-\u003eall(), session()-\u003egetId(), session('xxx'), session(['yyy' =\u003e time()])]);\n        // 此处抛出的异常会被上层捕获并记录到Swoole日志，开发者需要手动try/catch\n        $server-\u003epush($request-\u003efd, 'Welcome to LaravelS');\n    }\n    public function onMessage(Server $server, Frame $frame)\n    {\n        // \\Log::info('Received message', [$frame-\u003efd, $frame-\u003edata, $frame-\u003eopcode, $frame-\u003efinish]);\n        // 此处抛出的异常会被上层捕获并记录到Swoole日志，开发者需要手动try/catch\n        $server-\u003epush($frame-\u003efd, date('Y-m-d H:i:s'));\n    }\n    public function onClose(Server $server, $fd, $reactorId)\n    {\n        // 此处抛出的异常会被上层捕获并记录到Swoole日志，开发者需要手动try/catch\n    }\n}\n```\n\n2.更改配置`config/laravels.php`。\n\n```php\n// ...\n'websocket'      =\u003e [\n    'enable'  =\u003e true, // 注意：设置enable为true\n    'handler' =\u003e \\App\\Services\\WebSocketService::class,\n],\n'swoole'         =\u003e [\n    //...\n    // dispatch_mode只能设置为2、4、5，https://wiki.swoole.com/#/server/setting?id=dispatch_mode\n    'dispatch_mode' =\u003e 2,\n    //...\n],\n// ...\n```\n\n3.使用`SwooleTable`绑定FD与UserId，可选的，[Swoole Table示例](#使用swooletable)。也可以用其他全局存储服务，例如Redis/Memcached/MySQL，但需要注意多个`Swoole Server`实例时FD可能冲突。\n\n4.与Nginx配合使用（推荐）\n\u003e 参考 [WebSocket代理](http://nginx.org/en/docs/http/websocket.html)\n\n```nginx\nmap $http_upgrade $connection_upgrade {\n    default upgrade;\n    ''      close;\n}\nupstream swoole {\n    # 通过 IP:Port 连接\n    server 127.0.0.1:5200 weight=5 max_fails=3 fail_timeout=30s;\n    # 通过 UnixSocket Stream 连接，小诀窍：将socket文件放在/dev/shm目录下，可获得更好的性能\n    #server unix:/yourpath/laravel-s-test/storage/laravels.sock weight=5 max_fails=3 fail_timeout=30s;\n    #server 192.168.1.1:5200 weight=3 max_fails=3 fail_timeout=30s;\n    #server 192.168.1.2:5200 backup;\n    keepalive 16;\n}\nserver {\n    listen 80;\n    # 别忘了绑Host\n    server_name laravels.com;\n    root /yourpath/laravel-s-test/public;\n    access_log /yourpath/log/nginx/$server_name.access.log  main;\n    autoindex off;\n    index index.html index.htm;\n    # Nginx处理静态资源(建议开启gzip)，LaravelS处理动态资源。\n    location / {\n        try_files $uri @laravels;\n    }\n    # 当请求PHP文件时直接响应404，防止暴露public/*.php\n    #location ~* \\.php$ {\n    #    return 404;\n    #}\n    # Http和WebSocket共存，Nginx通过location区分\n    # !!! WebSocket连接时路径为/ws\n    # Javascript: var ws = new WebSocket(\"ws://laravels.com/ws\");\n    location =/ws {\n        # proxy_connect_timeout 60s;\n        # proxy_send_timeout 60s;\n        # proxy_read_timeout：如果60秒内被代理的服务器没有响应数据给Nginx，那么Nginx会关闭当前连接；同时，Swoole的心跳设置也会影响连接的关闭\n        # proxy_read_timeout 60s;\n        proxy_http_version 1.1;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Real-PORT $remote_port;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header Host $http_host;\n        proxy_set_header Scheme $scheme;\n        proxy_set_header Server-Protocol $server_protocol;\n        proxy_set_header Server-Name $server_name;\n        proxy_set_header Server-Addr $server_addr;\n        proxy_set_header Server-Port $server_port;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection $connection_upgrade;\n        proxy_pass http://swoole;\n    }\n    location @laravels {\n        # proxy_connect_timeout 60s;\n        # proxy_send_timeout 60s;\n        # proxy_read_timeout 60s;\n        proxy_http_version 1.1;\n        proxy_set_header Connection \"\";\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Real-PORT $remote_port;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header Host $http_host;\n        proxy_set_header Scheme $scheme;\n        proxy_set_header Server-Protocol $server_protocol;\n        proxy_set_header Server-Name $server_name;\n        proxy_set_header Server-Addr $server_addr;\n        proxy_set_header Server-Port $server_port;\n        proxy_pass http://swoole;\n    }\n}\n```\n\n5.心跳配置\n\n- Swoole的心跳配置\n\n    ```php\n    // config/laravels.php\n    'swoole' =\u003e [\n        //...\n        // 表示每60秒遍历一次，一个连接如果600秒内未向服务器发送任何数据，此连接将被强制关闭\n        'heartbeat_idle_time'      =\u003e 600,\n        'heartbeat_check_interval' =\u003e 60,\n        //...\n    ],\n    ```\n\n- Nginx读取代理服务器超时的配置\n\n    ```nginx\n    # 如果60秒内被代理的服务器没有响应数据给Nginx，那么Nginx会关闭当前连接\n    proxy_read_timeout 60s;\n    ```\n\n6.在控制器中推送数据\n\n```php\nnamespace App\\Http\\Controllers;\nclass TestController extends Controller\n{\n    public function push()\n    {\n        $fd = 1; // Find fd by userId from a map [userId=\u003efd].\n        /**@var \\Swoole\\WebSocket\\Server $swoole */\n        $swoole = app('swoole');\n        $success = $swoole-\u003epush($fd, 'Push data to fd#1 in Controller');\n        var_dump($success);\n    }\n}\n```\n\n## 监听事件\n\n### 系统事件\n\u003e 通常，你可以在这些事件中重置或销毁一些全局或静态的变量，也可以修改当前的请求和响应。\n\n- `laravels.received_request` 将`Swoole\\Http\\Request`转成`Illuminate\\Http\\Request`后，在Laravel内核处理请求前。\n\n    ```php\n    // 修改`app/Providers/EventServiceProvider.php`, 添加下面监听代码到boot方法中\n    // 如果变量$events不存在，你也可以通过Facade调用\\Event::listen()。\n    $events-\u003elisten('laravels.received_request', function (\\Illuminate\\Http\\Request $req, $app) {\n        $req-\u003equery-\u003eset('get_key', 'hhxsv5');// 修改querystring\n        $req-\u003erequest-\u003eset('post_key', 'hhxsv5'); // 修改post body\n    });\n    ```\n\n- `laravels.generated_response` 在Laravel内核处理完请求后，将`Illuminate\\Http\\Response`转成`Swoole\\Http\\Response`之前(下一步将响应给客户端)。\n\n    ```php\n    // 修改`app/Providers/EventServiceProvider.php`, 添加下面监听代码到boot方法中\n    // 如果变量$events不存在，你也可以通过Facade调用\\Event::listen()。\n    $events-\u003elisten('laravels.generated_response', function (\\Illuminate\\Http\\Request $req, \\Symfony\\Component\\HttpFoundation\\Response $rsp, $app) {\n        $rsp-\u003eheaders-\u003eset('header-key', 'hhxsv5');// 修改header\n    });\n    ```\n\n### 自定义的异步事件\n\u003e 此特性依赖`Swoole`的`AsyncTask`，必须先设置`config/laravels.php`的`swoole.task_worker_num`。异步事件的处理能力受Task进程数影响，需合理设置[task_worker_num](https://wiki.swoole.com/#/server/setting?id=task_worker_num)。\n\n1.创建事件类。\n\n```php\nuse Hhxsv5\\LaravelS\\Swoole\\Task\\Event;\nclass TestEvent extends Event\n{\n    protected $listeners = [\n        // 监听器列表\n        TestListener1::class,\n        // TestListener2::class,\n    ];\n    private $data;\n    public function __construct($data)\n    {\n        $this-\u003edata = $data;\n    }\n    public function getData()\n    {\n        return $this-\u003edata;\n    }\n}\n```\n\n2.创建监听器类。\n\n```php\nuse Hhxsv5\\LaravelS\\Swoole\\Task\\Event;\nuse Hhxsv5\\LaravelS\\Swoole\\Task\\Task;\nuse Hhxsv5\\LaravelS\\Swoole\\Task\\Listener;\nclass TestListener1 extends Listener\n{\n    public function handle(Event $event)\n    {\n        \\Log::info(__CLASS__ . ':handle start', [$event-\u003egetData()]);\n        sleep(2);// 模拟一些慢速的事件处理\n        // 监听器中也可以投递Task，但不支持Task的finish()回调。\n        // 注意：config/laravels.php中修改配置task_ipc_mode为1或2，参考 https://wiki.swoole.com/#/server/setting?id=task_ipc_mode\n        $ret = Task::deliver(new TestTask('task data'));\n        var_dump($ret);\n        // 此处抛出的异常会被上层捕获并记录到Swoole日志，开发者需要手动try/catch\n        // return false; // 停止将事件传播到后面的监听器\n    }\n}\n```\n\n3.触发事件。\n\n```php\n// 实例化TestEvent并通过fire触发，此操作是异步的，触发后立即返回，由Task进程继续处理监听器中的handle逻辑\nuse Hhxsv5\\LaravelS\\Swoole\\Task\\Event;\n$event = new TestEvent('event data');\n// $event-\u003edelay(10); // 延迟10秒触发\n// $event-\u003esetTries(3); // 出现异常时，累计尝试3次\n$success = Event::fire($event);\nvar_dump($success);// 判断是否触发成功\n```\n\n## 异步的任务队列\n\u003e 此特性依赖`Swoole`的`AsyncTask`，必须先设置`config/laravels.php`的`swoole.task_worker_num`。异步任务的处理能力受Task进程数影响，需合理设置[task_worker_num](https://wiki.swoole.com/#/server/setting?id=task_worker_num)。\n\n1.创建任务类。\n\n```php\nuse Hhxsv5\\LaravelS\\Swoole\\Task\\Task;\nclass TestTask extends Task\n{\n    private $data;\n    private $result;\n    public function __construct($data)\n    {\n        $this-\u003edata = $data;\n    }\n    // 处理任务的逻辑，运行在Task进程中，不能投递任务\n    public function handle()\n    {\n        \\Log::info(__CLASS__ . ':handle start', [$this-\u003edata]);\n        sleep(2);// 模拟一些慢速的事件处理\n        // 此处抛出的异常会被上层捕获并记录到Swoole日志，开发者需要手动try/catch\n        $this-\u003eresult = 'the result of ' . $this-\u003edata;\n    }\n    // 可选的，完成事件，任务处理完后的逻辑，运行在Worker进程中，可以投递任务\n    public function finish()\n    {\n        \\Log::info(__CLASS__ . ':finish start', [$this-\u003eresult]);\n        Task::deliver(new TestTask2('task2')); // 投递其他任务\n    }\n}\n```\n\n2.投递任务。\n\n```php\n// 实例化TestTask并通过deliver投递，此操作是异步的，投递后立即返回，由Task进程继续处理TestTask中的handle逻辑\nuse Hhxsv5\\LaravelS\\Swoole\\Task\\Task;\n$task = new TestTask('task data');\n// $task-\u003edelay(3); // 延迟3秒投递任务\n// $task-\u003esetTries(3); // 出现异常时，累计尝试3次\n$ret = Task::deliver($task);\nvar_dump($ret);// 判断是否投递成功\n```\n\n## 毫秒级定时任务\n\u003e 基于[Swoole的毫秒定时器](https://wiki.swoole.com/#/timer)，封装的定时任务，取代`Linux`的`Crontab`。\n\n1.创建定时任务类。\n```php\nnamespace App\\Jobs\\Timer;\nuse App\\Tasks\\TestTask;\nuse Swoole\\Coroutine;\nuse Hhxsv5\\LaravelS\\Swoole\\Task\\Task;\nuse Hhxsv5\\LaravelS\\Swoole\\Timer\\CronJob;\nclass TestCronJob extends CronJob\n{\n    protected $i = 0;\n    // !!! 定时任务的`interval`和`isImmediate`有两种配置方式（二选一）：一是重载对应的方法，二是注册定时任务时传入参数。\n    // --- 重载对应的方法来返回配置：开始\n    public function interval()\n    {\n        return 1000;// 每1秒运行一次\n    }\n    public function isImmediate()\n    {\n        return false;// 是否立即执行第一次，false则等待间隔时间后执行第一次\n    }\n    // --- 重载对应的方法来返回配置：结束\n    public function run()\n    {\n        \\Log::info(__METHOD__, ['start', $this-\u003ei, microtime(true)]);\n        // do something\n        // sleep(1); // Swoole \u003c 2.1\n        Coroutine::sleep(1); // Swoole\u003e=2.1 run()方法已自动创建了协程。\n        $this-\u003ei++;\n        \\Log::info(__METHOD__, ['end', $this-\u003ei, microtime(true)]);\n\n        if ($this-\u003ei \u003e= 10) { // 运行10次后不再执行\n            \\Log::info(__METHOD__, ['stop', $this-\u003ei, microtime(true)]);\n            $this-\u003estop(); // 终止此定时任务，但restart/reload后会再次运行\n            // CronJob中也可以投递Task，但不支持Task的finish()回调。\n            // 注意：修改config/laravels.php，配置task_ipc_mode为1或2，参考 https://wiki.swoole.com/#/server/setting?id=task_ipc_mode\n            $ret = Task::deliver(new TestTask('task data'));\n            var_dump($ret);\n        }\n        // 此处抛出的异常会被上层捕获并记录到Swoole日志，开发者需要手动try/catch\n    }\n}\n```\n\n2.注册定时任务类。\n\n```php\n// 在\"config/laravels.php\"注册定时任务类\n[\n    // ...\n    'timer'          =\u003e [\n        'enable' =\u003e true, // 启用Timer\n        'jobs'   =\u003e [ // 注册的定时任务类列表\n            // 启用LaravelScheduleJob来执行`php artisan schedule:run`，每分钟一次，替代Linux Crontab\n            // \\Hhxsv5\\LaravelS\\Illuminate\\LaravelScheduleJob::class,\n            // 两种配置参数的方式：\n            // [\\App\\Jobs\\Timer\\TestCronJob::class, [1000, true]], // 注册时传入参数\n            \\App\\Jobs\\Timer\\TestCronJob::class, // 重载对应的方法来返回参数\n        ],\n        'max_wait_time' =\u003e 5, // Reload时最大等待时间\n        // 打开全局定时器开关：当多实例部署时，确保只有一个实例运行定时任务，此功能依赖 Redis，具体请看 https://laravel.com/docs/7.x/redis\n        'global_lock'     =\u003e false,\n        'global_lock_key' =\u003e config('app.name', 'Laravel'),\n    ],\n    // ...\n];\n```\n\n3.注意在构建服务器集群时，会启动多个`定时器`，要确保只启动一个定期器，避免重复执行定时任务。\n\n4.LaravelS `v3.4.0`开始支持热重启[Reload]`定时器`进程，LaravelS 在收到`SIGUSR1`信号后会等待`max_wait_time`(默认5)秒再结束进程，然后`Manager`进程会重新拉起`定时器`进程。\n\n5.如果你仅需要用到`分钟级`的定时任务，建议启用`Hhxsv5\\LaravelS\\Illuminate\\LaravelScheduleJob`来替代Linux Crontab，这样就可以沿用[Laravel任务调度](https://learnku.com/docs/laravel/7.x/scheduling/7492)的编码习惯，配置`Kernel`即可。\n\n```php\n// app/Console/Kernel.php\nprotected function schedule(Schedule $schedule)\n{\n    // runInBackground()方法会新启子进程执行任务，这是异步的，不会影响其他任务的执行时机\n    $schedule-\u003ecommand(TestCommand::class)-\u003erunInBackground()-\u003eeveryMinute();\n}\n```\n\n## 修改代码后自动Reload\n\n- 基于`inotify`，仅支持Linux。\n\n    1.安装[inotify](http://pecl.php.net/package/inotify)扩展。\n\n    2.开启[配置项](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/Settings-CN.md#inotify_reloadenable)。\n\n    3.注意：`inotify`只有在`Linux`内修改文件才能收到文件变更事件，建议使用最新版Docker，[Vagrant解决方案](https://github.com/mhallin/vagrant-notify-forwarder)。\n\n- 基于`fswatch`，支持OS X、Linux、Windows。\n\n    1.安装[fswatch](https://github.com/emcrisostomo/fswatch)。\n\n    2.在项目根目录下运行命令。\n\n    ```bash\n    # 监听当前目录\n    ./bin/fswatch\n    # 监听app目录\n    ./bin/fswatch ./app\n    ```\n\n- 基于`inotifywait`，仅支持Linux。\n\n    1.安装[inotify-tools](https://github.com/rvoicilas/inotify-tools)。\n\n    2.在项目根目录下运行命令。\n\n    ```bash\n    # 监听当前目录\n    ./bin/inotify\n    # 监听app目录\n    ./bin/inotify ./app\n    ```\n\n- 当以上方法都不行时，终极解决方案：配置`max_request=1,worker_num=1`，这样`Worker`进程处理完一个请求就会重启，这种方法的性能非常差，`故仅限在开发环境使用`。\n\n## 在你的项目中使用`SwooleServer`实例\n\n```php\n/**\n * 如果启用WebSocket server，$swoole是`Swoole\\WebSocket\\Server`的实例，否则是是`Swoole\\Http\\Server`的实例\n * @var \\Swoole\\WebSocket\\Server|\\Swoole\\Http\\Server $swoole\n */\n$swoole = app('swoole');\nvar_dump($swoole-\u003estats());\n$swoole-\u003epush($fd, 'Push WebSocket message');\n```\n\n## 使用`SwooleTable`\n\n1.定义Table，支持定义多个Table。\n\u003e Swoole启动之前会创建定义的所有Table。\n\n```php\n// 在\"config/laravels.php\"配置\n[\n    // ...\n    'swoole_tables'  =\u003e [\n        // 场景：WebSocket中UserId与FD绑定\n        'ws' =\u003e [// Key为Table名称，使用时会自动添加Table后缀，避免重名。这里定义名为wsTable的Table\n            'size'   =\u003e 102400,//Table的最大行数\n            'column' =\u003e [// Table的列定义\n                ['name' =\u003e 'value', 'type' =\u003e \\Swoole\\Table::TYPE_INT, 'size' =\u003e 8],\n            ],\n        ],\n        //...继续定义其他Table\n    ],\n    // ...\n];\n```\n\n2.访问Table：所有的Table实例均绑定在`SwooleServer`上，通过`app('swoole')-\u003exxxTable`访问。\n\n```php\nnamespace App\\Services;\nuse Hhxsv5\\LaravelS\\Swoole\\WebSocketHandlerInterface;\nuse Swoole\\Http\\Request;\nuse Swoole\\WebSocket\\Frame;\nuse Swoole\\WebSocket\\Server;\nclass WebSocketService implements WebSocketHandlerInterface\n{\n    /**@var \\Swoole\\Table $wsTable */\n    private $wsTable;\n    public function __construct()\n    {\n        $this-\u003ewsTable = app('swoole')-\u003ewsTable;\n    }\n    // 场景：WebSocket中UserId与FD绑定\n    public function onOpen(Server $server, Request $request)\n    {\n        // var_dump(app('swoole') === $server);// 同一实例\n        /**\n         * 获取当前登录的用户\n         * 此特性要求建立WebSocket连接的路径要经过Authenticate之类的中间件。\n         * 例如：\n         * 浏览器端：var ws = new WebSocket(\"ws://127.0.0.1:5200/ws\");\n         * 那么Laravel中/ws路由就需要加上类似Authenticate的中间件。\n         * Route::get('/ws', function () {\n         *     // 响应状态码200的任意内容\n         *     return 'websocket';\n         * })-\u003emiddleware(['auth']);\n         */\n        // $user = Auth::user();\n        // $userId = $user ? $user-\u003eid : 0; // 0 表示未登录的访客用户\n        $userId = mt_rand(1000, 10000);\n        // if (!$userId) {\n        //     // 未登录用户直接断开连接\n        //     $server-\u003edisconnect($request-\u003efd);\n        //     return;\n        // }\n        $this-\u003ewsTable-\u003eset('uid:' . $userId, ['value' =\u003e $request-\u003efd]);// 绑定uid到fd的映射\n        $this-\u003ewsTable-\u003eset('fd:' . $request-\u003efd, ['value' =\u003e $userId]);// 绑定fd到uid的映射\n        $server-\u003epush($request-\u003efd, \"Welcome to LaravelS #{$request-\u003efd}\");\n    }\n    public function onMessage(Server $server, Frame $frame)\n    {\n        // 广播\n        foreach ($this-\u003ewsTable as $key =\u003e $row) {\n            if (strpos($key, 'uid:') === 0 \u0026\u0026 $server-\u003eisEstablished($row['value'])) {\n                $content = sprintf('Broadcast: new message \"%s\" from #%d', $frame-\u003edata, $frame-\u003efd);\n                $server-\u003epush($row['value'], $content);\n            }\n        }\n    }\n    public function onClose(Server $server, $fd, $reactorId)\n    {\n        $uid = $this-\u003ewsTable-\u003eget('fd:' . $fd);\n        if ($uid !== false) {\n            $this-\u003ewsTable-\u003edel('uid:' . $uid['value']); // 解绑uid映射\n        }\n        $this-\u003ewsTable-\u003edel('fd:' . $fd);// 解绑fd映射\n        $server-\u003epush($fd, \"Goodbye #{$fd}\");\n    }\n}\n```\n\n## 多端口混合协议\n\n\u003e 更多的信息，请参考[Swoole增加监听的端口](https://wiki.swoole.com/#/server/methods?id=addlistener)与[多端口混合协议](https://wiki.swoole.com/#/server/port)\n\n为了使我们的主服务器能支持除`HTTP`和`WebSocket`外的更多协议，我们引入了`Swoole`的`多端口混合协议`特性，在LaravelS中称为`Socket`。现在，可以很方便地在Laravel上构建`TCP/UDP`应用。\n\n1. 创建`Socket`处理类，继承`Hhxsv5\\LaravelS\\Swoole\\Socket\\{TcpSocket|UdpSocket|Http|WebSocket}`\n\n    ```php\n    namespace App\\Sockets;\n    use Hhxsv5\\LaravelS\\Swoole\\Socket\\TcpSocket;\n    use Swoole\\Server;\n    class TestTcpSocket extends TcpSocket\n    {\n        public function onConnect(Server $server, $fd, $reactorId)\n        {\n            \\Log::info('New TCP connection', [$fd]);\n            $server-\u003esend($fd, 'Welcome to LaravelS.');\n        }\n        public function onReceive(Server $server, $fd, $reactorId, $data)\n        {\n            \\Log::info('Received data', [$fd, $data]);\n            $server-\u003esend($fd, 'LaravelS: ' . $data);\n            if ($data === \"quit\\r\\n\") {\n                $server-\u003esend($fd, 'LaravelS: bye' . PHP_EOL);\n                $server-\u003eclose($fd);\n            }\n        }\n        public function onClose(Server $server, $fd, $reactorId)\n        {\n            \\Log::info('Close TCP connection', [$fd]);\n            $server-\u003esend($fd, 'Goodbye');\n        }\n    }\n    ```\n\n    这些连接和主服务器上的HTTP/WebSocket连接共享`Worker`进程，因此可以在这些事件回调中使用LaravelS提供的`异步任务投递`、`SwooleTable`、Laravel提供的组件如`DB`、`Eloquent`等。同时，如果需要使用该协议端口的`Swoole\\Server\\Port`对象，只需要像如下代码一样访问`Socket`类的成员`swoolePort`即可。\n\n    ```php\n    public function onReceive(Server $server, $fd, $reactorId, $data)\n    {\n        $port = $this-\u003eswoolePort; // 获得`Swoole\\Server\\Port`对象\n    }\n    ```\n\n    ```php\n    namespace App\\Http\\Controllers;\n    class TestController extends Controller\n    {\n        public function test()\n        {\n            /**@var \\Swoole\\Http\\Server|\\Swoole\\WebSocket\\Server $swoole */\n            $swoole = app('swoole');\n            // $swoole-\u003eports：遍历所有Port对象，https://wiki.swoole.com/#/server/properties?id=ports\n            $port = $swoole-\u003eports[1]; // 获得`Swoole\\Server\\Port`对象，$port[0]是主服务器的端口\n            foreach ($port-\u003econnections as $fd) { // 遍历所有连接\n                // $swoole-\u003esend($fd, 'Send tcp message');\n                // if($swoole-\u003eisEstablished($fd)) {\n                //     $swoole-\u003epush($fd, 'Send websocket message');\n                // }\n            }\n        }\n    }\n    ```\n\n2. 注册套接字。\n\n    ```php\n    // 修改文件 config/laravels.php\n    // ...\n    'sockets' =\u003e [\n        [\n            'host'     =\u003e '127.0.0.1',\n            'port'     =\u003e 5291,\n            'type'     =\u003e SWOOLE_SOCK_TCP,// 支持的嵌套字类型：https://wiki.swoole.com/#/consts?id=socket-%e7%b1%bb%e5%9e%8b\n            'settings' =\u003e [// Swoole可用的配置项：https://wiki.swoole.com/#/server/port?id=%e5%8f%af%e9%80%89%e5%8f%82%e6%95%b0\n                'open_eof_check' =\u003e true,\n                'package_eof'    =\u003e \"\\r\\n\",\n            ],\n            'handler'  =\u003e \\App\\Sockets\\TestTcpSocket::class,\n            'enable'   =\u003e true, // 是否启用，默认为true\n        ],\n    ],\n    ```\n\n    关于心跳配置，只能设置在`主服务器`上，不能配置在`套接字`上，但`套接字`会继承`主服务器`的心跳配置。\n\n    对于TCP协议，`dispatch_mode`选项设为`1/3`时，底层会屏蔽`onConnect`/`onClose`事件，原因是这两种模式下无法保证`onConnect`/`onClose`/`onReceive`的顺序。如果需要用到这两个事件，请将`dispatch_mode`改为`2/4/5`，[参考](https://wiki.swoole.com/#/server/setting?id=dispatch_mode)。\n\n    ```php\n    'swoole' =\u003e [\n        //...\n        'dispatch_mode' =\u003e 2,\n        //...\n    ];\n    ```\n\n3. 测试。\n\n- TCP：`telnet 127.0.0.1 5291`\n\n- UDP：Linux下 `echo \"Hello LaravelS\" \u003e /dev/udp/127.0.0.1/5292`\n\n4. 其他协议的注册示例。\n\n    - UDP\n    ```php\n    'sockets' =\u003e [\n        [\n            'host'     =\u003e '0.0.0.0',\n            'port'     =\u003e 5292,\n            'type'     =\u003e SWOOLE_SOCK_UDP,\n            'settings' =\u003e [\n                'open_eof_check' =\u003e true,\n                'package_eof'    =\u003e \"\\r\\n\",\n            ],\n            'handler'  =\u003e \\App\\Sockets\\TestUdpSocket::class,\n        ],\n    ],\n    ```\n\n    - Http\n    ```php\n    'sockets' =\u003e [\n        [\n            'host'     =\u003e '0.0.0.0',\n            'port'     =\u003e 5293,\n            'type'     =\u003e SWOOLE_SOCK_TCP,\n            'settings' =\u003e [\n                'open_http_protocol' =\u003e true,\n            ],\n            'handler'  =\u003e \\App\\Sockets\\TestHttp::class,\n        ],\n    ],\n    ```\n\n    - WebSocket：主服务器必须`开启WebSocket`，即需要将`websocket.enable`置为`true`。\n    ```php\n    'sockets' =\u003e [\n        [\n            'host'     =\u003e '0.0.0.0',\n            'port'     =\u003e 5294,\n            'type'     =\u003e SWOOLE_SOCK_TCP,\n            'settings' =\u003e [\n                'open_http_protocol'      =\u003e true,\n                'open_websocket_protocol' =\u003e true,\n            ],\n            'handler'  =\u003e \\App\\Sockets\\TestWebSocket::class,\n        ],\n    ],\n    ```\n\n\n## 协程\n\n\u003e [Swoole原始文档](https://wiki.swoole.com/#/start/coroutine)\n\n- 警告：协程下代码执行顺序是乱序的，请求级的数据应该以协程ID隔离，但Laravel/Lumen中存在很多单例、静态属性，不同请求间的数据会相互影响，这是`不安全`的。比如数据库连接就是单例，同一个数据库连接共享同一个PDO资源，这在同步阻塞模式下是没问题的，但在异步协程下是不行的，每次查询需要创建不同的连接，维护不同的IO状态，这就需要用到连接池。\n\n- `不要`使用协程，仅`自定义进程`中可使用协程。\n\n## 自定义进程\n\n\u003e 支持开发者创建一些特殊的工作进程，用于监控、上报或者其他特殊的任务，参考[addProcess](https://wiki.swoole.com/#/server/methods?id=addprocess)。\n\n1. 创建Proccess类，实现CustomProcessInterface接口。\n\n    ```php\n    namespace App\\Processes;\n    use App\\Tasks\\TestTask;\n    use Hhxsv5\\LaravelS\\Swoole\\Process\\CustomProcessInterface;\n    use Hhxsv5\\LaravelS\\Swoole\\Task\\Task;\n    use Swoole\\Coroutine;\n    use Swoole\\Http\\Server;\n    use Swoole\\Process;\n    class TestProcess implements CustomProcessInterface\n    {\n        /**\n         * @var bool 退出标记，用于Reload更新\n         */\n        private static $quit = false;\n\n        public static function callback(Server $swoole, Process $process)\n        {\n            // 进程运行的代码，不能退出，一旦退出Manager进程会自动再次创建该进程。\n            while (!self::$quit) {\n                \\Log::info('Test process: running');\n                // sleep(1); // Swoole \u003c 2.1\n                Coroutine::sleep(1); // Swoole\u003e=2.1 已自动为callback()方法创建了协程并启用了协程Runtime，注意所使用的组件与协程兼容性，不兼容时可仅开启部分协程，如：\\Swoole\\Runtime::enableCoroutine(SWOOLE_HOOK_TCP | SWOOLE_HOOK_SLEEP | SWOOLE_HOOK_FILE);\n                // 自定义进程中也可以投递Task，但不支持Task的finish()回调。\n                // 注意：修改config/laravels.php，配置task_ipc_mode为1或2，参考 https://wiki.swoole.com/#/server/setting?id=task_ipc_mode\n                $ret = Task::deliver(new TestTask('task data'));\n                var_dump($ret);\n                // 上层会捕获callback中抛出的异常，并记录到Swoole日志，然后此进程会退出，3秒后Manager进程会重新创建进程，所以需要开发者自行try/catch捕获异常，避免频繁创建进程。\n                // throw new \\Exception('an exception');\n            }\n        }\n        // 要求：LaravelS \u003e= v3.4.0 并且 callback() 必须是异步非阻塞程序。\n        public static function onReload(Server $swoole, Process $process)\n        {\n            // Stop the process...\n            // Then end process\n            \\Log::info('Test process: reloading');\n            self::$quit = true;\n            // $process-\u003eexit(0); // 强制退出进程\n        }\n        // 要求：LaravelS \u003e= v3.7.4 并且 callback() 必须是异步非阻塞程序。\n        public static function onStop(Server $swoole, Process $process)\n        {\n            // Stop the process...\n            // Then end process\n            \\Log::info('Test process: stopping');\n            self::$quit = true;\n            // $process-\u003eexit(0); // 强制退出进程\n        }\n    }\n    ```\n\n2. 注册TestProcess。\n\n    ```php\n    // 修改文件 config/laravels.php\n    // ...\n    'processes' =\u003e [\n        'test' =\u003e [ // Key为进程名\n            'class'    =\u003e \\App\\Processes\\TestProcess::class,\n            'redirect' =\u003e false, // 是否重定向输入输出\n            'pipe'     =\u003e 0,     // 管道类型：0不创建管道，1创建SOCK_STREAM类型管道，2创建SOCK_DGRAM类型管道\n            'enable'   =\u003e true,  // 是否启用，默认true\n            //'num'    =\u003e 3,  // 创建多个进程实例，默认为1\n            //'queue'    =\u003e [ // 启用消息队列作为进程间通信，配置空数组表示使用默认参数\n            //    'msg_key'  =\u003e 0,    // 消息队列的KEY，默认会使用ftok(__FILE__, 1)\n            //    'mode'     =\u003e 2,    // 通信模式，默认为2，表示争抢模式\n            //    'capacity' =\u003e 8192, // 单个消息长度，长度受限于操作系统内核参数的限制，默认为8192，最大不超过65536\n            //],\n            //'restart_interval' =\u003e 5, // 进程异常退出后需等待多少秒再重启，默认5秒\n        ],\n    ],\n    ```\n\n3. 注意：callback()方法不能退出，如果退出，Manager进程将会重新创建进程。\n\n4. 示例：向自定义进程中写数据。\n\n    ```php\n    // config/laravels.php\n    'processes' =\u003e [\n        'test' =\u003e [\n            'class'    =\u003e \\App\\Processes\\TestProcess::class,\n            'redirect' =\u003e false,\n            'pipe'     =\u003e 1,\n        ],\n    ],\n    ```\n\n    ```php\n    // app/Processes/TestProcess.php\n    public static function callback(Server $swoole, Process $process)\n    {\n        while ($data = $process-\u003eread()) {\n            \\Log::info('TestProcess: read data', [$data]);\n            $process-\u003ewrite('TestProcess: ' . $data);\n        }\n    }\n    ```\n\n    ```php\n    // app/Http/Controllers/TestController.php\n    public function testProcessWrite()\n    {\n        /**@var \\Swoole\\Process[] $process */\n        $customProcesses = \\Hhxsv5\\LaravelS\\LaravelS::getCustomProcesses();\n        $process = $customProcesses['test'];\n        $process-\u003ewrite('TestController: write data' . time());\n        var_dump($process-\u003eread());\n    }\n    ```\n\n## 常用组件\n\n### Apollo\n\u003e 启动`LaravelS`时会获取`Apollo`配置并写入到`.env`文件，同时会启动自定义进程`apollo`用于监听配置变更，当配置发生变更时自动`reload`。\n\n1. 启用Apollo组件：启动参数加上`--enable-apollo`以及Apollo的配置参数。\n    \n    ```bash\n    php bin/laravels start --enable-apollo --apollo-server=http://127.0.0.1:8080 --apollo-app-id=LARAVEL-S-TEST\n    ```\n\n2. 配置热更新（可选的）。\n\n    ```php\n    // 修改文件 config/laravels.php\n    'processes' =\u003e Hhxsv5\\LaravelS\\Components\\Apollo\\Process::getDefinition(),\n    ```\n\n    ```php\n    // 当存在其他自定义进程配置时\n    'processes' =\u003e [\n        'test' =\u003e [\n            'class'    =\u003e \\App\\Processes\\TestProcess::class,\n            'redirect' =\u003e false,\n            'pipe'     =\u003e 1,\n        ],\n        // ...\n    ] + Hhxsv5\\LaravelS\\Components\\Apollo\\Process::getDefinition(),\n    ```\n\n3. 可用的参数列表。\n\n| 参数名 | 描述 | 默认值 | 示例 |\n| -------- | -------- | -------- | -------- |\n| apollo-server | Apollo服务器URL | - | --apollo-server=http://127.0.0.1:8080 |\n| apollo-app-id | Apollo应用ID | - | --apollo-app-id=LARAVEL-S-TEST |\n| apollo-namespaces | APP所属的命名空间，可指定多个 | application | --apollo-namespaces=application --apollo-namespaces=env |\n| apollo-cluster | APP所属的集群 | default | --apollo-cluster=default |\n| apollo-client-ip | 当前实例的IP，还可用于灰度发布 | 本机内网IP | --apollo-client-ip=10.2.1.83 |\n| apollo-pull-timeout | 拉取配置时的超时时间（秒） | 5 | --apollo-pull-timeout=5 |\n| apollo-backup-old-env | 更新配置文件`.env`时是否备份老的配置文件 | false | --apollo-backup-old-env |\n\n### Prometheus\n\u003e 支持Prometheus监控与告警，Grafana可视化查看监控指标。请参考[Docker Compose](https://github.com/hhxsv5/docker)完成Prometheus与Grafana的环境搭建。\n\n1. 依赖[APCu \u003e= 5.0.0](https://pecl.php.net/package/apcu)扩展，请先安装它 `pecl install apcu`。\n\n2. 拷贝配置文件`prometheus.php`到你的工程`config`目录。视情况修改配置。\n    ```bash\n    # 项目根目录下执行命令\n    cp vendor/hhxsv5/laravel-s/config/prometheus.php config/\n    ```\n    如果是`Lumen`工程，还需要在`bootstrap/app.php`中手动加载配置`$app-\u003econfigure('prometheus');`。\n\n3. 配置`全局`中间件：`Hhxsv5\\LaravelS\\Components\\Prometheus\\RequestMiddleware::class`。为了尽可能精确地统计请求耗时，`RequestMiddleware`必须作为`第一个`全局中间件，需要放在其他中间件的前面。\n\n4. 注册 ServiceProvider：`Hhxsv5\\LaravelS\\Components\\Prometheus\\ServiceProvider::class`。\n\n5. 在`config/laravels.php`中配置 CollectorProcess 进程，用于定时采集 Swoole Worker/Task/Timer 进程的指标。\n    ```php\n    'processes' =\u003e Hhxsv5\\LaravelS\\Components\\Prometheus\\CollectorProcess::getDefinition(),\n    ```\n\n6. 创建路由，输出监控指标数据。\n    ```php\n    use Hhxsv5\\LaravelS\\Components\\Prometheus\\Exporter;\n\n    Route::get('/actuator/prometheus', function () {\n        $result = app(Exporter::class)-\u003erender();\n        return response($result, 200, ['Content-Type' =\u003e Exporter::REDNER_MIME_TYPE]);\n    });\n    ```\n\n7. 完成Prometheus的配置，启动Prometheus。\n    ```yml\n    global:\n      scrape_interval: 5s\n      scrape_timeout: 5s\n      evaluation_interval: 30s\n    scrape_configs:\n    - job_name: laravel-s-test\n      honor_timestamps: true\n      metrics_path: /actuator/prometheus\n      scheme: http\n      follow_redirects: true\n      static_configs:\n      - targets:\n        - 127.0.0.1:5200 # The ip and port of the monitored service\n    # Dynamically discovered using one of the supported service-discovery mechanisms\n    # https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config\n    # - job_name: laravels-eureka\n    #   honor_timestamps: true\n    #   scrape_interval: 5s\n    #   metrics_path: /actuator/prometheus\n    #   scheme: http\n    #   follow_redirects: true\n      # eureka_sd_configs:\n      # - server: http://127.0.0.1:8080/eureka\n      #   follow_redirects: true\n      #   refresh_interval: 5s\n    ```\n\n8. 启动Grafana，然后导入[panel json](https://github.com/hhxsv5/laravel-s/tree/PHP-8.x/grafana-dashboard.json)。\n\n\u003cimg src=\"https://raw.githubusercontent.com/hhxsv5/laravel-s/PHP-8.x/grafana-dashboard.png\" height=\"800px\" alt=\"Grafana Dashboard\"\u003e\n\n## 其他特性\n\n### 配置Swoole事件\n\n支持的事件列表：\n\n| 事件 | 需实现的接口 | 发生时机 |\n| -------- | -------- | -------- |\n| ServerStart | Hhxsv5\\LaravelS\\Swoole\\Events\\ServerStartInterface | 发生在Master进程启动时，`此事件中不应处理复杂的业务逻辑，只能做一些初始化的简单工作` |\n| ServerStop | Hhxsv5\\LaravelS\\Swoole\\Events\\ServerStopInterface | 发生在Server正常退出时，`此事件中不能使用异步或协程相关的API` |\n| WorkerStart | Hhxsv5\\LaravelS\\Swoole\\Events\\WorkerStartInterface | 发生在Worker/Task进程启动完成后 |\n| WorkerStop | Hhxsv5\\LaravelS\\Swoole\\Events\\WorkerStopInterface | 发生在Worker/Task进程正常退出后 |\n| WorkerError | Hhxsv5\\LaravelS\\Swoole\\Events\\WorkerErrorInterface | 发生在Worker/Task进程发生异常或致命错误时 |\n\n1.创建事件处理类，实现相应的接口。\n```php\nnamespace App\\Events;\nuse Hhxsv5\\LaravelS\\Swoole\\Events\\ServerStartInterface;\nuse Swoole\\Atomic;\nuse Swoole\\Http\\Server;\nclass ServerStartEvent implements ServerStartInterface\n{\n    public function __construct()\n    {\n    }\n    public function handle(Server $server)\n    {\n        // 初始化一个全局计数器(跨进程的可用)\n        $server-\u003eatomicCount = new Atomic(2233);\n\n        // 控制器中调用：app('swoole')-\u003eatomicCount-\u003eget();\n    }\n}\n```\n\n```php\nnamespace App\\Events;\nuse Hhxsv5\\LaravelS\\Swoole\\Events\\WorkerStartInterface;\nuse Swoole\\Http\\Server;\nclass WorkerStartEvent implements WorkerStartInterface\n{\n    public function __construct()\n    {\n    }\n    public function handle(Server $server, $workerId)\n    {\n        // 初始化一个数据库连接池对象\n        // DatabaseConnectionPool::init();\n    }\n}\n```\n\n2.配置。\n```php\n// 修改文件 config/laravels.php\n'event_handlers' =\u003e [\n    'ServerStart' =\u003e [\\App\\Events\\ServerStartEvent::class], // 按数组顺序触发事件\n    'WorkerStart' =\u003e [\\App\\Events\\WorkerStartEvent::class],\n],\n```\n\n### Serverless\n\n#### 阿里云函数计算\n\u003e [函数计算官方文档](https://help.aliyun.com/product/50980.html)。\n\n1.修改`bootstrap/app.php`，设置storage目录。因为项目目录只读，`/tmp`目录才可读写。\n\n```php\n$app-\u003euseStoragePath(env('APP_STORAGE_PATH', '/tmp/storage'));\n```\n\n2.创建Shell脚本`laravels_bootstrap`，并赋予`可执行权限`。\n\n```bash\n#!/usr/bin/env bash\nset +e\n\n# 创建storage相关目录\nmkdir -p /tmp/storage/app/public\nmkdir -p /tmp/storage/framework/cache\nmkdir -p /tmp/storage/framework/sessions\nmkdir -p /tmp/storage/framework/testing\nmkdir -p /tmp/storage/framework/views\nmkdir -p /tmp/storage/logs\n\n# 设置环境变量APP_STORAGE_PATH，请确保与.env的APP_STORAGE_PATH一样\nexport APP_STORAGE_PATH=/tmp/storage\n\n# Start LaravelS\nphp bin/laravels start\n```\n\n3.配置`template.xml`。\n\n```xml\nROSTemplateFormatVersion: '2015-09-01'\nTransform: 'Aliyun::Serverless-2018-04-03'\nResources:\n  laravel-s-demo:\n    Type: 'Aliyun::Serverless::Service'\n    Properties:\n      Description: 'LaravelS Demo for Serverless'\n    fc-laravel-s:\n      Type: 'Aliyun::Serverless::Function'\n      Properties:\n        Handler: laravels.handler\n        Runtime: custom\n        MemorySize: 512\n        Timeout: 30\n        CodeUri: ./\n        InstanceConcurrency: 10\n        EnvironmentVariables:\n          BOOTSTRAP_FILE: laravels_bootstrap\n\n```\n\n## 注意事项\n\n### 单例问题\n\n- 传统FPM下，单例模式的对象的生命周期仅在每次请求中，请求开始=\u003e实例化单例=\u003e请求结束后=\u003e单例对象资源回收。\n\n- Swoole Server下，所有单例对象会常驻于内存，这个时候单例对象的生命周期与FPM不同，请求开始=\u003e实例化单例=\u003e请求结束=\u003e单例对象依旧保留，需要开发者自己维护单例的状态。\n\n- 常见的解决方案：\n\n    1. 写一个`XxxCleaner`清理器类来清理单例对象状态，此类需实现接口`Hhxsv5\\LaravelS\\Illuminate\\Cleaners\\CleanerInterface`，然后注册到`laravels.php`的`cleaners`中。\n    \n    2. 用一个`中间件`来`重置`单例对象的状态。\n\n    3. 如果是以`ServiceProvider`注册的单例对象，可添加该`ServiceProvider`到`laravels.php`的`register_providers`中，这样每次请求会重新注册该`ServiceProvider`，重新实例化单例对象，[参考](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/Settings-CN.md#register_providers)。\n\n### 清理器\n\u003e [设置清理器](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/Settings-CN.md#cleaners)。\n\n### 常见问题\n\u003e [常见问题](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/KnownIssues-CN.md)：一揽子的已知问题和解决方案。\n\n### 调试方式\n\n- 记录日志；如想要在控制台输出，可使用`stderr`，Log::channel('stderr')-\u003edebug('debug message')。\n\n- [Laravel Dump Server](https://github.com/beyondcode/laravel-dump-server)（Laravel 5.7已默认集成）。\n\n### 读取请求\n应通过`Illuminate\\Http\\Request`对象来读取请求信息，$_ENV是可读取的，$_SERVER是部分可读的，`不能使用`$_GET、$_POST、$_FILES、$_COOKIE、$_REQUEST、$_SESSION、$GLOBALS。\n\n```php\npublic function form(\\Illuminate\\Http\\Request $request)\n{\n    $name = $request-\u003einput('name');\n    $all = $request-\u003eall();\n    $sessionId = $request-\u003ecookie('sessionId');\n    $photo = $request-\u003efile('photo');\n    // 调用getContent()来获取原始的POST body，而不能用file_get_contents('php://input')\n    $rawContent = $request-\u003egetContent();\n    //...\n}\n```\n\n### 输出响应\n推荐通过返回`Illuminate\\Http\\Response`对象来响应请求，兼容echo、vardump()、print_r()，`不能使用`函数 dd()、exit()、die()、header()、setcookie()、http_response_code()。\n\n```php\npublic function json()\n{\n    return response()-\u003ejson(['time' =\u003e time()])-\u003eheader('header1', 'value1')-\u003ewithCookie('c1', 'v1');\n}\n```\n\n### 持久连接\n`单例的连接`将被常驻内存，建议开启`持久连接`，获得更好的性能。\n1. 数据库连接，连接断开后会自动重连\n\n```php\n// config/database.php\n'connections' =\u003e [\n    'my_conn' =\u003e [\n        'driver'    =\u003e 'mysql',\n        'host'      =\u003e env('DB_MY_CONN_HOST', 'localhost'),\n        'port'      =\u003e env('DB_MY_CONN_PORT', 3306),\n        'database'  =\u003e env('DB_MY_CONN_DATABASE', 'forge'),\n        'username'  =\u003e env('DB_MY_CONN_USERNAME', 'forge'),\n        'password'  =\u003e env('DB_MY_CONN_PASSWORD', ''),\n        'charset'   =\u003e 'utf8mb4',\n        'collation' =\u003e 'utf8mb4_unicode_ci',\n        'prefix'    =\u003e '',\n        'strict'    =\u003e false,\n        'options'   =\u003e [\n            // 开启持久连接\n            \\PDO::ATTR_PERSISTENT =\u003e true,\n        ],\n    ],\n],\n```\n\n2. Redis连接，连接断开后`不会立即`自动重连，会抛出一个关于连接断开的异常，下次会自动重连。需确保每次操作Redis前正确的`SELECT DB`。\n\n```php\n// config/database.php\n'redis' =\u003e [\n    'client' =\u003e env('REDIS_CLIENT', 'phpredis'), // 推荐使用phpredis，以获得更好的性能\n    'default' =\u003e [\n        'host'       =\u003e env('REDIS_HOST', 'localhost'),\n        'password'   =\u003e env('REDIS_PASSWORD', null),\n        'port'       =\u003e env('REDIS_PORT', 6379),\n        'database'   =\u003e 0,\n        'persistent' =\u003e true, // 开启持久连接\n    ],\n],\n```\n\n### 关于内存泄露\n\n- 避免使用全局变量，如一定要，请手动清理或重置。\n\n- 无限追加元素到全局变量、静态变量、单例，将导致内存溢出。\n\n    ```php\n    class Test\n    {\n        public static $array = [];\n        public static $string = '';\n    }\n\n    // 某控制器\n    public function test(Request $req)\n    {\n        // 内存溢出\n        Test::$array[] = $req-\u003einput('param1');\n        Test::$string .= $req-\u003einput('param2');\n    }\n    ```\n\n- 内存泄露的检测方法\n\n    1. 修改`config/laravels.php`：`worker_num=1, max_request=1000000`，测试完成后记得改回去；\n\n    2. 增加路由`/debug-memory-leak`，不设置任何`路由中间件`，用于观察`Worker`进程的内存变化情况；\n\n    ```php\n    Route::get('/debug-memory-leak', function () {\n        global $previous;\n        $current = memory_get_usage();\n        $stats = [\n            'prev_mem' =\u003e $previous,\n            'curr_mem' =\u003e $current,\n            'diff_mem' =\u003e $current - $previous,\n        ];\n        $previous = $current;\n        return $stats;\n    });\n    ```\n\n    3. 启动`LaravelS`，请求`/debug-memory-leak`，直到`diff_mem`小于或等于零；如果`diff_mem`一直大于零，说明`全局中间件`或`Laravel框架`可能存在内存泄露；\n    \n    4. 完成`步骤3`后，`交替`请求业务路由与`/debug-memory-leak`（建议使用`ab`/`wrk`对业务路由进行大量的请求），刚开始出现的内存增涨是正常现象。业务路由经过大量请求后，如果`diff_mem`一直大于零，并且`curr_mem`持续增大，则大概率存在内存泄露；如果`curr_mem`始终在一定范围内变化，没有持续变大，则大概率不存在内存泄露。\n\n    5. 如果始终没法解决，[max_request](https://wiki.swoole.com/#/server/setting?id=max_request)是最后兜底的方案。\n\n\n### Linux内核参数调整\n\u003e [Linux内核参数调整](https://wiki.swoole.com/#/other/sysctl?id=%e5%86%85%e6%a0%b8%e5%8f%82%e6%95%b0%e8%b0%83%e6%95%b4)\n\n### 压力测试\n\u003e [压力测试](https://wiki.swoole.com/wiki/page/62.html)\n\n## 用户与案例\n\n- `特别赞助商` [果酱社区](https://guojiang.club/?utm_source=laravels\u0026utm_campaign=sponsor)：果酱社区是一个专业的会员制社区，这里提供高质量的技术资料，\n专注程序员成长，创业孵化，提供优秀且稳定的开源产品。\n    \n    \u003cimg src=\"https://user-images.githubusercontent.com/7278743/72538652-19bad000-38b9-11ea-844a-8f66a2c76185.jpeg\" height=\"300px\" alt=\"果酱社区\"\u003e\n\n- [KuCoin](https://www.kucoin.com/r/rf/QBSADJ41)：TOP5的加密货币交易所。\n\n- [医联](https://www.medlinker.com/)：WEB站、M站、APP、小程序的账户体系服务。\n    \n    \u003cimg src=\"https://user-images.githubusercontent.com/7278743/46649457-af05e980-cbcb-11e8-94a1-b13d743d33fd.png\" height=\"300px\" alt=\"医联\"\u003e\n\n- [ITOK在线客服平台](http://demo.topitsm.com)：用户IT工单的处理跟踪及在线实时沟通。\n    \n    \u003cimg src=\"https://user-images.githubusercontent.com/7278743/46649548-10c65380-cbcc-11e8-81e6-f4a8dca2eb2c.png\" height=\"300px\" alt=\"ITOK在线客服平台\"\u003e\n\n- [盟呱呱](http://mgg.yamecent.com)\n    \n    \u003cimg src=\"https://user-images.githubusercontent.com/7278743/46648932-b3310780-cbc9-11e8-971e-ca26e3378507.png\" height=\"300px\" alt=\"盟呱呱\"\u003e\n\n- [WookTeam](https://github.com/kuaifan/wookteam)：WookTeam是一款轻量级的在线团队协作工具，提供各类文档工具、在线思维导图、在线流程图、项目管理、任务分发，知识库管理等工具。\n\n- 微信公众号-广州塔：活动、商城\n    \n    \u003cimg src=\"https://user-images.githubusercontent.com/7278743/46649832-1a9c8680-cbcd-11e8-902e-978fa644f4d9.png\" height=\"300px\" alt=\"广州塔\"\u003e\n\n- 企鹅游戏盒子、明星新势力、以及小程序广告服务\n    \n    \u003cimg src=\"https://user-images.githubusercontent.com/7278743/46649296-2c7d2a00-cbcb-11e8-94d3-bc12fc9566d6.jpg\" height=\"300px\" alt=\"企鹅游戏盒子\"\u003e\n\n- 小程序-修机匠手机上门维修服务：手机维修服务，提供上门服务，支持在线维修。\n    \n    \u003cimg src=\"https://user-images.githubusercontent.com/7278743/46941544-eda11580-d09d-11e8-8c3a-16c567655277.png\" height=\"300px\" alt=\"修机匠手机上门维修服务\"\u003e\n\n- 亿健APP\n\n## 其他选择\n\n- [swooletw/laravel-swoole](https://github.com/swooletw/laravel-swoole)\n\n## 赞助\n\u003e 您的支持是我们坚持的最大动力。\n\n\u003cimg src=\"https://raw.githubusercontent.com/hhxsv5/laravel-s/PHP-8.x/sponsor.png\" height=\"300px\" alt=\"赞助\"\u003e\n\n### 感谢\n\n| 支持者 | 金额(元) |\n| --- | --- |\n| *思勇 | 18.88 |\n| *德国 | 18.88 |\n| 魂之挽歌 | 100 |\n| 小南瓜 | 10.01 |\n| *丁智 | 16.66 |\n| 匿名 | 20 |\n| 匿名 | 20 |\n| *洋 Blues | 18.88 |\n| *钧泽 Panda | 10.24 |\n| *翔 海贼王路飞 | 12 |\n| *跃 Axiong | 10 |\n| 落伽 | 10 |\n| 很胖的胖子 | 15 |\n| 霹格软件 | 18.88 |\n| Bygones | 18.88 |\n| *春 Flymoo | 100 |\n| 异乡人 | 20 |\n| only丶妳 | 100 |\n| 月殇 | 18.88 |\n| Shmily | 20 |\n| *俊 | 20 |\n| *哲 | 20 |\n| Alex | 20 |\n| X | 20 |\n| *洋 | 20 |\n| *洋 | 20 |\n| *强 | 50 |\n| Anthony | 18.88 |\n| *官龙 | 100 |\n| 0o飞舞o0木木 *科 | 288 |\n| *勇 | 66.66 |\n| 果酱社区 | 1076 |\n| *乾 | 18.88 |\n| *奥 | 8.88 |\n| 双木 | 20 |\n\n## License\n\n[MIT](https://github.com/hhxsv5/laravel-s/blob/PHP-8.x/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhhxsv5%2Flaravel-s","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhhxsv5%2Flaravel-s","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhhxsv5%2Flaravel-s/lists"}