我已經決定讓我的客戶「平」的服務器定期讓我知道客戶端還活着。我正在使用嵌入Java應用程序中的Jetty服務器,並且我有一個HttpServlet來處理客戶端請求。如何知道客戶端何時停止ping Java服務器?
在服務器端,我怎麼知道什麼時候客戶已經有一段時間沒有「ping通」(發送的請求)?
我已經決定讓我的客戶「平」的服務器定期讓我知道客戶端還活着。我正在使用嵌入Java應用程序中的Jetty服務器,並且我有一個HttpServlet來處理客戶端請求。如何知道客戶端何時停止ping Java服務器?
在服務器端,我怎麼知道什麼時候客戶已經有一段時間沒有「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();
}
}
}
的簡單的情況:
通過其sessionID識別客戶端,並跟蹤最後的ping。定期掃描所有最後的ping,並找出那些在N分鐘內未ping的人。
您可以使用ConcurrentHashMap將每個ping時間與相應的會話ID相關聯。您可以使用ScheduledExecutorService來觸發掃描。不要忘記在會話過期時刪除地圖條目。 – dnault 2012-07-20 19:22:12
此外,尋找出這個疑難雜症:迭代的ConcurrentMap的條目時,entry.getValue()可能如果被另一個線程刪除的條目返回null。 – dnault 2012-07-20 19:24:08
@dnault所以我應該駐留在不同的線程在servlet更新的ConcurrentHashMap?然後,線程將安全地掃描哈希映射,刪除已經變冷的條目。讓我知道如果我理解你的權利,我會搜索ConcurrentHashMap :) – Noah 2012-07-20 19:48:17
在您可以使用Java Date類存儲時,最後一次是你收到ping服務器端。
long lastRecv = System.getTimeMillis();
在另一個環路(也許是另一個線程定期檢查所有已知客戶的數組),你會檢查lastRecv時間碼,並將它與現在。
if(date.getTime() - lastRecv > TIMEOUT_IN_MS)
// Handle user timing out
每當一個新的ping recevied剛剛更新lastRecv時間爲新的當前時間。你會知道,當用戶已超時,因爲你有一些預設的閾值(如TIMEOUT_IN_MS)小於你看到一個ping來在最後的時間。
很明顯,你將不得不修改示例代碼我給這裏以反映客戶的數量,線程模型和數據模型,但我認爲它足夠好了。
也許使用HttpSessionListener與短會話超時?
http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpSessionListener.html
謝謝,這正是我所尋找的。你的老闆:D。我將使這個實現方法適應我當前的代碼。 – Noah 2012-07-20 21:12:47
不客氣。在走這條路徑之前,請考慮一下具有短會話超時的HttpSessionListener是否滿足您的需求。祝你好運! – dnault 2012-07-20 21:22:53