Spring WebSocket @SendToSession:向特定会话发送消息 [英] Spring WebSocket @SendToSession: send message to specific session
问题描述
是否可以向特定会话发送消息?
Is it possible to send a message to specific session?
我在客户端和Spring servlet之间有一个未经身份验证的websocket。当异步作业结束时,我需要向特定连接发送未经请求的消息。
I have an unauthenticated websocket between clients and a Spring servlet. I need to send an unsolicited message to a specific connection when an async job ends.
@Controller
public class WebsocketTest {
@Autowired
public SimpMessageSendingOperations messagingTemplate;
ExecutorService executor = Executors.newSingleThreadExecutor();
@MessageMapping("/start")
public void start(SimpMessageHeaderAccessor accessor) throws Exception {
String applicantId=accessor.getSessionId();
executor.submit(() -> {
//... slow job
jobEnd(applicantId);
});
}
public void jobEnd(String sessionId){
messagingTemplate.convertAndSend("/queue/jobend"); //how to send only to that session?
}
}
正如您在此代码中看到的,客户端可以启动异步作业,当它完成时,它需要结束消息。显然,我只需要向申请人发送信息,而不是向所有人广播。
拥有 @SendToSession
注释或 messagingTemplate.convertAndSendToSession
方法会很棒。
As you can see in this code, the client can start an async job and when it finishes, it needs the end message. Obviously, I need to message only the applicant and not broadcast to everyone.
It would be great to have an @SendToSession
annotation or messagingTemplate.convertAndSendToSession
method.
更新
我试过这个:
messagingTemplate.convertAndSend("/queue/jobend", true, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, sessionId));
但这会广播到所有会话,而不仅仅是指定的会话。
But this broadcasts to all sessions, not only the one specified.
更新2
使用convertAndSendToUser()方法进行测试。
此测试是官方Spring教程的测试和版本: https:// spring .io / guides / gs / messaging-stomp-websocket /
Test with convertAndSendToUser() method. This test is and hack of the official Spring tutorial: https://spring.io/guides/gs/messaging-stomp-websocket/
这是服务器代码:
@Controller
public class WebsocketTest {
@PostConstruct
public void init(){
ScheduledExecutorService statusTimerExecutor=Executors.newSingleThreadScheduledExecutor();
statusTimerExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
messagingTemplate.convertAndSendToUser("1","/queue/test", new Return("test"));
}
}, 5000,5000, TimeUnit.MILLISECONDS);
}
@Autowired
public SimpMessageSendingOperations messagingTemplate;
}
这是客户代码:
function connect() {
var socket = new WebSocket('ws://localhost:8080/hello');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/user/queue/test', function(greeting){
console.log(JSON.parse(greeting.body));
});
});
}
不幸的是,客户端没有按预期每5000毫秒收到一次会话回复。我确定1是连接的第二个客户端的有效sessionId,因为我在调试模式下看到它与 SimpMessageHeaderAccessor.getSessionId()
Unfortunately client doesn't receive its per-session reply every 5000ms as expected. I'm sure that "1" is a valid sessionId for the 2nd client connected because I see it in debug mode with SimpMessageHeaderAccessor.getSessionId()
背景情景
我想为远程作业创建进度条,客户端要求服务器进行异步job,它通过服务器发送的websocket消息检查其进度。这不是文件上传而是远程计算,因此只有服务器知道每个作业的进度。
我需要向特定会话发送消息,因为每个作业都是由会话启动的。
客户端要求远程计算
服务器启动此作业,并为每个作业步骤回复申请人客户端及其作业进度状态。
客户端获取有关其工作的消息并构建进度/状态栏。
这就是我需要每会话消息的原因。
我也可以使用每用户消息,但Spring 不为每个用户提供未经请求的消息。 (无法使用Spring Websocket发送用户消息)
I want to create a progress bar for a remote job, client asks server for an async job and it checks its progress by websocket message sent from server. This is NOT a file upload but a remote computation, so only server knows the progress of each job. I need to send a message to specific session because each job is started by session. Client asks for a remote computation Server starts this job and for every job step reply to applicant client with its job progress status. Client gets messages about its job and build up a progress/status bar. This is why I need a per-session messages. I could also use a per-user messages, but Spring does not provide per user unsolicited messages. (Cannot send user message with Spring Websocket)
工作解决方案
__ __ ___ ___ _ __ ___ _ _ ___ ___ ___ _ _ _ _____ ___ ___ _ _
\ \ / // _ \ | _ \| |/ /|_ _|| \| | / __| / __| / _ \ | | | | | ||_ _||_ _|/ _ \ | \| |
\ \/\/ /| (_) || /| ' < | | | .` || (_ | \__ \| (_) || |__| |_| | | | | || (_) || .` |
\_/\_/ \___/ |_|_\|_|\_\|___||_|\_| \___| |___/ \___/ |____|\___/ |_| |___|\___/ |_|\_|
从UPDATE2解决方案开始,我必须完成convertAndSendToUser方法last param(MessageHeaders):
Starting from the UPDATE2 solution I had to complete convertAndSendToUser method with last param (MessageHeaders):
messagingTemplate.convertAndSendToUser("1","/queue/test", new Return("test"), createHeaders("1"));
其中 createHeaders()
是这种方法:
private MessageHeaders createHeaders(String sessionId) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}
无需创建特定目的地,从Spring 4.1开始就已经开箱即用(参见 SPR-11309 。
No need to create specific destinations, it's already done out of the box as of Spring 4.1 (see SPR-11309).
给定用户订阅 / user / queue / something
队列,您可以通过以下方式向单个会话发送消息:
Given users subscribe to a /user/queue/something
queue, you can send a message to a single session with:
As 在SimpMessageSendingOperations的Javadoc 说,因为你的用户名实际上是一个的sessionId,你必须将其设置为标题,否则 DefaultUserDestinationResolver
将无法路由邮件并将其删除。
As stated in the SimpMessageSendingOperations Javadoc, since your user name is actually a sessionId, you MUST set that as a header as well otherwise the DefaultUserDestinationResolver
won't be able to route the message and will drop it.
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor
.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
messagingTemplate.convertAndSendToUser(sessionId,"/queue/something", payload,
headerAccessor.getMessageHeaders());
您无需为此用户进行身份验证。
You don't need users to be authenticated for this.
这篇关于Spring WebSocket @SendToSession:向特定会话发送消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!