用户目的地
应用程序可以发送针对特定用户的消息,而Spring的STOMP支持能够识别以/user/为前缀的目的地来实现这一功能。例如,客户端可能会订阅/user/queue/position-updates目的地。UserDestinationMessageHandler负责处理这个目的地,并将其转换为对用户会话唯一的目的地(如/queue/position-updates-user123)。这样既方便了用户订阅通用名称的目的地,同时又能确保不会与其他订阅相同目的地的用户发生冲突,从而让每个用户都能接收到唯一的股票持仓更新信息。
在处理用户目标时,重要的是要按照启用STOMP中的说明来配置代理和应用程序目标前缀,否则代理将会处理那些本应由UserDestinationMessageHandler处理的、带有"/user"前缀的消息。
在发送端,消息可以被发送到诸如 /user/{username}/queue/position-updates 这样的目的地,然后通过 UserDestinationMessageHandler 将这些消息转换为一个或多个目的地,每个与用户关联的会话对应一个目的地。这样,应用程序中的任何组件都可以发送针对特定用户的消息,而无需知道更多信息,只需用户的名字和通用的目的地即可。这一功能也通过注释和消息模板得到支持。
一种消息处理方法可以通过@SendToUser注解将消息发送给与所处理的消息关联的用户(该注解在类级别也受支持,以便共享一个共同的目标),如下例所示:
@Controller
public class PortfolioController {
@MessageMapping("/trade")
@SendToUser("/queue/position-updates")
public TradeResult executeTrade(Trade trade, Principal principal) {
// ...
return tradeResult;
}
}
如果用户有多个会话,那么默认情况下,所有订阅到该目标的所有会话都会被覆盖。然而,有时可能只需要覆盖发送当前正在处理的消息的会话。你可以通过将broadcast属性设置为false来实现这一目标,如下例所示:
@Controller
public class MyController {
@MessageMapping("/action")
public void handleAction() throws Exception{
// raise MyBusinessException here
}
@MessageExceptionHandler
@SendToUser(destinations="/queue/errors", broadcast=false)
public ApplicationError handleException(MyBusinessException exception) {
// ...
return appError;
}
}
虽然用户目的地通常意味着有经过认证的用户,但这并非强制要求。一个未与经过认证的用户关联的WebSocket会话也可以订阅用户目的地。在这种情况下,@SendToUser注解的行为与broadcast=false时完全相同(即,仅针对发送所处理消息的会话进行发送)。
你可以通过任何应用程序组件发送消息到用户目的地,例如,通过注入由Java配置或XML命名空间创建的Simp MessagingTemplate。(如果需要使用@Qualifier进行限定,则bean名为brokerMessagingTemplate。)以下示例展示了如何实现这一点:
@Service
public class TradeServiceImpl implements TradeService {
private final SimpMessagingTemplate messagingTemplate;
@Autowired
public TradeServiceImpl(SimpMessagingTemplate messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
// ...
public void afterTradeExecuted(Trade trade) {
this.messagingTemplate.convertAndSendToUser(
trade.getUserName(), "/queue/position-updates", trade.getResult());
}
}
当您将用户目的地与外部消息代理一起使用时,应查阅代理文档以了解如何管理不活跃的队列,以便在用户会话结束时,所有独特的用户队列都能被删除。例如,当您使用诸如 /exchange/amq.direct/position-updates 这样的目的地时,RabbitMQ 会创建自动删除队列。因此,在这种情况下,客户端可以订阅 /user/exchange/amq.direct/position-updates。同样,ActiveMQ 也有 配置选项 用于清除不活跃的目的地。
在多应用服务器的场景中,用户目的地可能无法被解析,因为用户连接的是另一台服务器。在这种情况下,您可以配置一个目的地来广播未解析的消息,以便其他服务器有机会尝试处理这些消息。这可以通过Java配置中的MessageBrokerRegistry的userDestinationBroadcast属性以及XML中message-broker元素的user-destination-broadcast属性来实现。