我有这个代码:客户端使用javascript:
socket = new SockJS(context.backend + '/myWebSocketEndPoint');
stompClient = Stomp.over(socket);
stompClient.connect({},function (frame) {
stompClient.subscribe('/queue/'+clientId+'/notification', function(response){
alert(angular.fromJson(response.body));
});
});
在此代码中,客户端在连接时,使用' / queue /' +他的客户端ID +' / notification /订阅接收通知。所以我为每个客户都排队。我使用stock与sockjs
在我的服务器(Java + spring boot)中,我有一个通知监听器,当事件发布时,它会向所有客户端发送通知。所以我有:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/queue");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/myWebSocketEndPoint")
.setAllowedOrigins("*")
.withSockJS();
}
}
类MenuItemNotificationChannel,它调用MenuItemNotificationSender将通知发送给用户。
@Component
public class MenuItemNotificationChannel extends AbstractNotificationChannel {
@Autowired
private MenuItemNotificationSender menuItemNotificationSender;
@Autowired
private UserRepository userRepository;
@Override
public void sendNotification(KitaiEvent<?> event, Map<String, Object> notificationConfiguration) throws Exception {
String menuItem = Optional.ofNullable((String) notificationConfiguration.get(MENU_ENTRY_KEY)).orElseThrow(IllegalArgumentException::new);
List<User> userList = userRepository.findAll();
for(User u: userList){
menuItemNotificationSender.sendNotification(new MenuItemDto(menuItem),u.getId());
}
MenuItemNotificationSender类是:
@Component
public class MenuItemNotificationSender {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Autowired
public MenuItemNotificationSender(SimpMessagingTemplate messagingTemplate){
this.messagingTemplate = messagingTemplate;
}
public void sendNotification(MenuItemDto menuItem,Long id) {
String address = "/queue/"+id+"/notification";
messagingTemplate.convertAndSend(address, menuItem);
}
}
此代码完美无缺:将通知发送给每个用户。但是,如果用户不在线,则会丢失通知。我的问题是:
如何验证whit stomp什么订阅是活动的,什么不是? (如果我可以验证订阅是否有效,我解决了我的问题,因为我保存用户离线通知,然后在登录时发送)
我可以使用持久队列吗? (我读了一些关于它的东西,但我不明白我是否只能用它来踩踏板和袜子)
抱歉我的英文! :d
答案 0 :(得分:1)
您可以在会话连接事件和会话断开连接事件上放置一个spring事件侦听器 我用弹簧4.3.4测试了这个。
@Component
public class WebSocketSessionListener
{
private static final Logger logger = LoggerFactory.getLogger(WebSocketSessionListener.class.getName());
private List<String> connectedClientId = new ArrayList<String>();
@EventListener
public void connectionEstablished(SessionConnectedEvent sce)
{
MessageHeaders msgHeaders = sce.getMessage().getHeaders();
Principal princ = (Principal) msgHeaders.get("simpUser");
StompHeaderAccessor sha = StompHeaderAccessor.wrap(sce.getMessage());
List<String> nativeHeaders = sha.getNativeHeader("userId");
if( nativeHeaders != null )
{
String userId = nativeHeaders.get(0);
connectedClientId.add(userId);
if( logger.isDebugEnabled() )
{
logger.debug("Connessione websocket stabilita. ID Utente "+userId);
}
}
else
{
String userId = princ.getName();
connectedClientId.add(userId);
if( logger.isDebugEnabled() )
{
logger.debug("Connessione websocket stabilita. ID Utente "+userId);
}
}
}
@EventListener
public void webSockectDisconnect(SessionDisconnectEvent sde)
{
MessageHeaders msgHeaders = sde.getMessage().getHeaders();
Principal princ = (Principal) msgHeaders.get("simpUser");
StompHeaderAccessor sha = StompHeaderAccessor.wrap(sde.getMessage());
List<String> nativeHeaders = sha.getNativeHeader("userId");
if( nativeHeaders != null )
{
String userId = nativeHeaders.get(0);
connectedClientId.remove(userId);
if( logger.isDebugEnabled() )
{
logger.debug("Disconnessione websocket. ID Utente "+userId);
}
}
else
{
String userId = princ.getName();
connectedClientId.remove(userId);
if( logger.isDebugEnabled() )
{
logger.debug("Disconnessione websocket. ID Utente "+userId);
}
}
}
public List<String> getConnectedClientId()
{
return connectedClientId;
}
public void setConnectedClientId(List<String> connectedClientId)
{
this.connectedClientId = connectedClientId;
}
}
连接客户端时,在客户端ID中添加客户端ID;断开连接时将其删除
然后,您可以将此bean或其List注入您要检查客户端是否处于活动状态的位置,然后您可以检查客户端ID是否在您可以发送消息的已连接客户端ID之间,否则您必须保存它并稍后重新发送
在客户端,您可以执行以下操作:
var socket = new SockJS('/ws');
stompClient = Stomp.over(socket);
stompClient.connect({userId:"customUserId"}, function (frame) {
});
安吉洛
答案 1 :(得分:0)
为什么不使用下面的某些事件,您可以将类导出到不同的文件,并使用SessionConnectedEvent
和SessionDisconnectEvent
或SessionSubscribeEvent
和SessionUnsubscribeEvent
。
请参阅此处的文档http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html#websocket-stomp-appplication-context-events
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
import org.springframework.web.socket.messaging.SessionSubscribeEvent;
import org.springframework.web.socket.messaging.SessionUnsubscribeEvent;
@Component
public class SessionConnectedListener extends SessionsListener implements ApplicationListener<SessionConnectedEvent> {
@Override
public void onApplicationEvent(SessionConnectedEvent event) {
users.add(event.getUser().getName());
}
}
@Component
class SessionDisconnectListener extends SessionsListener implements ApplicationListener<SessionDisconnectEvent> {
@Override
public void onApplicationEvent(SessionDisconnectEvent event) {
users.remove(event.getUser().getName());
}
}
@Component
class SessionSubscribeListener extends SessionsListener implements ApplicationListener<SessionSubscribeEvent> {
@Override
public void onApplicationEvent(SessionSubscribeEvent event) {
users.add(event.getUser().getName());
}
}
@Component
class SessionUnsubscribeListener extends SessionsListener implements ApplicationListener<SessionUnsubscribeEvent> {
@Override
public void onApplicationEvent(SessionUnsubscribeEvent event) {
users.remove(event.getUser().getName());
}
}
class SessionsListener {
protected List<String> users = Collections.synchronizedList(new LinkedList<String>());
public List<String> getUsers() {
return users;
}
}
并更改您的代码:
@Autowired
private SessionsListener sessionsListener;
@Override
public void sendNotification(KitaiEvent<?> event, Map<String, Object> notificationConfiguration) throws Exception {
String menuItem = Optional.ofNullable((String) notificationConfiguration.get(MENU_ENTRY_KEY)).orElseThrow(IllegalArgumentException::new);
List<String> userList = sessionsListener.getUsers();
for(String u: userList){
menuItemNotificationSender.sendNotification(new MenuItemDto(menuItem),u);
}