本文目录导读:

- 使用消息队列(推荐方案)
- 使用 Redis 队列
- 使用 PHP 的 pcntl_fork (Linux/Mac)
- 使用第三方服务(推荐生产环境)
- 使用 PHP 内置的 curl_multi
- 最佳实践建议
- 完整的异步邮件处理框架
在 PHP 中实现异步邮件发送通常有几种方案,由于 PHP 默认是同步阻塞的,需要借助额外工具或技术来实现真正的异步。
使用消息队列(推荐方案)
RabbitMQ 示例
// 生产者:发送邮件到队列
<?php
require_once 'vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
class EmailProducer {
private $connection;
private $channel;
public function __construct() {
$this->connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$this->channel = $this->connection->channel();
$this->channel->queue_declare('email_queue', false, true, false, false);
}
public function sendToQueue($to, $subject, $body) {
$data = json_encode([
'to' => $to,
'subject' => $subject,
'body' => $body,
'timestamp' => time()
]);
$msg = new AMQPMessage($data, [
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
]);
$this->channel->basic_publish($msg, '', 'email_queue');
}
public function __destruct() {
$this->channel->close();
$this->connection->close();
}
}
// 使用
$producer = new EmailProducer();
$producer->sendToQueue('user@example.com', 'Hello', 'Email body');
消费者:处理队列中的邮件
// consumer.php - 需要作为独立进程运行
<?php
require_once 'vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
class EmailConsumer {
private $connection;
private $channel;
public function __construct() {
$this->connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$this->channel = $this->connection->channel();
$this->channel->queue_declare('email_queue', false, true, false, false);
// 限制每次处理1个任务
$this->channel->basic_qos(null, 1, null);
}
public function process() {
$callback = function (AMQPMessage $msg) {
$data = json_decode($msg->body, true);
echo "Processing email to: {$data['to']}\n";
// 发送邮件
$this->sendEmail($data['to'], $data['subject'], $data['body']);
// 确认消息已处理
$msg->ack();
};
$this->channel->basic_consume('email_queue', '', false, false, false, false, $callback);
while ($this->channel->is_consuming()) {
$this->channel->wait();
}
}
private function sendEmail($to, $subject, $body) {
// 实际的邮件发送逻辑
sleep(2); // 模拟发送延迟
echo "Email sent to: $to\n";
}
}
$consumer = new EmailConsumer();
$consumer->process();
使用 Redis 队列
<?php
require_once 'vendor/autoload.php';
use Predis\Client;
class RedisEmailQueue {
private $redis;
private $queueName = 'email_queue';
public function __construct() {
$this->redis = new Client([
'scheme' => 'tcp',
'host' => '127.0.0.1',
'port' => 6379,
]);
}
// 入队
public function enqueue($emailData) {
$this->redis->rpush($this->queueName, json_encode($emailData));
return true;
}
// 出队并处理
public function dequeue() {
$data = $this->redis->lpop($this->queueName);
if ($data) {
$emailData = json_decode($data, true);
$this->processEmail($emailData);
}
}
private function processEmail($data) {
// 实际发送邮件
mail($data['to'], $data['subject'], $data['body']);
}
}
// 使用
$queue = new RedisEmailQueue();
$queue->enqueue([
'to' => 'user@example.com',
'subject' => 'Test',
'body' => 'Hello World'
]);
使用 PHP 的 pcntl_fork (Linux/Mac)
<?php
class AsyncEmailSender {
public static function send($to, $subject, $body) {
$pid = pcntl_fork();
if ($pid == -1) {
// fork失败
return false;
} elseif ($pid) {
// 父进程:立即返回
return true;
} else {
// 子进程:执行邮件发送
self::sendEmail($to, $subject, $body);
exit(0);
}
}
private static function sendEmail($to, $subject, $body) {
// 实际的邮件发送逻辑
mail($to, $subject, $body);
}
}
// 使用
AsyncEmailSender::send('user@example.com', 'Subject', 'Body');
使用第三方服务(推荐生产环境)
SendGrid API
<?php
require_once 'vendor/autoload.php';
use SendGrid\Mail\Mail;
class AsyncEmailService {
public function sendEmailAsync($to, $subject, $content) {
// 使用 HTTP 客户端发送请求但不等待响应
$client = new GuzzleHttp\Client([
'timeout' => 0.1 // 极短超时,触发异步
]);
$promise = $client->postAsync('https://api.sendgrid.com/v3/mail/send', [
'headers' => [
'Authorization' => 'Bearer YOUR_API_KEY',
'Content-Type' => 'application/json',
],
'json' => [
'personalizations' => [
['to' => [['email' => $to]]]
],
'from' => ['email' => 'from@example.com'],
'subject' => $subject,
'content' => [
['type' => 'text/plain', 'value' => $content]
]
]
]);
// 不等待结果
$promise->then(function() {
// 可选:记录成功日志
}, function() {
// 可选:记录失败日志
});
return true;
}
}
使用 PHP 内置的 curl_multi
<?php
class CurlAsyncMailer {
public static function sendAsync($url, $data) {
$mh = curl_multi_init();
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 100); // 快速超时
curl_multi_add_handle($mh, $ch);
do {
curl_multi_exec($mh, $running);
} while ($running > 0);
curl_multi_remove_handle($mh, $ch);
curl_multi_close($mh);
}
}
最佳实践建议
- 生产环境推荐使用消息队列(RabbitMQ、Redis、Amazon SQS)
- 避免在 Web 请求中直接发送邮件
- 添加重试机制和错误处理
- 监控邮件发送状态和队列长度
- 考虑使用专业的邮件服务(SendGrid、Mailgun、Amazon SES)
完整的异步邮件处理框架
// config.php
return [
'queue' => [
'driver' => 'redis', // 或 rabbitmq
'host' => 'localhost',
'port' => 6379,
],
'mailer' => [
'driver' => 'smtp',
'host' => 'smtp.gmail.com',
'port' => 587,
'username' => 'your@email.com',
'password' => 'your-password',
]
];
// EmailQueue.php
class EmailQueue {
private $queue;
private $config;
public function __construct($config) {
$this->config = $config;
$this->initializeQueue();
}
public function push(Email $email) {
$this->queue->enqueue(serialize($email));
}
public function process() {
while (!$this->queue->isEmpty()) {
$email = unserialize($this->queue->dequeue());
$this->sendEmail($email);
}
}
private function sendEmail(Email $email) {
try {
// 实际的邮件发送逻辑
mail($email->to, $email->subject, $email->body);
$this->logSuccess($email);
} catch (Exception $e) {
$this->logError($email, $e);
// 重新入队或移到失败队列
}
}
}
选择方案时要考虑你的具体需求:并发量、可靠性要求、维护成本等,对于大多数中小型应用,使用 Redis 队列配合后台 worker 进程就足够了。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播