개발 일지

MDC 란

북극곰은콜라 2022. 11. 23. 22:16
반응형


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