一佳技术

easy-notification 使用文档

easy-notification 是基于 easy-socketio 的独立部署通知中间件(fatjar)。服务端提供三级房间协议(user:{userCode} / product:{code} / broadcast)、握手鉴权 SPI、Redis 集群横向扩容;业务侧通过 NotificationPusher 推送事件。


1. 构建与启动

mvn package -pl middlewares/easy-notification

# 单机模式
java -jar middlewares/easy-notification/target/easy-notification-1.2.1-SNAPSHOT.jar

# 集群模式(多实例 + Redis 房间同步)
java -jar easy-notification.jar \
  --socketio.cluster.enabled=true \
  --spring.data.redis.host=192.168.x.x

启动成功标志:

SocketIO server started at port: 9092
Started NotificationApplication in 2.4 seconds

2. 配置项

配置项默认值说明
socketio.enabledtrue(fatjar 内置)服务开关;嵌入到业务服务时默认 false,需显式开启
socketio.hostname0.0.0.0监听地址
socketio.port9092服务端口(无 web 容器,这就是唯一端口)
socketio.namespace/命名空间;推送与监听统一经 resolveNamespace 处理
socketio.auth-requiredfalsetrue 时握手未携带有效 token 拒绝连接
socketio.cluster.enabledfalse集群开关;开启后房间经 Redis Pub/Sub 跨节点同步
spring.data.redis.*localhost:6379集群模式的 Redis 连接(标准 Spring Boot 配置)

3. 客户端接入协议

// socket.io client v4(EIO4)
const socket = io("http://host:9092", { auth: { token: "xxx" } });

// 订阅产品房间(服务端经 AuthListener.canSubscribe 校验)
socket.emit("subscribe", { room: "product:PRD-001" });
socket.emit("unsubscribe", { room: "product:PRD-001" });

// 收推送
socket.on("order.paid", (data) => { ... });

// chat 示例(房间内广播)
socket.emit("chat", { room: "product:PRD-001", nickname: "张三", content: "hello" });
socket.on("chat", (msg) => { ... });

握手成功后自动加入:user:{userCode}(鉴权通过)+ broadcast(全员);匿名连接只进 broadcast。

4. 服务端推送

嵌入模式(业务服务与通知同进程):业务服务引入 io.matrix:easy-notification 依赖,直接注入推送门面:

@Autowired NotificationPusher pusher;

pusher.toUser("u-1001", "order.paid", payload);    // 个人
pusher.toProduct("PRD-001", "status", payload);    // 产品房间订阅者
pusher.broadcast("notice", payload);               // 全员
pusher.toRoom("custom-room", "event", payload);    // 自定义房间

独立部署模式:通知中心单独跑 fatjar,业务侧推送经领域事件桥接(业务发布 NotificationEvt → 通知中心订阅 → NotificationPusher 投递)——桥接 handler 待建,见 DESIGN §七。

5. 鉴权接入

提供 AuthListener Bean 即接入(组件层 SPI):

@Component
public class PulseAuthListener implements AuthListener {
    @Override
    public String authenticate(String token) {
        return authorizeService.verify(token);  // 返回 userCode,失败返回 null
    }
    @Override
    public boolean canSubscribe(String userCode, String room) {
        return roomService.hasPermission(userCode, room);
    }
}

fatjar 内无实现时保持 auth-required: false,客户端以 anonymous 进 broadcast 房间。

6. 注意事项

项说明
旧客户端协议迁移旧 operations 事件(join/leave)已废弃,改用 subscribe/unsubscribe;旧 action 事件已删除
集群序列化器集群模式 Pub/Sub 消息经 RedisTemplate 序列化,需保留类型信息(JDK 序列化或带 defaultTyping 的 Jackson),否则跨节点反序列化失败
默认 JDK 17fatjar 按框架统一 JDK17 编译,运行环境需 JDK 17+
端口无 web 容器,server.port 无效,只有 socketio.port

7. 常见问题

Q:为什么业务服务嵌入模式也要依赖 easy-socketio? easy-notification 只含业务层,服务器装配/房间协议/Redis 分布式全部由 easy-socketio 提供,版本(netty-socketio 2.0.14)经它传递,请勿自行声明 netty-socketio 版本。

Q:集群模式多实例下,推送会重复吗? 不会。RedisStoreFactory 的 Pub/Sub 消息带 nodeId,本节点事件不回环;toUser 房间同步后各实例都能命中本机连接,单条消息只投一次。

Q:如何自定义 namespace? 配置 socketio.namespace=/devops 即可,推送与监听自动对齐;客户端连接 URL 需带同名 namespace。