Spring Cloud Netflix - llb1026/spring_cloud_netflix_practice GitHub Wiki

TOC


Intro

Spring Cloud Netflix๋Š” ์ž๋™ ํ™˜๊ฒฝ ์„ค์ •๊ณผ Spring Environment ๋ฐ ๋‹ค๋ฅธ Spring ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ ๊ด€๋…์˜ ๋ฐ”์ธ๋”ฉ์„ ๋ฐ”ํƒ•์œผ๋กœ Spring Boot ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ Netflix OSS (Open Source Software) ํ†ตํ•ฉ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ช‡ ๊ฐ€์ง€ ๊ฐ„๋‹จํ•œ ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด๋ถ€์˜ ๊ณตํ†ต ํŒจํ„ด์„ ์‹ ์†ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  battle-test๋ฅผ ๊ฑฐ์นœ Netflix ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ†ตํ•ด ๋Œ€๊ทœ๋ชจ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ๊ณต๋˜๋Š” ํŒจํ„ด์—๋Š” Service Discovery (Eureka), Circuit Breaker (Hystrix), Intelligent Routing (Zuul), ๊ทธ๋ฆฌ๊ณ  Client Side Load Balancing (Ribbon) ๋“ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. โ†’ ์ถœ์ฒ˜: https://spring.io/projects/spring-cloud-netflix#overview

์ „์ œ์กฐ๊ฑด

์—ฌ๊ธฐ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋ชฉํ‘œ/๋‹ˆ์ฆˆ๊ฐ€ ์žˆ๋Š” ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์˜ MSA ๊ธฐ๋ฐ˜ ์‹œ์Šคํ…œ์„ ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

  • ์ ์ • ํฌ๊ธฐ โ†’ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹ ์†ํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•˜๊ณ  ์ „์ฒด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „๋ฐ˜์ ์ธ ์žฅ์• ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋„๋ก ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ํ•˜๋‚˜ ๋‹น ํ•œ ๊ฐ€์ง€ ์ฑ…์ž„ ์˜์—ญ์— ์ง‘์ค‘ํ•จ
  • ์œ„์น˜ ํˆฌ๋ช…์„ฑ โ†’ ๋Œ€๊ณ ๊ฐ ์„œ๋น„์Šค์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ณ  ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ๋น ๋ฅด๊ฒŒ ์ถ”๊ฐ€/์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋„๋ก ์ธ์Šคํ„ด์Šค์˜ ๋ฌผ๋ฆฌ์  ์œ„์น˜๋ฅผ ๊ด€๋ฆฌํ•จ
  • ํšŒ๋ณต์„ฑ โ†’ ์„œ๋น„์Šค์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋Œ€๊ณ ๊ฐ ์„œ๋น„์Šค์— ์˜ํ–ฅ์„ ์ตœ์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•˜์—ฌ ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ•œ ์ธ์Šคํ„ด์Šค๋ฅผ ์šฐํšŒํ•˜๊ณ  '๋นจ๋ฆฌ ์‹คํŒจ'ํ•˜๊ฒŒ ํ•จ
  • ๋ฐ˜๋ณต์„ฑ โ†’ ์ƒˆ๋กœ์šด ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๊ฐ€ ์‹œ์ž‘๋  ๋•Œ๋งˆ๋‹ค ๋ณ„๋„์˜ ๋ณต์žกํ•œ ๋งค๋‰ด์–ผ ์ž‘์—… ์—†์ด ๊ธฐ์กด ์ธ์Šคํ„ด์Šค์™€ ๋™์ผํ•œ ์ฝ”๋“œ์™€ ๊ตฌ์„ฑ์œผ๋กœ ์‹œ์ž‘๋˜๋„๋ก ํ•จ
  • ํ™•์žฅ์„ฑ โ†’ ์„œ๋น„์Šค ๊ฐ„ ์˜์กด์„ฑ์„ ์ตœ์†Œํ™”ํ•˜๋ฉด์„œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹ ์†ํ•˜๊ฒŒ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ๋น„๋™๊ธฐ ํ”„๋กœ์„ธ์‹ฑ๊ณผ ์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•จ

