Solon v3.0.3

solon-net-stomp

</> markdown
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-net-stomp</artifactId>
</dependency>

1、描述

(v3.0.2 后支持)网络扩展插件。提供基础的 stomp-broker 支持。插件提供三个关键的类:

说明
StompBroker将 WebSocket 协议,转为 Stomp 协议;并提供 Broker 服务
StompEmitterStomp 消息发射器(用于发消息)
StompListenerStomp 监听器
@Mapping(...)Mvc 的方式,接收监听消息
@MessageMvc 请求方法限定注解,类似 @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 用法说明》