1

クライアントがまだ生きていることを確認できるように、クライアントに定期的にサーバーに「ping」を実行させることにしました。Javaアプリケーションに組み込まれたJettyサーバーを使用しており、クライアントの要求を処理するためのHttpServletがあります。

サーバー側では、クライアントがしばらくの間「ping」(要求を送信)していないことをどのように知ることができますか?

4

5 に答える 5

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();
        }
    }
}
于 2012-07-20T20:34:43.913 に答える
2

セッション ID でクライアントを識別し、最後の ping を追跡します。定期的にすべての最後の ping をスキャンし、N 分間 ping を実行していないユーザーを見つけます。

于 2012-07-20T19:09:40.880 に答える
0

たぶん、短いセッションタイムアウトでHttpSessionListenerを使用しますか?

http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpSessionListener.html

于 2012-07-20T20:30:33.043 に答える
0

些細なケース:

  1. クライアントに N 秒ごとに ping するように指示する
  2. クライアントが最後に ping を実行したときのタイムスタンプを保持し、クライアントが ping を実行するたびにタイムスタンプを更新します
  3. このタイムスタンプが N 秒よりも大幅に大きい場合、クライアントは ping を停止しています (少なくとも現時点では)。
于 2012-07-20T19:09:03.080 に答える
0

サーバー側では、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 の受信を確認した時間よりも少ないため、ユーザーがタイムアウトになったことがわかります。

クライアントの数、スレッド モデル、およびデータ モデルを反映するために、ここで示したサンプル コードを変更する必要があることは明らかですが、アイデアは十分に理解できると思います。

于 2012-07-20T19:11:29.253 に答える