Можно ли отправить сообщение на определенный сеанс?
У меня есть неавторизованный веб-узел между клиентами и сервлет Spring. Мне нужно отправить незапрашиваемое сообщение на конкретное соединение при завершении работы async.
@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
.
UPDATE
Я пробовал это:
messagingTemplate.convertAndSend("/queue/jobend", true, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, sessionId));
Но это транслируется на все сеансы, а не только на указанные.
ОБНОВЛЕНИЕ 2
Протестируйте метод convertAndSendToUser(). Этот тест и взлом официального учебника Spring: 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()
ПРЕДПОСЫЛКА СЦЕНАРИИ
Я хочу создать индикатор выполнения для удаленного задания, клиент запрашивает сервер для работы async и проверяет его прогресс посредством сообщения websocket, отправленного с сервера. Это НЕ загрузка файлов, а удаленное вычисление, поэтому только сервер знает ход выполнения каждого задания. Мне нужно отправить сообщение на конкретный сеанс, потому что каждое задание запускается сеансом. Клиент запрашивает удаленное вычисление Сервер запускает это задание и отвечает за каждый ответ шага задания клиенту-заявителю с его статусом выполнения задания. Клиент получает сообщения о своей работе и создает строку прогресса/состояния. Вот почему мне нужны сообщения за сеанс. Я мог бы также использовать сообщения для каждого пользователя, но Spring не предоставляет для каждого незатребованного сообщения пользователя. (Не удается отправить сообщение пользователя с помощью Spring Websocket)
РАБОЧЕЕ РЕШЕНИЕ
__ __ ___ ___ _ __ ___ _ _ ___ ___ ___ _ _ _ _____ ___ ___ _ _
\ \ / // _ \ | _ \| |/ /|_ _|| \| | / __| / __| / _ \ | | | | | ||_ _||_ _|/ _ \ | \| |
\ \/\/ /| (_) || /| ' < | | | .` || (_ | \__ \| (_) || |__| |_| | | | | || (_) || .` |
\_/\_/ \___/ |_|_\|_|\_\|___||_|\_| \___| |___/ \___/ |____|\___/ |_| |___|\___/ |_|\_|
Начиная с решения UPDATE2, мне пришлось завершить метод convertAndSendToUser с последним параметром (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();
}