
最近折腾微服务消息队列,在 Laravel 项目里集成 NATS(一个轻量级消息系统),踩了几个坑,终于把这块打通。这篇把问题和解决方案说清楚,顺便介绍下我做的 Laravel NATS 包。
做分布式系统,最大的挑战之一就是让服务之间可靠通信,又不搞出强耦合。Laravel 本身对队列、事件、广播、任务的处理已经相当完善,但涉及到 NATS 这个消息中间件,生态就比较匮乏了。
这就是我写 Laravel NATS 的初衷。
这个包不是简单封装一个现成的 PHP 客户端,而是想做真正的 Laravel 优先开发体验,同时把 NATS 的全部能力暴露给现代事件驱动架构。
这篇文章会聊到:
NATS 是一款专为云原生应用设计的高性能轻量级消息系统。
跟传统队列不一样,NATS 的核心关注点是:
传统方式下,应用之间直接互相调用:
订单服务 │ ▼
通知服务
改成 NATS 之后,应用只管发布事件:
订单服务 │ ▼ NATS 服务器 │ │ ▼ ▼
短信 数据分析
每个服务都独立了,耦合彻底消失。
现有的包大多数直接暴露底层 PHP 客户端。
这意味着开发者还是得理解:
但 Laravel 开发者期待的不一样。
我们习惯了这样的 API:
Cache::put(); Queue::push(); Event::dispatch();
Laravel NATS 的目标就是让 NATS 用起来跟这些一样自然。
安装过程很直接。
composer require zaeem2396/laravel-nats php artisan vendor:publish --tag=nats-config
然后配置环境变量:
NATS_HOST=127.0.0.1
NATS_PORT=4222
NATS_USER=
NATS_PASS=
NATS_TOKEN=
完事。
发布事件应该很简单。
Laravel NATS 提供了现代化的 NatsV2 Facade。
use LaravelNats\Laravel\Facades\NatsV2; NatsV2::publish( 'orders.created', [ 'order_id' => 123 ], [ 'X-Request-Id' => 'req-1' ]
);
注意,没有连接管理。
没有手动编码。
没有模板代码。
直接发就完了。
订阅也一样简洁。
NatsV2::subscribe('orders.created', function ($message) { logger($message->payload()); });
这样 Laravel 应用就能几乎实时响应事件了。
这个包支持两条路线:
新项目应该始终使用 NatsV2,因为它基于更新版的 basis-company/nats 客户端,同时保持了 Laravel 友好风格。
这样做的好处:
这个项目最大的目标不只是"让 NATS 能跑起来"。
目标是真正能在生产环境用。
主要特性包括:
消息系统配置错了很难调试。
Laravel NATS 会在建立连接之前先校验配置,把问题拦在门外。
生产环境跑不加密的消息传输是严重的安全隐患。
Laravel NATS 包含可选的 TLS 强制选项,防止不小心部署成不安全的方式。
多服务架构通常需要精细的访问控制。
这个包提供了可选的 ACL 集成来保护服务间通信。
分布式系统里消息重试不可避免。
没有幂等性的话:
支付已创建 ↓ 支付已创建 ↓ 支付已创建
可能不小心创建三笔支付。
Laravel NATS 内置可选的幂等性支持,重复投递不会变成重复的业务操作。
我最兴奋的一个特性是可观测性。
消息系统能追踪请求在服务间的流转,调试会轻松很多。
Laravel NATS 支持:
分布式调试变得简单很多。
这个包还包含了专用的队列驱动。
不是把 NATS 当成另一个传输层就完事了,而是让 Laravel 应用能把它融入队列生态。
开发者可以继续用熟悉的 Laravel 队列写法,底层跑的是 NATS。
1.6 版本引入了 Header 辅助方法。
例子:
NatsV2::publish( 'orders.created', $payload, [ 'X-Request-Id' => $requestId, 'traceparent' => $traceparent ]
);
Header 让这些信息可以随消息传递:
不需要改动消息体本身。
上手 NATS 不应该要折腾半天。
仓库里自带 Docker 支持。
docker compose up -d
Laravel 应用立刻就能跟本地 NATS 服务器通信了。
这对以下场景特别有用:
一个经常被忽视的决策是文档。
很多开源项目代码写得漂亮,但文档一塌糊涂。
Laravel NATS 刻意采用索引优先的文档结构。
开发者可以快速定位:
这让上手体验好很多。
Redis 队列很好用。
但 NATS 解决的是不同的问题。
| Redis | NATS |
|---|---|
| 队列为核心 | 事件驱动 |
| 主要处理任务 | 服务间通信 |
| 路由能力有限 | 主题式路由 |
| 适合任务处理 | 适合微服务 |
如果你的场景是:
NATS 是非常好的选择。
拿电商平台举例。
用户下单之后:
订单服务 ↓ orders.created
多个服务立刻收到事件:
库存服务 短信服务 数据分析服务 发票服务 风控服务
这些服务之间完全不知道彼此存在。
它们只管订阅事件。
这种架构扩展起来容易太多。
主要有几个方面。
没有直接暴露原始 PHP 客户端,包的设计拥抱了 Laravel 惯例。
用起来都很熟悉。
NatsV2 提供了比之前方案更干净的抽象。
开发者可以专注业务,而不是纠结传输层的细节。
这些特性通常是开发者自己写:
Laravel NATS 开箱就有。
迁移生产系统很难。
Laravel NATS 没有强行破坏性更新,遗留 API 保持可用,同时鼓励迁移到新实现。
这让采纳门槛低很多。
好的文档本身就是功能。
这个包包含:
上手比很多消息库都轻松。
这个项目不是为了简单包装一个现成的 PHP 库。
目标是改善 Laravel 生态,让 NATS 成为框架的自然组成部分。
Laravel 开发者值得同样的优雅体验,不管用的是队列、Redis、事件还是 NATS。
开源应该降低复杂度,而不是暴露复杂度。
这就是 Laravel NATS 的理念。
这个包会继续演进。
我感兴趣的扩展方向包括:
目标很简单:
让 Laravel NATS 成为每个使用 NATS 的 Laravel 应用的首选消息包。
构建分布式系统不应该牺牲 Laravel 的开发体验。
Laravel NATS 把这些结合在一起:
无论你在做微服务平台、事件驱动 SaaS、实时后端,还是只是想试试 NATS,Laravel NATS 都致力于扫清障碍,让你专注写代码而不是搭基础设施。