[Spring Boot & MSA] Thread Context & Hystrix

μ›μ•Œλ ‰μŠ€Β·2020λ…„ 8μ›” 12일
0

Spring Boot MSA

λͺ©λ‘ 보기
12/12
post-thumbnail

βœ” κΉƒν—ˆλΈŒ μ†ŒμŠ€μ½”λ“œ
βœ” Udemy κ°•μ˜

🌟 Thread Context & Hystrix

Β Β Β Β  @HystrixCommand κ°€ 싀행될 λ•Œ THREAD 와 SEMAPHORE λΌλŠ” 두 가지 λ‹€λ₯Έ 격리 μ „λž΅μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 기본적으둜 νžˆμŠ€νŠΈλ¦­μŠ€λŠ” THREAD 격리 μ „λž΅μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€. ν˜ΈμΆœμ„ λ³΄ν˜Έν•˜λŠ” 데 μ‚¬μš©λœ λͺ¨λ“  히슀트릭슀 λͺ…령은 ν˜ΈμΆœμ„ μ‹œλ„ν•œ λΆ€λͺ¨ μŠ€λ ˆλ“œμ™€ μ»¨ν…μŠ€νŠΈλ₯Ό κ³΅μœ ν•˜μ§€ μ•ŠλŠ” 격리된 μŠ€λ ˆλ“œ ν’€μ—μ„œ μˆ˜ν–‰λ©λ‹ˆλ‹€. μ΄λŠ” νžˆμŠ€νŠΈλ¦­μŠ€κ°€ 자기 ν†΅μ œν•˜μ—μ„œ μ›λž˜ ν˜ΈμΆœμ„ μ‹œλ„ν•œ λΆ€λͺ¨ μŠ€λ ˆλ“œμ™€ μ—°κ΄€λœ μ–΄λ–€ ν™œλ™λ„ λ°©ν•΄ν•˜μ§€ μ•Šκ³  μŠ€λ ˆλ“œ 싀행을 쀑단할 수 μžˆλ‹€λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€.

Β Β Β Β  λ°˜λ©΄μ— SEMAPHORE 기반 격리 방법을 μ‚¬μš©ν•˜λ©΄ νžˆμŠ€νŠΈλ¦­μŠ€λŠ” μƒˆλ‘œμš΄ μŠ€λ ˆλ“œλ₯Ό μ‹œμž‘ν•˜μ§€ μ•Šκ³  @HystrixCommand μ–΄λ…Έν…Œμ΄μ…˜μ΄ λ³΄ν˜Έν•˜λŠ” λΆ„μ‚° ν˜ΈμΆœμ„ κ΄€λ¦¬ν•˜λ©° νƒ€μž„ 아웃이 λ°œμƒν•˜λ©΄ λΆ€λͺ¨ μŠ€λ ˆλ“œλ₯Ό μ€‘λ‹¨μ‹œν‚΅λ‹ˆλ‹€. Tomcat 처럼 동기식 μ»¨ν…Œμ΄λ„ˆ μ„œλ²„ ν™˜κ²½μ—μ„œ λΆ€λͺ¨ μŠ€λ ˆλ“œλ₯Ό μ€‘λ‹¨ν•˜λ©΄ κ°œλ°œμžκ°€ μ˜ˆμ™Έ 처리λ₯Ό ν•  수 μ—†λŠ” μ˜ˆμ™Έκ°€ λ°œμƒν•©λ‹ˆλ‹€. 이처럼 λ°œμƒν•œ μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•  수 μ—†κ±°λ‚˜ μžμ› 정리 및 μ—λŸ¬ 처리λ₯Ό μˆ˜ν–‰ν•  수 μ—†λ‹€λ©΄ κ°œλ°œμžκ°€ μ½”λ“œλ₯Ό μž‘μ„±ν•  λ•Œ μ˜ˆμƒν•˜μ§€ λͺ»ν•œ κ²°κ³Όκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

βœ” λͺ…λ Ή 풀에 λŒ€ν•œ 격리 μ„€μ • 방법

@HystrixCommand(
  commandProperties = {
    @HystricProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")
  }
)
public void someMethod() {}

βœ” NOTE

Β Β Β Β  기본적으둜 λŒ€λΆ€λΆ„μ˜ λͺ…령에 λŒ€ν•΄ κΈ°λ³Έ 격리 μ „λž΅μΈ THREAD 방식을 ꢌμž₯ν•©λ‹ˆλ‹€. THREAD 격리 방식은 히슀트릭슀 λͺ…λ Ή μŠ€λ ˆλ“œμ™€ λΆ€λͺ¨ μŠ€λ ˆλ“œ 사이에 격리 μˆ˜μ€€μ„ 높이며, SEMAPHORE 격리 방식보닀 λ¬΄κ²μŠ΅λ‹ˆλ‹€. SEMAPHORE 격리 λͺ¨λΈμ€ κ²©λŸ‰μ΄λ©°, μ„œλΉ„μŠ€μ—μ„œ λŒ€μš©λŸ‰μ„ μ²˜λ¦¬ν•˜κ³  비동기 I/O ν”„λ‘œκ·Έλž˜λ° λͺ¨λΈμ„ μ μš©ν•  λ•Œ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸš€ ThreadLocal & Hystrix

