0

ここ数日、この問題に苦しんでいます。

要件 このアプリケーションでは、nodejs で開発された UI を介してファイルをアップロードし、ファイル レコードは amazon シンプル ワークフロー (SWF) を介して処理されます。amazon SWF への呼び出しは、ファイルの処理時に nodejs アプリが呼び出す Spring アプリを介して行われます。要件は、処理中のすべてのファイルについて、処理中にレコードに何が起こったかを詳述するログ ファイルをアプリケーションが作成する必要があることです。

どのように実装しましたか? SWF をトリガーする Spring アプリケーションで、静的な StringBuffer 変数を維持する FileLogger クラスを作成しました。この fileLogger クラスはワークフロー スコープに設定されます。つまり、クラスはワークフローの実行ごとに作成され、最後に破棄されます。ファイルが処理されると、FileLogger クラスの StringBuffer にログを追加し続け、処理の最後にファイルに書き込み、保存します。

問題 の説明 アプリケーションのインスタンスが 1 つしか実行されていない限り、このソリューションは正常に機能しているように見えました。アプリケーションを複数の amazon ec-2 インスタンスにデプロイするとすぐに、不完全なログがファイルに保存されているように見えます。さらに調べてみると、アプリケーションのすべてのインスタンスがログを保持するための独自の stringBuffer を持っており、アプリケーションに書き込みを行っているときに stringbuffers のコンテンツの 1 つしか読み取れないため、ログが不完全であることがわかりました。言うまでもなく、ログのパターンはランダムです。アプリケーションの N 個のインスタンスをデプロイすると、StringBuffer の N 個のインスタンスが存在することがわかりました。

ここに FileLogger クラスがあります

private static final Logger logger = LoggerFactory.getLogger(FileLogger.class);

    //private static final Logger logger = LoggerFactory.getLogger(FileLogger.class);   
    private static final SimpleDateFormat logFileDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    private static final SimpleDateFormat fileNameDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
    private static final String COLON = ":";
    private static StringBuffer logAppender = null;

    public synchronized void debug(Date date, String logMessage){
        if(appConfig.getLogLevel().equalsIgnoreCase(LogLevel.DEBUG.name())){
            this.log(LogLevel.DEBUG.name(), date, logMessage);
        }
    }

    public synchronized void info(Date date, String logMessage){
        if(appConfig.getLogLevel().equalsIgnoreCase(LogLevel.INFO.name()) || 
                appConfig.getLogLevel().equalsIgnoreCase(LogLevel.DEBUG.name())){
            this.log(LogLevel.INFO.name(), date, logMessage);
        }
    }

    public synchronized void error(Date date, String logMessage){
        if(appConfig.getLogLevel().equalsIgnoreCase(LogLevel.ERROR.name()) ||
                appConfig.getLogLevel().equalsIgnoreCase(LogLevel.DEBUG.name())){
            this.log(LogLevel.ERROR.name(), date, logMessage);
        }
    }

    private synchronized void log(String logLevel, Date date, String logMessage){
        logger.info("logAppender Hashcode: "+logAppender.hashCode());
        if(!logLevel.equalsIgnoreCase(LogLevel.NONE.name())){
            //StringBuffer logAppender = getLogAppender();
            getLogAppender().append(getLogAppender().hashCode());
            getLogAppender().append(COLON);
            getLogAppender().append(getFormattedDate(date, logFileDateFormat));
            getLogAppender().append(COLON);
            getLogAppender().append(logLevel);
            getLogAppender().append(COLON);
            getLogAppender().append(logMessage);
            getLogAppender().append(System.getProperty("line.separator"));
        }
    }
private synchronized StringBuffer getLogAppender(){
        logger.info("Getting logAppender .."+logAppender);
        if(logAppender == null){
            logger.info("Log appender is null");
            logAppender = new StringBuffer();
        }
        return logAppender;
    }

質問 アプリケーションの複数のインスタンス間で StringBuffer (logAppender) のインスタンスが 1 つだけであることを確認するにはどうすればよいですか?ログを追加し続け、最後に読み取り、内容をファイルに書き込んでから保存できますか?

4

2 に答える 2

1

まず第一に、非静的メソッドで静的フィールドを使用しています。これは悪い習慣です。静的メソッドを使用するか、非静的フィールドでシングルトンを使用します。

第 2 に、パフォーマンスが心配な場合は、必要のない同期化されたものをすべて削除することをお勧めします。log() メソッドでのみ必要です。Error()、warn()、および info() は、同期を必要とする方法で共有データにアクセスしません (読み取り専用)。また、 log() で getLogAppender() を複数回呼び出すのはなぜですか?

equalsIgnoreCase() を使用してログ レベルをテストすることも非効率的ですが、目立ちません。また、 error() にバグがあります。レベルが INFO の場合、ログに記録されません。

さて、あなたの要点: 他の人が言ったように、JVM はインスタンス間でユーザー データを共有しません。また、JVM はおそらく同じ OS インスタンス上のイベントではありません。簡単な方法はありません。DB へのログ記録は、適切に構成すれば、パフォーマンスにとってそれほど悪くない場合があります。既存のデータを更新しないため、トランザクションの分離はまったく必要ありません。ログ サーバーを使用することもできます。UNIX ライクな OS には、提供できるソリューションがたくさんあります。または、バッファをフラッシュするときに、すべてのサーバー インスタンスからフラッシュします。

于 2015-03-28T08:46:46.710 に答える
0

戻ってきて、解決策として、ログを一時的に保存するために最終的に Amazon の elastiCache (redis 実装) を選択し、操作の最後にキャッシュからすべてを読み取り、Amazon s3 のファイルに書き込むことを言及したかっただけです。これが役立つことを願っています。

于 2016-08-01T19:39:24.430 に答える