Spring WebSocket @SendToSession: Nachricht an bestimmte Sitzung senden
Ist es möglich, eine Nachricht an eine bestimmte Sitzung zu senden?
Ich habe ein nicht authentifiziertes Websocket zwischen Clients und einem Spring-Servlet. Ich muss eine unerwünschte Nachricht an eine bestimmte Verbindung senden, wenn ein asynchroner Job endet.
@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?
}
}
Wie Sie in diesem Code sehen können, kann der Client einen asynchronen Job starten und benötigt nach dessen Abschluss die Endnachricht. Offensichtlich muss ich nur dem Antragsteller eine Nachricht senden und nicht an alle senden. Es wäre toll, ein @ zu hab@SendToSession
annotation odermessagingTemplate.convertAndSendToSession
Methode
AKTUALISIERE
Ich habe es versucht:
messagingTemplate.convertAndSend("/queue/jobend", true, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, sessionId));
Aber dies sendet an alle Sitzungen, nicht nur an die angegebene Sitzung.
UPDATE 2
Test mit der Methode convertAndSendToUser (). Dieser Test ist ein Hack des offiziellen Spring Tutorials:https: //spring.io/guides/gs/messaging-stomp-websocket
Dies ist der Servercode:
@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;
}
und dies ist der Client-Code:
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));
});
});
}
Der Client erhält leider nicht alle 5000 ms die erwartete Antwort pro Sitzung. Ich bin sicher, dass "1" eine gültige Session-ID für den 2. verbundenen Client ist, da ich sie im Debug-Modus mit @ sehSimpMessageHeaderAccessor.getSessionId()
HINTERGRUND-SZENARIO
Ich möchte einen Fortschrittsbalken für einen Remote-Job erstellen. Der Client fragt den Server nach einem Async-Job und überprüft den Fortschritt anhand einer vom Server gesendeten Websocket-Nachricht. Dies ist KEIN Datei-Upload, sondern eine Fernberechnung, sodass nur der Server den Fortschritt jedes Jobs kennt. Ich muss eine Nachricht an eine bestimmte Sitzung senden, da jeder Job von einer Sitzung gestartet wird. Der Client fragt nach einem Remote-Rechenserver, der diesen Job startet, und antwortet dem antragstellenden Client bei jedem Jobschritt mit seinem Jobfortschrittsstatus. Der Client erhält Nachrichten über seinen Job und erstellt eine Statusleiste. Aus diesem Grund benötige ich Nachrichten pro Sitzung. Ich könnte auch Nachrichten pro Benutzer verwenden, aber Springbietet kein pro Benutzer unerwünschte Nachrichten. Kann keine Benutzermeldung mit Spring Websocket senden)
ARBEITSLÖSUNG
__ __ ___ ___ _ __ ___ _ _ ___ ___ ___ _ _ _ _____ ___ ___ _ _
\ \ / // _ \ | _ \| |/ /|_ _|| \| | / __| / __| / _ \ | | | | | ||_ _||_ _|/ _ \ | \| |
\ \/\/ /| (_) || /| ' < | | | .` || (_ | \__ \| (_) || |__| |_| | | | | || (_) || .` |
\_/\_/ \___/ |_|_\|_|\_\|___||_|\_| \___| |___/ \___/ |____|\___/ |_| |___|\___/ |_|\_|
Ausgehend von der UPDATE2-Lösung musste ich die convertAndSendToUser-Methode mit dem letzten Parameter (MessageHeaders) abschließen:
messagingTemplate.convertAndSendToUser("1","/queue/test", new Return("test"), createHeaders("1"));
wocreateHeaders()
ist diese Methode:
private MessageHeaders createHeaders(String sessionId) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}