<本文介绍如何基于Swoft 2.0框架>
<构建共享API网关平台,探讨公益与技术,商业化的平衡之道。>
## 前言
做公益这件事,我走了好多年。中间有过委屈,有过难,甚至看着自己搭建的平台,明明能帮人,却因"不会吆喝"而少有人问津时,也会陷入迷茫。但每当想起乡镇里,有人为了赶一趟班车,天不亮就守在路边;残障朋友想找份能做的活,却被大平台的门槛挡在门外;还有那些假期里,为买不到票、怕堵车而发愁的乡亲们,我又觉得这件事必须得做下去。
——摘自《七家坡的"摆渡人"》
作为一名在PHP领域深耕近20年的后端工程师,我一直在思考:如何用技术力量帮助更多人,同时又能让技术实现可持续发展?本文将分享我基于Swoft 2.0框架构建共享API平台的实践经验。
## 项目背景
我的共享API平台目前已经支撑了多个公益项目:
- **七家坡基层互助站**
(https://lo.lck.yn.cn/)
为云南乡镇居民提供顺风车、用工信息等便民服务
- **AI算命站点**
(https://ai.lck.yn.cn/)
基于AI的传统文化服务
这些项目虽然面向不同场景,但都需要相同的底层能力:用户认证、地理定位、支付、数据存储等。为了避免重复造轮子,我决定构建一个**共享API平台**,让所有项目都能快速接入这些通用能力。
## 技术选型:为什么选择Swoft 2.0?
在选型时,我对比了多个PHP框架:
| 框架 | 协程支持 | 微服务 | 学习成本 | 性能 |
|------|---------|--------|---------|------|
| Laravel | 无 | 无 | 低 | 中 |
| Hyperf | 有 | 有 | 中 | 高 |
| Swoft 2.0 | 有 | 有 | 中 | 高 |
最终选择**Swoft 2.0**的原因:
1. **成熟的协程支持**:基于Swoole扩展,提供原生协程能力,性能接近Go语言
2. **gRPC原生支持**:内置gRPC支持,可与任何语言无缝对接
3. **微服务架构**:内置RPC服务注册与发现,完美契合我的分布式架构需求
4. **注解驱动**:类似Spring Cloud的注解机制,代码简洁优雅
5. **完善的生态**:内置连接池、AOP、任务调度等企业级功能
### 关于框架选择的真实思考
**Swoft 2.0的现状**:
- 官方早已停止维护,官网已无法访问
- 基本查不到相关资料,社区支持很少
- 很多功能文档缺失,需要自己摸索
**为什么还是选择了它?**
1. **协程性能优势**:Swoole扩展的性能优势难以替代
2. **架构设计优秀**:微服务、RPC、注解机制的设计理念先进
3. **自主掌控**:既然官方不维护,我就自己维护
**填坑之路**:
- 很多功能的实现方案,AI都无法给出正确答案
- 每个坑都花了超过一周的时间去研究、测试、验证
- 通过理解作者意图、结合实战经验、深入底层源码,最终全部实现
**给后来者的建议**:
技术选型不要盲目追求"官方维护",关键是否适合你的场景。
框架只是工具,你的技术积累和解决问题的能力才是核心。
## 架构设计
### 整体架构
```
┌─────────────────────────────────────────────────────────┐
│ 前端应用层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 七家坡(H5/小程序) │ │ AI站点(H5/小程序) │ │ 其他项目 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ API网关层 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Gateway (api.micro.lck.yn.cn) │ │
│ │ - JWT认证 - 权限控制 │ │
│ │ - 限流 - 日志审计 │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 微服务层 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Traffic │ │ Stay │ │ Admin │ │ AIGC │ │
│ │ (出行) │ │ (住宿) │ │ (管理) │ │ (AI服务)│ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 数据存储层 │
│ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │ MySQL 8 │ │ Redis │ │ 支付宝/微信 │ │
│ │(主从) │ │ (缓存) │ │ 支付接口 │ │
│ └─────────┘ └─────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
```
### 服务拆分
根据业务领域,我将服务拆分为以下模块(端口为示意配置,实际部署时可自定义):
| 服务 | 职责 | 端口 |
|------|------|------|
| **gateway** | API网关,负责认证、路由、限流 | 8080 |
| **admin** | 用户、地址、分类等基础服务 | 8306 |
| **traffic** | 顺风车、岗位信息等出行相关服务 | 8307 |
| **stay** | 住宿、民宿等住宿服务 | 8308 |
| **aigc** | AI算命、AI生成等AI服务 | 8309 |
| **user** | 用户、权限管理 | 8310 |
| **pay** | 支付订单管理 | 8311 |
## 核心功能实现
### 1. API网关设计
网关是整个系统的入口,负责:
- **JWT认证**:基于`Swoft\Auth`组件实现,支持token自动刷新
- **域授权机制**:根据请求来源域名进行授权控制
- **本地开发友好**:localhost/127.0.0.1等本地域名默认已授权
- **权限控制**:通过中间件拦截未授权请求
- **服务路由**:将请求转发到对应的微服务
- **限流保护**:使用`Swoft\Limiter`防止接口滥用
- **文档生成**:自动生成Swagger文档
网关配置示例(`config/beans.php`):
```php
return [
'userAuth' => [
'class' => Swoft\Auth\Manager::class,
'name' => 'user',
'language' => 'zh',
'ttl' => 7200, // token有效期2小时
'refreshTtl' => 2592000, // 刷新token有效期30天
],
'jwtAuth' => [
'class' => Swoft\Auth\Parser\JWTAuthParser::class,
]
];
```
**Token自动刷新机制**:
```php
// 中间件中实现token自动刷新
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$token = $this->jwtAuth->parse($request);
$payload = $token->payload();
// 检查token是否即将过期(剩余时间小于10分钟)
if ($payload['exp'] - time() < 600) {
$newToken = $this->auth->refreshToken($token);
// 在响应头中返回新token
return $handler->handle($request)->withHeader('X-New-Token', $newToken);
}
return $handler->handle($request);
}
```
### 2. 地理位置查询优化(LBS)
顺风车和民宿服务都需要根据用户位置查询附近信息。这是典型的地理围栏查询场景。
#### 优化前的问题
传统使用`HAVING`计算距离的方式效率低下:
```sql
SELECT *, (6371 * ACOS(COS(RADIANS(lat)) * COS(RADIANS(24.88))
* COS(RADIANS(lng) - RADIANS(102.83))
+ SIN(RADIANS(lat)) * SIN(RADIANS(24.88)))) AS distance
FROM sys_carpooling
HAVING distance < 10
ORDER BY distance
```
**问题**:
- 每次查询都要计算全表距离,性能极差
- 无法使用索引,随着数据量增加性能急剧下降
#### 优化方案
采用**边界框预过滤 + 空间索引**的双重优化策略:
```sql
SELECT *, ST_Distance_Sphere(
location,
ST_GeomFromText('POINT(102.83 24.88)')
) AS distance
FROM sys_carpooling
WHERE lat BETWEEN 24.01 AND 25.75
AND lng BETWEEN 101.91 AND 103.75
AND ST_Distance_Sphere(location, POINT(102.83, 24.88)) <= 10000
ORDER BY distance
LIMIT 20
```
**优化要点**:
1. **添加空间索引**:
```sql
ALTER TABLE sys_carpooling
ADD COLUMN location POINT NOT NULL COMMENT '位置坐标' AFTER lat,
ADD SPATIAL INDEX idx_location (location);
```
2. **范围级别参数化**:
```php
$rangeMap = [
1 => 10, // 10公里
2 => 50, // 50公里
3 => 100, // 100公里(默认)
];
$range = $rangeMap[$level] ?? 100;
```
3. **计算边界框**:
```php
$lngMin = $lng - $range / 111.32 / cos($lat * PI() / 180);
$lngMax = $lng + $range / 111.32 / cos($lat * PI() / 180);
$latMin = $lat - $range / 111.32;
$latMax = $lat + $range / 111.32;
```
**性能对比**:
| 数据量 | 优化前 | 优化后 | 提升 |
|--------|--------|--------|------|
| 1万条 | 2.5s | 0.05s | 50x |
| 10万条 | 25s | 0.08s | 312x |
### 3. RPC微服务通信
平台支持**gRPC协议**,这是微服务通信的最佳选择。
#### 为什么选择gRPC?
gRPC相比传统HTTP API有以下优势:
| 特性 | HTTP/REST | gRPC |
|------|-----------|------|
| 协议 | 文本(JSON) | 二进制(Protobuf) |
| 传输效率 | 低(数据大) | 高(数据小) |
| 序列化速度 | 慢 | 快(5-10倍) |
| 跨语言支持 | 需要手动适配 | 自动生成代码 |
| 流式传输 | 不支持 | 支持(单向流/双向流) |
| 代码量 | 手动解析 | 自动生成 |
**关键优势**:
- **任何语言对接**:支持Go、Java、Python、Node.js、C#、PHP等20+种语言
- **协程友好**:完美配合Swoole协程,无阻塞
- **内存运行**:常驻内存,无需每次请求启动
- **性能更快**:二进制协议,传输速度提升3-5倍
- **资源占用少**:相比传统HTTP API,内存占用降低50%以上
服务间通过RPC调用,Swoft提供了优雅的注解方式:
**定义Proto文件**(`traffic.proto`):
```protobuf
syntax = "proto3";
package traffic;
service TrafficService {
rpc NearList(NearRequest) returns (NearResponse);
rpc CreateCarpooling(CarpoolingRequest) returns (CommonResponse);
}
message NearRequest {
float lng = 1;
float lat = 2;
int32 level = 3;
}
message NearResponse {
repeated Carpooling items = 1;
int32 total = 2;
}
```
**PHP服务端实现**:
```php
/**
* @RpcService(name="trafficService", version="1.0")
*/
class TrafficService
{
/**
* 查询附近顺风车
*/
public function nearList(float $lng, float $lat, int $level = 3): array
{
return CarpoolingDao::nearList($lng, $lat, $level);
}
}
```
**服务消费者通过`@Reference`注解注入**:
```php
/**
* @Reference(name="trafficService", version="1.0")
*/
private $trafficService;
public function near(Request $request): Response
{
$data = $this->trafficService->nearList(
$request->get('lng'),
$request->get('lat'),
$request->get('level', 3)
);
return context()->getResponse()->withData($data);
}
```
**跨语言调用示例**:
```go
// Go客户端
conn, _ := grpc.Dial("localhost:8307", grpc.WithInsecure())
client := pb.NewTrafficServiceClient(conn)
req := &pb.NearRequest{
Lng: 102.83,
Lat: 24.88,
Level: 3,
}
resp, _ := client.NearList(context.Background(), req)
```
### 4. 支付系统集成
支付系统支持微信和支付宝,需要区分PC端和移动端:
```php
public function createOrder(Request $request): Response
{
$deviceType = getDeviceType(); // H5/Pc
// 支付方式判断
$method = $this->getPaymentMethod($channel, $deviceType);
// 微信PC端: Native, 支付宝PC端: PcWeb, 移动端: H5
$order = [
'ser_name' => 'aigc',
'amount' => $amount * 100, // 转为分
'channel' => $channel, // Wechat/Alipay
'method' => $method,
'body' => '套餐名称',
'source' => '总部自营',
'user_id' => $userId ?: null,
'temp_id' => $tempId ?: null,
];
return $this->orderApi->create($order);
}
```
### 5. 自动化部署
为了提高开发效率,我实现了完整的自动化部署流程:
```bash
# 部署脚本
#!/bin/bash
git pull origin master
composer install --no-dev
php bin/swoft stop
php bin/swoft start -d
```
Git提交后自动触发部署,无需人工干预。
## API文档管理
使用Swagger自动生成API文档,部署在:
- **生产环境**:
https://api.micro.lck.yn.cn/swagger-ui.html
- **测试环境**:
https://apitest.micro.lck.yn.cn/swagger-ui.html
文档配置(`composer.json`中的description):
```json
{
"description": "共享API平台 - 5分钟快速接入\n1. 示例代码:Vue/React/微信小程序\n2. 成功案例:七家坡、AI站点\n3. 域授权:按项目域名授权,灵活接入\n4. 技术支持:微信 kmwmkj | 邮箱 admin@lck.yn.cn | 电话 15687658489"
}
```
```
## 接入模式与商业化思考
### 灵活的接入方式
平台提供两种接入模式,开发者可以根据项目发展阶段自由选择:
#### 1. 共享API模式(适合项目初期)
**适用场景**:
- 项目刚起步,用户量和并发较低
- 希望节省服务器和运维成本
- 快速验证商业想法
**优势**:
- **零运维成本**:无需搭建服务器、数据库等基础设施
- **快速上线**:5分钟即可完成接入
- **按需授权**:根据项目域名进行授权,灵活便捷
- **本地开发友好**:localhost等本地域名默认已授权,开发无忧
**授权方式**:
- 通过申请域名授权,绑定项目前端域名
- 本地开发环境(localhost、127.0.0.1)默认已授权
- 域名审核通过后,即可在生产环境使用
#### 2. 独立部署模式(适合项目成长期)
**适用场景**:
- 项目已有一定用户量和流量
- 需要更高的性能和稳定性
- 数据安全要求更高
**优势**:
- **完全独立**:网关、微服务、数据库全部独立部署
- **跨服务器架构**:可根据需要分布式部署
- **性能无上限**:根据业务需求灵活扩展
- **数据完全掌控**:所有数据存储在自有服务器
**部署灵活性**:
```
// 可以根据需求选择部署方式
方式1:单机部署(适合小型项目)
┌─────────────────────────────────┐
│ 单服务器部署所有服务 │
└─────────────────────────────────┘
方式2:分布式部署(适合中大型项目)
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 网关服务器│ │ 应用服务器│ │ 数据库 │
└──────────┘ └──────────┘ └──────────┘
方式3:集群部署(适合大型项目)
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 网关集群 │ │ 应用集群 │ │ 数据库集群│
└──────────┘ └──────────┘ └──────────┘
```
### 无缝迁移路径
平台设计了平滑的迁移路径:
```
项目起步期 项目成长期 项目成熟期
│ │ │
▼ ▼ ▼
共享API模式 → 独立部署模式 → 完全私有化
(零成本接入) (逐步过渡) (完全掌控)
```
**迁移优势**:
- **代码无需修改**:API接口保持一致
- **数据平滑迁移**:支持数据导出和导入
- **无缝切换**:域名授权即可切换模式
### 技术支持
提供多渠道技术支持:
- **微信**:kmwmkj
- **邮箱**:admin@lck.yn.cn
- **电话**:15687658489(微信同号)
### 公益与可持续
做公益不代表不能商业化,关键是找到平衡点:
**公益初心**:
- 始终记得为什么出发,不要因为商业化而偏离公益使命
- 为公益项目提供技术支持,降低公益项目的技术门槛
**可持续发展**:
- 公益也需要资金支持,合理的商业化能让平台走得更远
- 通过商业化项目反哺公益项目,形成良性循环
**开放共赢**:
- 欢迎更多公益项目接入,共同服务社会
- 建立开发者社区,共享技术资源和经验
## 经验总结
### 技术层面
1. **协程编程思维**:协程不是万能的,要避免阻塞操作,使用连接池、协程客户端等
2. **服务拆分粒度**:不要过度拆分,按业务领域划分,保持服务独立但不过于分散
3. **数据库优化**:善用MySQL 8.0的空间索引,地理位置查询性能提升显著
4. **监控告警**:建立完善的监控体系,及时发现和处理问题
### 产品层面
1. **文档先行**:好的API文档能让开发者快速上手,降低接入门槛
2. **示例代码**:提供Vue/React/小程序等多语言示例,覆盖主流技术栈
3. **渐进式开放**:先开放基础接口,逐步开放高级能力
4. **社区运营**:建立开发者社区,收集反馈,持续改进
### 商业层面
1. **灵活接入**:提供共享API和独立部署两种模式,满足不同阶段需求
2. **公益初心**:始终记得为什么出发,为公益项目降低技术门槛
3. **可持续发展**:合理的商业化能让平台更好地服务公益
4. **开放共赢**:欢迎更多项目接入,共同创造价值
## 展望
未来计划:
1. **扩展服务**:增加更多通用服务(短信、邮件、对象存储等)
2. **多区域部署**:在更多地区部署节点,降低延迟
3. **AI能力开放**:将AI算命等服务标准化后对外开放
4. **社区建设**:建立开发者社区,鼓励共享代码和经验
5. **接入示例**:提供更多接入示例(Vue、React、微信小程序等)
6. **管理后台**:开发可视化的域授权管理系统
## 接入指南
### 快速接入
1. **联系授权**:通过微信、邮箱或电话申请域名授权
2. **获取密钥**:获取项目的API密钥和配置信息
3. **配置调用**:根据文档配置API调用
4. **测试上线**:本地测试通过后即可上线
**本地开发说明**:
- localhost、127.0.0.1等本地域名默认已授权
- 开发过程中无需额外配置
### 技术支持
- **微信**:kmwmkj
- **邮箱**:admin@lck.yn.cn
- **电话**:15687658489(微信同号)
- **API文档**:https://api.micro.lck.yn.cn/swagger-ui.html
## 结语
技术不仅是工具,更是一种改变世界的力量。作为一名开发者,我有幸能用自己的专长帮助他人,这让我感到无比充实。
我希望通过这个共享API平台,降低技术门槛,让更多人能够快速实现自己的想法。无论你是想做公益项目,还是想开发商业应用,都可以在这里找到你需要的基础能力。
如果你也想接入我的共享API平台,欢迎联系我。让我们一起用技术创造价值,让世界变得更美好一点。
**联系方式**:
- 微信:kmwmkj
- 邮箱:admin@lck.yn.cn
- 电话:15687658489(微信同号)
- 网关地址:https://api.micro.lck.yn.cn/
---
**作者简介**:李成坤(Ckli),后端工程师,系统架构师,技术总监。从事PHP开发近20年,现任云南某科技公司技术总监。热衷于公益事业,用技术帮助需要帮助的人。
个人主页:https://www.lck.yn.cn/
技术博客:https://blog.lck.yn.cn/