์ด๋ฅผ ์œ„ํ•ด์„œ ์ด๋ก ์ ์œผ๋กœ ๋‹ค์Œ 6๊ฐ€์ง€ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ํŒจํ„ด์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

  1. ํ•ต์‹ฌ ๊ฐœ๋ฐœ ํŒจํ„ด (core development patterns) โ†’ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์–ด๋–ป๊ฒŒ ์ ์ • ํฌ๊ธฐ๋กœ ๋‚˜๋ˆŒ ๊ฒƒ์ธ์ง€
  2. ๋ผ์šฐํŒ… ํŒจํ„ด (routing patterns) โ†’ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐ„์˜ ์œ„์น˜ ํˆฌ๋ช…์„ฑ์„ ์–ด๋–ป๊ฒŒ ๋ณด์žฅํ•  ๊ฒƒ์ธ์ง€
  3. ํด๋ผ์ด์–ธํŠธ ํšŒ๋ณต์„ฑ ํŒจํ„ด (client resiliency patterns) โ†’ ํšŒ๋ณต์„ฑ์„ ์–ด๋–ป๊ฒŒ ๋ณด์žฅํ•  ๊ฒƒ์ธ์ง€
  4. ๋ณด์•ˆ ํŒจํ„ด (security patterns) โ†’ AAA (Authentication, Authorization, Accounts) ๋ฅผ ์–ด๋–ป๊ฒŒ ๋ณด์žฅํ•  ๊ฒƒ์ธ์ง€
  5. ๋กœ๊น… ๋ฐ ์ถ”์  ํŒจํ„ด (logging and tracing patterns) โ†’ ์–ด๋–ป๊ฒŒ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋””๋ฒ„๊น…ํ•˜๊ณ  ์ถ”์ ํ•  ๊ฒƒ์ธ์ง€
  6. ๋นŒ๋“œ ๋ฐ ๋ฐฐํฌ ํŒจํ„ด (build and deployment pattern) โ†’ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐ˜๋ณต์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ์–ด๋–ป๊ฒŒ ๋ณด์žฅํ•  ๊ฒƒ์ธ์ง€

์—ฌ๊ธฐ์„œ ์ •๋ฆฌํ•  Spring Cloud Netflix์˜ Eureka, Ribbon, Zuul, Hystrix๋Š” ์œ„ ํŒจํ„ด๊ณผ ๊ฐ๊ฐ ์•„๋ž˜์™€ ๊ฐ™์ด ๋งค์นญ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Name Patterns
Zuul ๋ผ์šฐํŒ… ํŒจํ„ด, ๋ณด์•ˆ ํŒจํ„ด, ๋กœ๊น… ๋ฐ ์ถ”์  ํŒจํ„ด
Eureka ๋ผ์šฐํŒ… ํŒจํ„ด
Ribbon ๋ผ์šฐํŒ… ํŒจํ„ด
Hystrix ํด๋ผ์ด์–ธํŠธ ํšŒ๋ณต์„ฑ ํŒจํ„ด

Eureka as Service Discovery

๊ธฐ์กด์˜ ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ

์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ์˜ ์—ญํ• ์€ ํฌ๊ฒŒ ์•„๋ž˜ 2๊ฐ€์ง€๋กœ ๊ผฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค(API Callee)์˜ ๋ฌผ๋ฆฌ์  ์ฃผ์†Œ๋ฅผ ํด๋ผ์ด์–ธํŠธ(API Caller)์—๊ฒŒ ๋“œ๋Ÿฌ๋‚ด์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค

๋งŒ์•ฝ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ธ์Šคํ„ด์Šค์˜ real IP์— ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค๋ฉด, ์ธ์Šคํ„ด์Šค๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์„ ๋•Œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์š”์ฒญ์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” ์ธ์Šคํ„ด์Šค ๋ชฉ๋ก์— ์‹ ๊ทœ ์ถ”๊ฐ€๋œ IP๋ฅผ ๋งค๋ฒˆ ์—…๋ฐ์ดํŠธํ•ด์ค˜์•ผ ํ•œ๋‹ค

  1. ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์˜ ํšŒ๋ณต์„ฑ์„ ์œ„ํ•ด ์ •์ƒ์ ์ธ ์ƒํƒœ๊ฐ€ ์•„๋‹Œ ์ธ์Šคํ„ด์Šค๋Š” ๊ฐ€์šฉ ์ธ์Šคํ„ด์Šค ๋ชฉ๋ก์—์„œ ์ œ๊ฑฐํ•ด์•ผ ํ•œ๋‹ค

