
本文介绍在php中通过策略模式与队列重试机制,构建高可用otp发送系统,支持短信api失败时自动降级至whatsapp等备用通道,提升消息送达率。
本文介绍在php中通过策略模式与队列重试机制,构建高可用otp发送系统,支持短信api失败时自动降级至whatsapp等备用通道,提升消息送达率。
在实际生产环境中,单一短信通道存在延迟、限流或运营商拦截等不可控风险,仅依赖一个API发送OTP极易导致用户无法及时接收验证信息,进而影响注册、登录等关键流程。为提升可靠性,推荐采用多通道容错架构:主通道(如Jazz SMS API)失败后,自动触发备用通道(如WhatsApp Business API、Twilio或本地短信网关),而非简单地“同时发送”——后者不仅增加成本,还可能引发重复通知和合规风险。
✅ 推荐架构:策略模式 + 异步队列重试
核心思想是将不同消息通道抽象为可互换的“策略”,并通过队列任务统一调度与错误处理:
-
定义统一接口
interface OtpSender { public function send(string $phone, string $message): bool; } -
实现各通道策略
// 短信通道(Jazz) class JazzSmsSender implements OtpSender { public function send(string $phone, string $message): bool { $phone = preg_replace('/[^0-9]/', '', $phone); $curl = curl_init(); $params = [ 'Username' => '030XXXXXX', 'Password' => 'XXXXXX', 'From' => 'XXXXXX', 'To' => $phone, 'Message' => $message, ]; $url = 'https://connect.jazz.com/sendsms_url.html?' . http_build_query($params); curl_setopt_array($curl, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15, CURLOPT_FOLLOWLOCATION => true, ]); $result = curl_exec($curl); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); $error = curl_errno($curl); curl_close($curl); return $error === 0 && $httpCode >= 200 && $httpCode
// WhatsApp通道(示例:使用Meta Graph API) class WhatsappSender implements OtpSender { private string $accessToken = 'YOUR_WA_ACCESS_TOKEN'; private string $phoneNumberId = 'YOUR_PHONE_NUMBER_ID';
public function send(string $phone, string $message): bool
{
// 注意:WhatsApp要求E.164格式(如 +923012345678)
$e164 = '+'.ltrim($phone, '0');
$payload = json_encode([
'messaging_product' => 'whatsapp',
'to' => $e164,
'type' => 'template',
'template' => [
'name' => 'otp_template', // 需提前在Meta审核通过
'language' => ['code' => 'en_US'],
'components' => [
['type' => 'body', 'parameters' => [['type' => 'text', 'text' => $message]]]
]
]
]);
$ch = curl_init('https://graph.facebook.com/v19.0/'.$this->phoneNumberId.'/messages');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer '.$this->accessToken,
'Content-Type: application/json'
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_errno($ch);
curl_close($ch);
return $error === 0 && in_array($httpCode, [200, 201]);
}
}
3. **创建可重试的队列任务(Laravel风格示例)**
```php
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendOtpJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
private string $phone,
private string $message,
private array $strategies = ['JazzSmsSender', 'WhatsappSender']
) {}
public function handle()
{
foreach ($this->strategies as $strategyClass) {
if (class_exists($strategyClass)) {
$sender = new $strategyClass();
if ($sender->send($this->phone, $this->message)) {
\Log::info("OTP sent successfully via {$strategyClass}", ['phone' => $this->phone]);
return;
}
}
}
throw new \Exception('All OTP channels failed');
}
public function failed(\Throwable $exception)
{
\Log::error('OTP delivery failed on all channels', [
'phone' => $this->phone,
'error' => $exception->getMessage()
]);
// 可选:通知运维、写入告警表、或触发人工干预流程
}
}
⚠️ 关键注意事项
- 号码格式统一:短信与WhatsApp对号码格式要求不同(如Jazz可能接受03012345678,WhatsApp强制+923012345678),务必在调用前标准化;
- 模板合规性:WhatsApp必须使用预审通过的模板消息,且OTP内容需符合Meta政策;
- 幂等性保障:避免因重试导致用户收到多条OTP,建议在发送前检查该手机号在N分钟内是否已成功发送(如Redis缓存otp_sent:{$phone});
- 监控与告警:记录各通道成功率、平均耗时及失败原因,当某通道失败率突增时自动告警;
- 降级策略可配置:将通道优先级、超时阈值、重试次数等参数外置至配置文件或数据库,便于动态调整。
通过该设计,您不再需要硬编码多个curl调用,而是获得一个可扩展、可测试、易维护的OTP分发系统——未来新增Telegram Bot或邮件通道,只需实现OtpSender接口并加入策略列表即可。











