Вопрос по html5 – SSE и Servlet 3.0

16

Я зарегистрировал типичный SSE при загрузке страницы:

Client:

sseTest: function(){

var source = new EventSource('mySSE');
source.onopen = function(event){
console.log("eventsource opened!");
};

source.onmessage = function(event){
var data = event.data;
console.log(data);
document.getElementById('sse').innerHTML+=event.data + "<br />";
};
}

Мой Javascript-отладчик говорит, что "источник событий открыт!" был успешно.

My Server Code is a Servlet 3.0:

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns={"/mySSE"}, name = "hello-sse", asyncSupported=true)
public class MyServletSSE extends HttpServlet {

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

resp.setContentType("text/event-stream");
resp.setCharacterEncoding("UTF-8");

Random random = new Random();
PrintWriter out = resp.getWriter();

//AsyncContext aCtx = req.startAsync(req, resp);
//ServletRequest sReq = aCtx.getRequest();

String next = "data: " + String.valueOf(random.nextInt(100) + 1) + "\n\n";
//out.print("retry: 600000\n"); //set the timeout to 10 mins in milliseconds
out.write(next);
out.flush();
// do not close the stream as EventSource is listening
//out.close();
//super.doGet(req, resp);
}
}

Код работает! Клиентский код запускает метод doGet () - каждые 3 секунды и получает новые данные.

Questions: Однако мне интересноhow I can make this code better с помощью нового Servlet 3.0 Futures, такого как Async-Support или asyncContext.addListener (asyncListener) или чего-то еще, чего я не знаю. Интересно, я никогда не закрываю потокhow my server will scale?

Теоретически, лучшим подходом было бы явным образом запускать метод doGet () через серверный код при наличии новых данных, поэтому клиенту не нужно запускать на стороне клиента «onmessage ()» -Method и поэтому на стороне сервера «doGet ()» - метод каждые 3 секунды для новых данных.

Если есть 1000 клиентов, значит ли это, что будет 1000 подключений к серверу? Harun
Это один из лучших вопросов, которые я видел в SO, хотя я и ответил на вопрос, на самом деле я многому научился, особенно в отношении EventSource! Eran Medan

Ваш Ответ

2   ответа
12

вот полный рабочий пример (Servlet 3.0 / Java EE 6)

Некоторые заметки:

it handles "browser tab / window closed" via out.checkError() that also calls flush() I wrote it quickly, so I'm sure it can be improved, just a POC, don't use in production before testing

Servlet: (для краткости опущен импорт, скоро будет обновлена полная версия)

@WebServlet(urlPatterns = {"/mySSE"}, asyncSupported = true)
public class MyServletSSE extends HttpServlet {

  private final Queue<AsyncContext> ongoingRequests = new ConcurrentLinkedQueue<>();
  private ScheduledExecutorService service;

  @Override
  public void init(ServletConfig config) throws ServletException {
    final Runnable notifier = new Runnable() {
      @Override
      public void run() {
        final Iterator<AsyncContext> iterator = ongoingRequests.iterator();
        //not using for : in to allow removing items while iterating
        while (iterator.hasNext()) {
          AsyncContext ac = iterator.next();
          Random random = new Random();
          final ServletResponse res = ac.getResponse();
          PrintWriter out;
          try {
            out = res.getWriter();
            String next = "data: " + String.valueOf(random.nextInt(100) + 1) + "num of clients = " + ongoingRequests.size() + "\n\n";
            out.write(next);
            if (out.checkError()) { //checkError calls flush, and flush() does not throw IOException
              iterator.remove();
            }
          } catch (IOException ignored) {
            iterator.remove();
          }
        }
      }
    };
    service = Executors.newScheduledThreadPool(10);
    service.scheduleAtFixedRate(notifier, 1, 1, TimeUnit.SECONDS);
  }

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse res) {
    res.setContentType("text/event-stream");
    res.setCharacterEncoding("UTF-8");

    final AsyncContext ac = req.startAsync();
    ac.setTimeout(60 * 1000);
    ac.addListener(new AsyncListener() {
      @Override public void onComplete(AsyncEvent event) throws IOException {ongoingRequests.remove(ac);}
      @Override public void onTimeout(AsyncEvent event) throws IOException {ongoingRequests.remove(ac);}
      @Override public void onError(AsyncEvent event) throws IOException {ongoingRequests.remove(ac);}
      @Override public void onStartAsync(AsyncEvent event) throws IOException {}
    });
    ongoingRequests.add(ac);
  }
}

JSP:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
        <script>
            function test() {
                var source = new EventSource('mySSE');
                source.onopen = function(event) {
                    console.log("eventsource opened!");
                };

                source.onmessage = function(event) {
                    var data = event.data;
                    console.log(data);
                    document.getElementById('sse').innerHTML += event.data + "<br />";
                };
            }
            window.addEventListener("load", test);
        </script>
    </head>
    <body>
        <h1>Hello SSE!</h1>
        <div id="sse"></div>
    </body>
</html>
Можете ли вы дать ссылку "Сервлет 3.1 с Async I / O пишет"?
Одно медленное чтение клиента замедлит все записи других клиентов, так как этот метод использует блокировку записи. (который был соответствующим ответом еще в 2013 году). Современные приложения, вероятно, должны использовать Servlet 3.1 с Async I / O.
Йоаким: Это действительно проблема? Я только что сделал тестовый прогон. Два клиента, один из которых блокирует оповещение (). Сервер пишет короткое сообщение обоим каждые 30 с. Пробежал всю ночь без заминок. Возможно, в цепочке много буферов, но все же ...
1

Люди могут получить «IllegalStateException: не поддерживается» для startAsync (), в этом случае либо не забудьте:

@WebServlet(urlPatterns = "/Sse", asyncSupported=true)

или использовать

request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);

отэтот сообщение

И на всех применимых фильтрах тоже.

Похожие вопросы