์—ฌ๊ธฐ๊นŒ์ง€ ๋ณด๋ฉด ๊ธฐ์กด์˜ L4๋‚˜ DNS๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋ฌด๋ฐฉํ•  ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์•„๋ž˜์™€ ๊ฐ™์€ ํ•œ๊ณ„์ ์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋ฅผ ๋‹ค์ค‘ํ™”ํ•œ๋‹ค๊ณ  ํ•ด๋„, ์—ฌ์ „ํžˆ SPOF(Single Point Of Failure, ๋‹จ์ผ ์žฅ์•  ์ง€์ )์ด ๋  ์ˆ˜ ์žˆ๋‹ค โ†’ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๊ฐ€ ๋ป—์œผ๋ฉด ๋ชจ๋“  ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜๋„ ๋‹ค์šด๋จ
  • ๋ณดํ†ต ์ƒ์šฉ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋Š” hot-swap ์ด์ค‘ํ™”๋ฅผ ์ง€์›ํ•œ๋‹ค (active ํ•œ ๋Œ€, stand-by ํ•œ ๋Œ€)
  • ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค์˜ ์ถ”๊ฐ€/์‚ญ์ œ ๊ณผ์ •์ด ๋А๋ฆฌ๋‹ค (์ž๋™ํ™” X, ์‚ฌ๋žŒ ์ˆ˜์ž‘์—… ํ•„์š”) โ†’ ์œ„์น˜ ํˆฌ๋ช…์„ฑ ๋ณด์žฅ์ด ๋ถˆํˆฌ๋ช…

ํด๋ผ์šฐ๋“œ ๊ธฐ๋ฐ˜ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜์—์„œ์˜ ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ

ํด๋ผ์šฐ๋“œ ๊ธฐ๋ฐ˜์˜ MSA ํ™˜๊ฒฝ์—์„œ๋Š” ์•„๋ž˜ ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ๊ฐ€ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

  • ๊ธฐ์กด ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋ณด๋‹ค ๊ณ ๊ฐ€์šฉ์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค โ†’ ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ ํด๋Ÿฌ์Šคํ„ฐ ๊ตฌ์ถ•
  • ๊ธฐ์กด ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๊ฐ€ ํ•˜๋˜ ๋ถ€ํ•˜ ๋ถ„์‚ฐ ์—ญํ• ๋„ ๋‹น์—ฐํžˆ ์ˆ˜ํ–‰ํ•œ๋‹ค
  • ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ ํด๋Ÿฌ์Šคํ„ฐ์˜ ๊ฐ ๋…ธ๋“œ๋Š” ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค์˜ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•œ๋‹ค
  • ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ๋Š” ๊ฐ ์ธ์Šคํ„ด์Šค์˜ ์ƒํƒœ๋ฅผ ์ง€์†์ ์œผ๋กœ ๊ฐ์ง€ํ•˜๋ฉฐ ํŠน์ • ์ธ์Šคํ„ด์Šค๊ฐ€ ์ •์ƒ์ ์ด์ง€ ์•Š์„ ๋•Œ ๊ฐ€์šฉ ์„œ๋น„์Šค ๋ชฉ๋ก์—์„œ ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค
  • ๋ชจ๋“  ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๋Š” ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ์˜ ๊ฐ€์šฉ ์„œ๋น„์Šค ๋ชฉ๋ก์„ ๋กœ์ปฌ์— ์บ์‹ฑํ•ด ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ๊ฐ€ ์ฃฝ์—ˆ์„ ๋•Œ ์บ์‹ฑ๋œ ๋ชฉ๋ก์„ ํ™œ์šฉํ•ด ๊ณ„์† ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค

๊ฐ„๋‹จํžˆ ์œ„ ๋ชฉ๋ก์„ ๋„์‹ํ™”ํ•œ ๊ทธ๋ฆผ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Eureka Server ๊ตฌ์„ฑ

๊ธฐ๋ณธ์ ์œผ๋กœ ์•„๋ž˜์˜ 3๊ฐœ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  1. ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ ๋…ธ๋“œ๊ฐ€ ๋  Eureka Server ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜
  2. Eureka๋ฅผ ํ†ตํ•ด API๋ฅผ ์š”์ฒญํ•  API Caller ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ A
  3. A์˜ ์š”์ฒญ์— ๋Œ€ํ•ด ์‘๋‹ต์„ ์ค„ API Callee ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ B

