0

説明:フレームワークなしで Linux に Java Web アプリケーション (jsp + tomcat) があります。DBMS として postgresql を使用しています。

テーブル ( options ) とそれをカプセル化するシングルトン クラス ( Options ) があります。Options インスタンスは、アプリケーションの起動時に読み込まれ、そこに無期限に残ります。

ユーザーがオプションを変更すると、メソッド ( .refreshData() ) がメモリに保持されているインスタンスを更新します。

問題は、DB に直接アクセスし、オプションテーブルのいくつかのフィールドを更新するリモート サービスが存在することです。このコードを制御することはできません。

外部サービスがオプション テーブルを更新するときに、refresh メソッドをトリガーしたいと考えています。また、サービスが 1 日 1 回午後 3 時に開始されることも知っていますが、いつ終了するかはわかりません。

postgresql によって提供される LISTEN - NOTIFY 機能 ( Javaキャッシュを更新する Postgres トリガー) は、この目標を達成するための最もエレガントな方法のように思えます。このトピックに続いて、私は単純なリスナーを試しており、自分のニーズに合わせて「適応」しています( Postgress ドキュメントのコードのサンプル)。

@Craigの提案後に編集:

public class OptionsListener extends Thread {
    private int threadMills = 1000;
    private Connection conn;
    private org.postgresql.PGConnection pgconn;
    private Options optionsInstance;
    private static final String DB_URL;
    private static final String DB_USERNAME;
    private static final String DB_PASSWORD;

    static {
        try {
            Context initContext = new InitialContext();
            Context envContext = (Context) initContext.lookup("java:/comp/env");

            DB_URL = (String) envContext.lookup("application/DB/url");
            DB_USERNAME = (String) envContext.lookup("application/DB/username");
            DB_PASSWORD = (String) envContext.lookup("application/DB/password");
        } catch (NamingException e) {
            throw new RuntimeException(e);
        }
    }

    OptionsListener(Options instance, int threadMillis) {
        optionsInstance = instance;
        this.threadMills = threadMillis;

        try {
            Class.forName("org.postgresql.Driver");
            conn = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
            pgconn = (PGConnection) DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);

            Statement stmt = conn.createStatement();
            stmt.execute("LISTEN otionsUpdate");
            stmt.close();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Override
    public void run() {
        while (true) {
            Log.addItem("Polling ?");
            try {
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery("SELECT 1");
                rs.close();
                stmt.close();

                PGNotification notifications[] = pgconn.getNotifications();
                if (notifications != null) {
                    Log.addItem("NOTIFY received");
                    optionsInstance.loadDbData();
                }

                Thread.sleep(threadMills); //tempo di attesa in millisecondi
            } catch (Exception e) {
                Log.addItem(getClass().getName() + " " + e.getMessage());
            }
        }
    }
}

Options クラスでは、次のメソッドを使用してリスナーを手動で開始します。

public static void startExternalChangesListener(Options instance, int millis) {
    OptionsListener listener = new OptionsListener(instance, millis);
    listener.start();
}

そして最後に

Options.startExternalChangesListener(options, 5000);

スレッドを改ざんするのはこれが初めてです...

チャネルに通知するAFTER UPDATE トリガーを作成し、PGAdmin3 でテストしました。それは魅力のように機能しますが、Javaは気付かないようです...

4

2 に答える 2

0

リスナーが予期した通知を受け取っていない場合、ここでの質問は、慎重に順番に並べ替える必要がある質問です。完全を期すために、すでに行った手順を含めていることに注意してください。

  1. 通知は実際に発生していますか?これを psql で確認できますか? そうでない場合は、そこで行うトラブルシューティングがあります。これを行ったようですので、ステップ 2 に進みます。

  2. 他のセッションは実際にリッスンしていますか? ここでは、db からの通知を確認できるため、パケット スヌーパーが役立つ可能性があります。リッスンしていない場合は、アプリケーションで LISTEN コマンドが適切なタイミングで実行されていることを確認し、そこからデバッグする必要があります。

  3. アプリケーションでポーリングが正しく行われていますか? ポーリング プロセス中に標準エラーに出力された警告または通知がある場合、これも役立ちます。

現時点では、問題がどこにあるのかを特定するのは困難です。ただし、これらの質問に順番に取り組むと (#1 に対処し、#2 から開始できるように思えます)、それほど長くはかからずに問題を見つけることができるはずです。

于 2013-06-06T06:10:57.687 に答える