我决定让我的客户端定期“ping”服务器,这样我就知道客户端还活着。我正在使用嵌入在 Java 应用程序中的 Jetty 服务器,并且我有一个 HttpServlet 来处理客户端请求。
在服务器端,我怎么知道客户端有一段时间没有“ping”(发送请求)?
我决定让我的客户端定期“ping”服务器,这样我就知道客户端还活着。我正在使用嵌入在 Java 应用程序中的 Jetty 服务器,并且我有一个 HttpServlet 来处理客户端请求。
在服务器端,我怎么知道客户端有一段时间没有“ping”(发送请求)?
通过其 sessionID 识别客户端,并跟踪最后一次 ping。定期扫描所有最后的 ping 并找到在 N 分钟内没有 ping 的那些。
扩展 srini.venigalla 的答案,您可以使用类似这样的内容作为起点。
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.*;
public class PingServlet extends HttpServlet {
private static final long EXPIRATION_MILLIS = TimeUnit.SECONDS.toMillis(30);
private final ConcurrentMap<String, Long> sessionIdToLastPingTime = new ConcurrentHashMap<String, Long>();
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
public void init() {
executor.scheduleWithFixedDelay(new Runnable() {
public void run() {
scanForExpiredSessions();
}
}, 30, 30, TimeUnit.SECONDS);
}
private void scanForExpiredSessions() {
for (Map.Entry<String, Long> entry : sessionIdToLastPingTime.entrySet()) {
if (Thread.currentThread().isInterrupted()) {
return;
}
final String sessionId = entry.getKey();
final Long lastPing = entry.getValue();
if (lastPing == null) {
// entry was removed from the map by another thread
continue;
}
if (System.currentTimeMillis() > lastPing + EXPIRATION_MILLIS) {
sessionIdToLastPingTime.remove(sessionId);
try {
expireSession(sessionId);
} catch (Exception e) {
// production code should use a logger
e.printStackTrace();
}
}
}
}
private void expireSession(String sessionId) {
// production code should use a logger
System.out.println("client stopped pinging for session " + sessionId);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
String sessionId = req.getSession().getId();
sessionIdToLastPingTime.put(sessionId, System.currentTimeMillis());
}
@Override
public void destroy() {
executor.shutdownNow();
try {
// this is optional, but may prevent Tomcat from reporting the webapp as a memory-leaker
executor.awaitTermination(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// production code should use a logger
System.out.println("interrupted while waiting for executor to shut down");
Thread.currentThread().interrupt();
}
}
}
小事一桩:
在服务器端,您可以使用 java Date 类来存储您上次收到 ping 的时间。
long lastRecv = System.getTimeMillis();
在另一个循环中(可能是另一个定期检查所有已知客户端数组的线程),您将检查 lastRecv 时间码并将其与现在进行比较。
if( date.getTime() - lastRecv > TIMEOUT_IN_MS )
// Handle user timing out
每次收到新的 ping 时,只需将 lastRecv 时间更新为新的当前时间。您将知道用户何时超时,因为您有一些预设阈值(例如 TIMEOUT_IN_MS)小于您上次看到 ping 进入的时间。
显然,您必须修改我在此处提供的示例代码以反映客户端的数量、您的线程模型和数据模型,但我认为它已经很好地传达了这个想法。
也许使用具有短会话超时的 HttpSessionListener?
http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpSessionListener.html