Eureka Server ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์˜์กด์„ฑ ์ถ”๊ฐ€ (build.gradle ํŒŒ์ผ)

    dependencies {
        implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka- 
    server'
        ...
    }
    
  2. ์œ ๋ ˆ์นด ์„œ๋ฒ„ ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€

    @SpringBootApplication
    @EnableEurekaServer       // ์ด ๋ถ€๋ถ„
    public class EurekaServerApplication {
        ...
    }
    
  3. ๊ตฌ์„ฑ ์„ค์ • (application.properties ํŒŒ์ผ)

    server.port=8761                        // Eureka Server๊ฐ€ listenํ•  ํฌํŠธ
    eureka.instance.hostname=eureka         // Eureka Server์˜ ํ˜ธ์ŠคํŠธ๋ช…
    eureka.client.registerWithEureka=false  // Eureka Server์— ์ž๊ธฐ ์ž์‹ ์„ ๋“ฑ๋กํ•˜์ง€ ์•Š๊ฒ ๋‹ค๋Š” ์˜๋ฏธ
    eureka.client.fetchRegistry=false       // ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์ •๋ณด๋ฅผ ๋กœ์ปฌ์— ์บ์‹ฑํ•˜์ง€ ์•Š๊ฒ ๋‹ค๋Š” ์˜๋ฏธ
    eureka.client.serviceUrl.defaultZone=http://${registry.host:localhost}:${server.port}/eureka/
    

์—ฌ๊ธฐ๊นŒ์ง€ ๊ตฌ์„ฑ ํ›„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰์‹œ์ผœ localhost:8761 ๋กœ ์ ‘์†ํ•˜๋ฉด ๊ฐ„๋‹จํ•œ Eureka Server ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ์€ Eureka Server์— ๋“ฑ๋ก๋œ ์ธ์Šคํ„ด์Šค๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋นˆ ๋ชฉ๋ก์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

์ธ์Šคํ„ด์Šค ๊ตฌ์„ฑ

์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ์— ๋“ฑ๋กํ•  API Caller, Callee ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ด…๋‹ˆ๋‹ค.

  1. ์˜์กด์„ฑ ์ถ”๊ฐ€ (build.gradle ํŒŒ์ผ)

    dependencies {
        implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka- 
    client'
        ...
    }
    
  2. ์ธ์Šคํ„ด์Šค ๋“ฑ๋ก ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€

    @SpringBootApplication
    @EnableDiscoveryClient   // ์ด ๋ถ€๋ถ„
    public class ApiCalleeApplication {
        ...
    }
    
  3. ๊ตฌ์„ฑ ์„ค์ • (application.properties ํŒŒ์ผ)

    spring.application.name=apicallee       // ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ์— ๋“ฑ๋กํ•  ์ธ์Šคํ„ด์Šค์˜ ๋…ผ๋ฆฌ์  ์ด๋ฆ„
    eureka.instance.preferIpAddress=true    // ์ธ์Šคํ„ด์Šค ์ด๋ฆ„ ๋Œ€์‹  IP์ฃผ์†Œ๋ฅผ ๋“ฑ๋กํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ
    eureka.client.registerWithEureka=true   // Eureka Server์— ์ž๊ธฐ ์ž์‹ ์„ ๋“ฑ๋กํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ
    eureka.client.serviceUrl.defaultZone=http://${registry.host:localhost}:${server.port}/eureka/
    

Eureka Server์— ๋“ฑ๋กํ•˜๋Š” ์ธ์Šคํ„ด์Šค ์‹๋ณ„์ž๋กœ๋Š” Application ID์™€ Instance ID๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Application ID๋Š” ์ธ์Šคํ„ด์Šค์˜ ๊ทธ๋ฃน์„ ์˜๋ฏธํ•˜๋ฉฐ, ์œ„ ํ”„๋กœํผํ‹ฐ ํŒŒ์ผ์—์„œ ๋ช…์‹œํ•œ spring.application.name ์ž…๋‹ˆ๋‹ค. Instance ID๋Š” ๋™์ผํ•œ ์ธ์Šคํ„ด์Šค๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ ๋–  ์žˆ์„ ๋•Œ, ๊ฐ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ์ž„์˜์˜ ์ˆซ์ž์ž…๋‹ˆ๋‹ค.

