关注我的公众号:【编程朝花夕拾】,可获取首发内容。
01 引言
前一段时间一直做推送业务,于是整理了一个项目:Stream-Nexus。但是想要表达的东西总是达不到自己想要的效果,于是这两天一直折腾。之前在GitHub上托管了一版,总觉的差点意思,现在终于可以确定下来,不会大改了。后续想要有趣的功能会持续集成。本节就重新介绍一下这个项目。
02 简介
Stream-Nexus中文叫流枢,一个让消息以流的形式持续流动、由中心枢纽统一调度和路由的推送系统。只用来接收订阅和推送消息给客户端。至于从那里推送,可以通过客户端接入的方式任意推送。

项目结构:
text
stream-nexus
├── pom.xml # 父 POM
├── .dockerignore # 构建上下文瘦身
├── docs/ # 使用说明文档
├── nexus-client/ # 独立的客户端
├── nexus-common/ # 跨模块契约(消息体、枚举、常量、入参出参)
├── nexus-sse/ # SSE 推送服务实现(端口 8088)
└── nexus-websocket/ # WebSocket 推送服务实现【待优化】
nexus-common:跨模块公用参数nexus-sse:SSE推送服务nexus-websocket:Websocket推送服务nexus-client:独立的客户端,引入业务项目快速推送消息
GitHub地址:github.com/simonking-w...
03 SSE推送服务
3.1 简介
nexus-sse基于原生EventSource,以SpringBoot4.x为基座实现了极简的推送服务。业务系统通过 HTTP 灌消息,浏览器用 EventSource 收消息。
- 零中间件依赖:连接表与消息 ID 全在内存,不需要
Redis / MQ - 单机
java -jar即可跑,接入成本极低 - 适合中小规模实时推送场景
⚠️ 受浏览器同源策略影响,同源仅支持 6 个 SSE 长连接,后续计划集成 WebSocket 模块来突破这个限制。
3.2 特点
- 客户端的的订阅、发送消息增加了鉴权,并按应用限制模块白名单
- 心跳保活,服务端每
15s下发PING,客户端回PONG。防止 LB / NAT 空闲断链 - 管理页面需要登录才能进入
/admin连接台账 + 应用管理,/console推送测试页,也可以测试其他EventSource连接。- 推送页面增加动态监听
event事件标签,用来测试外部自定事件 - 页面主题增加主题切换:亮色背景/暗色背景
- 部署支持
Docker部署
3.3 效果实例
登录页

管理界面

推送页面

亮色主题

04 Websocket服务
4.1 简介
nexus-websocket 与 SSE 通道定位互补:SSE 是「HTTP 生态、单向、浏览器原生重连」,WebSocket 是「全双工、可上行、适合高频交互」。两者协议与端口完全独立,可同时部署。
nexus-websocket内置了TCP和websocket服务端。websocket用来和前端建立建立,而TCP作为转达层,通过业务系统的TCP客户单发送消息,然后有TCP服务端直接通过调用WebSocket服务推送给前端。
这个实践是来源于之前发布的一篇文章实现的:<<神了,WebSocket竟然可以这么设计!>>
WebSocket 服务独立部署,业务系统不写任何 Netty / WebSocket 服务端代码,而是通过「REST 接口(或 TCP 长连接)」把消息交给它,由它转发给终端。
text
浏览器 / H5 / App
│ ws://host:9090/ws?modules=test
▼
nexus-websocket(独立部署,一个进程三组端口)
├── 9090 Netty WebSocket ------ 终端长连接
├── 9091 Netty TCP ------ 业务系统长连接(连上即可推,只对内网开放)
└── 8089 HTTP ------ REST 推送入口 / 管理界面 / 测试页 / 运维接口
▲
│ HTTP POST /ws/push(短连接)或 TCP 长连接(9091)
业务系统 ← nexus-client(或直接 HTTP 调用)
4.2 特点
- 除了
TCP推送以外,还支持Rest调用,Rest调用增加鉴权 - 客户单的建立支持参数,用来区分不同模块
- 四条判据(回调 / 心跳超时 / 软重置 / 写入失败)统一回收,避免半开连接堆积
4.3 示例
nexus-websocket目前还没有细化,只是作出了大致框架,页面的展示同nexus-sse。


05 SDK客户端
nexus-client是nexus-sse / nexus-websocket 推送服务的客户端 SDK ,一次 HTTP POST 或一条 TCP 长连接就能把消息推到浏览器, 完全不需要编写任何 SSE / Netty / WebSocket 服务端代码。
传统做法里,每个业务系统都要复制一份长连接服务端代码(SseEmitter / ServerBootstrap、握手、ChannelGroup......), N 个系统就是 N 份重复实现。现在不需要了,只要引入依赖就可以无门槛使用。
为了能够适配低版本的JDK,nexus-client按照Java 8打包。
5.1 使用
xml
<dependency>
<groupId>com.simonking.nexus</groupId>
<artifactId>nexus-client</artifactId>
<version>1.0.0</version>
</dependency>
这个目前还没有发布到中央仓库,后续可以考虑发布到中央仓库,方便使用。
5.2 使用示例
java
// 1. 构造:两个服务各一组「地址 + 鉴权」,用哪个填哪个(只推 SSE 就可以不写 ws*)
NexusRestClient client = NexusRestClient.builder()
.sseBaseUrl("http://10.0.0.8:8088").sseAppId("order-service").sseApiKey("sse-key")
.wsBaseUrl("http://10.0.0.8:8089").wsAppId("order-service").wsApiKey("ws-key")
.build();
// 2. 推:按终端类型挑方法,一次 HTTP POST,同步拿回执
Map<String, Object> data = new HashMap<>();
data.put("orderId", 1001);
data.put("amount", 99.9);
PushRequest request = PushRequest.builder().bizModule("order").action("CREATE").data(data).build();
PushResult sseResult = client.ssePush(request); // 推给 SSE 终端(POST /sse/push)
PushResult wsResult = client.wsPush(request); // 推给 WebSocket 终端(POST /ws/push)
System.out.println("命中 " + wsResult.getTotal() + " 条,成功 " + wsResult.getSuccess() + " 条");
// 3. 应用退出时释放(停调度线程、清连接池)
client.close();