我正在编写一个用于提供AJAX事件的长轮询服务。为此,我使用的是Tomcat7 CometProcessor servlet。 servlet类似于Tomcat聊天servlet示例。
对于每个传入请求,都会使用Connection
,CometEvent
,HttpServletRequest
和一些请求参数创建HttpServletResponse
对象。该请求按userId
存储在多个地图中,即每个userId
可能有一个或多个打开的请求/连接。
AJAX事件通过一个单独的线程发送,只要用户的新数据到达就会触发该线程。 SenderThread
获取事件列表,此用户的打开连接列表,并将事件发送到每个连接。
现在问题是:当数据在SenderThread
中发送时,我想关闭连接。我该怎么做?我是否需要在发件人主题中致电event.close()
,我应关闭OutputStream
还是设置Connection: close
标题?
我尝试了各种各样的东西,但似乎都没有正常工作,文档相当薄。
下面的代码只是说明代码的样子,因为实际代码太长了。所以不要看错误检查或竞争条件。
public class AjaxServlet extends HttpServlet implements CometProcessor {
ConcurrentMultiMap connections = new ConcurrentMultiMap();
SenderThread senderThread = new SenderThread();
public void event(CometEvent event) {
HttpServletRequest req = event.getHttpServletRequest();
HttpServletResponse res = event.getHttpServletResponse();
// error checking omitted
String userId = req.getParameter("userId");
Long lastId = Long.parseLong(req.getParameter("lastId"));
Long requestId = req.getAttribute("requestId");
switch(event.getEventType()) {
case BEGIN:
event.setTimeout(30000L);
req.setAttribute("requestId", requestId.incrementAndGet());
addConnection(userId, requestId, lastId, event);
break;
case END:
removeConnection(userId, requestId);
event.close();
break;
case ERROR:
boolean timeout = event.getSubType().equals(TIMEOUT);
if (timeout)
res.setStatus(408);
event.close();
break;
}
}
public void addConnection(...) {
// add new connection to connections
}
public void removeConnection(long userId, long requestId) {
// remove connection from connections
}
public void init() {
Thread t = new Thread(senderThread);
t.setDaemon(true);
t.start();
}
class Connection {
CometEvent event;
long requestId;
long lastId;
Connection(CometEvent event, long requestId, long lastId) {
// details omitted
}
}
class SenderThread implements Runnable {
private boolean running = true;
private BlockingQueue<Long> queue = new LinkedBlockingQueue<Long>();
public void stop() {
running = false;
}
public void pushToUser(long userId) {
queue.add(userId);
}
public void run() {
while (running) {
try {
long userId = queue.take(); // block until
List<Event> events = eventService.forUser(userId);
if (!events.isEmpty()) {
List<Connection> conn = connections.get(userId);
for (Connection c:connections.get(userId)) {
sendEvents(events);
// HOW DO I CLOSE THE CONNECTION HERE?
// removeConnection(userId, c.requestId);
// c.event.close();
}
}
} catch(Exception e) {
// error checking omitted
}
}
}
private void sendEvents(List<Event> events) {
String json = ...
byte[] data = json.getBytes("UTF-8");
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setContentLength(data.length);
OutputStream os = response.getOutputStream();
os.write(b);
os.flush();
os.close(); // WILL THIS CLOSE THE CONNECTION?
}
}
}