์ธ์Šคํ„ด์Šค์˜ ๋…ผ๋ฆฌ์  ์ด๋ฆ„ ๋Œ€์‹  IP์ฃผ์†Œ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ์ด์œ ๋Š” ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜ (ex. Docker) ๋ฐฐํฌ ์‹œ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ DNS ์—”ํŠธ๋ฆฌ๊ฐ€ ์—†๋Š” ์ž„์˜๋กœ ์ƒ์„ฑ๋œ ํ˜ธ์ŠคํŠธ๋ช…์„ ๋ถ€์—ฌ๋ฐ›์•„ ์‹œ์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, API Caller๊ฐ€ Callee์˜ ์œ„์น˜๋ฅผ ์ •์ƒ์ ์œผ๋กœ ์ฐพ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํ•ญ์ƒ eureka.instance.preferIpAddress=true๋กœ ์„ค์ •ํ•ด๋‘๋Š” ํŽธ์ด ์ ์ ˆํ•˜๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ธ์Šคํ„ด์Šค๊ฐ€ Eureka Server์— ๋“ฑ๋ก๋˜๋ฉด Eureka Server๋Š” ์ง€์†์ ์ธ health check๋ฅผ ํ†ตํ•ด ์ธ์Šคํ„ด์Šค์˜ ์ƒํƒœ๋ฅผ ๊ฐ€์šฉ ์ธ์Šคํ„ด์Šค ๋ชฉ๋ก์— ์—…๋ฐ์ดํŠธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ณดํ†ต Eureka Server์— ๋“ฑ๋ก๋œ ์ธ์Šคํ„ด์Šค๋Š” 30์ดˆ๋งˆ๋‹ค ์ž์‹ ์˜ ์ƒํƒœ๋ฅผ Eureka Server์— ์•Œ๋ฆฌ๋ฉฐ, ์ด ์ฃผ๊ธฐ๋Š” ๋ณ„๋„๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

Ribbon as Client Side Load Balancing

์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์€ ์ด์œ 

Eureka Server๋งŒ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, ๋ชจ๋“  API ์š”์ฒญ์€ Eureka Server๋ฅผ ํ†ตํ•˜๊ฒŒ ๋˜์–ด Eureka Server๊ฐ€ ์ƒˆ๋กœ์šด ๋ณ‘๋ชฉ ์ง€์ ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ ์˜์กด์„ฑ์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ํด๋ผ์ด์–ธํŠธ ๋ถ€ํ•˜ ๋ถ„์‚ฐ์ด๋ผ๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์—ฌ๊ธฐ์— Ribbon์ด ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.

ํด๋ผ์ด์–ธํŠธ ๋ถ€ํ•˜ ๋ถ„์‚ฐ์˜ ์—ญํ• 

  • ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ์˜ ๊ฐ€์šฉ ์ธ์Šคํ„ด์Šค ๋ชฉ๋ก์„ ์บ์‹ฑํ•˜์—ฌ ๊ฐ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค ๋กœ์ปฌ์— ์ €์žฅํ•œ๋‹ค
  • ์–ด๋–ค ์ธ์Šคํ„ด์Šค๊ฐ€ API๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋จผ์ € ๋กœ์ปฌ์— ์บ์‹ฑ๋œ ์ธ์Šคํ„ด์Šค ๋ชฉ๋ก์„ ์ฐธ์กฐํ•œ๋‹ค
  • ์ผ๋ฐ˜์ ์œผ๋กœ ํด๋ผ์ด์–ธํŠธ ์ธก ์บ์‹ฑ์€ RR(Round-Robbin) ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•ด API ํ˜ธ์ถœ์„ ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค๋กœ ๋ถ„์‚ฐํ•œ๋‹ค
  • ์ฃผ๊ธฐ์ ์œผ๋กœ ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ์— ์ ‘์†ํ•ด ๋กœ์ปฌ ์บ์‹œ๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค

ํด๋ผ์ด์–ธํŠธ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋ฅผ ์ถ”๊ฐ€ํ•œ ๊ทธ๋ฆผ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

RestTemplate์„ ์‚ฌ์šฉํ•œ Ribbon ๊ตฌ์„ฑ

