Mercure 实时推送
Doggy 基于 Mercure 协议实现服务端实时推送。Mercure Hub 运行在独立 Docker 容器中(dunglas/mercure),应用通过 Caddyfile 配置集成。
架构
客户端 (EventSource)
↑ SSE
└── http://localhost:8000/.well-known/mercure
↑
Caddyfile (FrankenPHP)
↑ 代理 Hub 请求
docker:mercure (dunglas/mercure)Docker 部署
yaml
# docker-compose.yml
mercure:
image: dunglas/mercure
environment:
MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_EXTRA_DIRECTIVES: cors_origins http://127.0.0.1:8000
command: /usr/bin/caddy run --config /etc/caddy/dev.CaddyfileCaddyfile 配置
FrankenPHP 的 Caddyfile 中嵌入 Mercure Hub:
caddy
http://localhost:8000 {
route /.well-known/mercure {
mercure {
transport bolt {
path /var/www/var/mercure.db
}
publisher_jwt !ChangeThisMercureHubJWTSecretKey!
subscriber_jwt !ChangeThisMercureHubJWTSecretKey!
cors_origins http://localhost:8000
publish_origins *
anonymous
}
}
}JWT 令牌
MercureTokenFactory 生成带订阅权限的 JWT:
php
// src/Security/MercureTokenFactory.php
$token = $factory->createSubscribeToken(
topics: ['/user/123/*', '/entity/*'],
roles: ['ROLE_USER'],
expiresIn: 3600
);Mercure Cookie
MercureCookieSubscriber 在每个响应中注入 mercureAuthorization Cookie:
php
// 自动生成的订阅主题
$topics = [
'https://enterprise.local/user/' . $userId . '/export',
'/user/' . $userId . '/*',
'/entity/*',
];前端订阅
javascript
const url = new URL('/.well-known/mercure', window.location.origin);
url.searchParams.append('topic', '/user/' + userId + '/*');
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
// 处理推送消息
};核心应用
- 导出进度:大文件导出实时进度推送
- 在线状态:
PresenceService+PresenceApiController - 实时通知:系统消息、审批提醒
- 数据同步:多用户协作实时更新