Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 스프링 핵심 기능
- http
- springdatajpa
- 스프링 핵심 원리
- pointcut
- kotlin
- JDBC
- jpa
- 인프런
- 그리디
- 김영한
- Servlet
- Android
- QueryDSL
- db
- transaction
- Greedy
- Proxy
- SpringBoot
- AOP
- Spring Boot
- Exception
- 자바
- 백준
- java
- JPQL
- spring
- 알고리즘
- 스프링
- Thymeleaf
Archives
- Today
- Total
개발자되기 프로젝트
ThreadLocal - 개발 본문
- FieldLogTrace 에서 발생했던 동시성 문제를 ThreadLocal 로 해결해보자.
- TraceId traceIdHolder 필드를 쓰레드 로컬을 사용하도록
ThreadLocal<TraceId> traceIdHolder로 변경 - 필드 대신에 쓰레드 로컬을 사용해서 데이터를 동기화하는 ThreadLocalLogTrace 를 새로 만들자.
1. ThreadLocalLogTrace
기존 TraceId traceIdHolder를 ThreadLocal<TraceId> traceHolder로 변경하였다.
@Slf4j
public class ThreadLocalLogTrace implements LogTrace{
private static final String START_PREFIX = "-->";
private static final String COMPLETE_PREFIX = "<--";
private static final String EX_PREFIX = "<X-";
//private TraceId traceIdHolder; //traceId 동기화, 동시성 이슈 발생.
private ThreadLocal<TraceId> traceIdHolder = new ThreadLocal<>();
@Override
public TraceStatus begin(String message) {
syncTraceId();
TraceId traceId = traceIdHolder.get();
Long startTimeMs = System.currentTimeMillis();
log.info("[{}] {}{}", traceId.getId(), addSpace(START_PREFIX, traceId.getLevel()), message);
return new TraceStatus(traceId, startTimeMs, message);
}
private void syncTraceId(){
TraceId traceId = traceIdHolder.get();
if (traceId == null){
traceIdHolder.set(new TraceId());
} else {
traceIdHolder.set(traceId.creatNextId());
}
}
@Override
public void end(TraceStatus status) {
complete(status, null);
}
@Override
public void exception(TraceStatus status, Exception e) {
complete(status, e);
}
private void complete(TraceStatus status, Exception e){
Long stopTimeMs = System.currentTimeMillis();
long resultTimeMs = stopTimeMs - status.getStartTimeMs();
TraceId traceId = status.getTraceId();
if (e == null){
log.info("[{}] {}{} time={}ms", traceId.getId(), addSpace(COMPLETE_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs);
}else {
log.info("[{}] {}{} time={}ms ex={}", traceId.getId(), addSpace(EX_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs, e.toString());
}
releaseTraceId();
}
private void releaseTraceId() {
TraceId traceId = traceIdHolder.get();
if (traceId.isFirstLevel()){
traceIdHolder.remove();
}else {
traceIdHolder.set(traceId.creatPreviousId());
}
}
private static String addSpace(String prefix, int level){
StringBuilder sb = new StringBuilder();
for (int i=0; i < level; i++){
sb.append((i == level -1) ? "|" + prefix : "| ");
}
return sb.toString();
}
}
2. ThreadLocal.remove()
- 추가로 쓰레드 로컬을 모두 사용하고 나면
- 꼭 ThreadLocal.remove() 를 호출해서 쓰레드 로컬에 저장된 값을 제거해주어야 한다.
- 쉽게 이야기해서 다음의 마지막 로그를 출력하고 나면 쓰레드 로컬의 값을 제거해야 한다.
[3f902f0b] hello1
[3f902f0b] |-->hello2
[3f902f0b] |<--hello2 time=2ms
[3f902f0b] hello1 time=6ms //end() -> releaseTraceId() -> level==0,
ThreadLocal.remove() 호출
- 여기서는 releaseTraceId() 를 통해 level 이 점점 낮아져서 2 1 0이 되면
- 로그를 처음 호출한 부분으로 돌아온 것이다.
- 따라서 이 경우 연관된 로그 출력이 끝난 것이다. 이제 더 이상 TraceId 값을 추적하지 않아도 된다.
- 그래서 traceId.isFirstLevel() ( level==0 )인 경우
- ThreadLocal.remove() 를 호출해서 쓰레드 로컬에 저장된 값을 제거해준다.
3. GitHub: 21112 ThreadLocal_2
'인프런 > [인프런] 스프링 핵심 원리 - 고급' 카테고리의 다른 글
ThreadLocal - 주의사항 (0) | 2021.11.22 |
---|---|
ThreadLocal - 적용 (0) | 2021.11.22 |
ThreadLocal - 예제 (0) | 2021.11.22 |
Thread Local - 소개 (0) | 2021.11.22 |
동시성 문제 - 예제 코드 (0) | 2021.11.22 |
Comments