Skip to content

Latest commit

ย 

History

History
131 lines (102 loc) ยท 6.4 KB

2022-06-16-slueth-fuel.md

File metadata and controls

131 lines (102 loc) ยท 6.4 KB

Spring Cloud Sleuth๋Š” ๋ถ„์‚ฐ ํ™˜๊ฒฝ์—์„œ ์ผ๋ จ์˜ Request์— ๋Œ€ํ•œ ์ƒ๊ด€๊ด€๊ณ„๋ฅผ ํ‘œ์‹œํ•˜์—ฌ ์„œ๋น„์Šค ๊ฐ„ ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์ถ”์ ์„ ์ง€์›ํ•ด ์ฃผ๋Š” ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค. Sleuth๋Š” RestTemplate, Feign, WebClient์™€ ๊ฐ™์€ ์Šคํ”„๋ง ์ง„ํ˜•์˜ HTTP Client ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ Sleuth ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์œผ๋กœ๋„ ์„ค์ •์ด ์ž๋™์ ์œผ๋กœ ๋™์ž‘ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ HTTP Client ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ํ•ด๋‹น ์„ค์ •์„ ์ˆ˜๋™์œผ๋กœ ์ง„ํ–‰ ํ–์•„ ํ•˜๋Š”๋ฐ์š”. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ฝ”ํ‹€๋ฆฐ ๊ธฐ๋ฐ˜์˜ Fuel HTTP Client ๋ชจ๋“ˆ์— Sleuth๋ฅผ ์—ฐ๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Sleuth ์ ์šฉ ๋ฐฉ๋ฒ•

dependencyManagement {
    imports {
        mavenBom("org.springframework.cloud:spring-cloud-dependencies:2021.0.2")
    }
}

dependencies {
    implementation("org.springframework.cloud:spring-cloud-starter-sleuth")
}

Sleuth ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ logback ์„ค์ •๊ณผ ์—ฐ๊ณ„๋˜์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๊ทธ์— ๋ฐ”๋กœ ์ ์šฉ์ด ๋ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์„ค์ •์€ [application name, Trance ID, Span ID] ํ˜•์‹์œผ๋กœ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. Application name์€ spring.application.name: xxx ์„ค์ •๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ์ง€์ •๋ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ ํ˜•์‹์„ ๋ฐ”๊พธ๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์—๋Š” loback ์„ค์ •์„ ์ง์ ‘ ํ•˜์—ฌ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Trance ID & Span ID

์ด๋ฆ„ ์„ค๋ช…
Trace ID ์ „์ฒด Request์˜ ๊ณ ์œ ํ•œ ๊ฐ’
Span ID ์ „์ฒด Request์ค‘ ์ผ๋ถ€์˜ ์ผ๋ถ€์˜ ๊ณ ์œ ํ•œ ๊ฐ’
Parent Span ID ์ด์ „ Request์˜ Span ID๋กœ ์š”์ฒญ์˜ ํ๋ฆ„์„ ํŒŒ์•…์„ ์œ„ํ•œ ๊ฐ’

  1. API Gateway์—์„œ Request๋ฅผ ๋ฐ›์•„ Trace ID, Span ID๋ฅผ ๋™์ผํ•œ ๊ฐ’์œผ๋กœ ์ƒ์„ฑํ•˜๋ฉฐ Parent Span ID๋Š” null์œผ๋กœ ์ง€์ •
  2. A Service์—์„œ๋Š” Trace ID๋Š” ๋™์ผํ•˜๊ฒŒ ์„ค์ •, Span ID๋Š” Request์˜ ์ค‘ ์ผ๋ถ€๋กœ ๊ณ ์œ ํ•œ ๊ฐ’์„ ์„ค์ •, Parent Span ID๋Š” ์ด์ „ Request์˜ Span ID๋กœ ์ง€์ •
  3. B Service์—์„œ๋Š” Trace ID๋Š” ๋™์ผํ•˜๊ฒŒ ์„ค์ •, Span ID๋„ ๋™์ผํ•˜๊ฒŒ ์œ ์ผํ•œ ๊ฐ’, Parent Span ID๋„ ๋™์ผํ•˜๊ฒŒ ์ด์ „ Request์˜ Span ID๋กœ ์„ค์ •

Request์˜ ์ „์ฒด ํ๋ฆ„์„ Trace ID๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํŠธ๋ž˜ํ‚น ํ•˜๋ฉฐ Span ID๋กœ๋Š” ํ•ด๋‹น Request์˜ ์†ํ–ˆ๋˜ ์„œ๋น„์Šค์˜ ์œ ๋‹ˆํฌํ•˜๊ฒŒ ์‹๋ณ„์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ Parent Span ID๋ฅผ ํ†ตํ•ด์„œ ํ˜ธ์ถœ ๊ฐ„์˜ ์ƒ๊ด€๊ด€๊ณ„๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Sleuth + Fuel

