async
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAsync-First — Quick Reference
异步优先架构——速查指南
Décharger les traitements > 200 ms vers des workers en arrière-plan, pour garder l'UI/API réactives.
将耗时超过200毫秒的任务转移至后台工作进程,以保持UI/API的响应性。
Quand activer l'async
何时启用异步
| Cas | Async ? |
|---|---|
| Envoi d'email transactionnel | ✅ |
| Génération PDF / export CSV | ✅ |
| Appel API tierce > 200 ms | ✅ |
| Traitement batch nocturne | ✅ |
| Lecture base de données triviale | ❌ |
| 场景 | 是否异步? |
|---|---|
| 发送事务性邮件 | ✅ |
| 生成PDF / 导出CSV | ✅ |
| 调用第三方API耗时>200毫秒 | ✅ |
| 夜间批量处理 | ✅ |
| 简单数据库查询 | ❌ |
Cinq invariants non-négociables
五项不可妥协的原则
- HTTP < 200 ms. Toute opération qui dépasse ce seuil doit être déportée.
- Idempotence obligatoire. Un message peut être rejoué — concevoir comme tel (idempotency keys, dedupe).
- Retry + Dead Letter Queue. 3 tentatives, backoff exponentiel + jitter. Au-delà → DLQ + alerte.
- Competing consumers. 4-8 workers en parallèle pour absorber les pics.
- Lifecycle tracking. Logs structurés, métriques (latence, taux erreur, DLQ depth) et alertes.
- HTTP响应 < 200毫秒。任何超过此阈值的操作都必须转移至后台。
- 必须保证幂等性。消息可能会被重发——需按此设计(幂等键、去重机制)。
- 重试 + 死信队列。3次重试,指数退避+抖动。超过次数→死信队列+告警。
- 竞争消费者模式。4-8个并行工作进程以应对峰值。
- 生命周期追踪。结构化日志、指标(延迟、错误率、死信队列深度)及告警。
Frameworks recommandés
推荐框架
| Stack | Framework | Notes |
|---|---|---|
| Symfony | Symfony Messenger | Transport AMQP, Redis, Doctrine ; supports Stamps |
| Laravel | Laravel Queue (Horizon en prod) | Backed by Redis ou SQS ; supervision native |
| PHP framework-agnostic | Ecotone | DDD, sagas, scheduler, messaging unifié |
| Node.js | BullMQ ou RabbitMQ | Redis-backed avec dashboard |
| 技术栈 | 框架 | 说明 |
|---|---|---|
| Symfony | Symfony Messenger | 支持AMQP、Redis、Doctrine传输;支持Stamps机制 |
| Laravel | Laravel Queue(生产环境搭配Horizon) | 基于Redis或SQS;原生支持监控 |
| 无框架PHP | Ecotone | 支持领域驱动设计(DDD)、Sagas、调度器、统一消息传递 |
| Node.js | BullMQ 或 RabbitMQ | 基于Redis,附带仪表盘 |
Pattern minimal (Symfony Messenger)
最简实现模式(Symfony Messenger)
php
// Message (immutable DTO)
final class SendWelcomeEmail
{
public function __construct(public readonly string $userId) {}
}
// Handler
final class SendWelcomeEmailHandler
{
public function __invoke(SendWelcomeEmail $message): void
{
// Idempotency check
if ($this->emailLog->wasSent($message->userId, 'welcome')) {
return;
}
$this->mailer->sendWelcome($message->userId);
$this->emailLog->record($message->userId, 'welcome');
}
}
// Dispatch (controller)
$this->bus->dispatch(new SendWelcomeEmail($user->id));php
// Message (immutable DTO)
final class SendWelcomeEmail
{
public function __construct(public readonly string $userId) {}
}
// Handler
final class SendWelcomeEmailHandler
{
public function __invoke(SendWelcomeEmail $message): void
{
// Idempotency check
if ($this->emailLog->wasSent($message->userId, 'welcome')) {
return;
}
$this->mailer->sendWelcome($message->userId);
$this->emailLog->record($message->userId, 'welcome');
}
}
// Dispatch (controller)
$this->bus->dispatch(new SendWelcomeEmail($user->id));Anti-patterns critiques
需避免的反模式
- ❌ Mettre une opération idempotente directement dans le handler sans clé d'idempotency (replay = double effet).
- ❌ dans un handler → le message est marqué traité alors qu'il a échoué.
try { ... } catch (\Throwable) { /* swallow */ } - ❌ Stocker la payload complète d'un email/PDF dans le message → préférer un ID + lookup, sinon RabbitMQ explose.
- ❌ Pas de circuit breaker autour des appels tiers : un provider down fait monter la DLQ en flèche.
- ❌ 在处理器中直接处理幂等操作但未设置幂等键(重发会导致重复执行)。
- ❌ 在处理器中使用→消息被标记为已处理,但实际执行失败。
try { ... } catch (\Throwable) { /* swallow */ } - ❌ 将邮件/PDF的完整负载存储在消息中→优先使用ID+查询,否则RabbitMQ会过载。
- ❌ 第三方调用未使用断路器:服务提供商宕机会导致死信队列数量激增。
Pour aller plus loin
深入学习
Détails complets, patterns Laravel/Ecotone, exemples de configuration, observabilité OpenTelemetry, lifecycle events, checklists par phase : voir.@.claude/skills/async/REFERENCE.md
完整细节、Laravel/Ecotone实现模式、配置示例、OpenTelemetry可观测性、生命周期事件、各阶段检查清单:详见。@.claude/skills/async/REFERENCE.md