solon-net-stomp
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-net-stomp</artifactId>
</dependency>
1、描述
(v3.0.2 后支持)网络扩展插件。提供基础的 stomp-broker 支持。插件提供三个关键的类:
类 | 说明 |
---|---|
StompBroker | 将 WebSocket 协议,转为 Stomp 协议;并提供 Broker 服务 |
StompEmitter | Stomp 消息发射器(用于发消息) |
StompListener | Stomp 监听器 |
@Mapping(...) | Mvc 的方式,接收监听消息 |
@Message | Mvc 请求方法限定注解,类似 @Get 、@Post |
@To(...) | Mvc 消息发射器的“注解”(用于发消息,对应 StompEmitter 能力) |
ToHandlerStompListener | 将 Stomp 事件转为 Solon Handler 通用体系。从而实现控制器模式开发 |
2、三个发送接口
StompEmitter 接口 | 对应的 @To 注解 | 说明 |
---|---|---|
@To("target:destination?") | To 注解表达式(stomp 请求时有效) | |
sendToSession | @To(".:/...") 或@To(".") | 发给当前客户端订阅者 |
sendToUser | @To("user:/...") 或@To("user") | 发给特定用户订阅者 |
sendTo | @To("*:/...") 或@To("*") | 发给代理,再转发给所有订阅者 |
提示:
@To
没有 destination 时,即转发给请求的 destination- 发送或转换时,不会自动增加、或去掉地址片段。
- 发送给用户(
sendToUser
)的特性,需要给连接会话命名才可用(session.nameAs(...)
)。
3、规划 destination 路径
使用 solon stomp 时,一般会有三个参与角色。分别是:
- 客户端(一般是,订阅者)。可用 user 表示
- 服务端代理。可用 broker 表示
- 服务端应用处理(可以是,订阅者)。可用 app 表示
为了让消息可以自由发送,对 destination 路径做个规划(或者约定),使用会更方便、清晰:
路径前缀 | 说明 |
---|---|
/topic/** 或 /queue/** 或别的 | 发给 broker,再转发给所有 “订阅者” |
/app/** 或别的 | user 直接发给 app(不经过 broker) |
/user/** 或别的 | app 直接发给 user(不经过 broker) |
4、使用示例
(1)注册经理人(StompBroker),并实现鉴权和异常监听。
@ServerEndpoint("/chat")
public class ChatStompBroker extends StompBroker implements StompListener {
public ChatStompBroker(){
//可选:添加鉴权监听器(此示例,用本类实现监听)
this.addListener(this);
//必选
this.setBrokerDestinationPrefixes("/topic/");
}
@Override
public void onOpen(StompSession session) {
String user = session.param("user");
if (user == null) {
//签权拒绝
session.close();
} else {
//签权通过;并对会话命名
session.nameAs(user); //命名后,可对 user 发消息
}
}
@Override
public void onFrame(StompSession session, Frame frame) throws Throwable {
//可选:打印所有帧
System.out.println(frame);
}
@Override
public void onError(StompSession session, Throwable error) {
//可选:如果出错,反馈给客户端(比如用 "/user/app/errors")
getEmitter().sendToSession(session,
"/user/app/errors",
new Message(error.getMessage()));
}
}
(2)业务场景应用(用经典的 MVC 模式;与 http 请求差不多,支持 content-type
的序列化自动处理)
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Http;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.annotation.Message;
import org.noear.solon.annotation.To;
@Controller
public class TestController {
@Inject //@Inject("/chat") 多经理人时,指定名字
StompEmitter stompEmitter;
@Message
@Mapping("/app/hello")
@To("*:/topic/greetings")
public Greeting greetings(@Body String msg) throws Exception {
return new Greeting("Hello, " + msg + "!");
}
//有返回值,又没有 To 注解时。则用来源 destination 给 broker 发消息
@Message
@Mapping("/topic/chat/message")
public Hint message(@Header("user") user, ChartMessage message) throws Exception {
return new Hint(user + ",你发送太频繁啦!");
}
@Http
@Mapping("/http/hello")
public void greeting3(Context ctx, HelloMessage message) throws Exception {
String payload = ctx.renderAndReturn(new Greeting("Hello, " + message.getName() + "!"));
stompEmitter.sendTo("/topic/greetings", payload);
}
}
(3)Web 客户端可例用 "stomp.js"(接口参考: https://stomp-js.github.io/api-docs/latest/classes/Client.html )
npm install @stomp/stompjs@7.0.0
import { Client, Versions } from '@stomp/stompjs';
const stompClient = new Client({
webSocketFactory: ()=> new WebSocket('ws://127.0.0.1:8080/chat?user=demo'),
onConnect: function (frame) {
//订阅:所有主题消息(只示例)
stompClient.subscribe('/topic/**', function (resp) {
console.log(resp.body);
});
//订阅:接收 app 返回的错误
stompClient.subscribe('/user/app/errors', function (resp) {
console.log(resp.body);
});
}
stompClient.activate()
stompClient.publish({
destination: "/app/hello",
body: "hi"
});
5、注解使用补充说明
Mapping 说明(当有路径匹配上时)
- 没有 “方式限定”(
@Http
、@Get
、@Message
..)时,@Mapping
表示匹配所有的请求。 - 添加
@Message
时,表示只匹配ctx.method() == 'MESSAGE'
的请求 - 添加
@Http
时,表示匹配ctx.method() == 'GET' | 'POST'..
等 Http 的上下文 - 可以添加多个 “方式限定” 注解
Message 说明
- 跟
@Get
之类的一样,表过请求方式限定
To 说明
- 表示处理结果,转发给另一个目标地址
- 当没有注解,又有返回值时。则转发给来源地址
关于 Mapping 注解,更多可参考:《@Mapping 用法说明》