【问题标题】:How to implement push to client using Java EE 7 WebSockets?如何使用 Java EE 7 WebSockets 实现向客户端推送?
【发布时间】:2014-10-16 16:59:29
【问题描述】:

我浏览了很多 Web Socket 示例、演示幻灯片,它们大多集中在一个相当简单的场景,其中客户端-服务器通信由客户端发起。

我对另一个似乎同样实用的场景感兴趣:纯服务器推送到客户端。

我想到的示例是一个更新网站上股票价值的应用程序。想象一下,有一个外部系统股票交易系统,它为每个订阅的股票价值变化发送一条 JMS 消息。

我想知道如何将此类传入的 JMS 事件转换为服务器推送,并从 Java EE 7 的角度高效且惯用地转换为它。

据我了解的规范,我应该编写一个 Web 套接字端点

@ServerEndpoint("/demo")
public class WSEndpoint {
  private static final Logger LOG = Logger.getLogger(WSEndpoint.class);

  @OnMessage
  public void onMessage(String message, Session session) {
    LOG.info("Received : " + message + ", session:" + session.getId());
  }

  @OnOpen
  public void open(Session session) {
    LOG.info("Open session:" + session.getId());         
  }

  @OnClose
  public void close(Session session, CloseReason c) {
    log.info("Close session:" + session.getId());
  }
}

当我从前端收到消息时,一切都很容易,我可以在@OnMessage 方法中做任何我喜欢的事情。但在我的示例中,我不会从客户端收到任何消息,我会从某个外部系统获得事件。

有几种方法。例如,我可以在@OnOpen 方法中创建一个线程,如this blog 所示。在实践中,这种方法可能会显示出一个缺点,因为我需要为每个客户创建一个新的、可能长期存在的线程。

使用带有选择器的 NIO 通道可以做得更好,但这需要某种“手工”通道管理。可行,但比较麻烦。

另一种解决方案是 ping 其他系统以进行更新,但这又有点难看。此外,我也不确定@OnOpen 方法是否应该以这种方式使用。

理想情况下,传入的 JMS 消息会触发向客户端推送 Web Socket。任何想法如何很好地实现这样的东西?

【问题讨论】:

  • 感谢您的链接。这看起来相当不错(在第 7 点有实际的推送实现)。它是通过以下方式完成的:我们必须创建一个所有会话的集合(静态同步集)。然后,如果我们想向客户端发送推送,我们将通过该集合并触发推送。这种方法有一些缺点,但没有什么是无法克服的。
  • 我们的问题密切相关:stackoverflow.com/questions/27037570/…
  • 以上链接已损坏。我实际上正在尝试做与 OP 相同的事情,但它开始看起来好像不使用某种计时器,或者在第一次连接时从服务器启动推送消息(@OnOpen/@OnWebsocketConnect)是不可能的。

标签: java jakarta-ee websocket java-ee-7


【解决方案1】:

这可能不是最优雅的方式,只是为了展示想法。方法 broadcast() 将向所有连接的客户端发送消息。

@ServerEndpoint("/echo")
public class ServerEndPoint {

    private static Set<Session> userSessions = Collections.newSetFromMap(new ConcurrentHashMap<Session, Boolean>());

    @OnOpen
    public void onOpen(Session userSession) {
        userSessions.add(userSession);
    }

    @OnClose
    public void onClose(Session userSession) {
        userSessions.remove(userSession);
    }

    @OnMessage
    public void onMessage(String message, Session userSession) {
        broadcast(message);
    }

    public static void broadcast(String msg) {
        for (Session session : userSessions) {
            session.getAsyncRemote().sendText(msg);
        }
    }

}

【讨论】:

  • 使用 getAsyncRemote() 还是 getBasicRemote() 方法更好?
【解决方案2】:

我是这样做的(不需要客户请求):

@ServerEndpoint("/hello")
public class HelloWebSocket {

   @OnOpen
   public void greetTheClient(Session session){
       try {
        session.getBasicRemote().sendText("Hello stranger");

    } catch (IOException ioe) {
        System.out.println(ioe.getMessage());
    }
   }
}

【讨论】:

    【解决方案3】:

    将活动的sessionList 存储在另一个类SessionManager 中。

    List<Session> socketSessions = new ArrayList<>();
    

    @OnOpen 中的传入会话添加到列表中。从@OnClose的列表中删除会话

    @OnClose
    public void close(Session session) {
      sessionManager.removeSession(session);
    }
    

    要向所有人发送消息,

    public void broadcast(String message){
        for(Session session: sessionList){
            session.getBasicRemote().sendText(message);
        }
    }
    

    您可以在事件触发的任何地方使用sessionManager.broadcast() 方法。

    这里是一个完整的例子,说明如何使用 websocket 通过 HTML5 websocket API 进行推送。 https://metamug.com/article/java-push-notification-with-websocket.php

    【讨论】:

      【解决方案4】:

      我已经使用 omnifaces 套接字组件实现了与您的问题类似的解决方案。通知由 JMS 消息触发,接收者可能是特定用户或所有用户。博文网址https://konstpan.wordpress.com/2018/03/25/push-notifications-in-a-jee-web-application-secured-by-keycloak/

      【讨论】:

        猜你喜欢
        • 2018-02-27
        • 2021-05-05
        • 1970-01-01
        • 2018-06-03
        • 2012-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-30
        相关资源
        最近更新 更多