Jetty 中 WebSocketListener 的轻量级 IPC

Lightweight IPC to WebSocketListener in Jetty

Android、iOS 和桌面浏览器客户端目前每隔几秒轮询一个 PHP-后端(利用 CentOS Linux 上的 PostgreSQL 数据库)。

我想通过使用独立 Jetty Websocket Server 来通知客户新数据可用于在后端获取。

所以在自定义 WebSocketListener 中,我对连接的客户端进行身份验证并将它们存储在 ConcurrentHashMap<String,Session>:

public class MyListener implements WebSocketListener
{
    private Session mSession;

    @Override
    public void onWebSocketConnect(Session session) {
        mSession = session;
    }

    @Override
    public void onWebSocketText(String message) {
        if (mSession != null && mSession.isOpen()) {
            // 1. validate client id and password
            // 2. store client id and session into Map
        }
    }

我的问题:如何通知已连接(通过 websockets)的客户端?

即在 PHP-scripts 中,我想 运行 一个轻量级程序 java -jar MyNotify.jar client-1234 来告诉 Jetty 独立服务器:

Hey, there is new data available for the client-1234 at the database!

Please send it a short message over websockets by calling MyMap.get("client-1234").getRemote().sendString("hey", null);

你必须把你的

ConcurrentHashMap<String,Session> sessionMap. 

进入自定义 javax.servlet.ServletContextEvent 上的 public 静态字段。应在事件

上初始化字段
  @Override
    public void contextInitialized(ServletContextEvent ctx) {

然后在您应用程序的任何位置,您都可以以正常方式(使用点语法)访问此静态字段。

因为 contextInitialized 在任何 servlet 或 websockets 方法(get、put、onMessage)之前触发,所以 map 将在那里。同样是concurrent map,里面应该没有重复的id。

当然,您还需要清理会话映射的策略。总而言之,您必须与来自 javax.servlet API.

的事件一起构建您的系统

类似例子:

package example;

import java.io.FileNotFoundException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.*;

/**
 * Application lifecycle events. Handles:
 * <ul>
 *  <li>start, shutdown of application
 *  <li>start, stop of session
 * </ul>
 * 
 * @author mitjag
 *
 */
public class AppInit implements HttpSessionListener, ServletContextListener {

    public static final Logger log = Logger.getLogger(AppInit.class.getCanonicalName());

    public static final Map<String, HttpSession> SESSION_MAP = new ConcurrentHashMap<String, HttpSession>(); /* access AppInit.SESSION_MAP from anywhere in your app*/

    @Override
    public void contextInitialized(ServletContextEvent ctx) {}

    @Override
    public void sessionCreated(HttpSessionEvent arg0) {
        // With this trick we maintain the list of sessionid's together with corresponding session
        // It is used to grab the session if you have the valid session id
        final String sid = arg0.getSession().getId();
        log.info("SESSION CREATED with id " + arg0.getSession().getId());
        SESSION_MAP.put(sid, arg0.getSession());
    }

    /** 
     * Called on session invalidation (manual or session timeout trigger, defined in web.xml (session-timeout)).
     * @see javax.servlet.http.HttpSessionListener#sessionDestroyed(javax.servlet.http.HttpSessionEvent)
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        // remove session from our list (see method: sessionCreated)
        final String sid = arg0.getSession().getId();
        SESSION_MAP.remove(sid);
    }

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {

    }

}