クライアントがまだ生きていることを確認できるように、クライアントに定期的にサーバーに「ping」を実行させることにしました。Javaアプリケーションに組み込まれたJettyサーバーを使用しており、クライアントの要求を処理するためのHttpServletがあります。
サーバー側では、クライアントがしばらくの間「ping」(要求を送信)していないことをどのように知ることができますか?
クライアントがまだ生きていることを確認できるように、クライアントに定期的にサーバーに「ping」を実行させることにしました。Javaアプリケーションに組み込まれたJettyサーバーを使用しており、クライアントの要求を処理するためのHttpServletがあります。
サーバー側では、クライアントがしばらくの間「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();
}
}
}
セッション ID でクライアントを識別し、最後の ping を追跡します。定期的にすべての最後の ping をスキャンし、N 分間 ping を実行していないユーザーを見つけます。
たぶん、短いセッションタイムアウトでHttpSessionListenerを使用しますか?
http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpSessionListener.html
些細なケース:
サーバー側では、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 の受信を確認した時間よりも少ないため、ユーザーがタイムアウトになったことがわかります。
クライアントの数、スレッド モデル、およびデータ モデルを反映するために、ここで示したサンプル コードを変更する必要があることは明らかですが、アイデアは十分に理解できると思います。