servlet - 管理同步服务器推送通知

时间:2015-04-17 13:30:24

标签: java servlets long-polling

我正在设计一个非常简单的聊天应用程序,它使用长轮询在用户之间进行通信。我正在使用servlet将AsyncContexts存储在Hashmap中。当userA向userB发送消息时,servlet从HashMap获取相应的AsyncContext并将数据推送到客户端。

我正在使用的代码如下:

protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("application/json");
        if ("getAll".equals(request.getParameter("op"))) {
            String friend = request.getParameter("for");
            String jsonMessages = getChatMessagesForUser((String)request.getSession()
                    .getAttribute("id"),friend);
            PrintWriter writer = response.getWriter();
            writer.println(jsonMessages);
            writer.flush();
            return;
        }

        else if ("getFrnd".equals(request.getParameter("op"))) {
            String jsonFrndList = getFriends((String)request.getSession()
                    .getAttribute("id"));
            PrintWriter writer = response.getWriter();
            writer.println(jsonFrndList);
            writer.flush();
            return;
        }

        final AsyncContext asyncContext = request.startAsync(request, response);
        asyncContext.setTimeout(30 * 60 * 1000);
        asyncContext.addListener(new AsyncListener() {

            @Override
            public void onTimeout(AsyncEvent event) throws IOException {
                AsyncContext ctx = event.getAsyncContext();
                PrintWriter writer = ctx.getResponse().getWriter();
                ChatMessage directive = new ChatMessage();
                directive.setType('D');
                directive.setMessage("Keep-Alive");
                writer.println(gson.toJson(directive));
                writer.flush();
                contexts.remove(ctx);
                ctx.complete();
            }

            @Override
            public void onStartAsync(AsyncEvent event) throws IOException {
                // TODO Auto-generated method stub

            }

            @Override
            public void onError(AsyncEvent event) throws IOException {
                AsyncContext ctx = event.getAsyncContext();
                PrintWriter writer = ctx.getResponse().getWriter();
                ChatMessage directive = new ChatMessage();
                directive.setType('D');
                directive.setMessage("Keep-Alive");
                writer.println(gson.toJson(directive));
                writer.flush();
                contexts.remove(ctx);
                ctx.complete();

            }

            @Override
            public void onComplete(AsyncEvent event) throws IOException {
                // TODO Auto-generated method stub

            }
        });
        String id = (String) request.getSession().getAttribute("id");

        if(!contexts.containsKey(id)) {
            contexts.put(id, asyncContext);
        }
    }
protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("application/json");
        String receiver = request.getParameter("name");
        String sender = (String) request.getSession().getAttribute("id");
        String message = request.getParameter("message");

        System.out.println(receiver + " " + message);

        ChatMessage chatMessage = new ChatMessage();

        chatMessage.setType('M');
        chatMessage.setReceiver(receiver);
        chatMessage.setSender(sender);
        chatMessage.setMessage(message);
        chatMessage.setImage(false);

        if(contexts.containsKey(receiver)) {

            AsyncContext recieverContext = (AsyncContext) contexts.remove(receiver);
            System.out.println("Connections Open:" + contexts.size());
            PrintWriter writer = recieverContext.getResponse().getWriter();
            writer.println(gson.toJson(chatMessage));
            System.out.println("Message transmitted:" + 
            gson.toJson(chatMessage));
            writer.flush();
            recieverContext.complete();
        }

        try {
            System.out.println("Trying to add to database");
            ChatDAO.addToChat(chatMessage);
        } catch (SQLException e) {
            System.err.println(e);
        }

    }

让我解释一下发生了什么 - :

  • 用户A将初始GET请求发送到servlet。
  • servlet启动AsyncContext并将其添加到HashMap。
  • 用户B做同样的事情。
  • 用户A通过发布到servlet(doPost)向用户B发送消息。
  • servlet从hashmap获取用户B的AsyncContext。从用户A推送消息并在其上调用complete()。
  • 用户B通过执行另一个GET(doGet)立即重新连接到servlet。

显而易见的问题是,当多个人尝试与一个人聊天时。 例如 - :如果用户B和用户C尝试与用户A聊天,则会出现竞争条件。

它们可能同时向用户A发送消息,具体取决于首先处理的消息,将关闭与用户A的连接。 因此,如果聊天消息在收件人重新连接之前到达,则该消息将被丢弃。

我需要的是,如果servlet无法为特定用户找到异步上下文,那么它将等待一段时间,然后在等待之后如果它仍然没有找到它,那么它将丢弃该消息。

我可以写一些像: -

synchronized(contexts) {
wait(100);
}

1 个答案:

答案 0 :(得分:0)

最好将消息存储与异步上下文分开,以便即使没有收件人的上下文也可以存储消息。下次收件人发送get请求时,它会找到一条等待的消息并立即将其返回。

如果您担心陈旧的未传递邮件,那么您可以使用单个线程定期检查特定年龄的邮件并将其返回给发件人。它也可能只是破坏旧的返回发件人消息