在 Spring Websocket 消息传递的预定方法中获取主体

Posted

技术标签:

【中文标题】在 Spring Websocket 消息传递的预定方法中获取主体【英文标题】:Get principal inside scheduled method for Spring Websocket messaging 【发布时间】:2016-02-28 09:14:01 【问题描述】:

想要使用 websocket 向特定客户端发送通知。有一个发送通知的计划任务,但无法在该任务中获取 Principal。找到this 帖子,但据我所知,Spring 计划的方法必须是无参数的。

@Scheduled(fixedDelay=5000)
public void sendMessages(Principal principal)
messagingTemplate
    .convertAndSendToUser(principal.getName(), "/queue/horray", "Horray, " + principal.getName() + "!");

这可能吗?如何在预定方法中获取 websocket 主体?

【问题讨论】:

【参考方案1】:

您无法在计划的方法中获取主体,因为方法调用不是由用户发起的。

你可以按照这个方法:

1) 创建一个 websocket 端点“/app/events”

2) 让所有用户订阅那个端点

3) 获取您要发送通知的所有用户 ID

4) 向单个用户发送通知

simpMessagingTemplate.convertAndSendToUser("userId", "/app/events", "messageEntity"); 

userId: can be actual user id if authenticated or it can be websocket session id.

【讨论】:

【参考方案2】:

我为这种情况写了一个解决方法。首先,我为 websockets 事件创建了一个侦听器。在订阅请求的情况下,我将 userId 保留在请求中并保存在 ConcurrentHashMap 中。另一方面,当客户端断开连接或发送取消订阅请求时,我会从该地图中删除他的 userId。

我的监听类:

@Service
public class MyEventListener 

  @Autowired
  private NotificationPublisher notificationPublisher;

  @EventListener(SessionSubscribeEvent.class)
  public void onWebSocketSubscribeEvent(SessionSubscribeEvent event) 
      notificationPublisher.subscribedUsers.put(event.getUser().getName(), Calendar.getInstance().getTimeInMillis());
  

  @EventListener(SessionUnsubscribeEvent.class)
  public void onWebSocketUnsubscribeEvent(SessionUnsubscribeEvent event) 
      notificationPublisher.subscribedUsers.remove(event.getUser().getName());
  

  @EventListener(SessionDisconnectEvent.class)
  public void onWebSocketDisconnectEvent(SessionDisconnectEvent event) 
      notificationPublisher.subscribedUsers.remove(event.getUser().getName());
  

实际作业正在运行的通知发布者类:

public class NotificationPublisher 
  public final Map<String, Long> subscribedUsers = new ConcurrentHashMap<>();

  @Autowired
  private SimpMessagingTemplate messagingTemplate;

  @Autowired
  private MyService myService;

  @Value("$task.notifications.publisher.websocket_timeout_seconds")
  private int websocketSessionTimeout;

  public void sendDataUpdates() 
    SocketResponseCount count = null;

    for(String key: subscribedUsers.keySet()) 
        long subscribeTime = subscribedUsers.get(key);

        if(Calendar.getInstance().getTimeInMillis() - subscribeTime > websocketSessionTimeout*1000) 
            subscribedUsers.remove(key);
            continue;
        

        count = myService.getNotificationsCount(key);
        this.messagingTemplate.convertAndSendToUser(key, "/queue/publish",count);
    
  

也许它会帮助某人

【讨论】:

【参考方案3】:

我的解决方案:我得到所有用户会话

@Autowired
private SimpMessagingTemplate template;
@Autowired
private MyRepository myRepository;
@Autowired
private SessionRegistry sessionRegistry;



@Scheduled(fixedRate = 5000)
public void greeting() 

    List<SessionInformation> activeSessions = new ArrayList<>();

    for (Object principal : sessionRegistry.getAllPrincipals() )
    
        activeSessions.addAll( sessionRegistry.getAllSessions( principal, false ) );
    
    for (SessionInformation session : activeSessions )
    
        Object principalObj = session.getPrincipal();
        if ( principalObj instanceof CurrentUser)
        
            CurrentUser user = (CurrentUser) principalObj;
            this.template.convertAndSendToUser(user.getUsername().toUpperCase(),"/queue/reply2",myRepository.findAll());

        

    

【讨论】:

以上是关于在 Spring Websocket 消息传递的预定方法中获取主体的主要内容,如果未能解决你的问题,请参考以下文章

使用 StompClient 在 Spring WebSocket 上传递消息时出错

Spring websocket 和消息传递支持有多成熟?

Spring STOMP over Websocket - “私人”消息传递

Spring Chapter4 WebSocket 胡乱翻译

如果资源服务器应该是无状态的,如何使用 websocket 将消息发送到队列

如何使用 websocket 检查消息是不是真的在 Netty 中传递?