RestTemplate, Feign, WebClient์ฒ˜๋Ÿผ ์Šคํ”„๋ง ์ง„ํ˜•์˜ HTTP Client๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Sleuth ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์ž๋™์œผ๋กœ Sleuth๊ฐ€ ๋™์ž‘ํ•˜๊ฒŒ ๋˜๋ฉฐ HTTP Header ์ •๋ณด์— Trace ID, Span ID, Parent Span ID๋ฅผ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ ์™ธ์— HTTP Client ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ํ•ด๋‹น ์„ค์ •์„ ์ง„ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ณธ ์˜ˆ์ œ๋Š” Kotlin ๊ธฐ๋ฐ˜์˜ HTTP Client ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ Fuel๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

@Configuration
class FuelConfiguration {
    @Bean
    fun fuelManager(tracer: Tracer) =
        FuelManager.instance.apply {
            this.timeoutReadInMillisecond = 120_000 // 2๋ถ„ // 1
            this.timeoutReadInMillisecond = 120_000 // 2๋ถ„ // 2
            this.addRequestInterceptor(tracingRequestInterceptor(tracer = tracer)) // 3
            this.addRequestInterceptor(LogRequestInterceptor) // 4
            this.addResponseInterceptor(LogResponseInterceptor) // 5

        }

    // 6
    private fun tracingRequestInterceptor(tracer: Tracer) = { next: (Request) -> Request ->
        { request: Request ->
            val span = tracer.currentSpan() ?: tracer.nextSpan()
            request.header(
                "x-b3-traceid" to span.context().traceId(),
                "x-b3-spanid" to tracer.nextSpan().context().spanId(),
                "x-b3-parentspanid" to tracer.nextSpan().context().parentId().toString()
            )
            next(request)
        }
    }
}
  • FuelManager์„ ํ†ตํ•ด์„œ ์„ค์ •์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • (1),(2): Timeout ์„ค์ •์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์„ค์ •์ด 15s์ด๊ธฐ ๋•Œ๋ฌธ์— ์กฐ์ •์ด ํ•„์š”ํ•˜๋ฉด ์„œ๋น„์Šค์— ๋งž๋ฐ ์„ค์ •์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • (3): Header ๊ฐ์ฒด์— x-b3-traceid, x-b3-spanid, x-b3-parentspanid์˜ ๊ฐ’์„ Tracer ๊ฐ์ฒด ๊ธฐ๋ฐ˜์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • (4)(5): Request, Response๋ฅผ Logging ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
@RestController
@RequestMapping("/a-service")
class AServiceApi(
    private val tracer: Tracer
) {

    @GetMapping
    fun a() {
        "http://localhost:8686/b-service"
            .httpGet()
            .header(CONTENT_TYPE to "application/json")
            .response()
    }
}

Fuel์„ ๊ธฐ๋ฐ˜์œผ๋กœ B Service๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. Fuel์€ ๋งค์šฐ ์ง๊ด€์ ์œผ๋กœ HTTP ํ†ต์‹ ์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. API Gateway -> A Service -> B Service๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ตฌ์กฐ์—์„œ A Service์˜ ๋กœ๊ทธ ์ •๋ณด๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

# API Gateway
2022-05-28 18:09:04.159  INFO [gateway-server,757d0493f099b94b,757d0493f099b94b] 11352 --- [ctor-http-nio-4] com.server.gateway.GlobalFilter          : =======API Gateway\======
...

# A service Log
2022-05-28 18:09:04.163  INFO [service-a,757d0493f099b94b,7fba8ecffbdbabcd] 9927 --- [nio-8787-exec-5] c.example.msaerrorresponse.AServiceApi   : =======a-service======
--> GET http://localhost:8686/b-service
Body : (empty)
Headers : (4)
Content-Type : application/json
x-b3-spanid : 4e8d66a6aa1c1ed6
x-b3-parentspanid : 7fba8ecffbdbabcd
x-b3-traceid : 757d0493f099b94b

<-- 200 http://localhost:8686/b-service
Response : 
Length : 0
Body : (empty)
Headers : (4)
Connection : keep-alive
Date : Sat, 28 May 2022 09:09:04 GMT
Content-Length : 0
Keep-Alive : timeout=60

# B service Log
2022-05-28 18:09:04.165  INFO [service-b,757d0493f099b94b,4e8d66a6aa1c1ed6] 9989 --- [nio-8686-exec-3] c.example.msaerrorresponse.BServiceApi   : =======b-service======

๋ชจ๋“  Request๋Š” Trace ID: 757d0493f099b94b์œผ๋กœ ๊ทธ๋ฃนํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜๋ฉฐ ๊ฐ ์„œ๋น„์Šค๋งˆ๋‹ค Span ID๋งˆ๋‹ค ๊ณ ์œ ํ•œ ๊ฐ’์œผ๋กœ ํŠธ๋ž˜ํ‚น์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋˜ Parent Span ID๋ฅผ ํ†ตํ•ด์„œ Request์˜ ์ƒ๊ด€๊ด€๊ณ„๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ Slueth๋ฅผ ํ†ตํ•ด์„œ Request์˜ ์ƒ๊ด€๊ด€๊ณ„๋ฅผ ๋กœ๊น…์„ํ•˜๋ฉด ํ•ด๋‹น ์ •๋ณด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์‹œ๊ฐํ™”๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์ด๋ฏธ์ง€๋Š” Elastic Search APM๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.