bingcool / swoolefy
swoolefy is a HV Framework based on swoole extension, for everyone use it easily!
Requires
- php: ^8.4
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- ext-openssl: *
- ext-pcntl: *
- ext-pdo: *
- ext-posix: *
- ext-redis: *
- ext-sockets: *
- ext-swoole: >=6.1
- doctrine/collections: ~3.0
- dragonmantank/cron-expression: ~3.6.0
- guzzlehttp/guzzle: ~7.9
- php-standard-library/php-standard-library: ~6.1
- psr/log: *
- symfony/console: ~7.4
- symfony/translation: ~7.4
- symfony/yaml: ~7.4
- vlucas/phpdotenv: ~5.6
- zircote/swagger-php: ~4.8.0
Requires (Dev)
- kwn/php-rdkafka-stubs: dev-master
- swoole/ide-helper: dev-master
- viest/php-ext-xlswriter-ide-helper: dev-master
- dev-master
- v6.2.1
- v6.2.0
- v6.1.2
- v6.1.1
- v6.1.0
- v6.0.5
- v6.0.4
- v6.0.3
- v6.0.2
- v6.0.1
- v6.0.0
- v5.1.12
- v5.1.11
- v5.1.10
- v5.1.9
- v5.1.8
- v5.1.7
- v5.1.6
- v5.1.5
- v5.1.4
- v5.1.3
- v5.1.2
- v5.1.1
- v5.1.0
- v5.0.22
- v5.0.21
- v5.0.20
- v5.0.19
- v5.0.18
- v5.0.17
- v5.0.16
- v5.0.15
- v5.0.14
- v5.0.13
- v5.0.12
- v5.0.11
- v5.0.10
- v5.0.9
- v5.0.8
- v5.0.7
- v5.0.6
- 5.0.5
- v5.0.4
- v5.0.3
- v5.0.2
- v5.0.1
- v5.0.0
- v4.9.12
- v4.9.11
- v4.9.10
- v4.9.9
- v4.9.8
- v4.9.7
- v4.9.6
- v4.9.5
- v4.9.3
- v4.9.1
- v4.9.0
- v4.8.23
- v4.8.22
- v4.8.21
- v4.8.20
- v4.8.19
- v4.8.18
- v4.8.17
- v4.8.16
- v4.8.15
- v4.8.14
- v4.8.13
- v4.8.12
- v4.8.11
- v4.8.10
- v4.8.9
- v4.8.8
- v4.8.7
- v4.8.6
- v4.8.5
- v4.8.4
- v4.8.3
- v4.8.2
- v4.8.1
- v4.8.0
- 4.5.0
- 4.3.1
- 4.3.0
- 4.2.3
- 4.2.2
- 4.2.1
- 4.2.0
- 4.1.12
- 4.1.2
- 4.1.1
- 4.1.0
- 4.0.8
- 4.0.7
- 4.0.6
- 4.0.5
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- 1.0.6
- 1.0.5
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- dev-swoolefy-6.x
- dev-swoolefy-php82-83
- dev-swoolefy-6.x-rename-library
- dev-swoolefy-4.8-lts
- dev-swoolefy-5.x
- dev-swoolefy1.x
This package is auto-updated.
Last update: 2026-06-04 17:13:32 UTC
README
______ _ _ _ _ _
/ ____| | | | _ _ _| _ _
| (__ __ __ ___ ___ | | ___ | | | | | |
\___ \ \ \ /\ / / / _ \ / _ \ | | / _ \ | |_ _ _ | | | |
____) | \ V V / | (_) | | (_) | | | | ___/ | _ _ _| | |_| |
|_____ / \_/\_/ \___/ \___/ |_| \___| | | \__, |
|_| | |
__ / |
|_ _ /
📑 导航
- 一、📖 简介
- 🎯 核心特性
- 🏛️ 架构设计
- 二、📦 版本选择
- 三、⚙️ 实现的功能特性
- 四、🔌 适配协程环境组件
- 五、📚 bingcool/library 组件库
- 六、📥 安装
- 七、📝 添加 cli.php 入口
- 八、📂 创建 App 项目
- 九、🚀 启动 http 应用
- 十、🌐 访问
- 十一、🧩 定义组件
- 十二、💡 使用组件
- 十三、⚙️ Protocol/conf.php
- 十四、🛣️ 路由系统
- 十五、⚡ 协程单例
- 十六、⚡ 协程并发
- 十七、🗄️ 数据库操作
- 十八、📦 SDK 自动生成
- 十九、📘 ApiDoc 自动生成
- 二十、☁️ Nacos 微服务集成
一、📖 简介
swoolefy是一个基于swoole实现的轻量级高性能的常驻内存型的协程级应用服务框架,
高度支持httpApi,websocket,udp服务器,以及基于tcp实现可扩展的rpc服务,worker多进程消费模型
同时支持composer包方式安装部署项目。基于实用主义设计出发,swoolefy抽象Event事件处理类,
实现与底层的回调的解耦,支持协程单例调度,同步|异步调用,全局事件注册,心跳检查,异步任务,多进程(池),连接池等,
内置log、session、mysql、pgsql、redis、mongodb、kafka、amqp、uuid、route midelware、cache、queue、rateLimit、traceId等常用组件等.
🎯 核心特性
- ⚡ 高性能: 基于 Swoole 协程,单机支持数万并发连接
- 🔧 易扩展: 自定义进程、进程池、连接池机制
- 🏗️ 多协议: HTTP/WebSocket/TCP/UDP/MQTT 统一架构
- 🎨 易用性: Laravel 风格的路由、中间件、ORM
- 🔄 热更新: 文件修改自动重启 Worker,无需停机 (开发环境)
- 👥 多进程管理:
- 守护进程 (Daemon): 常驻内存,自动拉起多个 Worker 进程,支持进程健康监控和动态扩缩容
- Cron 计划任务: 类似 Linux crontab,支持 local/fork/url 三种调度模式,定时执行业务逻辑
- ⚛️ 协程并发:
- goApp(): 一键创建协程单例,自动处理 DB/Redis/Curl 等组件的协程隔离
- Parallel: 限制最大并发数,防止瞬间创建大量协程拖垮下游服务
- GoWaitGroup: 类似 Go 语言的 WaitGroup,优雅的协程同步等待机制
- 📦 组件化:
- bingcool/library 大量常用协程组件库 @see https://github.com/bingcool/library
- ☁️ Nacos 微服务集成:
- 配置变更监听: 长轮询 Nacos 配置,拉取最新内容写入
APP_PATH/.env,自动执行restart --force使 Worker 加载新配置 - 服务注册: 应用实例注册到 Nacos 注册中心,支持心跳保活(
application.yaml→nacos.service_register) - 服务发现:
DiscoveryClient拉取健康实例,内置random/round_robin/weight负载均衡 - SDK 服务发现:
gen:sdk生成的 API 客户端在未传入 Guzzle Client 时,自动通过 Nacos 解析目标服务base_uri(serviceName在生成时从application.yaml注入)
- 配置变更监听: 长轮询 Nacos 配置,拉取最新内容写入
🏛️ 架构设计
进程模型
┌─────────────────────────────────────────────────────┐
│ Master Process (主进程) │
│ - 管理 Reactor 线程 │
│ - 接收并分发客户端连接 │
└──────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Master Process (主进程) │
│ - 管理 Reactor 线程 │
│ - 接收并分发客户端连接 │
└──────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Manager Process (管理进程) │
│ - 管理 Worker 进程池 │
│ - 管理 Task 进程池 │
│ - 管理自定义进程 (通过 addProcess 拉起) │
│ - 进程重启和监控 │
└──────────┬────────────────────┬─────────────────────────┘
│ │
├───────────┬────────┴──────────┐
│ │ │
┌──────▼──────┐ ┌──▼──────────┐ ┌──────▼──────────┐
│ Worker │ │ Task │ │ User Process │
│ Processes │ │ Processes │ │ (MainProcess) │
│ (业务处理) │ │ (异步任务) │ │ (管理进程) │
│ │ │ │ │ │
│ - onRequest │ │ - onTask │ │ 通过 MainManager │
│ - onConnect │ │ │ │ 拉起多个 Worker │
│ - onReceive │ │ │ │ │
│ │ │ │ │ - Cron 任务管理 │
│ 协程池/组件池 │ │ │ │ - Daemon 常驻 │
│ - DB 连接池 │ │ │ │ - 动态进程管理 │
│ - Redis 池 │ │ │ │ │
│ - Curl 池 │ │ │ │ run() -> start() │
└─────────────┘ └─────────────┘ └──────┬──────────┘
│
┌────────────────┼───────────────┐
│ │ │
┌──────▼─────┐ ┌──────▼─────┐ ┌──────▼─────┐
│ Cron │ │ Daemon │ │ Script │
│ Workers │ │ Workers │ │ Workers │
│ (定时任务) │ │ (常驻进程) │ │ (脚本) │
│ │ │ │ │ │
│ - 定时调度 │ │ - 消息消费 │ │ - 临时脚本 │
│ - 任务队列 │ │ - 数据处理 │ │ - 数据迁移 │
│ - URL请求 │ │ - 实时计算 │ │ - 修复工具 │
└────────────┘ └────────────┘ └────────────┘
进程层级说明:
- Master Process: 最高层级,管理 Reactor 线程和连接分发
- Manager Process: 第二层级,统一管理所有子进程
- Worker/Task/User Process: 第三层级,由 Manager 直接管理
- Cron/Daemon/Script Workers: 第四层级,由 User Process (MainProcess) 通过
MainManager::start()拉起
http请求处理流程
Client Request
↓
┌────────────────────────┐
│ Swoole HTTP Server │
│ (Reactor 线程接收) │
└───────────┬────────────┘
│
↓
┌────────────────────────┐
│ Worker Process │
│ (onRequest 回调) │
└───────────┬────────────┘
│
↓
┌────────────────────────┐
│ 1. App::__construct() │
│ - 加载配置 │
│ - 初始化协程 ID │
└───────────┬────────────┘
│
↓
┌────────────────────────┐
│ 2. App::run() │
│ - parseHeaders() │
│ - initCoreComponent()│
│ - Application::setApp()│ ← 绑定到协程上下文
│ - defer() │ ← 注册清理钩子
└───────────┬────────────┘
│
↓
┌────────────────────────┐
│ 3. HttpRoute::dispatch()│
│ - 加载路由配置 │
│ - 匹配路由 │
└───────────┬──────────── ┘
│
↓
┌────────────────────────────────┐
│ 4. 执行中间件 (Middleware) │
│ - beforeHandle (前置中间件) │
│ - 验证/鉴权/CORS 等 │
│ - 请求参数处理 │
└───────────┬────────────────────┘
│
↓
┌────────────────────────────────┐
│ 5. 调用控制器 Action │
│ - Controller::action() │
│ - 业务逻辑处理 │
└───────────┬────────────────────┘
│
↓
┌─────────────────────────────────────┐
│ 6. 执行业务 (Business Logic) │
│ │
│ ┌─────────────────────────────┐ │
│ │ goApp(function() { │ │
│ │ // 协程并发处理 │ │
│ │ - DB 查询 │ │
│ │ - Redis 操作 │ │
│ │ - HTTP 请求 │ │
│ │ - 文件 IO │ │
│ │ }) │ │
│ │ │ │
│ │ Parallel::run(50, $list, │ │
│ │ function($item) { │ │
│ │ // 限制并发数处理 │ │
│ │ } │ │
│ │ ) │ │
│ └─────────────────────────────┘ │
│ │
│ - 协程调度器自动切换 │
│ - IO 密集型任务异步执行 │
│ - CPU 继续执行其他协程 │
└───────────┬────────────────────────┘
│
↓
┌────────────────────────┐
│ 7. 后置中间件 │
│ - afterHandle │
│ - 响应格式化 │
│ - 日志记录 │
└───────────┬────────────┘
│
↓
┌────────────────────────┐
│ 8. App::end() │
│ - handleLog() │
│ - pushComponentPools()│ ← 归还连接池
│ - clearComponent() │
│ - response->end() │
└───────────┬────────────┘
│
↓
Client Response
二、📦 版本选择
6.x 版本 (推荐 - 最新稳定版)
最低要求:
- PHP >= 8.4
- Swoole >= 6.1 (推荐使用 Swoole 6.x 最新版本)
4.9 LTS 版本 (长期维护版)
最低要求:
- PHP 7.3 ~ 7.4
- Swoole 4.8.x (推荐 4.8.13+)
选择哪个版本?
1、如果确定项目是使用php81+的,那么直接选择 swoole > 6.1.x,推荐直接使用 swoole-6.2.0+ 以上最新版本更好 安装,然后选择 bingcool/swoolefy:^6.1 作为项目分支安装最新稳定版本
2、如果确定项目是使用 php7.3 ~ php7.4 的,那么选择 swoole-v4.8+ 版本来进行编译安装(不能直接使用 swoole-cli-v4.8+ 了, 因为其内置的是php8.1,与你的项目的php7不符合)
所有只能通过编译swoole源码的方式来生成swoole扩展,然后选择 bingcool/swoolefy:^4.9 作为项目分支稳定版本
3、依赖编译: ./configure --enable-openssl --enable-sockets --enable-swoole-curl --enable-swoole-pgsql --enable-swoole-stdext --enable-iouring
4、若不希望自己编译构建,也可以直接使用本目录下的Dockerfile来构建镜像:
// 构建镜像
docker build --no-cache -t swoolefy-php84-swoole62:v1 -f ./php84-swoole62-io-uring.Dockerfile .
// 启动容器(开发环境下 --security-opt seccomp=unconfined的作用是禁用这个默认配置,让容器内的进程可以使用所有系统调用比如io_uring)
// 生产环境下建议使用配置文件方式 --security-opt seccomp=./seccomp_profile.json
// @see https://github.com/moby/moby/blob/v28.3.3/profiles/seccomp/default.json
docker run -d -it --security-opt seccomp=unconfined -p 9501:9501 -p 9502:9502 -v /host_mnt/Users/macbook/Documents/wwwphp:/home/wwwroot --name=swoolefy-php84-v62 swoolefy-php84-swoole62:v1
三、⚙️ 实现的功能特性
基础特性
- 支持架手脚一键创建项目自动生成最小项目骨架
- 支持swagger一键生成api文档
- 支持分组路由, 路由中间件middleware, 前置路由组件, 后置路由组件middleware,多模块应用
- 支持扫描Router路由配置自动生成PHP SDK,自动提取 Request/Response DTO,生成类型安全的客户端SDK代码
- 支持按模块扫描Router路由配置自动生成open-api协议的swagger的API文档
- 支持自定义注册不同根命名空间,快速多项目部署
- 支持httpServer,实用轻量Api接口开发
- 支持多协议websocketServer、udpServer、mqttServer
- 支持基于tcp实现的rpc服务,开放式的系统接口,可自定义协议数据格式,并提供rpc-client协程组件
- 支持DI容器,组件IOC、配置化,Channel公共组件池
- 支持协程单例注册,协程上下文变量寄存
- 支持mysql、postgreSql、redis协程组件
- 支持全局logger组件,包括system log, runtime log, request log, sql log
- 支持opentelemetry的trace链路追踪组件
- 支持分布式锁组件
- 支持滑动窗口的流量速率组件
- 支持mysql协程连接池
- 支持redis协程池
- 支持curl协程池
- 支持protobuf buffer的数据接口结构验证,压缩传输等
- 支持异步务管理TaskManager
- 定时器管理TickManager
- 内存表管理TableManager
- 支持自定义进程管理ProcessManager,进程池管理PoolsManger
- 支持底层异常错误的所有日志捕捉,支持全局日志,包括debug、info、notice、warning、error等级
- 支持自定义进程的redis,rabbitmq,kafka的订阅发布,消息队列等
- 支持热更新reload worker 监控以及更新
- 支持定时的系统信息采集,并以订阅发布,udp等方式收集至存贮端
- 支持命令行形式高度封装启动|停止控制的脚本,简单命令即可管理整个框架, 并对外提供控制启动|停止|重启|查看状态的api接口,可开发成可视化控制页面
高级特性
-
支持cron计划任务模式. 类似crontab,支持local|fork|remote url三种方式
支持方式 说明 local 自定义进程内定时执行代码 fork 自定义进程定时拉起一个新的进程,由新的进程去执行任务,可异步,类似laravel的schedule计划任务 url 自定义进程定时发起远程url请求,可设置callback回调处理结果 -
支持daemon模式.worker下后台daemon模式的多进程协程消费模型,包括进程自动拉起,进程数动态调整,进程健康状态监控
-
支持console终端脚本模式. 跑完脚本自动退出,可用于修复数据、数据迁移等临时脚本功能
-
Nacos 配置中心与服务治理(
Swoolefy\Support\Nacos,详见 src/Support/Nacos/README.md)创建应用(
php cli.php create App)时会自动生成APP_PATH/application.yaml模板;Nacos 连接信息放在APP_PATH/nacos.yaml。文件 说明 APP_PATH/nacos.yamlNacos 服务器连接(host、port、data_id、username/password 等) APP_PATH/application.yaml应用行为: service_register、discovery_service_client、monitor_config_change能力 说明 主要类 配置变更监听 长轮询配置 → 写入 .env→ 后台cli.php restart {App} --force=1NacosMonitor、ConfigWatcher(见 Monitor/README.md)服务注册 注册实例到 Nacos 并定时心跳 ServiceRegister服务发现 实例列表缓存 + 负载均衡选节点 DiscoveryClient、DiscoveryConfig、LoadBalancerFactorySDK 服务发现 gen:sdk生成客户端,未传 Guzzle 时自动 Nacos 发现base_uriBaseClientApi、SdkNacosServiceDiscovery(sdk自动生成)自定义进程示例(
Event.php中注册):// 配置变更监听重启服务 ProcessManager::getInstance()->addProcess( 'nacos-config-change-reload', \App\Process\NacosProcess\NacosConfigReload::class, true, [], null, true, ); // 服务注册 + 心跳 ProcessManager::getInstance()->addProcess( 'nacos-service-register', \App\Process\NacosProcess\NacosServiceRegister::class, true, [], null, true, );
服务发现代码示例:
use Swoolefy\Support\Nacos\Discovery\DiscoveryClient; use Swoolefy\Support\Nacos\NacosConfig; $client = DiscoveryClient::create('my-service', NacosConfig::load()); $uri = $client->chooseUri();
四、🔌 适配协程环境组件
| 组件名称 | 安装 | 说明 |
|---|---|---|
| predis | composer require predis/predis:~1.1.7 | predis组件、或者Phpredis扩展 |
| mongodb | composer require mongodb/mongodb:~1.3 | mongodb组件,需要使用mongodb必须安装此组件 |
| rpc-client | composer require bingcool/rpc-client:dev-master | swoolefy的rpc客户端组件,当与rpc服务端通信时,需要安装此组件,支持在php-fpm中使用 |
| cron-expression | composer require dragonmantank/cron-expression:~3.3.0 | crontab计划任务组件,类似Linux的crobtab |
| redis lock | composer require malkusch/lock | Redis锁组件 |
| amqp | composer require php-amqplib/php-amqplib:~3.7.0 | amqp php原生实现amqp协议客户端 |
| ffmpeg | composer require php-ffmpeg/php-ffmpeg:~1.4.0 | php proc-open 调用ffmpeg处理音视频 |
| image | composer require intervention/image:~3.11.0 | php 图像处理组件 |
| validate | composer require vlucas/valitron | validate数据校验组件 |
| guzzlehttp | composer require guzzlehttp/guzzle:~7.9.0 | guzzlehttp 组件 |
| oauth 2.0 | composer require league/oauth2-server | oauth 2.0 授权认证组件 |
| php-standard-library | composer require php-standard-library/php-standard-library | php标准库(推荐) |
| bingcool/library | composer require bingcool/library | library组件库 |
五、📚 bingcool/library 是 swoolefy require 内置库,专为 swoole 协程实现的组件库
实现了包括:
- Db ORM Model 组件(支持mysql、 postSql、 sqlite、 Oracle)
- DB Query Builder 链式操作查询组件
- Kafka Producer Consumer组件
- Rabbitmq Queue组件
- Rabbitmq Delay Queue 死信延迟队列组件
- Redis Cache组件
- Redis Queue队列组件
- Redis Delay Queue延迟队列组件
- RedisLock锁组件
- RateLimit限流组件
- Redis Public Subscribe组件
- Db、Redis、 Curl协程连接池组件
- UUid 分布式自增id组件
- OpenTelemetry 链路追踪组件
- nacos 服务注册、服务发现、服务配置组件
- Curl基础组件
- Jwt 组件
- Validate 组件
- Encrypt 加密解密组件
- Captcha 验证码组件
- Translation 国际化(I18N)
github: https://github.com/bingcool/library
六、📥 安装
1、先配置环境变量(必须设置)
// 独立物理机或者云主机配置系统环境变量
vi /etc/profile
在/etc/profile末尾添加一行,标识环境变量,下面是支持的4个环境,框架将通过这个环境变量区分环境,加载不同的配置
export SWOOLEFY_CLI_ENV='dev' // 开发环境
export SWOOLEFY_CLI_ENV='test' // 测试环境
export SWOOLEFY_CLI_ENV='gra' // 灰度环境
export SWOOLEFY_CLI_ENV='prd' // 生产环境
// 最后是配置生效
source /etc/profile
// 如果是通过dockerfile 创建容器的, 可以根据不同环境生成的内置环境变量不同镜像,每个不同的环境镜像可以用在不同环境,代码将通过这个环境变量区分环境,加载不同的配置
ENV SWOOLEFY_CLI_ENV=dev
2、创建项目
// 下载代码到到你的自定义目录,这里定义为myproject, 新建composer.json
{
"name": "project/order-service",
"description": "description",
"minimum-stability": "dev",
"prefer-stable": true,
"license": "proprietary",
"require": {
"bingcool/swoolefy": "~6.2",
"bingcool/library": "dev-library-6.x"
}
}
// 终端执行安装
composer install
七、📝 添加项目入口启动文件 cli.php, 并定义你的项目目录,命名为 App
<?php // 在myproject目录下添加cli.php, 这个是启动项目的入口文件 date_default_timezone_set('Asia/Shanghai'); include __DIR__.'/vendor/autoload.php'; $appName = ucfirst($_SERVER['argv'][2]); // 定义app name define('APP_NAME', $appName); // 启动目录 defined('START_DIR_ROOT') or define('START_DIR_ROOT', __DIR__); // composer安装时,必须定义成如下路径 defined('SRC_DIR_ROOT') or define('SRC_DIR_ROOT', __DIR__."/vendor/bingcool/swoolefy/src"); // 应用父目录 defined('ROOT_PATH') or define('ROOT_PATH',__DIR__); // 应用目录 defined('APP_PATH') or define('APP_PATH',__DIR__.'/'.$appName); registerNamespace(APP_PATH); // 你的项目命名为App,对应协议为http协议服务器,支持多个项目的,只需要在这里添加好项目名称与对应的协议即可 define('APP_META_ARR', [ 'Test' => [ 'protocol' => 'http', 'worker_port' => 9501, ], 'App' => [ 'protocol' => 'http', 'worker_port' => 9502, ] ]); // 定义服务端口 define('WORKER_PORT', APP_META_ARR[$appName]['worker_port']); define('IS_WORKER_SERVICE', 0); define('IS_DAEMON_SERVICE', 0); define('IS_SCRIPT_SERVICE', 0); define('IS_CRON_SERVICE', 0); define('PHP_BIN_FILE','/usr/bin/php'); define('WORKER_START_SCRIPT_FILE', str_contains($_SERVER['SCRIPT_FILENAME'], $_SERVER['PWD']) ? $_SERVER['SCRIPT_FILENAME'] : $_SERVER['PWD'].'/'.$_SERVER['SCRIPT_FILENAME']); define('WORKER_SERVICE_NAME', makeServerName($appName)); define('WORKER_PID_FILE_ROOT', '/tmp/workerfy/log/'.WORKER_SERVICE_NAME); define('WORKER_CTL_LOG_FILE',WORKER_PID_FILE_ROOT.'/ctl.log'); define('SERVER_START_LOG_JSON_FILE', WORKER_PID_FILE_ROOT.'/start.json'); // 当使用nacos管理配置时,启动获取最新配置保存到.env // $beforeFunc = function () { // \Swoolefy\Support\Nacos\NacosFactory::fetchConfigToEnv(APP_PATH . '/nacos.yaml'); //}; include dirname(SRC_DIR_ROOT).'/swoolefy';
八、📂 执行创建你定义的 App 项目
// 你定义的项目目录是App, 在myproject目录下执行下面命令行
php cli.php create App
或者
swoole-cli cli.php create App
// 执行完上面命令行后,将会自动生成 App 项目目录以及内部子目录
myproject
|—— App // 应用项目目录
| |── Config // 应用配置
| | |__ component // 协程单例组件
| | |—— database.php // 数据库相关组件
| | |—— log.php // 日志相关组件
| | |—— cache.php // 缓存组件,可以继续添加其他组件,命名自由
| │ ├── dc.php // 环境配置项
| │ └── constants.php
| | |—— app.php // 应用层配置
| |
| ├── Controller
| │ └── IndexController.php // 控制器层
| ├── Model
| │ └── ClientModel.php // 数据模型层
| ├── Module // 模块层
| ├── Protocol // 协议配置
| │ ├── conf.php // 全局配置
| │
| ├── Router
| │ └── api.php // 路由文件,不同模块定义不同文件即可
| |—— Storage
| | |—— Crontab // cron service 的调度日志
| | |—— Logs // 日志文件目录
| | |—— Sql // sql 日志目录
| |—— Scripts
| | |—— Kernel.php // 计划任务定义
| |__ .env // 自动生成环境变量文件
| │—— autoloader.php // 自定义项目自动加载
| |—— Event.php // 事件实现类
| |—— HttpServer.php // http server
|
|——— cli.php // http应用启动入口文件
|——— cron.php // 定时 worker 任务的多进程启动入口文件
|——— daemon.php // 守护进程 worker 的多进程启动入口文件
|——— script.php // 脚本启动入口文件
|——— swag.php // 生成 swagger 接口文档入口文件
九、🚀 启动 http应用项目
http应用启动命令行
// 终端启动 ctl+c 停止进程
php cli.php start App
或者
swoole-cli cli.php start App
// 守护进程方式启动,添加-D参数控制
php cli.php start App --daemon=1
或者
swooole-cli cli.php start App --daemon=1
// 停止进程
php cli.php stop App
或者
swooole-cli cli.php stop App --force=1
// 查看进程状态
swooole-cli cli.php status App
// 完全重启服务
php cli.php restart App
或者
swooole-cli cli.php restart App
启动Cron定时计划任务服务
// 创建生成Cron定时计划任务服务,默认生成WorkerCron目录
php script.php start App --c=gen:cron:service
// 启动Cron服务, CTRL+C 停止进程
php cron.php start App
// --daemon=1 以守护进程启动
php cron.php start App --daemon=1
// 重启Cron服务
php cron.php restart App
// 停止Cron服务,终端交互询问需要输入`yes` or `no` 再次确认是否需要停止服务
php cron.php stop App
// --force=1 强制停止Cron服务,不询问直接停止服务
php cron.php stop App --force=1
启动Daemon常驻进程服务
// 创建生成Daemon常驻进程消费服务,默认生成WorkerDaemon目录
php script.php start App --c=gen:daemon:service
// 启动Daemon服务,CTRL+C 停止进程
php daemon.php start App
// --daemon=1 以守护进程启动
php daemon.php start App --daemon=1
// 重启Daemon服务
php daemon.php restart App
// 停止Daemon服务, 终端交互询问是否需要输入`yes` or `no` 再次确认是否需要停止服务
php daemon.php stop App
// --force=1 强制停止Daemon服务,不询问直接停止服务
php daemon.php stop App --force=1
十、🌐 访问
默认端口是9502,可以通过 http://localhost:9502 访问默认控制器
<?php namespace App\Controller; use Swoolefy\Core\Application; use Swoolefy\Core\Controller\BController; // 默认生成的IndexController class IndexController extends BController { public function index() { // 最简单的协程单例,goApp()即可创建一个协程,在单例中的db,redis等其他注册的组件都是单例的,不同协程单例相互隔离 goApp(function() { var_dump('this is a coroutine single app test'); }); Application::getApp()->response->write('<h1>Hello, Welcome to Swoolefy Framework! <h1>'); } }
至此一个最简单的http的服务就创建完成了,更多例子请参考项目下Test的demo
十一、🧩 定义组件
1、应用层配置文件:Config/app.php
<?php return [ // db|redis连接池 'component_pools' => [ // 取components的`DB`组件名称相对应 'db' => [ 'max_pool_num' => 5, // db实例数 'max_push_timeout' => 2, // db实例进入channel池最长等待时间,单位s 'max_pop_timeout' => 1, // db实例出channel池最长等待时间,单位s.在规定时间内获取不到db对象,将降级为实时创建db实例 'max_life_timeout' => 10, // db实例的有效期,单位s.过期后将被掉弃,重新创建新DB实例 'enable_tick_clear_pool' => 0 // 是否每分钟定时清空pool,防止长时间一直占用链接,max_pool_num设置很大的时候需要设置,否则不需要设置 ], // 取components的`redis`组件名称相对应 'redis' => [ 'max_pool_num' => 5, 'max_push_timeout' => 2, 'max_pop_timeout' => 1, 'max_life_timeout' => 10, 'enable_tick_clear_pool' => 0 // 是否每分钟定时清空pool,防止长时间一直占用链接,max_pool_num设置很大的时候需要设置,否则不需要设置 ] ], // default_db 'default_db' => 'db', // 加载组件配置 'components' => \Swoolefy\Core\SystemEnv::loadComponent() // 其他配置 ...... ]
2、组件Component.php
<?php $dc = \Swoolefy\Core\SystemEnv::loadDcEnv(); return [ // 用户行为记录的日志 'log' => function($name) { $logger = new Log($name); $logger->setChannel('application'); if(SystemEnv::isDaemonService()) { $logFilePath = LOG_PATH.'/daemon/info.log'; }else if (SystemEnv::isScriptService()) { $logFilePath = LOG_PATH.'/script/info.log'; }else if (SystemEnv::isCronService()) { $logFilePath = LOG_PATH.'/cron/info.log'; } else { $logFilePath = LOG_PATH.'/cli/info.log'; } $logger->setLogFilePath($logFilePath); return $logger; }, // 用户行为记录错误日志 'error_log' => function($name) { $logger = new Log($name); $logger->setChannel('application'); if(SystemEnv::isDaemonService()) { $logFilePath = LOG_PATH.'/daemon/error.log'; }else if (SystemEnv::isScriptService()) { $logFilePath = LOG_PATH.'/script/error.log'; }else if (SystemEnv::isCronService()) { $logFilePath = LOG_PATH.'/cron/error.log'; } else { $logFilePath = LOG_PATH.'/cli/error.log'; } $logger->setLogFilePath($logFilePath); return $logger; }, // 系统捕捉抛出异常错误日志 'system_error_log' => function($name) { $logger = new \Swoolefy\Util\Log($name); $logger->setChannel('application'); if(SystemEnv::isDaemonService()) { $logFilePath = LOG_PATH.'/daemon/system_error.log'; }else if (SystemEnv::isScriptService()) { $logFilePath = LOG_PATH.'/script/system_error.log'; }else if (SystemEnv::isCronService()) { $logFilePath = LOG_PATH.'/cron/system_error.log'; } else { $logFilePath = LOG_PATH.'/cli/system_error.log'; } $logger->setLogFilePath($logFilePath); return $logger; } // Redis Cache 'redis' => function() use($dc) { $redis = new \Swoolefy\Library\Redis\Redis(); $redis->connect($dc['redis']['host'], $dc['redis']['port']); return $redis; }, // Predis Cache 'predis' => function() use($dc) { $predis = new \Swoolefy\Library\Redis\predis([ 'scheme' => $dc['predis']['scheme'], 'host' => $dc['predis']['host'], 'port' => $dc['predis']['port'], ]); return $predis; }
十二、💡 使用组件
use Swoolefy\Core\Application; class TestController extends BController { /** * 控制器 */ public function test() { // 获取组件,组件就是配置回调中定义的组件 $redis = Application::getApp()->redis; //或者通过get指明组件名获取(推荐) // $redis = Application::getApp()->get('redis'); // swoole hook 特性,这个过程会发生协程调度 $redis->set('name', swoolefy); // predis组件 $predis = Application::getApp()->predis; //或者通过get指明组件名获取(推荐) // $predis = Application::getApp()->get('predis'); // 这个过程会发生协程调度 $predis->set('predis','this is a predis instance'); $predis->get('predis'); // PDO的mysql实例,这个过程会发生协程调度 $db = Application::getApp()->db; // 或者 // $mysql = Application::getApp()->get('db'); // 添加一条数据 $sql = "INSERT INTO `user` (`username` ,`sex`) VALUES (:username, :sex)"; $numRows = $db->createCommand($sql)->insert([ ':username'=>'bingcool-test', ':sex' => 1 ]); var_dump($numRows) // DB Query查询 $db = Application::getApp()->db; $db->newQuery()->table('user')->where([ 'user_id' => 10000 ])->select() // DB 插入单条数据 $data = [ 'username'=>'bingcool-test', 'sex' => 1 ] $db = Application::getApp()->db; $db->newQuery()->table('user')->insert($data); // DB 插入多条数据 $data = [ [ 'username'=>'bingcool-test1111', 'sex' => 1 ], [ 'username'=>'bingcool-test2222', 'sex' => 1 ] ] $db = Application::getApp()->db; $db->newQuery()->table('user')->insertAll($data); // 查询 $result = $db->createCommand('select * from user where id>:id')->queryOne([':id'=>100]); var_dump($result); // pg实例 $pg = Application::getApp()->get('pg'); // 添加一条数据 $sql = "INSERT INTO `user` (username ,sex) VALUES (:username, :sex)"; $pg->createCommand($sql)->insert([ ':username'=>'bingcool-test', ':sex' => 1 ]); } }
十三、⚙️ 默认协议层全局配置文件 Protocol/conf.php
配置项
开发者可以根据实际使用适当调整
$dc = \Swoolefy\Core\SystemEnv::loadDcEnv(); return [ // 应用层配置 'app_conf' => \Swoolefy\Core\SystemEnv::loadAppConf(), // 应用层配置 'application_index' => '', 'event_handler' => \Test\Event::class, 'exception_handler' => \Test\Exception\ExceptionHandle::class, 'response_formatter' => \Swoolefy\Core\ResponseFormatter::class, 'master_process_name' => 'php-swoolefy-http-master', 'manager_process_name' => 'php-swoolefy-http-manager', 'worker_process_name' => 'php-swoolefy-http-worker', 'www_user' => '', 'host' => '0.0.0.0', 'port' => '9501', 'time_zone' => 'PRC', 'swoole_process_mode' => SWOOLE_PROCESS, 'include_files' => [], 'runtime_enable_coroutine' => true, // swoole setting 'setting' => [ 'admin_server' => '0.0.0.0:9503', 'reactor_num' => 1, 'worker_num' => 4, 'max_request' => 10000, 'task_worker_num' => 2, 'task_tmpdir' => '/dev/shm', 'daemonize' => 0, 'dispatch_mode' => 3, 'reload_async' => true, 'enable_coroutine' => 1, 'task_enable_coroutine' => 1, // 压缩 'http_compression' => true, // $level 压缩等级,范围是 1-9,等级越高压缩后的尺寸越小,但 CPU 消耗更多。默认为 1, 最高为 9 'http_compression_level' => 1, 'log_file' => \Swoolefy\Core\SystemEnv::loadLogFile('/tmp/' . APP_NAME . '/swoole_log.txt'), 'pid_file' => \Swoolefy\Core\SystemEnv::loadPidFile('/data/' . APP_NAME . '/log/server.pid'), ], 'coroutine_setting' => [ 'max_coroutine' => 50000 ], // 是否内存化线上实时任务 'enable_table_tick_task' => true, // 内存表定义 'table' => [ 'table_process' => [ // 内存表建立的行数,取决于建立的process进程数,最小值64 'size' => 64, // 定义字段 'fields'=> [ ['pid','int', 10], ['process_name','string', 56], ] ] ], // 依赖于EnableSysCollector = true,否则设置没有意义,不生效 'enable_pv_collector' => false, 'enable_sys_collector' => true, 'sys_collector_conf' => [ 'type' => SWOOLEFY_SYS_COLLECTOR_UDP, 'host' => '127.0.0.1', 'port' => 9504, 'from_service' => 'http-app', 'target_service' => 'collectorService/system', 'event' => 'collect', 'tick_time' => 2, 'callback' => function () { $sysCollector = new \Swoolefy\Core\SysCollector\SysCollector(); return $sysCollector->test(); } ], // 热更新 'reload_conf'=> [ 'enable_reload' => false, // 是否启用热文件更新功能 'after_seconds' => 3, // 检测到只要有文件更新,3s内不在检测,等待重启既可 'monitor_path' => APP_PATH, // 开发者自己定义目录 'reload_file_types' => ['.php', '.html', '.js'], 'ignore_dirs' => [], 'callback' => function () {} ] ];
十四、🛣️ 路由系统
支持类似 Laravel 的分组路由和中间件:
Router/api.php
<?php use Swoolefy\Http\Route; use Swoolefy\Http\RequestInput; // 直接路由-不分组 Route::get('/index/index', [ 'beforeHandle' => function(RequestInput $requestInput) { Context::set('name', 'bingcool'); $name = $requestInput->getPostParams('name'); }, // 这里需要替换长对应的控制器命名空间 'dispatch_route' => [\Test\Controller\IndexController::class, 'index'], 'afterHandle' => function(RequestInput $requestInput) { }, 'afterHandle1' => function(RequestInput $requestInput) { }, ]); // 分组路由 Route::group([ // 路由前缀 'prefix' => 'api', // 路由中间件,多个按顺序执行 'middleware' => [ \Test\Middleware\Route\ValidLoginMiddleware::class, ] ], function () { Route::get('/', [ // 前置路由,闭包函数形式 'beforeHandle' => function(RequestInput $requestInput) { var_dump('beforeHandle'); }, // 前置路由,中间件类形式(推荐) 'beforeHandle2' => \Test\Middleware\Route\ValidLoginMiddleware::class, // 前置路由,中间件数组类形式(推荐) 'beforeHandle3' => [ \Test\Middleware\Route\ValidLoginMiddleware::class, \Test\Middleware\Route\ValidLoginMiddleware::class, ], // 控制器action 'dispatch_route' => [\Test\Controller\IndexController::class, 'index'], // 后置路由 'afterHandle1' => function(RequestInput $requestInput) { var_dump('afterHandle'); }, // 前置路由,中间件类形式(推荐) 'afterHandle2' => \Test\Middleware\Route\ValidLoginMiddleware::class, // 前置路由,中间件数组类形式(推荐) 'afterHandle3' => [ \Test\Middleware\Route\ValidLoginMiddleware::class, \Test\Middleware\Route\ValidLoginMiddleware::class ], ]); });
十五、⚡ 协程单例
协程单例
// 协程单例使用goApp直接调用创建, 每个协程的DB,redis,kafka,mq的socket对象相互隔离,互不影响,代码通用 goApp(function() { $db = Application::getApp()->get('db'); // 查询列表 $db->newQuery()->table('tbl_users')->where('id','>', 1)->field(['id', 'user_name'])->limit(0,10)->select(); // redis $redis = Application::getApp()->get('redis'); $redis->set('name','bingcool'); // 再开启一个协程单例 goApp(function() { // $db1与父级协程的$db完全隔离,不是同一个对象 $db1 = Application::getApp()->get('db'); }) })
协程隔离示意图:
协程 A (cid=1001) 协程 B (cid=1002) ↓ ↓ App Instance A App Instance B ↓ ↓ containers['db'] A containers['db'] B ↓ ↓ Redis Object A Redis Object B (独立 Socket 连接) (独立 Socket 连接)
十六、⚡ 协程并发
Parallel 并发限制器
use Swoolefy\Core\Coroutine\Parallel; // 场景:有 1000 个请求,限制每次并发 50 个 $parallel = new Parallel(50); for ($i = 0; $i < 1000; $i++) { $parallel->add(function() use ($i) { // 协程任务 $result = file_get_contents("http://api.example.com/data?id={$i}"); return json_decode($result, true); }, "key_{$i}"); } // 长等待10s获取结果 $results = $parallel->runWait(10.0); // 场景:少量的请求,通过add添加闭包 $parallel = new Parallel(); $parallel->add(function() { return file_get_contents("http://api.example.com/data"); }, "key1"); $parallel->add(function() { return file_get_contents("http://api.example.com/data"); }, "key2"); $parallel->add(function() { return file_get_contents("http://api.example.com/data"); }, "key3"); // 最长等待10s获取结果 $parallel->runWait(10.0)
Parallel::run 迭代并发
use Swoolefy\Core\Coroutine\Parallel; // 分批处理大数据集(无需等待数据返回) $list = range(1, 10000); Parallel::run( 100, // 每批 100 个协程 $list, // 数据数组 function($item) { // 处理每个元素 echo "Processing: {$item}\n"; }, 0.01 // 每批间隔 0.01 秒 );
GoWaitGroup
use Swoolefy\Core\Coroutine\GoWaitGroup; $wg = new GoWaitGroup(); for ($i = 0; $i < 10; $i++) { $wg->add(); goApp(function() use ($wg, $i) { try { // 并发任务 sleep(1); echo "Task {$i} done\n"; } finally { $wg->done(); } }); } $wg->wait(); // 等待所有任务完成
十七、🗄️ 数据库操作
$db = Application::getApp()->get('db'); // 插入单条数据 $db->newQuery()->table('tbl_users')->insert([ 'user_name' => '李四-'.rand(1,9999), 'sex' => 0, 'birthday' => '1991-07-08', 'phone' => 12345678 ]); // 批量插入 $db->newQuery()->table('tbl_users')->insertAll([ [ 'user_name' => '李四-'.rand(1,9999), 'sex' => 0, 'birthday' => '1991-07-08', 'phone' => 12345678 ], [ 'user_name' => '李四-'.rand(1,9999), 'sex' => 0, 'birthday' => '1991-07-08', 'phone' => 12345678 ] ]); // 查询列表 $db->newQuery()->table('tbl_users')->where('id','>', 1)->field(['id', 'user_name'])->limit(0,10)->select(); // 查询单条 $db->newQuery()->table('tbl_users')->where(['id', '=', 100])->field(['id', 'user_name'])->find(); .....还有很多其他链式操作
十八、📦 SDK 自动生成
swoolefy 提供了 SDK 自动生成工具,可以扫描项目的 Route 路由配置,自动提取 API 接口信息和 Request/Response DTO,生成类型安全的 PHP 客户端 SDK 代码。
核心特性
- 🔍 自动扫描路由: 解析
App/Router目录下的所有路由配置文件 - 📝 提取 DTO: 自动识别控制器方法中的 Request 和 Response 类型声明
- 🎯 类型安全: 生成的 SDK 包含完整的类型声明,IDE 智能提示友好
- 🔄 自动更新: 路由变更后重新生成即可,无需手动维护
- ☁️ Nacos 服务发现: 生成时从
application.yaml→nacos.service_register.service_name注入BaseClientApi::$serviceName;构造 API 客户端时未传入ClientInterface则委托框架DiscoveryClient自动解析base_uri(需APP_PATH或SDK_NACOS_CONFIG_DIR下存在nacos.yaml/application.yaml)
使用方法
# 基本用法:扫描默认 App/Router 目录,生成到 GenerateSdk 目录 php script.php start App --c=gen:sdk # 指定路由目录 php script.php start App --c=gen:sdk --router=App/Router # 指定输出目录 ProjectName 是具体项目名 OrderService php script.php start App --c=gen:sdk --out=../sdk-library/{ProjectName}
生成的 SDK 结构
GenerateSdk/
├── {ProjectName}/
│ └── {AppName}/
│ ├── Support/ # SDK 基础支撑类
│ │ ├── BaseClientApi.php # HTTP 客户端基类(可选 Nacos 发现)
│ │ ├── SdkNacosServiceDiscovery.php # 委托 DiscoveryClient 解析 base_uri
│ │ ├── SdkArrayDto.php
│ │ ├── SdkCovertProperty.php
│ │ └── ...
│ ├── Controller/
│ │ └── Client/
│ │ ├── IndexApi.php # 对应 IndexController
│ │ ├── UserApi.php # 对应 UserController
│ │ └── OrderApi.php # 对应 OrderController
│ ├── Request/ # Request DTO
│ │ └── UserLoginRequest.php
│ └── Response/ # Response DTO
│ └── UserListResponse.php
示例代码
使用生成的 API 客户端(固定地址 / 自定义 Client):
use GenerateSdk\MyProject\App\Controller\Client\UserApi; use GenerateSdk\MyProject\App\Request\UserLoginRequest; // 指定固定 base_uri(不走 Nacos 服务发现) $api = UserApi::makeService(null, 'http://127.0.0.1:9501'); // 自定义 Guzzle Client(完全手动) $client = new \GuzzleHttp\Client(['base_uri' => 'http://api.example.com/']); $api = new UserApi($client); // 使用 Request DTO 调用 $request = new UserLoginRequest(); $request->setUsername('admin'); $request->setPassword('123456'); /** @var UserLoginResponse $response */ $response = $api->login($request); var_dump($response->getToken()); var_dump($response->getUserId());
Nacos 服务发现 SDK 详细用法:
生成 SDK 时会从 application.yaml → nacos.service_register.service_name 注入 BaseClientApi::$serviceName。
调用 makeService() 且不传 $httpClient / $baseUri 时,自动通过 Nacos 发现可用实例并设置 Guzzle base_uri。
前置条件
| 项 | 说明 |
|---|---|
| 依赖 | SDK 包需 composer require bingcool/swoolefy(SdkNacosServiceDiscovery 委托 DiscoveryClient) |
| 配置文件 | 配置目录下需存在 nacos.yaml(Nacos 连接)与 application.yaml(discovery_service_client 等) |
| 配置目录优先级 | APP_PATH 常量 → 环境变量 SDK_NACOS_CONFIG_DIR → 当前工作目录 getcwd() |
use GenerateSdk\MyProject\Order\Client\OrderApi; use GenerateSdk\MyProject\Order\Request\CreateOrderRequest; // ① Nacos 服务发现(推荐) // serviceName 已在 gen:sdk 时注入,无需手写 base_uri $orderApi = OrderApi::makeService(); // ② GET / PUT / DELETE 等幂等请求:Connect/Request 异常默认重试 1 次(最大可通过 options 调到 3) $orderListReq = new OrderListRequest(); $orderListReq->setName('手机'); $orderListReq->setPage(1); $orderListReq->setSize(20); // 可选, $options = [ // 可选,可设置 headers、connect_retry_num、timeout 等 Guzzle 选项 'headers' => [ 'Authorization' => 'Bearer ' . $token, 'X-Request-Id' => uniqid('req_', true), ], 'connect_retry_num' => 2, // 可选,0~3;不传则 GET 默认 1 ]; $list = $orderApi->list($orderListReq, $options); // ③ POST 写操作:默认不重试(保证幂等由业务决定),需显式开启 $createReq = new CreateOrderRequest(); $createReq->setProductId(1001); $createReq->setQuantity(2); $result = $orderApi->create($createReq, [ // POST 默认 connect_retry_num=0;仅当业务确认接口幂等时才设置connect_retry_num时开启重试机制 'connect_retry_num' => 1, 'headers' => [ 'Authorization' => 'Bearer ' . $token, 'Idempotency-Key' => 'order-create-' . $createReq->getProductId(), //建议配合幂等键 ], ]); // ④ 固定 base_uri(不走 Nacos,指定GuzzleHttp\Client对象,失败时退避 200ms / 500ms / 1s 后重试同一地址) $client = new \GuzzleHttp\Client([ 'base_uri' => 'http://api.example.com/', ]); $orderApi = OrderApi::makeService($client); // ⑤ GET / PUT / DELETE 等幂等请求 $orderDetailReq = new OrderDetailRequest(); $orderListReq->setOrderId(1001); $detail = $orderApi->detail($orderDetailReq);
重试与日志说明
| 场景 | 行为 |
|---|---|
| Nacos 发现 + 重试 | RequestException 时重新 choose 节点后立即重试,无退避 |
| 固定地址 + 重试 | 同一 base_uri 退避 200ms → 500ms → 1s 后重试 |
| GET/HEAD/PUT/DELETE/OPTIONS | 默认重试 1 次 |
| POST/PATCH 等 | 默认 0 次,须 $options['connect_retry_num'] 显式指定 |
| 重试上限 | connect_retry_num 最大 3 |
| 日志 | 重试时写入 guzzle_curl 日志(CurlProxyHandler::buildLogChannel()),含失败/下一跳 IP:端口及异常信息 |
$options['connect_retry_num']为 SDK 专用参数,不会传给 Guzzle;自定义请求头通过$options['headers']传入,会与默认Content-Type: application/json合并。
控制器返回值类型声明最佳实践:
为了让 SDK 生成更准确,建议在控制器 action 方法中添加返回值类型声明:
<?php namespace App\Controller; use Swoolefy\Core\Controller\BController; use App\Request\UserLoginRequest; use App\Response\UserLoginResponse; class UserController extends BController { // ✅ 有返回对象时声明具体类型 public function login(UserLoginRequest $request): UserLoginResponse { return $response; } // ✅ 无返回值时使用 bool public function delete(int $id): bool { return true; } // ✅ 返回数组时使用 array public function list(): array { return ['total' => 100, 'list' => []]; } }
工作原理
- 扫描路由文件: 解析
App/Router/*.php中的所有路由定义 - 反射分析: 通过 PHP Reflection 分析控制器方法的参数和返回类型
- 提取 DTO: 识别
Test\*命名空间下的 Request/Response 类 - 生成代码:
- 复制 DTO 类到 SDK 目录,移除框架依赖
- 生成 API 客户端类,每个控制器对应一个
*Api.php文件 - 生成 Guzzle HTTP 客户端调用代码
- 类型转换: 自动处理 JSON 响应到 DTO 对象的转换
注意事项
- 控制器方法建议使用类型声明(Request/Response 类或标量类型)
- DTO 类应位于
App/Request、App/Response或App/Dto目录下 - 生成的 SDK 可在 PHP-FPM 或 CLI 中使用;启用 Nacos 服务发现时需依赖
bingcool/swoolefy(SdkNacosServiceDiscovery委托DiscoveryClient) - SDK 基于 Guzzle HTTP 客户端,需要安装
guzzlehttp/guzzle依赖 - Nacos 发现配置目录优先级:显式传入路径 → 常量
APP_PATH→ 环境变量SDK_NACOS_CONFIG_DIR→ 当前工作目录
十九、📘 ApiDoc 自动生成
swoolefy 提供了 ApiDoc 自动生成工具,可以扫描项目的 Route 路由配置,通过 dispatch_route 解析控制器和 action,再结合 Request/Response DTO、属性注解和类型声明生成符合 OpenAPI 3.0 协议的 YAML 文档。
核心特性
- 🔍 按模块生成文档: 扫描
App/Router目录,每个路由模块生成一个openapi-{module}.yaml - 📝 自动提取接口说明: action 上的
#[ApiOperation(...)]会作为接口summary/description - 📦 自动提取请求/响应结构: 通过反射读取 Request/Response DTO 的属性类型与嵌套对象
- ✅ 自动识别必填字段: 读取
#[ValidationRule(rule: 'required|...')]、require、must等规则 - 🧩 支持数组对象递归: 支持
ValidationRule(itemClass: XxxDto::class)和#[ArrayList(itemClass: XxxDto::class)] - 🏷️ 自动生成 tags: 默认使用路由文件名;若路由文件注释中存在
@api,则生成api内容(文件名)
使用方法
# 基本用法:扫描默认 App/Router 目录,生成到 swaggerui/apidoc 目录 php script.php start App --c=gen:apidoc # 指定路由目录 php script.php start App --c=gen:apidoc --router=App/Router # 指定输出目录(可以到具体模块) php script.php start App --c=gen:apidoc --router=App/Router/Order --out=swaggerui/apidoc
模块信息配置
可以在路由目录下添加 api_router_module.json,用于配置每个模块 OpenAPI 文档的 title 和 description。未匹配到模块配置时,默认使用 公共API。
{
"Product": {
"title": "用户产品中心",
"description": "用户产品模块"
},
"Order": {
"title": "用户订单中心",
"description": "用户订单模块"
}
}
路由文件 tags
生成器会读取路由文件名作为 OpenAPI 的 tags。例如 App/Router/Common/User.php 默认生成:
tags: - User
如果路由文件注释中存在第一个 @api:
/** * @api 用户模块API */
则生成:
tags: - 用户模块API(User)
生成的 ApiDoc 结构
swaggerui/apidoc/
├── openapi-common.yaml
├── openapi-product.yaml
├── openapi-order.yaml
└── openapi-user.yaml
注解示例
控制器 action 描述:
use Swoolefy\Annotation\ApiOperation; class UserController extends BController { #[ApiOperation( "Create user" )] public function create(UserCreateRequest $request): UserCreateResponse { $response = new UserCreateResponse(); $response->setUserId(1); // todo create user return $response; } }
请求 DTO 字段描述、必填和数组对象:
use Swoolefy\Annotation\ApiProperty; use Swoolefy\Annotation\Validation\ValidationRule; class UserCreateRequest extends BaseRequest { #[ApiProperty(description: "Username")] #[ValidationRule(rule: "required|string", message: "username is required")] protected string $username = ""; #[ApiProperty(description: "Role list")] #[ValidationRule(rule: "required|array", itemClass: RoleDto::class)] protected array $roles = []; }
工作原理
- 扫描路由文件: 解析
App/Router/**/*.php中包含dispatch_route的路由定义 - 按模块分组: 根据 Router 目录下的模块文件或子目录输出
openapi-{module}.yaml - 反射分析: 通过 PHP Reflection 读取控制器 action 参数与返回值类型
- 生成 Schema:
- 通过
#[ApiProperty]读取字段说明 - 通过
#[ValidationRule]判断字段是否必填和数组 item 类型 - 通过属性类型递归生成对象结构
- 通过
#[IntToString]/#[StringToInt]修正字段类型
- 通过
- 统一响应包装: 生成
code、msg、trace_id、data结构
注意事项
- 控制器 action 建议显式声明 Request 参数类型和 Response 返回值类型
- Request/Response/Dto 属性建议添加
#[ApiProperty(description: "...")] - 数组对象字段建议使用
ValidationRule(itemClass: XxxDto::class)或#[ArrayList(itemClass: XxxDto::class)] - 路由文件的
@api注释只读取第一个匹配项,用于拼接 OpenAPItags - 每次生成前会清理输出目录下旧的
openapi-*.yaml,避免遗留过期文档
二十、☁️ Nacos 微服务集成
框架内置 Nacos 配置监听、服务注册、服务发现,并与 gen:sdk 生成的 HTTP 客户端打通。实现位于 src/Support/Nacos/,应用侧参考 Test/nacos.yaml、Test/application.yaml 与 Test/Process/NacosProcess/。
配置文件
| 文件 | 内容 |
|---|---|
APP_PATH/nacos.yaml |
Nacos 服务器连接(host、port、data_id、鉴权等) |
APP_PATH/application.yaml |
nacos.service_register(本服务注册)、discovery_service_client(调用方发现)、monitor_config_change(配置监听) |
application.yaml 片段示例:
nacos: service_register: ip: 192.168.1.103 port: 9501 service_name: my-service heartbeat_interval: 10 discovery_service_client: load_balancer: random # random | round_robin | weight cache_ttl: 60 healthy_only: true monitor_config_change: listener_timeout_ms: 30000
配置变更监听(自动重启)
- 长轮询 Nacos 配置变更
- 拉取最新配置写入
APP_PATH/.env - 随机短暂延迟后执行
php cli.php restart {APP_NAME} --force=1,Worker 加载新环境变量
在 Event.php 注册自定义进程 NacosConfigReload,或调用 NacosMonitor::run()。详见 src/Support/Nacos/Monitor/README.md。
服务注册
ServiceRegister 读取 application.yaml → nacos.service_register,将当前实例(ip、port、service_name、weight 等)注册到 Nacos 并定时心跳。建议在自定义进程 NacosServiceRegister 中启动。
use Swoolefy\Support\Nacos\NacosConfig; use Swoolefy\Support\Nacos\ServiceRegister; $register = new ServiceRegister(NacosConfig::load(), $logger); $register->register();
服务发现
DiscoveryClient 读取 discovery_service_client 配置,拉取实例列表(支持缓存 TTL),通过负载均衡器选择节点:
use Swoolefy\Support\Nacos\Discovery\DiscoveryClient; $client = DiscoveryClient::create('my-service'); // 也可传 NacosConfig / DiscoveryConfig $instance = $client->choose(); $uri = $client->chooseUri();
与 gen:sdk 的关系
| 步骤 | 行为 |
|---|---|
| 生成时 | 读取 APP_PATH/application.yaml 的 service_register.service_name,写入 BaseClientApi::$serviceName |
| 运行时 | UserApi::make() 未传 Guzzle Client → SdkNacosServiceDiscovery → DiscoveryClient::chooseUri() → 设置 Guzzle base_uri |
php script.php start App --c=gen:sdk --router=App/Router --out=../generate-sdk-library/OrderService
更多 API 说明见 src/Support/Nacos/README.md。
License
MIT
Copyright (c) 2017-2026 zengbing huang