Spring Cloud ์ดˆ๊ธฐ์—๋Š” Ribbon์ด RestTemplate ํด๋ž˜์Šค๋ฅผ ์ง€์›ํ–ˆ์ง€๋งŒ, ํ˜„์žฌ๋Š” ๋”์ด์ƒ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ RestTemplate์—์„œ Ribbon์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด '@LoadBalanced' ์–ด๋…ธํ…Œ์ด์…˜์„ ์ง์ ‘ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋Œ€์•ˆ์€ Netflix Feign ํด๋ผ์ด์–ธํŠธ๋กœ ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ธ๋ฐ, ํ›„์— ์ถ”๊ฐ€ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

  1. ์˜์กด์„ฑ ์ถ”๊ฐ€ (build.gradle ํŒŒ์ผ)

    dependencies {
        implementation 'org.springframework.cloud:spring-cloud-starter-netflix-ribbon'
        ...
    }
    
  2. restTemplate ์ •์˜

    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
  3. ํด๋ผ์ด์–ธํŠธ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€

    @SpringBootApplication
    @EnableDiscoveryClient
    @RibbonClient(name = "apicallee", configuration = RibbonConfiguration.class)
    public class ApiCallerApplication {
        ...
        @Autowired
        RestTemplate restTemplate;
    
        ...
        @RequestMapping("/call/api")
        public String blah() {
                ...
                // ์œ„ RibbonClient ์–ด๋…ธํ…Œ์ด์…˜์—์„œ ๋ช…์‹œํ•œ ๋ชฉ์ ์ง€๋ฅผ ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ์—์„œ ๊ฒ€์ƒ‰
                this.restTemplate.getForObject("http://apicallee/answer", String.class);
        }
    }
    

์œ„์˜ ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ์—์„œ ApiCaller ์ธ์Šคํ„ด์Šค๋กœ "/call/api" ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ApiCallee ์ธ์Šคํ„ด์Šค์˜ "/answer"๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

'@RibbonClient' ์–ด๋…ธํ…Œ์ด์…˜์—์„œ ๋ช…์‹œํ•œ name์€ application.properties ํŒŒ์ผ์˜ spring.application.name๊ณผ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.

Zuul as API Gateway

API ๊ฒŒ์ดํŠธ์›จ์ด์˜ ์—ญํ• 

API ํ˜ธ์ถœ์— ๋Œ€ํ•œ ๋ณด์•ˆ๊ณผ ๋กœ๊น… ๋“ฑ์„ ํŠธ๋ž˜ํ‚นํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ ์š”์ฒญ์— ๋Œ€ํ•œ ํ•„ํ„ฐ์™€ ๋ผ์šฐํ„ฐ ์—ญํ• ์„ ํ•˜๋Š” API ๊ฒŒ์ดํŠธ์›จ์ด๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ API ๊ฒŒ์ดํŠธ์›จ์ด๊ฐ€ ์—†์ด ๋ณด์•ˆ, ๋กœ๊น…, ์‚ฌ์šฉ์ž ์ถ”์  ๋“ฑ์„ ๊ฐ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค์—์„œ ์ œ๊ฐ๊ฐ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ์ผ๊ด€์„ฑ์ด ๋–จ์–ด์ง€๊ณ  ์ธ์Šคํ„ด์Šค ๊ฐ„์˜ ์˜์กด์„ฑ์ด ๋” ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์•„ํ‚คํ…์ฒ˜์—์„œ API ๊ฒŒ์ดํŠธ์›จ์ด๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  • ํ•˜๋‚˜์˜ entry point URL ๋’ค์— ๋ชจ๋“  ์„œ๋น„์Šค๋ฅผ ๋ฐฐ์น˜ํ•˜๊ณ , ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ๋ฅผ ์ด์šฉํ•ด ๋ชจ๋“  ํ˜ธ์ถœ์„ ์‹ค์ œ ์ธ์Šคํ„ด์Šค๋กœ ๋งคํ•‘ํ•œ๋‹ค
  • API ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ๊ฒฝ์œ ํ•˜๋Š” ๋ชจ๋“  ํ˜ธ์ถœ์— TID (๊ณ ์œ ํ•œ ID)๋ฅผ ์‚ฝ์ž…ํ•œ๋‹ค
  • ํ˜ธ์ถœ ์‹œ ์ƒ์„ฑ๋œ TID๋ฅผ ์‘๋‹ต์— ์‚ฝ์ž…ํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ(API Caller)์— ํšŒ์‹ ํ•œ๋‹ค
  • ์ •์ /๋™์  ๋ผ์šฐํŒ…์„ ์ง€์›ํ•œ๋‹ค
  • ํ˜ธ์ถœ์— ๋Œ€ํ•œ ์ธ์ฆ(authentication)๊ณผ ์ธ๊ฐ€(authorization)์„ ์ฒ˜๋ฆฌํ•œ๋‹ค
  • ํ˜ธ์ถœ ๋กœ๊ทธ์™€ ์ด์— ๋”ฐ๋ฅธ ์ง€ํ‘œ๋ฅผ ์ˆ˜์ง‘ํ•œ๋‹ค