Β Β Β Β  기본적으둜 νžˆμŠ€νŠΈλ¦­μŠ€λŠ” λΆ€λͺ¨ μŠ€λ ˆλ“œμ˜ μ»¨ν…μŠ€νŠΈλ₯Ό 히슀트릭슀 λͺ…령이 κ΄€λ¦¬ν•˜λŠ” μŠ€λ ˆλ“œμ— μ „νŒŒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ λΆ€λͺ¨ μŠ€λ ˆλ“œμ—μ„œ ThreadLocal둜 μ„€μ •λœ 값은 기본적으둜 λΆ€λͺ¨ μŠ€λ ˆλ“œκ°€ ν˜ΈμΆœν•˜λŠ” λ©”μ†Œλ“œμ—μ„œ μ‚¬μš©ν•  수 μ—†κ³  @HystrixCommand 객체둜 λ³΄ν˜Έλ©λ‹ˆλ‹€.

Β Β Β Β  REST 기반 ν™˜κ²½μ—μ„œ μ’…μ’… μ„œλΉ„μŠ€λ₯Ό 운영 κ΄€λ¦¬ν•˜λŠ” 데 도움이 λ˜λŠ” μ»¨ν…μŠ€νŠΈ κ΄€λ ¨ 정보λ₯Ό μ„œλΉ„μŠ€ 호좜둜 μ „λ‹¬ν•˜λŠ” κ²½μš°κ°€ μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄ HTTP Header에 상관 관계 ID(Correlation ID)λ‚˜ 인증 토큰을 λ‹΄μ•„ μ „λ‹¬ν•œ ν›„ λͺ¨λ“  ν•˜μœ„ μ„œλΉ„μŠ€ ν˜ΈμΆœμ— μ „νŒŒν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ—¬κΈ°μ„œ 상관 관계 IDλ₯Ό μ‚¬μš©ν•˜λ©΄ ν•œ νŠΈλžœμž­μ…˜ λ‚΄ μ—¬λŸ¬ μ„œλΉ„μŠ€ ν˜ΈμΆœμ„ 좔적할 수 μžˆλŠ” κ³ μœ ν•œ μ‹λ³„μž(ID)λ₯Ό κ°–κ²Œ λ©λ‹ˆλ‹€.

Β Β Β Β  이 값듀을 λͺ¨λ“  μ„œλΉ„μŠ€ ν˜ΈμΆœμ— μ‚¬μš©ν•  수 있게 ν•˜λ €λ©΄ μŠ€ν”„λ§ ν•„ν„°(Spring Filter) 클래슀λ₯Ό μ‚¬μš©ν•΄ REST μ„œλΉ„μŠ€μ— λŒ€ν•œ λͺ¨λ“  ν˜ΈμΆœμ„ κ°€λ‘œμ±„κ³ , λ“€μ–΄μ˜€λŠ” HTTP μš”μ²­μ—μ„œ μ»¨ν…μŠ€νŠΈ 정보λ₯Ό μΆ”μΆœν•΄ μ‚¬μš©μž μ •μ˜ν•œ UserContext 객체에 μ €μž₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

βœ” μŠ€ν”„λ§ ν•„ν„° 예제 μ½”λ“œ

@Component
public class UserContextFilter implements Filter {

    private static final Logger logger = LoggerFactory.getLogger(UserContextFilter.class);


    /**
     *
     * HTTP Request Headerμ—μ„œ κ²€μƒ‰ν•œ 값을 UserContextHolder의 UserContext에 μ €μž₯ν•œλ‹€.
     *
     */
    @Override
    public void doFilter(
            ServletRequest servletRequest,
            ServletResponse servletResponse,
            FilterChain filterChain
    ) throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        UserContextHolder.getContext().setCorrelationId(httpServletRequest.getHeader(UserContext.CORRELATION_ID));
        UserContextHolder.getContext().setUserId(httpServletRequest.getHeader(UserContext.USER_ID));
        UserContextHolder.getContext().setAuthToken(httpServletRequest.getHeader(UserContext.AUTH_TOKEN));
        UserContextHolder.getContext().setOrgId(httpServletRequest.getHeader(UserContext.ORG_ID));

        logger.debug("UserContextFilter Correlation id: {}", UserContextHolder.getContext().getCorrelationId());

        filterChain.doFilter(httpServletRequest, servletResponse);
    }
}

UserContextHolder ν΄λž˜μŠ€λŠ” ThreadLocal ν΄λž˜μŠ€μ— UserContextλ₯Ό μ €μž₯ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€. UserContextκ°€ ThreadLocal μ €μž₯μ†Œμ— μ €μž₯되면 μš”μ²­μœΌλ‘œ μ‹€ν–‰λœ λͺ¨λ“  μ½”λ“œμ—μ„œ UserContextHolder의 UserContext 객체λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

βœ” UserContextHolder & UserContext 예제 μ½”λ“œ

public class UserContextHolder {

    // 정적 ThreadLocal λ³€μˆ˜μ— μ €μž₯λ˜λŠ” UserContext
    private static final ThreadLocal<UserContext> userContext = new ThreadLocal<UserContext>();

    public static UserContext getContext() {
        UserContext context = userContext.get();

        if(context == null) {
            context = createEmptyContext();
            userContext.set(context);
        }
        return userContext.get();
    }

    public static void setContext(UserContext context) {
        Assert.notNull(context, "Only non-null UserContext instances are permitted.");
        userContext.set(context);
    }

    public static UserContext createEmptyContext() {
        return new UserContext();
    }
}

@Component
@Getter
@Setter
public class UserContext {

    public static final String CORRELATION_ID = "spmia-correlation-id";
    public static final String AUTH_TOKEN = "spmia-auth-token";
    public static final String USER_ID = "spmia-user-id";
    public static final String ORG_ID = "spmia-org-id";

    private String correlationId = "";
    private String authToken = "";
    private String userId = "";
    private String orgId = "";
}
profile
Alex's Develog πŸ€”

0개의 λŒ“κΈ€