我们已经在Asyncservlet上遇到了一些问题,因此我们研究了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 +"nn");
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接收,连接已关闭。
"修复"的方法是使用响应。StartAsync(),它可以保持连接打开。
但是,我们的问题完全符合这种机制" asynconcontext"/" asyncservlet"。因此,想知道(在我切换到tomcat的Websocket Impl之前)是否还有其他方法支持使用tomcat8.5或tomcat9的async推消息,也许使用http/2 spec。
谢谢
如果在项目的上下文中可行,请考虑使用泽西库库以启用SSE功能。
泽西岛是一种流行,易于使用且文化良好的解决方案。此外,该文档还包含有关如何使用库的明确示例。您可以在下面找到这样的示例。
广播示例
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"方法订阅。
- 使用"广播媒介"方法向每个订阅客户端广播消息。
运行示例
为了运行示例,请按照以下步骤:
- 为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