本文目录导读:

我来详细讲解如何在Symfony项目中使用Mercure实现实时推送功能。
Mercure简介
Mercure是一个实时推送协议,基于Server-Sent Events (SSE)构建,用于实现服务器到客户端的实时数据推送。
安装和配置
安装Mercure组件
composer require symfony/mercure-bundle
配置Mercure
config/packages/mercure.yaml
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%' # Mercure Hub地址
public_url: '%env(MERCURE_PUBLIC_URL)%' # 公开URL
jwt:
secret: '%env(MERCURE_JWT_SECRET)%' # JWT密钥
publish: '*'
subscribe: '*'
环境变量配置
.env文件
# Docker环境 MERCURE_URL=http://mercure:3000/.well-known/mercure MERCURE_PUBLIC_URL=https://example.com/.well-known/mercure MERCURE_JWT_SECRET=your-secret-key # 或使用Symfony Docker MERCURE_URL=http://localhost:3000/.well-known/mercure MERCURE_PUBLIC_URL=http://localhost:3000/.well-known/mercure
发布消息
在控制器中发布消息
// src/Controller/NotificationController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class NotificationController extends AbstractController
{
#[Route('/send-notification', name: 'send_notification')]
public function sendNotification(HubInterface $hub): Response
{
// 创建更新消息
$update = new Update(
'https://example.com/notifications', // Topic
json_encode([ // Data
'message' => '新的通知!',
'type' => 'info',
'timestamp' => time()
])
);
// 发布到Hub
$hub->publish($update);
return $this->json(['status' => 'success']);
}
}
带Cookie认证的发布
// 使用Cookie进行认证
use Symfony\Component\Mercure\Jwt\TokenFactoryInterface;
class AdminController extends AbstractController
{
#[Route('/admin/broadcast', name: 'admin_broadcast')]
public function broadcast(
HubInterface $hub,
TokenFactoryInterface $tokenFactory
): Response {
// 生成JWT令牌
$token = $tokenFactory->create([
'mercure' => [
'publish' => ['*']
]
]);
// 设置Cookie
$response = new Response();
$response->headers->setCookie(
new Cookie(
'mercureAuthorization',
$token,
time() + 3600,
'/.well-known/mercure',
null,
false,
true,
false,
'lax'
)
);
return $response;
}
}
前端订阅实现
使用EventSource API
// public/js/mercure-subscriber.js
class MercureSubscriber {
constructor(hubUrl, topics) {
this.hubUrl = hubUrl;
this.topics = topics;
this.eventSource = null;
}
connect() {
// 构建URL
const url = new URL(this.hubUrl);
this.topics.forEach(topic => {
url.searchParams.append('topic', topic);
});
// 建立连接
this.eventSource = new EventSource(url.toString());
// 处理消息
this.eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
this.handleMessage(data);
};
// 处理连接打开
this.eventSource.onopen = () => {
console.log('Mercure连接已建立');
};
// 处理错误
this.eventSource.onerror = (error) => {
console.error('Mercure连接错误:', error);
// 自动重连
setTimeout(() => this.connect(), 5000);
};
}
handleMessage(data) {
// 在这里处理接收到的消息
switch(data.type) {
case 'info':
this.showNotification(data.message);
break;
case 'warning':
this.showWarning(data.message);
break;
case 'error':
this.showError(data.message);
break;
default:
console.log('未知消息类型:', data);
}
}
showNotification(message) {
// 显示通知
const notification = document.createElement('div');
notification.className = 'alert alert-info';
notification.textContent = message;
document.getElementById('notifications').appendChild(notification);
// 自动移除
setTimeout(() => notification.remove(), 5000);
}
disconnect() {
if (this.eventSource) {
this.eventSource.close();
}
}
}
// 使用示例
const subscriber = new MercureSubscriber(
'http://localhost:3000/.well-known/mercure',
['https://example.com/notifications']
);
subscriber.connect();
Fetch API方式(现代浏览器)
// 使用Fetch API的流式响应
async function subscribeToMercure(hubUrl, topics) {
const url = new URL(hubUrl);
topics.forEach(topic => {
url.searchParams.append('topic', topic);
});
try {
const response = await fetch(url.toString(), {
headers: {
'Accept': 'text/event-stream',
'Cache-Control': 'no-cache'
}
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
// 解析SSE格式
const events = parseSSEChunk(chunk);
events.forEach(event => {
if (event.data) {
handleEvent(JSON.parse(event.data));
}
});
}
} catch (error) {
console.error('连接失败:', error);
// 自动重连
setTimeout(() => subscribeToMercure(hubUrl, topics), 5000);
}
}
function parseSSEChunk(chunk) {
const events = [];
const lines = chunk.split('\n');
let currentEvent = {};
for (const line of lines) {
if (line.startsWith('data: ')) {
currentEvent.data = line.slice(6);
} else if (line.startsWith('event: ')) {
currentEvent.event = line.slice(7);
} else if (line === '') {
if (Object.keys(currentEvent).length > 0) {
events.push(currentEvent);
currentEvent = {};
}
}
}
return events;
}
高级用法
带认证的私有Topic
// 创建私有Topic的更新
class PrivateNotificationController extends AbstractController
{
#[Route('/private-notification/{userId}', name: 'private_notification')]
public function sendPrivateNotification(
int $userId,
HubInterface $hub
): Response {
// 为用户创建私有Topic
$topic = sprintf('https://example.com/user/%d/notifications', $userId);
$update = new Update(
$topic,
json_encode([
'message' => '这是私有消息',
'userId' => $userId
]),
true // private标志
);
$hub->publish($update);
return $this->json(['status' => 'sent']);
}
}
批量发布消息
use Symfony\Component\Mercure\Update;
use Symfony\Component\Mercure\HubInterface;
class BatchNotificationController extends AbstractController
{
#[Route('/batch-notification', name: 'batch_notification')]
public function batchNotification(HubInterface $hub): Response
{
$updates = [];
// 创建多个更新
for ($i = 0; $i < 10; $i++) {
$updates[] = new Update(
'https://example.com/notifications',
json_encode([
'id' => $i,
'message' => "消息 #{$i}",
'timestamp' => time()
])
);
}
// 批量发布
foreach ($updates as $update) {
$hub->publish($update);
}
return $this->json(['status' => 'batch sent', 'count' => count($updates)]);
}
}
Symfony Messenger集成
// src/Message/NotificationMessage.php
class NotificationMessage
{
public function __construct(
private string $topic,
private array $data
) {}
public function getTopic(): string
{
return $this->topic;
}
public function getData(): array
{
return $this->data;
}
}
// src/MessageHandler/NotificationHandler.php
class NotificationHandler implements MessageHandlerInterface
{
public function __construct(
private HubInterface $hub
) {}
public function __invoke(NotificationMessage $message)
{
$update = new Update(
$message->getTopic(),
json_encode($message->getData())
);
$this->hub->publish($update);
}
}
// 在控制器中发送
class SomeController extends AbstractController
{
public function someAction(MessageBusInterface $bus): Response
{
$bus->dispatch(new NotificationMessage(
'https://example.com/notifications',
['message' => '异步通知']
));
return $this->json(['status' => 'queued']);
}
}
Docker配置
# docker-compose.yml
version: '3.8'
services:
mercure:
image: dunglas/mercure
restart: unless-stopped
ports:
- "3000:3000"
environment:
SERVER_NAME: ':3000'
MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_EXTRA_DIRECTIVES: |-
cors_origins http://localhost:8080
anonymous
volumes:
- mercure_data:/data
networks:
- app-network
volumes:
mercure_data:
networks:
app-network:
driver: bridge
安全注意事项
JWT安全配置
# config/packages/mercure.yaml
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish:
- 'https://example.com/notifications'
- 'https://example.com/users/*'
subscribe:
- 'https://example.com/users/{subscriber}'
验证订阅者身份
// 验证订阅者身份
class MercureSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
MercureEvents::PUBLISH => 'onPublish',
];
}
public function onPublish(MercureEvent $event)
{
$update = $event->getUpdate();
// 检查发布者权限
if (!$this->security->isGranted('ROLE_ADMIN')) {
$event->setPropagationStopped(true);
}
}
}
最佳实践
- 使用HTTPS:生产环境必须使用HTTPS
- 合理设置超时:配置适当的SSE超时时间
- 错误处理:实现重连机制和错误处理
- 负载均衡:使用Redis或其他共享状态支持多实例
- 监控:监控Mercure Hub的健康状态
- 主题命名规范:使用标准的URL格式命名Topic
- 数据压缩:对于大量数据考虑压缩传输
这样配置完成后,你的Symfony项目就具备了完整的Mercure实时推送能力。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播