Tomcat - 服务器在Tomcat8.5上使用HTTP2发送没有异步servlet的事件

时间:2016-12-20 16:43:24

标签: tomcat websocket server-sent-events http2

我们遇到了AsyncServlets的一些问题,因此我们研究了SSE和WebSockets。

SSE的所有tomcat示例如下:

@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {

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

        //content type must be set to text/event-stream
        response.setContentType("text/event-stream"); 
        //cache must be set to no-cache
        response.setHeader("Cache-Control", "no-cache");     
        //encoding is set to UTF-8
        response.setCharacterEncoding("UTF-8");

        PrintWriter writer = response.getWriter();

        for(int i=0; i<10; i++) {
            System.out.println(i);
            writer.write("data: "+ i +"\n\n");
            writer.flush();
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        writer.close(); 
    }
}

但是,我希望保持连接的长时间运行,希望无需保持处理请求的线程处于活动状态。 以下是从PubSubHub接收信息并希望将其推送到浏览器的示例。

@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {

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

        //content type must be set to text/event-stream
        response.setContentType("text/event-stream"); 
        //cache must be set to no-cache
        response.setHeader("Cache-Control", "no-cache");     
        //encoding is set to UTF-8
        response.setCharacterEncoding("UTF-8");

        final PrintWriter writer = response.getWriter();

        PublishSubsribeHub.getInstance().subscribe("TestData", new Callback(String msg){
           public void run(){
               writer.write("data:"+msg);
               writer.flush(); // this throws NullPointerException since undeline HttpServletResponse is closed
           });
}

如上面的评论所述,在数据准备就绪并通过PublishSubsribeHub接收时,连接已经关闭。

“修复”这种方法的方法是使用response.startAsync()来保持连接打开。

然而,我们的问题正是这个机制“asyncContext”/“asyncServlet”。所以想知道(在我切换到Tomcat的WebSocket impl之前),如果有任何其他方式来支持使用Tomcat8.5或Tomcat9的异步推送消息,可能使用HTTP / 2规范。

谢谢

1 个答案:

答案 0 :(得分:0)

如果在您的项目环境中可行,请考虑使用泽西library以启用SSE functionality

泽西岛是一种流行的,易于使用且记录良好的解决方案。此外,该文档包含有关如何使用库的明确示例。你可以在下面找到这样一个例子。

广播员示例

import org.glassfish.jersey.media.sse.SseBroadcaster;

@Singleton
@Path("broadcast")
public static class BroadcasterResource {
    private SseBroadcaster broadcaster = new SseBroadcaster();

    @POST
    @Produces(MediaType.TEXT_PLAIN)
    @Consumes(MediaType.TEXT_PLAIN)
    public String broadcastMessage(String message) {
        OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder();
        OutboundEvent event = eventBuilder.name("message")
            .mediaType(MediaType.TEXT_PLAIN_TYPE)
            .data(String.class, message)
            .build();

        broadcaster.broadcast(event);

        return "Message '" + message + "' has been broadcast.";
    }

    @GET
    @Produces(SseFeature.SERVER_SENT_EVENTS)
    public EventOutput listenToBroadcast() {
        final EventOutput eventOutput = new EventOutput();
        this.broadcaster.add(eventOutput);
        return eventOutput;
    }
}

此具体示例使用广播公司:

  • 允许客户通过'listenToBroadcast'方法订阅。
  • 使用'broadcastMessage'方法向每个订阅的客户端广播消息。

运行示例

要运行该示例,请按以下步骤操作:

  • 为SSE类创建新包
  • 在此程序包中,根据上述广播示例
  • 创建一个类
  • 添加另一个能够自动发现(和注册)同一个包中存在的每个类的类:

-

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("api")
public class ApiConfig extends Application { 

}

使用Tomcat 7,8,8.5测试上述解决方案。使用的球衣版本:2.x