我用Swoft 2.0搭了个共享API平台,支持Vue/React/小程序一键接入

11 阅读12分钟

我用Swoft 2.0搭了个共享API平台,支持Vue/React/小程序一键接入

前言

做公益这件事,我走了好多年。中间有过委屈,有过难,甚至看着自己搭建的平台,明明能帮人,却因"不会吆喝"而少有人问津时,也会陷入迷茫。但每当想起乡镇里,有人为了赶一趟班车,天不亮就守在路边;残障朋友想找份能做的活,却被大平台的门槛挡在门外;还有那些假期里,为买不到票、怕堵车而发愁的乡亲们,我又觉得这件事必须得做下去。

——摘自《七家坡的"摆渡人"》

作为一名在PHP领域深耕近20年的后端工程师,我一直在思考:如何用技术力量帮助更多人,同时又能让技术实现可持续发展?本文将分享我基于Swoft 2.0框架构建共享API平台的实践经验。

项目背景

我的共享API平台目前已经支撑了多个公益项目:

  • 七家坡基层互助站 (lo.lck.yn.cn/) 提共基于用户当前位置(查询、发布)拼车、运营(车辆、路线)信息等便民服务

  • AI合集 (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都无法给出正确答案
  • 每个坑都花了超过一周的时间去研究、测试、验证
  • 通过理解作者意图、结合实战经验、深入底层源码,最终全部实现

给后来者的建议: 技术选型不要盲目追求"官方维护",关键是否适合你的场景。 框架只是工具,你的技术积累和解决问题的能力才是核心。

架构设计

整体架构

┌──────────────────────┐
│    前端应用层       │
│ ┌────────┐┌────────┐│
│ │七家坡  ││AI站点  ││
│ └────────┘└────────┘│
└──────────────────────┘
         │
         ▼
┌──────────────────────┐
│    API网关层        │
│ Gateway            │
│ - JWT认证          │
│ - 权限控制         │
│ - 限流             │
│ - 日志审计         │
└──────────────────────┘
         │
         ▼
┌──────────────────────┐
│    微服务层         │
│ ┌────┐┌────┐┌────┐│
│ │Traf││Stay││Admi││
│ │fic  ││    ││n   ││
│ └────┘└────┘└────┘│
└──────────────────────┘
         │
         ▼
┌──────────────────────┐
│    数据存储层       │
│ ┌────┐┌────┐┌────┐│
│ │MyS ││Redi││支付││
│ │QL  ││s   ││接口││
│ └────┘└────┘└────┘│
└──────────────────────┘

服务拆分

根据业务领域,我将服务拆分为以下模块(端口为示意配置,实际部署时可自定义):

网关层:

服务职责端口
gatewayAPI网关,负责认证、路由、限流、域名授权80

微服务层:

服务职责包含的子服务
login登录服务登录
user用户服务用户、地址等
code验证服务邮箱验证、短信验证(已测试通过)
category分类服务分类
traffic交通服务拼车、运营(岗位信息)
stay住宿服务小屋(民宿)
aigcAI合集服务八字取名等AI服务
pay支付服务订单、支付回调

8个微服务,每个服务下可能有1或多个子服务。

核心功能实现

1. API网关设计

网关是整个系统的入口,负责:

  • JWT认证:基于Swoft\Auth组件实现,支持token自动刷新
  • 域授权机制:根据请求来源域名进行授权控制
  • 本地开发友好:localhost/127.0.0.1等本地域名默认已授权
  • 权限控制:通过中间件拦截未授权请求
  • 服务路由:将请求转发到对应的微服务
  • 限流保护:使用Swoft\Limiter防止接口滥用
  • 文档生成:自动生成Swagger文档

网关配置示例(config/beans.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自动刷新机制

// 中间件中实现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计算距离的方式效率低下:

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

问题

  • 每次查询都要计算全表距离,性能极差
  • 无法使用索引,随着数据量增加性能急剧下降
优化方案

采用边界框预过滤 + 空间索引的双重优化策略:

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. 添加空间索引
ALTER TABLE sys_carpooling
ADD COLUMN location POINT NOT NULL COMMENT '位置坐标' AFTER lat,
ADD SPATIAL INDEX idx_location (location);
  1. 范围级别参数化
$rangeMap = [
    1 => 10,   // 10公里
    2 => 50,   // 50公里
    3 => 100,  // 100公里(默认)
];

$range = $rangeMap[$level] ?? 100;
  1. 计算边界框
$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.5s0.05s50x
10万条25s0.08s312x

3. RPC微服务通信

平台支持gRPC协议,这是微服务通信的最佳选择。

为什么选择gRPC?

gRPC相比传统HTTP API有以下优势:

特性HTTP/RESTgRPC
协议文本(JSON)二进制(Protobuf)
传输效率低(数据大)高(数据小)
序列化速度快(5-10倍)
跨语言支持需要手动适配自动生成代码
流式传输不支持支持(单向流/双向流)
代码量手动解析自动生成

关键优势

  • 任何语言对接:支持Go、Java、Python、Node.js、C#、PHP等20+种语言
  • 协程友好:完美配合Swoole协程,无阻塞
  • 内存运行:常驻内存,无需每次请求启动
  • 性能更快:二进制协议,传输速度提升3-5倍
  • 资源占用少:相比传统HTTP API,内存占用降低50%以上

服务间通过RPC调用,Swoft提供了优雅的注解方式:

定义Proto文件traffic.proto):

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服务端实现

/**
 * @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注解注入

/**
 * @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客户端
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端和移动端:

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. 自动化部署

为了提高开发效率,我实现了完整的自动化部署流程:

# 部署脚本
#!/bin/bash
git pull origin master
composer install --no-dev
php bin/swoft stop
php bin/swoft start -d

Git提交后自动触发部署,无需人工干预。

API文档管理

使用Swagger自动生成API文档,部署在:

接入模式与商业化思考

灵活的接入方式

平台提供两种接入模式,开发者可以根据项目发展阶段自由选择:

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. 扩展服务:验证服务已支持邮箱验证(发送验证码、邮箱验证)和阿里云短信验证(代码已完成并测试通过),后续更换通过审核的短信模板ID即可正常使用
  2. 多区域部署:在更多地区部署节点,降低延迟
  3. AI能力开放:将AI算命等服务标准化后对外开放
  4. 社区建设:建立开发者社区,鼓励共享代码和经验
  5. 接入示例:提供更多接入示例(Vue、React、微信小程序等)
  6. 管理后台:开发可视化的域授权管理系统

接入指南

快速接入

  1. 联系授权:通过微信、邮箱或电话申请域名授权
  2. 获取密钥:获取项目的API密钥和配置信息
  3. 配置调用:根据文档配置API调用
  4. 测试上线:本地测试通过后即可上线

本地开发说明

  • localhost、127.0.0.1等本地域名默认已授权
  • 开发过程中无需额外配置

技术支持

结语

技术不仅是工具,更是一种改变世界的力量。作为一名开发者,我有幸能用自己的专长帮助他人,这让我感到无比充实。

我希望通过这个共享API平台,降低技术门槛,让更多人能够快速实现自己的想法。无论你是想做公益项目,还是想开发商业应用,都可以在这里找到你需要的基础能力。

如果你也想接入我的共享API平台,欢迎联系我。让我们一起用技术创造价值,让世界变得更美好一点。

联系方式


作者简介:李成坤(Ckli),后端工程师,系统架构师,技术总监。从事PHP开发近20年,现任云南某科技公司技术总监。热衷于公益事业,用技术帮助需要帮助的人。

个人主页:www.lck.yn.cn/ 技术博客:blog.lck.yn.cn/