1. ํ์ (PROBLEM)
GATEWAY → EUREKA → SERVICE ๊ตฌ์กฐ ๊ตฌ์ถ ํ, ์ ๋ ์นด ๋์๋ณด๋๋ฅผ ํตํด ๋ชจ๋ ์ธ์คํด์ค๊ฐ ์ ์์ ์ผ๋ก ๋ฑ๋ก๋ ๊ฒ์ ํ์ธํ์ต๋๋ค.

ํ์ง๋ง ํฌ์คํธ๋งจ์ผ๋ก ๊ฒ์ดํธ์จ์ด(8080)๋ฅผ ํตํด ํ์๊ฐ์ API๋ฅผ ํธ์ถํ์ ๋, ๊ธฐ๋์ ๋ฌ๋ฆฌ 500 INTERNAL_ERROR๊ฐ ๋ฐ์ํ์ต๋๋ค.
์๋น์ค๊ฐ ์ด์์๋๋ฐ 500์ด ๋จ๋ ์ด์ ๋ฅผ ํ์ธํ๊ธฐ ์ํด ๋ก๊ทธ๋ฅผ ์ถ์ ํ ๊ฒฐ๊ณผ ๋ค์ ๋ฉ์์ง๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค.
java.lang.IllegalStateException: No servers available for service: remitro-member
(์๋ ๋ก๋๋ฐธ๋ฐ์ฑ ์คํจ ์ 503์ด ๋ ์ผ ํ์ง๋ง, ๋ด๋ถ ํํฐ ์์ธ ์ฒ๋ฆฌ ๊ณผ์ ์์ 500์ผ๋ก ๋ํ ๋์ด ์ถ๋ ฅ๋์์ต๋๋ค.)
2. ์์ธ ๋ถ์ (ANALYSIS)
์์คํ ์ด ๋ก๋๋ฐธ๋ฐ์ฑ ์คํจ(503)๊ฐ ์๋ 500 ์๋ฌ๋ฅผ ๋ฐํํ ์ด์ ๋ ํธ๋ค๋ฌ์ ์์ธ ์ฒ๋ฆฌ ๋ก์ง์ ์์์ต๋๋ค.
private GatewayErrorCode resolveErrorCode(Throwable throwable) {
// ...
// 1. ํน์ HTTP ์ํ ์ฝ๋ ์์ธ ์ฒ๋ฆฌ (404, 401 ๋ฑ)
// 2. DownStream ํต์ ์ค๋ฅ ์ฒดํฌ (WebClientException ๋ฑ)
if (isDownstreamException(throwable)) {
return GatewayErrorCode.DOWNSTREAM_UNAVAILABLE;
}
// 3. ๊ทธ ์ธ์๋ ๋ชจ๋ 500์ผ๋ก ๊ท๊ฒฐ
return GatewayErrorCode.INTERNAL_ERROR;
}
์ ํธ๋ค๋ฌ๋ NOT_FOUND, UNAUTHORIZED, ๊ทธ๋ฆฌ๊ณ ์ค์ ํต์ ๋จ๊ณ์ธ DownStream ์์ธ๋ฅผ ์ ์ธํ๋ฉด ๋ชจ๋ 500 ์๋ฌ๋ฅผ ๋ฑ๋๋ก ์ค๊ณํ์ต๋๋ค. ๋ก๋๋ฐธ๋ฐ์ ๋ถ์ฌ๋ก ๋ฐ์ํ IllegalStateException์ ํต์ ์๋์กฐ์ฐจ ๋ชป ํ ๋จ๊ณ์ ์๋ฌ์๊ธฐ์ 500์ผ๋ก ๋ํ ๋ ๊ฒ์ด์์ต๋๋ค.
๋ก๊ทธ์ ์ฐํ "No servers available"์ด๋ผ๋ ๋ฌธ๊ตฌ๋ ๊ฒ์ดํธ์จ์ด๊ฐ ์์ฒญ์ ๋ณด๋ผ ๋์์ ์ฐพ์ง ๋ชปํ ๋ ๋ฐ์ํฉ๋๋ค.
์ ๋ ์ฌ๊ธฐ์ ๋ ๊ฐ์ง ๊ฐ๋ฅ์ฑ์ ๊ฒํ ํ์ต๋๋ค.
- ์ค์ ์๋น์ค๊ฐ ์ฃฝ์๋๊ฐ? ์ ๋ ์นด ๋์๋ณด๋์์ ์๋น์ค๊ฐ UP ์ํ์ธ ๊ฒ์ ์ด๋ฏธ ํ์ธํ์ผ๋ฏ๋ก ํต๊ณผํ์ต๋๋ค.
- ์๋น์ค๋ ์ด์์๋๋ฐ ์ ๋ชป ์ฐพ๋๊ฐ? ์๋น์ค ๋ชฉ๋ก์ ์ ๋ ์นด๋ก๋ถํฐ ๊ฐ์ ธ์์ผ๋, ๊ทธ ๋ชฉ๋ก ์ค ํ๋๋ฅผ ์ ํํ์ฌ ์ค์ ์์ฒญ์ ๋ณด๋ผ ์ ํ์(LoadBalancer)๊ฐ ์๋ํ์ง ์๊ณ ์๋ค๋ ๊ฒฐ๋ก ์ ๋๋ฌํ์ต๋๋ค.
๋จ์ํ ํต์ ์๋ฌ(ConnectException)๊ฐ ์๋๋ผ '์ฌ์ฉ ๊ฐ๋ฅํ ์๋ฒ๊ฐ ์๋ค'๋ ๋ฉ์์ง๋, ๊ฒ์ดํธ์จ์ด๊ฐ ๋ด๋ถ์ ์ผ๋ก ์ธ์คํด์ค ๋ฆฌ์คํธ๋ฅผ ๋ค์ก์ง๋ง ์ ํจํ ํ๋๋ฅผ ๊ณ ๋ฅด๋ ๋ก์ง ๋จ๊ณ์์ ์คํจํ์์ ์๋ฏธํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
โ Spring Cloud LoadBalancer ์์กด์ฑ ๋ถ์ฌ:
Spring Cloud 2020 ๋ฒ์ ์ดํ๋ก ๊ธฐ์กด์ ์ฌ์ฉ๋๋ Netflix-Ribbon์ด ์ญ์ ๋์์ต๋๋ค.
๊ฒ์ดํธ์จ์ด๊ฐ ์ ๋ ์นด์์ ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๋๋ผ๋, ์ค์ ๋ก ์์ฒญ์ ๋ถ์ฐ ์ฒ๋ฆฌํ ๋ก๋๋ฐธ๋ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ผ๋ฉด ์์ฒญ์ ์ ๋ฌํ ์ ์์ต๋๋ค.
โก ์๋น์ค ID ๋์๋ฌธ์ ๋งค์นญ ์คํจ:
์ ๋์๋ณด๋ ์ด๋ฏธ์ง์์ ๋ณด์ด๋ฏ ์ ๋ ์นด๋ ๋ฑ๋ก ์ ๊ธฐ๋ณธ์ ์ผ๋ก hostname๊ณผ appName์ ๋๋ฌธ์๋ก ์ ์ฅํ๋ ๊ท์ฝ์ ๊ฐ์ง๋๋ค. ๋ฐ๋ฉด Gateway yml ์ค์ ์ ์๋ฌธ์๋ฅผ ์ฌ์ฉํ๋ค ๋ณด๋ ์ด ์ฌ์ด์์ ๋งค์นญ ๋ถ์ผ์น๊ฐ ๋ฐ์ํ ๊ฒ์ด์์ต๋๋ค.
3. ํด๊ฒฐ (RESOLUTION)
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ ๊ฐ์ง ์์ ์ ์ํํ์ต๋๋ค.
Step 1. LoadBalancer ์์กด์ฑ ์ถ๊ฐ: build.gradle์ ๋ก๋๋ฐธ๋ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ์ต๋๋ค.
implementation 'org.springframework.cloud:spring-cloud-starter-loadbalancer'
Step 2. ๋์๋ฌธ์ ๋งคํ ํ์ฉ: ๊ฒ์ดํธ์จ์ด๊ฐ ์๋น์ค ID๋ฅผ ์ฐพ์ ๋ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์๋๋ก ์ค์ ํ์ต๋๋ค.
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true # ์๋ฌธ์ ๋งคํ ํ์ฉ
๋๋ถ์ด, ํฅํ ๋์ผํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ ๋ ๋ก๊ทธ๋ฅผ ๋ค์ง์ง ์๊ณ ์๋ต ์ฝ๋๋ง์ผ๋ก๋ ์ํฉ์ ์๋ณํ ์ ์๋๋ก, ํธ๋ค๋ฌ ๋ก์ง์ ํด๋น ์์ธ ๋ฉ์์ง๋ฅผ ํค์๋๋ก ์ฒดํฌํ์ฌ 503(DOWNSTREAM_UNAVAILABLE)์ ๋ฐํํ๋ ์ฝ๋๋ฅผ ๋ณด๊ฐํ์ต๋๋ค.
์ ์ญ ์์ธ ํธ๋ค๋ฌ๋ ํด๋ผ์ด์ธํธ์๊ฒ ๊น๋ํ ์๋ต์ ์ฃผ๋ ๊ธฐ๋ฅ๋ ์์ง๋ง, ๊ฐ๋ฐ์๊ฐ ์ค๋ฅ๋ฅผ ์ ์ํ๊ฒ ์ถ์ ํ๊ณ ๋ชจ๋ํฐ๋งํ ์ ์๋๋ก ๋ถ๋ฅํ ์ ์์ด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๋ํ, ๊ฒ์ดํธ์จ์ด๊ฐ ์ ๋ ์นด๋ก๋ถํฐ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ ๋ก๋๋ฐธ๋ฐ์๋ฅผ ๊ฑฐ์ณ ๋ด๋ถ ์๋น์ค๋ก ์ ๋ฌํ๋ ์ ์ฒด์ ์ธ ๋์ ํ๋ฆ์ ์ง์ ์ค์ ํ๋ฉฐ, MSA ํ๊ฒฝ์์ ๊ฐ ์ปดํฌ๋ํธ ๊ฐ์ ํต์ ๊ท์ฝ๊ณผ ๊ธฐ๋ณธ์ ์ธ ์ธํ๋ผ ์ค์ ์ด ์ ์ฒด ์์คํ ์ ๊ฐ์ฉ์ฑ์ ์ผ๋ง๋ ์ํฅ์ ๋ฏธ์น๋์ง ๋ฐฐ์ฐ๋ ๊ณ๊ธฐ๊ฐ ๋์์ต๋๋ค.