์•ž์„œ ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ ๋ถ€๋ถ„์—์„œ ์–ธ๊ธ‰ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ, API ๊ฒŒ์ดํŠธ์›จ์ด๋„ SPOF๊ฐ€ ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•œ API ๊ฒŒ์ดํŠธ์›จ์ด ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ณ  statelessํ•˜๊ฒŒ ์œ ์ง€ํ•˜์—ฌ ์‰ฝ๊ฒŒ ๋‹ค์ค‘ํ™”ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ผ์šฐํ„ฐ ๊ตฌ์„ฑ

  1. ์˜์กด์„ฑ ์ถ”๊ฐ€ (build.gradle ํŒŒ์ผ)

    dependencies {
        implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul'
        ...
    }
    
  2. API ๊ฒŒ์ดํŠธ์›จ์ด ์–ด๋…ธํ…Œ์ด์…˜ ์ถ”๊ฐ€

    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableZuulProxy       // ์ด ๋ถ€๋ถ„
    public class ApiCallerApplication {
        ...
    }
    
  3. ๊ตฌ์„ฑ ์„ค์ • (application.properties ํŒŒ์ผ)

    server.port=8762
    spring.application.name=zuul
    eureka.client.registerWithEureka=true
    eureka.client.fetchRegistry=false
    eureka.client.serviceUrl.defaultZone=http://${registry.host:localhost}:${server.port}/eureka/
    

์ด์ œ Eureka Server, API Caller ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜, API Callee ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜, ๊ทธ๋ฆฌ๊ณ  Zuul ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•œ ํ›„ localhost:8762/actuator/routes ๋กœ ์ ‘์†ํ•ด๋ณด๋ฉด ์„œ๋น„์Šค ๋””์Šค์ปค๋ฒ„๋ฆฌ์— ์˜ํ•ด Zuul์ด ๋ผ์šฐํŒ…ํ•  ์ˆ˜ ์žˆ๋Š” API ๋ชฉ๋ก์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์Šต

์ŠคํŽ™

  • Java 11
  • Spring Boot 2.2.6
  • Eureka
  • Ribbon
  • Swagger 2
  • Sleuth
  • Zipkin

๊ตฌ์„ฑ๋„

์†Œ์Šค์ฝ”๋“œ

https://github.com/llb1026/spring_cloud_netflix_practice ๊ฐœ์ธ repo์— ์šฐ์„  ์ปค๋ฐ‹

๋กœ์ปฌ์—์„œ ์‹คํ–‰

  1. Build projects using gradle

    $ cd PROJECT_DIRECTORY
    $ gradle bootjar
    
  2. Run jar file with specific port

    // =========== Port Info ===========
    // eureka-server: 8761
    // zuul-application: 8762
    // zipkin-server: 9411
    // ribbon-client: 8888
    // ribbon-server: 9090, 9091, 9092 ...
    // =================================
    $ java -jar -Dserver.port=XXXX build/libs/YYYY.jar
    
  3. Open the browser and go to http://localhost:8761 to check the Eureka server dashboard

  4. Open the browser and go to http://localhost:8762/actuator/routes to check all the routes that Zuul can use according to the Eureka

  5. Open the browser and go to http://localhost:8888/frontend to check how Ribbon works

  6. Open the browser and go to http://localhost:8762/ribbonclient/frontend to check how Zuul routes request

  7. Open the browser and go to http://localhost:8888/swagger-ui.html to check the swagger dashboard for ribbon-client

  8. Open the browser and go to http://localhost:9411/zipkin to check the Zipkin server dashboard