2012-07-20 134 views
1

我已經決定讓我的客戶「平」的服務器定期讓我知道客戶端還活着。我正在使用嵌入Java應用程序中的Jetty服務器,並且我有一個HttpServlet來處理客戶端請求。如何知道客戶端何時停止ping Java服務器?

在服務器端,我怎麼知道什麼時候客戶已經有一段時間沒有「ping通」(發送的請求)?

回答

2

擴展在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(); 
     } 
    } 
} 
+0

謝謝,這正是我所尋找的。你的老闆:D。我將使這個實現方法適應我當前的代碼。 – Noah 2012-07-20 21:12:47

+0

不客氣。在走這條路徑之前,請考慮一下具有短會話超時的HttpSessionListener是否滿足您的需求。祝你好運! – dnault 2012-07-20 21:22:53

0

的簡單的情況:

  1. 告訴客戶端來ping每隔N秒
  2. 保持當最後一次客戶端ping通的時間戳,每個客戶端Ping
  3. 如果此時間戳之後更新的時間戳顯着大於N秒,客戶端已經停止ping(至少現在)
2

通過其sessionID識別客戶端,並跟蹤最後的ping。定期掃描所有最後的ping,並找出那些在N分鐘內未ping的人。

+1

您可以使用ConcurrentHashMap將每個ping時間與相應的會話ID相關聯。您可以使用ScheduledExecutorService來觸發掃描。不要忘記在會話過期時刪除地圖條目。 – dnault 2012-07-20 19:22:12

+0

此外,尋找出這個疑難雜症:迭代的ConcurrentMap的條目時,entry.getValue()可能如果被另一個線程刪除的條目返回null。 – dnault 2012-07-20 19:24:08

+0

@dnault所以我應該駐留在不同的線程在servlet更新的ConcurrentHashMap?然後,線程將安全地掃描哈希映射,刪除已經變冷的條目。讓我知道如果我理解你的權利,我會搜索ConcurrentHashMap :) – Noah 2012-07-20 19:48:17

0

在您可以使用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來在最後的時間。

很明顯,你將不得不修改示例代碼我給這裏以反映客戶的數量,線程模型和數據模型,但我認爲它足夠好了。

+1

優先於新的Date().getTime()方法System.currentTimeMillis()。將結果存儲在long中,而不是int。 – dnault 2012-07-20 19:19:17

+0

感謝您的迴應!所以已知客戶端的數組和lastRecv時間位於另一個線程中。 servlet如何更新數組? – Noah 2012-07-20 19:24:34

+0

我是否必須潛入跨線程調用和鎖定中,以便線程在更新時不檢查時間? – Noah 2012-07-20 19:26:32

相關問題