chap 13 - JAVA-JIKIMI/SPRING-IN-ACTION-5 GitHub Wiki
์คํ๋ง ํด๋ผ์ฐ๋, ๋ง์ดํฌ๋ก์๋น์ค ๊ฐ๋ฐ์ ์๊ฐํ๋ฉด์ ๋จ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์ MSA๋ก ๋ถ๋ฆฌํ์ฌ ๊ฐ๋ฐํ๋ ๋ฐฉ๋ฒ์ ์์๋ณธ๋ค. 13์ฅ์์๋ MSA์ ๊ฐ์๋ฅผ ์์๋ณด๊ณ ์๋น์ค ๋ฐ๊ฒฌ(discovery)๋ฅผ ์์๋ณผ ๊ฒ์ด๋ค. ์ด ๋, ์คํ๋ง ๊ธฐ๋ฐ์ MSA๋ฅผ ๋ฑ๋กํ๊ณ ๋ฐ๊ฒฌํ๊ธฐ ์ํด ๋ทํ๋ฆญ์ค์ ์ ๋ ์นด ์๋น์ค ๋ ์ง์คํธ๋ฆฌ ๋ฅผ ์ฌ์ฉํ๋ค.
MSA ์์๋ณด๊ธฐ
์๋น์ค ๋ ์ง์คํธ๋ฆฌ ์์ฑํ๊ธฐ
์๋น์ค ๋ฑ๋ก ๋ฐ ๋ฐ๊ฒฌํ๊ธฐ
- ๋๋ชจ์ ๋ฌธํผ์ฌ์ ๊ฐ๋ค.
- ๊ฐ๊ฐ์ ๋ฌธํผ์ฌ๋ค์ด ๋ชจ์ฌ ํฉ์์น, ๋ฌธ์, ๋ฐฐ ๋ฑ ์ฌ๋ฏธ์๋ ๋ชจ์์ ๋ง๋ค์ด๋ธ๋ค.
- ๊ทธ๋ฌ๋ ๊ฐ๊ฐ์ ๋ฌธํผ์ฌ๋ ์์ ๋ง์ ๋น๋, ์ง๋๋ฌ๋ฏธ, ์๊ฐ๋ฏธ, ๋, ์ฅ๊ธฐ, ๊ทธ๋ฆฌ๊ณ ํฌ๋ง๊ณผ ๊ฟ์ ๊ฐ์ง๋ค.
์๋น์ค์ ์ํคํ ์ฒ๋ฅผ ๊ตฌ์ฑํ ๋ ๋ชจ๋ ์๋น์ค๋ฅผ ํ๋์ ํจํค์ง์ ๋ด์ ๋น๋ํ๊ณ ๋ฐฐํฌํ๋ ๋ฐฉ๋ฒ
- ์ ์ฒด๋ฅผ ํ์ ํ๊ธฐ ์ด๋ ต๋ค
- ํ ์คํธ๊ฐ ๋ ์ด๋ ต๋ค
- ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ์ ์ถฉ๋์ด ์๊ธฐ๊ธฐ ์ฝ๋ค
- ํ์ฅ ์์ ๋นํจ์จ์ ์ด๋ค
- ์ ์ฉํ ํ ํฌ๋๋ฌ์ง๋ฅผ ๊ฒฐ์ ํ ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด๋ฅผ ๊ณ ๋ คํด์ผ ํ๋ค(์ธ์ด, ํ๋ ์์ํฌ..)
- ํ๋ก๋์ ์ผ๋ก ์ด์ํ๊ธฐ ์ํด ๋ง์ ๋ ธ๋ ฅ์ด ํ์ํ๋ค
- ๋ถ๋ถ์ ์ฅ์ ๊ฐ ์ ์ฒด ์๋น์ค์ ์ฅ์ ๋ก ์ด์ด์ง๋ค.(out of memory...)
๋จ์ผ ์ดํ๋ฆฌ์ผ์ด์ ๊ณผ ๋์กฐ๋๋ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ณ์ ์ฅ์ - ๋ณต์ก์ฑ ํด๊ฒฐ
- ์ฝ๊ฒ ์ดํดํ ์ ์๋ค.
- ํ ์คํธ๊ฐ ์ฝ๋ค
- ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋นํธํ์ฑ ๋ฌธ์ ๊ฐ ์๊ธฐ์ง ์๋๋ค(๋ํ๋ค)
- ๋ ์์ ์ผ๋ก ๊ท๋ชจ๋ฅผ ์กฐ์ ํ ์ ์๋ค
- ๊ฐ ๋ง์ดํฌ๋ก์๋น์ค์ ์ ์ฉํ ํ ํฌ๋๋ฌ์ง๋ฅผ ๋ค๋ฅด๊ฒ ์ ํํ ์ ์๋ค
- ์ธ์ ๋ ํ๋ก๋์ ์ผ๋ก ์ด์ํ ์ ์๋ค
์ฐธ๊ณ
Nginx ๊ณต์๋ฌธ์ (https://www.nginx.com/blog/introduction-to-microservices/)
๋ญ๊ฐ๋ฅผ ๋ฐ๊ฒฌํ๊ฑฐ๋ ์์๋์ ๋ ์ง๋ฅด๋ ๊ธฐ์จ์ ํ์ฑ.
๋ง์ดํฌ๋ก์๋น์ค์์ ์๋ก๋ฅผ ์ฐพ์ ๋ ์ฌ์ฉ๋๋ ์๋น์ค ๋ ์ง์คํธ๋ฆฌ์ ์ด๋ฆ
์ ๋ ์นด๋ ๋ชจ๋ ์๋น์ค์ ์ค์ ์ง์ค ๋ ์ง์คํธ๋ฆฌ๋ก ์๋ํ๋ค.
์ ๋ ์นด ์์ฒด๋ ๋ง์ดํฌ๋ก์๋น์ค๋ผ๊ณ ์๊ฐํ ์ ์๋ค.
๋ชจ๋ ๋ง์ดํฌ๋ก์๋น์ค๋ ์ ๋ ์นด์ ์์ ์ 30์ด์ ํ ๋ฒ์ฉ ๋ฑ๋กํ๋ค
other service๊ฐ ์ ๋ ์นด์์ 'some service'๋ ์ด๋ฆ์ผ๋ก ์ฐพ๋๋ค.
some service๊ฐ ์ฌ๋ฌ ๊ฐ๋ผ๋ฉด ๋ฆฌ๋ณธ์ด ์ธ์คํด์ค๋ฅผ ์ ํํ๊ณ ํด๋น ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ๋ค.
ํด๋ผ์ด์ธํธ ์ธก์ ๋ก๋๋ฐธ๋ฐ์
์ค์์ง์คํ๋ ๋ก๋๋ฐธ๋ฐ์์ ๋น๊ตํด ์ฅ์ ์ ๊ฐ์ง๋ค.
- ํด๋ผ์ด์ธํธ ์์ ๋น๋กํด ๋ก๋๋ฐธ๋ฐ์ ํฌ๊ธฐ๊ฐ ์กฐ์ ๋๋ค.
- ๊ฐ ํด๋ผ์ด์ธํธ์ ๊ฐ์ฅ ์ ํฉํ ๋ก๋ ๋ฐธ๋ฐ์ฑ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ๋๋ก ์์ ํ ์ ์๋ค.
ํ๋ก์ ํธ๋ช service-registry
ํจํค์ง ์ด๋ฆ taco
์คํ๋ง ์คํํฐ ํ๋ก์ ํธ ์์กด์ฑ ๋ํ์์์ Spring Cloud Discovery๋ฅผ ํ์ฅ, Eureka Server๋ฅผ ์ ํํ๊ณ Finish๋ฅผ ํด๋ฆญ.
<properties>
...
<!-- ๋ค๋ฅธ ๋ฒ์ ์ ์คํ๋ง ํด๋ผ์ฐ๋๋ฅผ ์ฌ์ฉํ๊ณ ์ถ์ ๊ฒฝ์ฐ ๋ณ๊ฒฝ -->
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
</properties>
...
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
...
</dependencies>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
...
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootAppliation
@EnableEurekaServer // ์ถ๊ฐ!
public class ServiceRegistryApplication {
@Test
public void contextLoads() {
}
}
์ ํ๋ฆฌ์ผ์ด์ ์ด ์์๋๋ ๋ถํธ์คํธ๋ฉ ํด๋์ค์ธ ServiceRegistryApplication์ ์์ ํ๋ค.
์ ๋ ์นด ์๋ฒ๊ฐ ํ์ฑํ๋๋ค.
์ฌ๊ธฐ๊น์ง ์์ ํ๊ณ ์ฑ์ ์์ํ๋ฉด ์น๋ธ๋ผ์ฐ์ ์์ ์ ๋ ์นด ๋์๋ณด๋๊ฐ ๋ํ๋๋ค.
๋์๋ณด๋์์ ๊ทธ ์ค ์ด๋ค ์๋น์ค ์ธ์คํด์ค๊ฐ ์ ๋ ์นด์ ๋ฑ๋ก๋์๋์ง๋ฅผ ๋ณผ ์ ์๋ค. ์๋น์ค๋ฅผ ๋ฑ๋กํ ๋ ์ ๋ ์นด ๋์๋ณด๋๋ฅผ ์์ฃผ ๋ณด๊ฒ ๋ ๊ฒ์ด๋ค.
์ ๋ ์นด๊ฐ 30์ด์ ํ๋ฒ์ฉ ์์ธ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ ์ค์ธ๋ฐ, ์์ง ๋ ์ง์คํธ๋ฆฌ๋ฅผ ์์ ํ๊ฒ ๊ตฌ์ฑํ์ง ์์๊ธฐ ๋๋ฌธ์ด๋ฉฐ, ์ ๋ ์นด๊ฐ ์ ์ ๋์ํ๋ ๊ฒ์ ๋ง๋ค.
์ฅ์ ์ ๋๋นํ๊ธฐ ์ํด ์ฌ๋ฌ ๊ฐ์ ์ ๋ ์นด ์๋ฒ๊ฐ ํจ๊ป ๋์ํ๋๋ก ํ๋ ๊ฒ์ด ์์ ํ๋ค.
๋ฐ๋ผ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๋ ์นด๋ ๋ค๋ฅธ ์ ๋ ์นด ์๋ฒ๋ก๋ถํฐ ์๋น์ค ๋ ์ง์คํธ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ๋ค๋ฅธ ์ ๋ ์นด ์๋ฒ์ ์๋น์ค๋ก ์์ ์ ๋ฑ๋กํ๊ธฐ๋ ํ๋ค.
๋๋ฌธ์ ํ๋ก๋์ ์ค์ ์์๋ ์ ๋ ์นด๋ฅผ ๋ ๋ ์ด์ ๋์ํ๊ฒ ๋๋ค. ๊ทธ๋ฌ๋ ๊ฐ๋ฐ ๋ชฉ์ ์ผ๋ก๋ ํ๋์ ์ ๋ ์นด ์๋ฒ๋ฉด ์ถฉ๋ถํ๋ค.
์ด ๋ ์ ๋ ์นด ์๋ฒ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌ์ฑํ์ง ์์ผ๋ฉด 30์ด๋ง๋ค ์์ธ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ค. ์ ๋ ์นด๋ 30์ด๋ง๋ค ๋ค๋ฅธ ์ ๋ ์นด ์๋ฒ์ ํต์ ํ๋ฉด์ ์์ ์ด ์๋ ์ค์์ ์๋ฆฌ๊ณ ๋ ์ง์คํธ๋ฆฌ ์ ๋ณด๋ฅผ ๊ณต์ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ ๋ ์นด ์๋ฒ๊ฐ ํผ์์์ ์๋๋ก ๊ตฌ์ฑํ ํ์๊ฐ ์๋ค.
๋จ์ผ ์ ๋ ์นด ์๋ฒ ๊ตฌ์ฑ
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
eureka.client.registerWithEureka
, eureka.client.fetchRegistry
๋ ์ ๋ ์นด๊ฐ ๋ค๋ฅธ ์ ๋ ์นด์ ์ํธ ์์ฉํ๋ ๋ฐฉ๋ฒ์ ์๋ ค์ฃผ๋ ์์ฑ์ด๋ค. ๋ ์์ฑ์ ๊ธฐ๋ณธ๊ฐ์ true๋ก, ํด๋น ์ ๋ ์นด ์๋ฒ๊ฐ ๋ค๋ฅธ ์ ๋ ์นด ์๋ฒ๋ก๋ถํฐ ๋ ์ง์คํธ๋ฆฌ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๊ฒ ํ ๊ฒ์ธ์ง(fetchRegistry), ๋ค๋ฅธ ์ ๋ ์นด ์๋ฒ์ ์๋น์ค๋ก ์์ ์ ๋ฑ๋กํ ๊ฒ์ธ์ง(registerWithEureka)๋ฅผ ์ค์ ํ ์ ์๋ค.
serviceUrl.defaultZone์ ์ ๋ ์นด url์ ๋งํ๋ค. ์ค๊ดํธ ์์ ์ง์ ๋ ๋ค๋ฅธ ์์ฑ๊ฐ์ผ๋ก ๋์ฒด๋์ด ์ค ์ฃผ์๋ http://localhost:8761/eureka ๊ฐ ๋๋ค.
์์ฒด ๋ณด์กด ๋ชจ๋ ๋นํ์ฑํ
server:
enable-self-preservation: false
์ ๋ ์นด ์๋ฒ๋์ธ์คํด์ค๊ฐ ์์ ์ ๋ฑ๋กํ๊ณ ๋ฑ๋ก ๊ฐฑ์ ์์ฒญ์ 30์ด๋ง๋ค ์ ์กํ๊ธฐ๋ฅผ ๊ธฐ๋ํ๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ธ ๋ฒ์ ๊ฐฑ์ ๊ธฐ๊ฐ(90์ด) ๋์ ์๋น์ค ์ธ์คํด์ค๋ก๋ถํฐ ๋ฑ๋ก ๊ฐฑ์ ์์ฒญ์ ๋ฐ์ง ๋ชปํ๋ฉด ํด๋น ์๋น์ค ์ธ์คํด์ค์ ๋ฑ๋ก์ ์ทจ์ํ๊ฒ ๋๋ค. ๋ง์ผ ์ด๋ ๊ฒ ์ค๋จ๋๋ ์๋น์ค๊ฐ ์๊ณ๊ฐ(threshold)์ ์ด๊ณผํ๋ฉด ์ ๋ ์นด ์๋ฒ๋ ๋คํธ์ํฌ ๋ฌธ์ ๊ฐ ์๊ธด ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๊ณ ๋ ์ง์คํธ๋ฆฌ์ ๋ฑ๋ก๋ ๋๋จธ์ง ์๋น์ค ๋ฐ์ดํฐ๋ฅผ ๋ณด์กดํ๊ธฐ ์ํด ์์ฒด ๋ณด์กด(self-preservation) ๋ชจ๋๊ฐ ๋๋ค. ๋ฐ๋ผ์ ์ถ๊ฐ์ ์ธ ์๋น์ค ์ธ์คํด์ค์ ๋ฑ๋ก ์ทจ์๊ฐ ๋ฐฉ์ง๋๋ค.
ํ๋ก๋์ ์ค์ ์์๋ ์ด ๊ฐ์ true๋ก ์ค์ ํด ์ค์ ๋คํธ์ํฌ ๋ฌธ์ ๊ฐ ์๊ฒผ์ ๋ ๋๋จธ์ง ํ์ฑํ๋ ์๋น์ค๋ค์ ๋ฑ๋ก ์ทจ์๋ฅผ ๋ฐฉ์งํ ์ ์๋ค.
๊ฐ๋ฐ ์๋ฒ์์๋ ๋ฌธ์ ๊ฐ ๋ ์ ์์ผ๋ฏ๋ก ์ด ๊ฐ์ false๋ก ํ๋ค. ์ธ์คํด์ค ์ํ๊ฐ ์์ฃผ ๋ณ๊ฒฝ๋ ์ ์๋ ๊ฐ๋ฐํ๊ฒฝ์์ ์ด ๋ชจ๋๊ฐ ํ์ฑํ๋๋ค๋ฉด ์ค๋จ๋ ์๋น์ค์ ๋ฑ๋ก์ด ๊ณ์ ์ ์ง๋์ด ๋ค๋ฅธ ์๋น์ค๊ฐ ํด๋น ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ ๋ ๋ฌธ์ ๋ฅผ ๋ฐ์์ํฌ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๋์ ์ด ๊ฐ์ false๋ก ํ๋ฉด ์๋์ ๊ฐ์ ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋ ๊ฒ์ด๋ค.
THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTEXT INSTANCE EXPIRY
IN CASE OF NETWORK/OTHER PROBLEMS.
๋ ๊ฐ ์ด์์ ์ ๋ ์นด ์ธ์คํด์ค๋ฅผ ๊ตฌ์ฑํ๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ application.yml ํ์ผ์ ์คํ๋ง ํ๋กํ์ผ์ ์ง์ ํ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ํ ๋ฒ์ ํ๋์ฉ ํ๋กํ์ผ์ ์ฌ์ฉํด์ ์ ๋ ์นด๋ฅผ ๋๋ฒ ์์์ํจ๋ค.
# ๊ธฐ๋ณธ ํ๋กํ์ผ(๊ณตํต)
eureka:
client:
# registerWithEureka: false ์ญ์
# fetchRegistry: false ์ญ์
serviceUrl:
defaultZone: http://${other.eureka.host}:${other.eureka.port}/eureka/
---
# ํ๋กํ์ผ1
spring:
profiles: eureka-1
application:
name: eureka-1
server:
port: 8761
eureka:
instance:
hostname: eureka1.tacocloud.com
# ํ๋ ์์ํฌ์ ์๊ด์์ด ๊ณตํต ํ๋กํ์ผ์ ๋ณ์๊ฐ์ ๋์ฒดํ๊ธฐ ์ํ ์ฉ๋
other:
eureka:
host: eureka2.tacocloud.com
port: 8762
---
# ํ๋กํ์ผ2
spring:
profiles: eureka-2
application:
name: eureka-2
server:
port: 8762
eureka:
instance:
hostname: eureka2.tacocloud.com
# ํ๋ ์์ํฌ์ ์๊ด์์ด ๊ณตํต ํ๋กํ์ผ์ ๋ณ์๊ฐ์ ๋์ฒดํ๊ธฐ ์ํ ์ฉ๋
other:
eureka:
host: eureka1.tacocloud.com
port: 8762
์ง๊ธ๊น์ง ์ ๋ ์นด ์๋ฒ๋ฅผ ์ค์ ํ๊ณ ๋ฑ๋กํ๋ค๋ฉด ์ด์ ์ ๋ ์นด ์๋น์ค ๋ ์ง์คํธ๋ฆฌ์ ๋ฑ๋กํ ๋ง์ดํฌ๋ก์๋น์ค๋ฅผ ๊ตฌ์ฑํด๋ณด๊ฒ ๋ค.
ingredient-service ํ๋ก์ ํธ์ ์์กด์ฑ์ ์ถ๊ฐํด์ค๋ค.
pom.xml ํ์ผ์์ ๋ง์ฐ์ค ์ค๋ฅธ์ชฝ ํด๋ฆญ > Spring - Edit Starters ํด๋ฆญ > Spring Cloud Discovery - Spring Discovery Client ์ ํ ํ OK
// pom.xml
</dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>
spring-cloud-starter-netflix-eureka-client
</artifactId>
</dependency>
</dependencies>
// spring cloud version๋ ์ถ๊ฐ๋จ.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
์ ๋ ์นด ํด๋ผ์ด์ธํธ ์์กด์ฑ์ ์ถ๊ฐํ๋ฉด ์ ๋ ์นด๋ฅผ ์ด์ฉํด ์๋น์ค๋ฅผ ์ฐพ๋๋ฐ ํ์ํ ๋ชจ๋ ๊ฒ์ด ์๋์ผ๋ก ์ถ๊ฐ๋๋ค. (์ ๋ ์นด์ ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ๋ฆฌ๋ณธ ๋ก๋ ๋ฐธ๋ฐ์ ๋ฑ)
์ด๋ ๊ฒ ๋๋ฉด ์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ ์นด ์๋น์ค ๋ ์ง์คํธ๋ฆฌ์ ํด๋ผ์ด์ธํธ๋ก ํ์ฑํ์ํฌ ์ ์๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์๋๋ฉด 8761ํฌํธ๋ก ์๋น์คํ๋ ์ ๋ ์นด ์๋ฒ์ ์ฐ๊ฒฐํ๊ณ , UNKNOWN์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ์ ๋ ์นด ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ ์ ๋ฑ๋กํ๋ค.
์ ๋ ์นด ํด๋ผ์ด์ธํธ ์์ฑ ๊ตฌ์ฑ
์๋น์ค์ ๊ธฐ๋ณธ ์ด๋ฆ์ธ UNKNOWN์ ๊ทธ๋๋ก ๋๋ฉด ์ ๋ ์นด ์๋ฒ์ ๋ฑ๋ก๋๋ ๋ชจ๋ ์๋น์ค ์ด๋ฆ์ด ๊ฐ๊ฒ ๋๋ฏ๋ก ๋ณ๊ฒฝํด๋ณด์.
spring:
application:
name: ingredient-service
์ด์ ์ ๋ ์นด ๋์๋ณด๋์ ingredient-service ๋ผ๋ ์ด๋ฆ์ผ๋ก ์๋น์ค๊ฐ ๋ํ๋๋ค.
server:
port: 0
์๋น์ค ํฌํธ์ ์ถฉ๋์ ๋ง๊ธฐ์ํด ํฌํธ๋ฅผ 0์ผ๋ก ์ค์ ํ๋ค. ์๋ฒ ํฌํธ๋ฅผ 0์ผ๋ก ์ค์ ํ๋ฉด ๊ฐ ์๋น์ค ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์๋ ๋ ํฌํธ ๋ฒํธ๊ฐ ๋ฌด์์๋ก ์ ํ๋๋ค.
eureka:
client:
service-url:
defaultZone: http://eureka1.tacocloud.com:8761/eureka/,
http://eureka2.tacocloud.com:8762/eureka/
์ url์ ํฌํธ๋ก ๋ฆฌ์ค๋ํ๋ ์ ๋ ์นด ์๋ฒ์ ๋ฑ๋ก๋๋๋ก ํด๋ผ์ด์ธํธ๊ฐ ๊ตฌ์ฑ๋๋ค. ์ฅ์ ๋๋น๋ฅผ ์ํด ๋ ๊ฐ์ ์ ๋ ์นด ์๋ฒ๋ฅผ ์ฌ์ฉํ๋๋ก ๊ตฌ์ฑํ๋ค.
์ด๋ ๊ฒ ๋๋ฉด ์ฒซ ๋ฒ์งธ ์ ๋ ์นด ์๋ฒ์ ๋ฑ๋ก์ ์๋ํ๊ณ , ์คํจํ๋ฉด ๋๋ฒ์งธ์ ํผ์ด(peer)๋ก ์ง์ ๋ ์ ๋ ์นด ์๋ฒ์ ๋ ์ง์คํธ๋ฆฌ์ ๋ฑ๋ก์ ์๋ํ๊ฒ ๋๋ค.
์ ๋ ์นด๋ฅผ ์ฌ์ฉํ๋ฉด ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ ์ปจ์๋จธ ์ฝ๋์ URL์ ํ๋์ฝ๋ฉํ๋ ๋์ ์๋น์ค๋ช ์ ์ ์ผ๋ฉด ๋๋ค.
๋๋ถ์ ์ฌ์ฉ๋๋ ์๋น์ค์ ํน์ ์ธ์คํด์ค์ ํด๋น ์ปจ์๋จธ๊ฐ ๋ฐ์ ํ๊ฒ ๊ฒฐํฉ๋๋ ๊ฒ์ ๋ง๊ณ , ์ฌ์ฉ๋๋ ์๋น์ค์ ํฌ์คํธ๋ ํฌํธ๊ฐ ๋ณ๊ฒฝ๋์ด๋ ํด๋น ์ปจ์๋จธ๊ฐ ์ ์ ๋์ํ๋๋ก ํ๋ ์ฅ์ ์ด ์๋ค.
์ปจ์๋จธ๊ฐ ์ฐพ์ ์๋น์ค์ ์ธ์คํด์ค๊ฐ ์ฌ๋ฌ ๊ฐ์ผ ๊ฒฝ์ฐ์๋?
์ปจ์๋จธ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ธ์คํด์ค๋ฅผ ์ ํํ ํ์์์ด ๋ฆฌ๋ณธ ํด๋ผ์ด์ธํธ ๋ก๋๋ฐธ๋ฐ์๊ฐ ํ๋์ ์๋น์ค ์ธ์คํด์ค๋ฅผ ์ ํํด์ค๋ค.
์ ๋ ์นด์์ ์ฐพ์ ์๋น์ค๋ฅผ ์ ํ ๋ฐ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ ๊ฐ์ง๊ฐ ์๋ค
A. ๋ก๋ ๋ฐธ๋ฐ์ฑ๋ RestTemplate
B. Feign์์ ์์ฑ๋ ํด๋ผ์ด์ธํธ ์ธํฐํ์ด์ค
์ด ์ค ์ด๋ ๋ฐฉ๋ฒ์ ์ ํํ ์ง๋ ๊ฐ์ธ์ ์ทจํฅ์ ๋ฌ๋ ค์๋ค.
A-1. RestTemplate ์ฌ์ฉํด ์๋น์ค ์ฌ์ฉํ๊ธฐ
before
public Ingredient getIngredientById(String ingredientId) {
return rest.getForObject(
"http://localhost:8080/ingredients/{id}",
Ingredient.class, ingredientId);
}
after
// RestTemplateConfig.java
@Configuration
@Conditional(NotFeignAndNotWebClientCondition.class)
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
...
// IngredientServiceClient.java
@Component
public class IngredientServiceClient {
private RestTemplate rest;
public IngredientServiceClient(@LoadBalanced RestTemplate rest) {
this.rest = rest;
}
public Ingredient getIngredientById(String ingredientId) {
return rest.getForObject(
"http://ingredient-service/ingredients/{id}",
Ingredient.class, ingredientId);
}
...
}
์คํ๋ง RestTemplate์์ HTTP GET ์์ฒญ์ ์ํํ ๋๋ before์ฒ๋ผ URL์ด ํ๋์ฝ๋ฉ ๋์ด์์๋ค.
RestTemplateConfig.java
๋ก๋๋ฐธ๋ฐ์ฑ๋ RestTemplate ๋น์ ์ ์ธํ๊ณ , @Bean @LoadBalanced ์ ๋ฉ์๋์ ์ง์ ํ๋ค.
@LoadBalanced ๋ ๋ ๊ฐ์ง ์๋ฏธ๋ฅผ ์ง๋๋ค.
- ํ์ฌ์ RestTemplate์ด ๋ฆฌ๋ณธ์ ํตํด์๋ง ์๋น์ค๋ฅผ ์ฐพ๋๋ค๋ ๊ฒ์ ์คํ๋ง ํด๋ผ์ฐ๋์ ์๋ ค์ค๋ค
- ์ฃผ์ ์๋ณ์๋ก ๋์ํ๋ค. ์ฃผ์ ์๋ณ์๋ ์๋น์ค ์ด๋ฆ์ผ๋ก, getForObject()์ HTTP ์์ฒญ์์ ํธ์คํธ์ ํฌํธ ๋์ ์ฌ์ฉํ ์ ์๋ค.
IngredientServiceClient.java
์์์ฌ๋ฅผ ์ฐพ๊ธฐ ์ํด ๋ก๋ ๋ฐธ๋ฐ์ฑ๋ RestTemplate๋ฅผ ์ฌ์ฉํ๋ ์์์ด๋ค.
before๊ณผ ๋ฌ๋ผ์ง๋ ์ ์ ์๋์ ๊ฐ๋ค.
- ๋ก๋๋ฐธ๋ฐ์ฑ๋ RestTemplate์ ํ์๋ก ํ๋ ๋น์ ์ฃผ์ ํ๊ณ ,
- getIngredientById() ๋ฉ์๋์ ํธ์คํธ์ ํฌํธ ๋์ ํด๋น ์๋น์ค๋ช ์ ์ฌ์ฉํ๋๋ก ๋ณ๊ฒฝํ๋ค.
์ด๋ ๊ฒ ๋๋ฉด (๋์์๋ฆฌ) ๋ด๋ถ์ ์ผ๋ก ingredient-service ๋ผ๋ ์๋น์ค ์ด๋ฆ์ ์ฐพ์์ ์ธ์คํด์ค๋ฅผ ์ ํํ๋๋ก RestTemplate์ด ๋ฆฌ๋ณธ์ ์์ฒญํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ ํ๋ ์๋น์ค ์ธ์คํด์ค์ ํธ์คํธ์ ํฌํธ ์ ๋ณด๋ฅผ ํฌํจํ๋๋ก ๋ฆฌ๋ณธ์ด URL์ ๋ณ๊ฒฝํํ ์๋๋๋ก RestTemplate์ด ์ฌ์ฉ๋๋ค.
ingredient-service ์๋น์ค์ ์ปจํธ๋กค๋ฌ(IngredientController.java)
@Controller
@RequestMapping("/ingredients")
public class IngredientController {
private IngredientServiceClient client;
public IngredientController(IngredientServiceClient client) {
this.client = client;
}
...
@GetMapping("/{id}")
public String ingredientDetailPage(@PathVariable("id") String id, Model model) {
log.info("Fetched an ingredient from a RestTemplate-based service.");
model.addAttribute("ingredient", client.getIngredientById(id));
return "ingredientDetail";
}
}
A-2. WebClient๋ก ์๋น์ค ์ฌ์ฉํ๊ธฐ
11์ฅ์์ ๋ฐฐ์ด WebClient๊ฐ HTTP ํด๋ผ์ด์ธํธ๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์๋ค. ๋ฆฌ์กํฐ๋ธ ํ๋ก๊ทธ๋๋ฐ์ ๊ด์ฌ์ด ๋ง๋ค๋ฉด RestTemplate ๋์ WebClient๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
Flux์ Mono๊ฐ์ ๋ฆฌ์กํฐ๋ธ ํ์ ๊ณผ ํจ๊ป ๋์ํ๊ฒ ํ๋ ค๋ฉด, RestTemplate ์ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก WebClient๋ฅผ ๋ก๋ ๋ฐธ๋ฐ์ฑ๋ ํด๋ผ์ด์ธํธ๋ก ์ฌ์ฉํ ์ ์๋ค.
// WebClientConfig.java
@Configuration
public class WebClientConfig {
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
...
}
// IngredientServiceClient.java
@Service
@Profile("webclient")
public class IngredientServiceClient {
private WebClient.Builder wcBuilder;
public IngredientServiceClient(@LoadBalanced WebClient.Builder wcBuilder) {
this.wcBuilder = wcBuilder;
}
public Mono<Ingredient> getIngredientById(String ingredientId) {
return wcBuilder.build()
.get()
.uri("http://ingredient-service/ingredients/{id}", ingredientId)
.retrieve().bodyToMono(Ingredient.class);
}
B. Feign ํด๋ผ์ด์ธํธ ์ธํฐํ์ด์ค ์ ์ํ๊ธฐ
Feign ์ REST ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ฉฐ, ์ธํฐํ์ด์ค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด REST ํด๋ผ์ด์ธํธ๋ฅผ ์ ์ํ๋ค.
๊ฐ๋จํ ๋งํด์, ์คํ๋ง ๋ฐ์ดํฐ๊ฐ ๋ฆฌํผ์งํฐ๋ฆฌ ์ธํฐํ์ด์ค๋ฅผ ์๋์ผ๋ก ๊ตฌํํ๋ ๊ฒ๊ณผ ์ ์ฌํ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค.
์์กด์ฑ ์ถ๊ฐ
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
Feign ํ์ฑํ
@Configuration
@EnableFeignClients
public class FeignClientConfig {
}
IngredientClient.java ์ธํฐํ์ด์ค ์ ์
package tacos.ingredientclient.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import tacos.ingredientclient.Ingredient;
@FeignClient("ingredient-service")
public interface IngredientClient {
@GetMapping("/ingredients/{id}")
Ingredient getIngredient(@PathVariable("id") String id);
...
}
ingredient-service๋ผ๋ ์ด๋ฆ์ผ๋ก ์ ๋ ์นด์ ๋ฑ๋ก๋ ์๋น์ค๋ฅผ ์ฌ์ฉํด ์์์ฌ๋ฅผ ๊ฐ์ ธ์ค๋ ํด๋ผ์ด์ธํธ๋ฅผ ์์ฑํ์๋ค.
์ธํฐํ์ด์ค๋ง ์ ์ํ๋ฉด Feign์ด ๋ฐํ์์์ ์ด ์ธํฐํ์ด์ค๋ฅผ ์ฐพ๋๋ค. ๊ทธ๋ฆฌ๊ณ Feign์ด ์๋์ผ๋ก ๊ตฌํ ํด๋์ค๋ฅผ ์์ฑํ ํ ์คํ๋ง ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ์ ๋น์ผ๋ก ๋ ธ์ถ์ํจ๋ค.
@FeignClient
์ด๋
ธํ
์ด์
์ ํด๋น ํด๋์ค ๋ด์ ๋ชจ๋ ๋ฉ์๋๊ฐ ingredient-service์ ๋ํ ์์ฒญ์์ ๋ํ๋ธ๋ค. ๋ด๋ถ์ ์ผ๋ก ingredieng-service๋ ๋ฆฌ๋ณธ์ด ์ฐพ๊ฒ ๋๋ค.
@GetMapping
์ด๋
ธํ
์ด์
์ ๋ฆฌ๋ณธ์ด ์ ํํ ํธ์คํธ์ ํฌํธ์ /ingredients/{id}
๊ฒฝ๋ก๋ก HTTP GET ์์ฒญ์ ์ํํ๊ฒ ๋๋ค.
ingredient-service ์๋น์ค์ ์ปจํธ๋กค๋ฌ(IngredientController.java)
RestTemplate๋ก ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ ๊ฐ๋ค.
@Controller
@RequestMapping("/ingredients")
public class IngredientController {
private IngredientClient client;
@Autowired
public IngredientController(IngredientClient client) {
this.client = client;
}
...
@GetMapping("/{id}")
public String ingredientDetailPage(@PathVariable("id") String id, Model model) {
log.info("Fetched an ingredient from a Feign client.");
model.addAttribute("ingredient", client.getIngredient(id));
return "ingredientDetail";
}
}
์ถ๊ฐ. Feign์๋ ์์ ์ ์ด๋ ธํ ์ด์ ์ธ @RequestLine ๊ณผ @Param ์ด ์๋๋ฐ, ์คํ๋ง MVC์ @RequestMapping, @PathVariable๊ณผ ๊ฑฐ์ ์ ์ฌํ๋ค.
Feign, WebClient, RestTemplate ์ด์ค ์ด๋ ๊ฒ์ ์ฌ์ฉํ๋ REST ํด๋ผ์ด์ธํธ์์ ํน์ ํธ์คํธ ์ด๋ฆ, ํฌํธ๋ฅผ ํ๋์ฝ๋ฉํ๋ ๊ฒ์ ๋ง์์ค๋ค.