5

実稼働システムで例外がポップアップするという問題がありますが、誰が例外を引き起こしているのかについての良い情報がありません。個人のユーザー名は tomcat セッションに変数として格納されており、明らかにdoPostordoGetメソッドでアクセスできますが、その情報を各ビジネス オブジェクトにパラメーターとして渡さない限り、セッションにアクセスすることはできません。 . 明らかな理由から、ログ メッセージにユーザー名を追加して、何が起こっているのかを把握したいと思います。

だから私の解決策は、このようなことをすることです

public class ExceptionUtil {
    private ExceptionUtil() { } // no instantiation
    private static final ThreadLocal<String> local = new ThreadLocal<String>();
    public static void set(String user) { local.set(user); }
    public static String get() { return local.get(); }
}

次に、投稿/取得で、これを行うことができます

String username = request.getSession().getAttribute("username");
ExceptionUtil.set(username);

次に、私の例外では、これを行う可能性があります(不自然な、悪い習慣の例)

catch(SQLException e) {
    logger.error(ExceptionUtil.get() + " did something dumb in sql", e);
    throw e;
}

私が懸念している唯一の問題は、Tomcat が私のスレッドをどのように管理するかということです。彼らがスレッドを保持するとどうなりますか?彼らは持続しますか?ThreadLocal 値も保持されますか? String だけでなく ThreadLocal に Session 全体を格納していた場合、重大なメモリ リークが発生する可能性があります。また、誰かが複数のリクエストに対して永続化されたスレッドのユーザー名/セッションを再設定するのを忘れた (または完了時にクリアするのを忘れた) 場合、そこに古いデータが存在する可能性があることも意味します。

私の皮肉を呼んでください、しかし私はプログラムの正確さのために何かをすることを忘れないでプログラマーに頼る必要はありません (特に私自身!)。自分のコードをばか証明できるなら、そうしたいです。これは、Tomcat がスレッドをどのように使用するかをよりよく理解することを意味します。

したがって、一文形式の質問は次のとおりです。

Tomcat (7.0.27) で実行されている webapp で ThreadLocal を使用すると、Thread が複数の要求に使用され、以前の要求からのデータが永続化されるリスクがありますか?

「Tomcat/ThreadLocal のシェナニガン」の正確な質問には答えていませんが、ロギング目的でセッション変数にエレガントにアクセスできる代替ソリューションを受け入れることに注意してください。また、私のソリューションの潜在的な落とし穴についてのコメントも受け付けています。解決しなければならないビジネス上の問題がありますが、解決策は 1 つではありません。私の製品システムで誰が例外を引き起こし続けているのか知りたいだけです:)

4

2 に答える 2

4

はい、Tomcat は ThreadPool の概念を使用します。つまり、スレッドが再利用されているため、「スレッド ローカルは値を保持します」と提案したように、

私が提案する代替案は

  1. ビューコントローラーのどこかで、完了後にスレッドをクリーンアップします

  2. リクエスト フィルターを作成し、フィルターの開始時にすべてをクリーンアップして新しい値をプッシュし、これをサーバー上のすべての URL パターンに割り当てます

クラスに特定の値を保存する代わりに、次のアプローチに従っている場合は、リクエストをスレッドローカルに保存し、リクエストを使用して、自家製のutilクラスを使用してセッションから値をプルし、リクエストを受け取り、目的の値を返します。スレッドにセッションを保存して値を取得しますが、毎回新しく追加し、完了後にリクエストをクリーンアップするようにしてください(そのためには2番目のオプションを使用してください)。

于 2012-08-10T23:37:33.880 に答える
1

車輪を再発明する必要はありません。ログ システムがそれを行います。

logback /log4jがロガーの実装である場合、マップされた診断コンテキスト ( MDC ) が間違いなく答えです。MDC は論理的には ThreadLocal に似ていますが、より優れています。

  1. MDC は、スレッドセーフと同期を透過的に処理します

  2. 子スレッドは、その親のマップされた診断コンテキストのコピーを自動的に継承します。したがって、マルチスレッドを使用してリクエストを処理しても問題ありません。

したがって、目標を達成するために、次のようにサーブレット フィルターに MDC を設定します。

  public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {

    boolean successfulRegistration = false;

    HttpServletRequest req = (HttpServletRequest) request;    
    Principal principal = req.getUserPrincipal();
    // Please note that we could have also used a cookie to 
    // retrieve the user name

    if (principal != null) {
      String username = principal.getName();
      successfulRegistration = registerUsername(username);
    } 

    try {
      chain.doFilter(request, response);
    } finally {
      if (successfulRegistration) {
        MDC.remove(USER_KEY);
      }
    }
  }

  private boolean registerUsername(String username) {
    if (username != null && username.trim().length() > 0) {
      MDC.put(USER_KEY, username);
      return true;
    }
    return false;
  }

次に、ログ構成で、パターン レイアウトに %X{USER_KEY} を追加して、MDC で設定した値を使用します。

logbackには、すぐに使用できるフィルターMDCInsertingServletFilterがあり、remoteHost/requestUrl などの詳細情報をログに記録できます。これは、ログ記録に非常に役立つ情報です。

MDC http://logback.qos.ch/manual/mdc.htmlの logback ドキュメントを確認してください。

于 2015-09-10T09:48:45.583 に答える