我们遇到了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规范。
谢谢
答案 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;
}
}
此具体示例使用广播公司:
要运行该示例,请按以下步骤操作:
-
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