반응형
MDC란
- Mapped Diagnostic Context
- 멀티 클라이언트 환경에서 다른 클라이언트와 값을 구별하여 로그를 추적할 수 있도록 제공되는 map
- ThreadLocal을 통해 구별할 수 있는 키 값을 저장하여 Thread가 살아있는 동안 해당 키값 활용
- 사실상 쓰레드안에서 공유되는 Map을 할당하여 사용하는 개념
Interface
- Singleton으로 생성됨
- Map과 비슷하게 CRUD 인터페이스 제공
동작원리
MDC class
- 내부적으로 MDCAdapter라는 인스턴스를 singleton하게 가지고 있으며 해당 어댑터를 통해서 interface들을 제공
MDCAdapter
public MDCAdapter getMDCA() {
return new LogbackMDCAdapter();
}
기본적으로 LogbackMDCAdapter라는 클래스로 인스턴스 초기화되어서 생성된다.
- 내부적으로 Thread에 변수를 할당할 수 있는 ThreadLocal을 가지고 있다
- ThreadLocal을 통해서 각 쓰레드에 변수(Map)을 할당하고 해당 Map에 CRUD를 지원해준다.
public void put(String key, String val) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
} else {
Map<String, String> oldMap = (Map)this.copyOnThreadLocal.get();
Integer lastOp = this.getAndSetLastOperation(1);
if (!this.wasLastOpReadOrNull(lastOp) && oldMap != null) {
oldMap.put(key, val);
} else {
Map<String, String> newMap = this.duplicateAndInsertNewMap(oldMap);
newMap.put(key, val);
}
}
}
- ThreadLocal을 통해서 할당되는 Map은 최초 put할시 생성되서 할당된다.
ThreadLocal과 ThreadPool
ThreadLocal
- 내부적으로 Thread의 정보를 key로 하여 Map형태로 데이터를 활용
- 이를통해 동일한 쓰레드에서 동일한 변수를 Load 할 수 있음
ThreadPool
- 매번 Thread 인스턴스를 만드는 비용이 아까우니, 미리 만들어서 돌려씀
문제점
- ThreadPool의 경우 Thread를 돌려쓰는? 개념이다보니 ThreadLocal입장에서는 하나의 쓰레드가 안사라지고 계속 있음
- 비즈니스상 pool로 공유되는 쓰레드의 ThreadLocal 변수들을 잘못 사용할 수 있음....
해결방안
- ExecutorService의 beforeExecute 메서드를 활용
- 해당 메서드가 실행되는 시점에서 부모Thread의 정보를 받아오기 힘듦... (pool에서 꺼낸 쓰레드에서 동작하기 때문에)
- 정확한 비즈니스 로직을 위해서는 해당 메서드 재정의 필요할듯..ㅎㅎ
MDC와 Netty
- Netty또한 쓰레드풀을 가용하기 때문에 MDC(ThreadLocal)의 활용이 애매하다.
매번 MDC 초기화
public class MDCThreadFactory implements ThreadFactory {
private final Map<String, String> mdc;
@Override
public Thread newThread( Runnable runnable ) {
return new MDCThread( group, runnable, name, mdc );
}
private static class MDCThread extends Thread {
private final Map<String, String> mdc;
MDCThread( ThreadGroup group, Runnable target, String name, Map<String, String> mdc ) {
super( group, target, name, 0 );
this.mdc = mdc;
// .. if needed, set properties like daemon, priority
}
@Override
public void run() {
if ( mdc != null ) {
MDC.setContextMap( mdc );
}
try {
super.run();
}
finally {
if ( mdc != null ) {
MDC.clear();
}
}
}
}
}
MDC 가용한 lib 사용
<dependency>
<groupId>org.mdedetrich</groupId>
<artifactId>mdc-async-netty-eventloopgroup</artifactId>
<version>0.1_4.0</version>
</dependency>
Thread(Task)내에서 MDC 초기화...
참고문헌
ThreadLocal with ThreadPool
- https://sabarada.tistory.com/163
- https://blog.naver.com/tmondev/221212500642
MDC & Netty
- https://github.com/mdedetrich/mdc-async-netty-eventloopgroup
- https://github.com/netty/netty/issues/6810
반응형
'개발 일지' 카테고리의 다른 글
Spring TransactionEventListener (0) | 2022.11.23 |
---|---|
Spring boot With Embedded Tomcat (0) | 2022.11.23 |
QuerydslPredicate 란 (0) | 2022.11.23 |
Nginx vs HAProxy (0) | 2022.11.23 |
OIDC 란 (0) | 2022.11.23 |