プロジェクトのマイクロサービスアーキテクチャ、要求は、我々は全体のログ要求のリンクを見たいとき、それは幸いなことに、困難になる、より多くのマイクロサービスがログを要求生み出す複数のマイクロサービスを呼び出すことができ我々はない一緒ならば、それらを照会することは非常に困難であり、これは非常に重要な問題である、私たちは一緒にこれらのログを必要とし、このように非常に人気のあるELKなど、いくつかの集中管理、ログ収集ツールを持って、私たちの練習は、要求システムを起動することですグローバルにユニークなIDを生成する際、IDは、サービスが他のサービスを呼び出すときに我々はログを表示する場合、単にIDを検索し、リンクを形成するために受け継がれることを、このコール・サイクル全体の要求に関連付けられています全体のリンクをチェックアウトすることができますログインします。
今マイクロサービスアーキテクチャの背景ダボ、栗を与えます:
A -> B -> C
私たちは、私たちはA、その後、ダボRpcContextにのみコンシューマとプロバイダは、A-> Bと同じRpcContextを、共有しないことをすべて知っている、チェーンに合わせてA / B / C / 3マイクロプリントサービスとの間でログインする必要がありますし、 B RpcContextは、同じコンテンツを取得することができますが、B->はC、およびCはRpcContextに同じコンテンツを共有することができない場合には、そのログ・チェーンを印刷しないでください。
どのように我々はそれを行うのですか?
私たちは、その後、交換前、我々はThreadLocalのにログ情報を保存するために必要なすべての最初の意志、右手はRpcContextで、左手はThreadLocalのスレッドであると仮定し、解決するために彼の左手でアイデアを交換することができます。
広く微小血管サービスの二つのタイプに分け我々のマイクロプロジェクト・サービスにおいて、一つはダボ容器である、そのようなAの容器の特性はバネ容器を開始するためにのみ使用され、その後、飼育係にサービスおよび登録サービスを露出さダボ使用消費者にサービスを提供することと、もう一つは私たちの共通のWebコンテナであるSpringMVCの容器である、それが唯一のコンテナが私たちのプロジェクトのインターフェイスを開くことができますですが、また、プロジェクトのゲートウェイ機能として動作します。
マイクロコンテナサービスの理解では、我々は今、コールチェーン第一層は、層内のSpringMVCコンテナでなければならないことを知って、我々はojbkアップ時に、この層のカスタムインターセプタに直接書き込む、話は安いです、あなたのデモを表示コード:
例えば以下のようにデモコードプレインターセプター共通コードで傍受。
public class CommonInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler)
throws Exception {
// ...
// 初始化全局的Context容器
Request request = initRequest(httpServletRequest);
// 新建一个全局唯一的请求traceId,并set进request中
request.setTraceId(JrnGenerator.genTraceId());
// 将初始化的请求信息放进ThreadLocal中
Context.initialLocal(request);
// ...
return true;
}
// ...
}
システムコンテキストオブジェクト内:
public class Context {
// ...
private static final ThreadLocal<Request> REQUEST_LOCAL = new ThreadLocal<>();
public final static void initialLocal(Request request) {
if (null == request) {
return;
}
REQUEST_LOCAL.set(request);
}
public static Request getCurrentRequest() {
return REQUEST_LOCAL.get();
}
// ...
}
インターセプターは実装しorg.springframework.web.servlet.HandlerInterceptor
、その主な役割は、要求を処理切片であり、あなたはAOP MVC層と同等であるMVC層、中にいくつかの伐採や権限のチェックや他の操作を行うことができ、インタフェースを、横断的関心事のすべての機能に沿ったものであることもできますインターセプタの実装に。
ここinitRequest(httpServletRequest);
では、オブジェクトのコンテンツにシステム情報を要求する要求にあるRequest
ThreadLocalのフィールド内のシステムの文脈に置き、その後、グローバルに一意なTRACEIDリクエストに初期化し、そして。
その後RpcContextへのThreadLocalの内容は、話す前に、私は最初のダボSPIベースの拡張機構を言う方法についての話は、インターセプタの拡張機能の公式ドキュメントは、以下のように説明しました:
サービスプロバイダおよびサービスコンシューマがインターセプトするプロシージャを呼び出す大型多目的ダボ自体は、この拡張ポイントの実装、それぞれのリモートメソッドの実行に基づいており、インターセプターが実行されます、してください注意を払うパフォーマンスへの影響へ。
それは、我々は、リモートサービスを呼び出す前に、インターセプタは、リモートサービスの消費者を呼び出す前に、我々はThreadLocalのRpcContextコンテナの内容に潜入することができ、そう簡単にハンドルに、この呼処理をインターセプトしている、我々は基づいてすることができますSPI機構のダボは、1つの消費者端とプロバイダ端効果に1つで動作する、2つのインターセプタを拡張します。
以下のように、META-INFにcom.alibaba.dubbo.rpc.Filterファイルに参加:
provider=com.objcoding.dubbo.filter.ProviderFilter
consumer=com.objcoding.dubbo.filter.ConsumerFilter
消費者側のインターセプト処理:
public class ConsumerFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation)
throws RpcException {
//1.从ThreadLocal获取请求信息
Request request = Context.getCurrentRequest();
//2.将Context参数放到RpcContext
RpcContext rpcCTX = RpcContext.getContext();
// 将初始化的请求信息放进ThreadLocal中
Context.initialLocal(request);
// ...
}
}
Context.getCurrentRequest();
リクエストリクエストが中にThreadLocalからコンテンツを取得することですcontextToDubboContext(request);
、現在のスレッドに要求RpcContextコンテナの内容。
それはThreadLocalのに出てコンテンツRpcContextのプロバイダであると考えるのは簡単です:
public class ProviderFilter extends AbstractDubboFilter implements Filter{
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation)
throws RpcException {
// 1.获取RPC远程调用上下文
RpcContext rpcCTX = RpcContext.getContext();
// 2.初始化请求信息
Request request = dubboContextToContext(rpcCTX);
// 3.将初始化的请求信息放进ThreadLocal中
Context.initialLocal(request);
// ...
}
}
次は、私たちはただTRACEIDを検索し、要求ELKを照会したいときに、あなたが見ることができ、共通TRACEIDを持って、各印刷要求に関連した容器の中に私たちに同じメッセージを作成するには、configure log4j2に必要リンク要求ログの全体。
私たちは、コンテキストのコンテキストオブジェクトは、initialLocal(Request request)
メソッドのコンテキストでTRACEIDのlog4j2情報を追加しました:
public class Context {
// ...
final public static String TRACEID = "_traceid";
public final static void initialLocal(Request request) {
if (null == request) {
return;
}
// 在log4j2的上下文中添加traceId
ThreadContext.put(TRACEID, request.getTraceId());
REQUEST_LOCAL.set(request);
}
// ...
}
次の実現org.apache.logging.log4j.core.appender.rewrite.RewritePolicy
:
@Plugin(name = "Rewrite", category = "Core", elementType = "rewritePolicy", printObject = true)
public final class MyRewritePolicy implements RewritePolicy {
// ...
@Override
public LogEvent rewrite(final LogEvent source) {
HashMap<String, String> contextMap = Maps.newHashMap(source.getContextMap());
contextMap.put(Context.TRACEID, contextMap.containsKey(Context.TRACEID) ? contextMap.get(Context.TRACEID) : NULL);
return new Log4jLogEvent.Builder(source).setContextMap(contextMap).build();
}
// ...
}
RewritePolicyの役割は、私たちがログ出力をするたびに、log4jのは、このクラスは、プロセスの一部を運営呼び出すということです。
設定log4j2.xml:
<Configuration status="warn">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="[%d{yyyy/MM/dd HH:mm:ss,SSS}][${ctx:_traceid}]%m%n" />
</Console>
<!--定义一个Rewrite-->
<Rewrite name="Rewrite">
<MyRewritePolicy/>
<!--引用输出模板-->
<AppenderRef ref="Console"/>
</Rewrite>
</Appenders>
<Loggers>
<!--使用日志模板-->
<Logger name="com.objcoding.MyLogger" level="debug" additivity="false">
<!--引用Rewrite-->
<AppenderRef ref="Rewrite"/>
</Logger>
</Loggers>
</Configuration>
カスタムログカテゴリ:
public class MyLogger {
private static final Logger logger = LoggerFactory.getLogger(MyLogger.class);
public static void info(String msg, Object... args) {
if (canLog() == 1 && logger.isInfoEnabled()) {
logger.info(msg, args);
}
}
public static void debug(String message, Object... args) {
if (canLog() == 1 && logger.isDebugEnabled()) {
logger.debug(message, args);
}
}
// ..
}
いいえ公共よりエキサイティングな記事は、バックエンド技術関連公衆数の焦点である「バックエンドアドバンス」、の維持に注意を払うを喜びません。
いいえ世間の注目とは、バックエンドに関連した無料の電子書籍を受け取るために、「バックエンド」を返信しません。
シェアへようこそ、ソースを保管してください再現。