마이크로서비스 이해하기
- 단일 애플리케이션의 문제점
- 코드 전체를 파악하기 어렵다.
- 테스트가 복잡해진다.
- 라이브러리 간의 충돌이 생기기 쉽다.
- 확장 시 비효율적이다. 확장 목적으로 더 많은 서버에 애플리케이션을 배포해야 할 때는 애플리케이션의 일부가아닌 전체를 배포해야 한다.
- 적용할 기술을 결정할 때도 애플리케이션 전체를 고려해야 한다.
- 프로덕션으로 이양하기 위해 많은 노력이 필요하다. 단일 애플리케이션은 크기와 복잡도 때문에 더 엄격한 개발 프로세스와 테스트가 필요하다.
- 마이크로서비스 아키텍처
- 코드를 이해하기 쉽다.
- 테스트가 쉽다.
- 라이브러리 비호환성 문제가 생기지 않는다.
- 독자적으로 규모를 조정할 수 있다. 마이크로서비스의 규모가 더 커야 한다면, 다른 마이크로서비스에 영향을 주지 않고 메모리 할당이나 인스턴스의 수를 더 크게 조정할 수 있다.
- 각 마이크로서비스에 적용할 기술을 다르게 선택할 수 있다.
- 마이크로서비스는 언제든 프로덕션으로 이양할 수 있다.
- 마이크로서비스 아키텍처는 분산 아키텍처이므로 네트워크 지연과 같은 문제들이 발생할 수 있다.
- 애플리케이션이 상대적으로 작거나 간단하면 단일 애플리케이션으로 재발하는 것이 좋다. 그리고 점차 규모가 커질 때 마이크로서비스 아키텍처로 변경하는 것을 고려할 수 있다.
서비스 레지스트리 설정하기
서비스 레지스트리: 마이크로서비스가 서로를 찾을 때 사용되는 마이크로서비스
유레카: 넷플릭스 오픈 소스인 스프링 서비스 레지스트리
some-service 인스턴스가 시작될 때 해당 서비스는 자신의 이름을 유레카에 등록한다.
- 등록되는 인스턴스는 여러개 생성될 수 있고 모두 같은 이름으로 유레카에 등록된다.
- other-service가 some-service를 사용할 때는 some-service의 호스트 이름과 포트 정보를 하드코딩하지 않고, some-service라는 이름을 유레카에서 찾으면 된다.
- other-service는 some-service의 어떤 인스턴스를 사용할지 결정해야 된다. 이때 클라이언트 측에서 로드 밸런싱 알고리즘을 적용하는 것이 좋고, 이때 사용할 수 있는 것이 넷플릭스 프로젝트인 리본이다.
중앙 집중화된 로드 밸런서가 아닌 클라이언트 측의 로드밸런서를 사용하는 이유
- 각 클라이언트에 하나의 로컬 로드 밸런서가 있으므로 클라이언트의 수에 비례하여 자연스럽게 로드 밸런서의 크기가 조정된다.
- 서버에 연결된 모든 서비스에 획질적으로 같은 구성을 사용하는 대신, 각 클라이언트에 가장 적합한 로드 밸런싱 알고리즘을 사용하도록 구성할 수 있다.
유레카 서버 스타터 의존성
부트스트랩 클래스에
@EnableEurekaServer
애노테이션을 추가하면 실행가능하다.
@EnableEurekaServer
@SpringBootApplication
class ServiceRegistryApplication
fun main(args: Array<String>) {
runApplication<ServiceRegistryApplication>(*args)
}
유레카 구성하기
server.port=8761
eureka.instance.hostname=localhost
eureka.client.fetch-registry=false
eureka.client.register-with-eureka=false
eureka.client.service-url.defalt-zone=http://${eureka.instance.hostname}:${server.port}/eureka/
eureka.server.enable-self-preservation=false
- 일반적으로 단일 장애점 방지를 위해 여러 유레카 서버들이 클러스터로 구성된다.
- 그러나 개발 시에 두 개 이상의 유레카 서버를 실행하는 것은 불편하기도 하고 불필요하다. 개발 목적으로는 하나의 유레카 서버면 충분하다.
server.port
: 유레카 서버 포트eureka.instance.hostname
: 유레카 서버 호스트 네임eureka.client.fetch-registry
: 해당 서버가 다른 유레카 서버로부터 레지스트 정보를 가져오는지 여부eureka.client.register-with-eureka
: 해당서버가 다른 유레카 서버의 서비스로 자신을 등록하는지 여부eureka.client.service-url
: 해당 키 하위의 정보는Map
형태로 저장된다.- 위 예시에서는 키인
defaultZone
은 자신이 원하는 영역을 지정하지 않았을 때 사용한다.
- 위 예시에서는 키인
eureka.server.enable-self-preservation
: 자체-보존 모드 비활성화- 유레카 서버는 다른 서비스 인스턴스가 살아 있는지 확인하기 위해 30초마다 등록 갱신 요청을 전송하기를 기대한다.
- 일반적으로 세 번의 갱신기간(90초) 동안 서비스 인스턴스로부터 등록 갱신 요청을 받지 못하면 해당 서비스 인스턴스의 등록을 취소하게 된다.
- 만일 이렇게 중단되는 서비스가 임계값을 초과하면 유레카 서버는 네트워크 문제가 생긴 것으로 간주하고 레지스트리에 등록된 나머지 서비스 데이터를 지우지 않는 자체-보존 모드가 된다.
- 프로덕션 환경에서는 도움이되지만 개발 환경에서는 여러 가지 이유로 갱신 요청을 받을 수 없기 때문에 비활성화 한다.
유레카 확장하기
- 가장 간단한 방법은 properties 파일에 스프링 프로파일을 지정하고, 한 번에 하나씩 프로파일을 사용해서 유레카를 두 번 시작시키면 된다.
eureka.client.service-url.default-zone=http://${other.eureka.host}:${other.eureka.port}/eureka
#---
spring.config.activate.on-profile=eureka-1
spring.application.name=eureka-1
server.port=8761
eureka.instance.hostname=eureka1.tacocloud.com
other.eureka.host=eureka2.tacocloud.com
other.eureka.port=8762
#---
spring.config.activate.on-profile=eureka-2
spring.application.name=eureka-2
server.port=8762
eureka.instance.hostname=eureka2.tacocloud.com
other.eureka.host=eureka1.tacocloud.com
other.eureka.port=8761
서비스 등록하고 찾기
- 애플리케이션을 서비스 레지스트리 클라이언트로 활성화 하기 위해 의존성 추가
- 유레카 클라이언트 스타터 의존성을 추가하면 유레카를 이용해서 서비스를 찾는 데 필요한 모든 것이 자동으로 추가된다.
- 유레카의 클라이언트 라이브러리, 리본 로드 밸런서 등
유레카 클라이언트 속성 구성하기
- 유레카 서버에 서비스 이름을 등록하기 위해서 클라이언트에
spring.application.name
프로퍼티에 서비스 이름을 명시한다. - 유레카 서버의 위치를 등록하기위해
eureka.client.service-url
속성을 사용할 수 있다.- 어떤 이유로든 한 유레카 서버가 중단되면 클라이언트가 서비스 등록에 실패하는 것을 방지하기 위해서 두 개 이상의 유레카 서버를 사용하도록 구성할 수 있다.
- 아래와 같이 구성하면 서비스가 시작될 때 첫 번째 유레카 서버에 등록을 시도하고, 실패하면 두 번째에 등록을 시도하게 된다.
서비스 사용하기
- 스프링 클라우드의 유레카 클라이언트 지원에 포함된 리본 클라이언트 로드 밸런스를 사용해서 서비스 인스턴스를 쉽게 찾아 선택할 수 있다.
- 유레카 서버에서 찾은 서비스를 선택 및 사용하는 방법에는 두 가지가 있다.
- 로드 밸런싱된
RestTemplate
Feign
에서 생성된 클라이언트 인터페이스
- 로드 밸런싱된
RestTemplate
- 이전의 방법: 요청을 보내기 위해 호스트와 포트를 하드 코딩했다.
- 로드 밸런싱된
RestTemplate
사용법- config 클래스에
@LoadBalanced
어노테이션을 붙여 빈으로 등록 @LoadBalanced
를 붙여서 로드 밸런싱된RestTemplate
를 주입하고 사용- 위 사진과 같이 호스트와 포트를 하드 코딩하지 않고 서비스명을 적어두면 된다.
- config 클래스에
- 이전의 방법: 요청을 보내기 위해 호스트와 포트를 하드 코딩했다.
WebClient
: 리액티브 프로그래밍에서 사용할 수 있는 HTTP 클라이언트RestTemplate
과 같은 방법으로 로드 밸런싱된 클라이언트를 사용할 수 있다.
- Feign: 인터페이스를 기반으로 하는 방법을 사용해서 REST 클라이언트를 정의한다.
- 넷플릭스 프로젝트였지만, 자웅에 독립된 오픈 소스 프로젝트가 되었다.
- 의존성 추가
@EnableFeignClients
애노테이션을 추가하면 Feign이 활성화된다.- 인터페이스를 통한 정의
@FeignClient
애노테이션에 명시한 이름의 서비스를 리본을 통해 찾게된다.@GetMapping
과@PathVariable
애노테이션은 스프링 MVC에서 사용했던 것과 같은 애노테이션이다.- 아래 사진에서는 ‘ingredient-service’ 서비스를 리본을 통해서 찾아서 선택하게 되고, 선택한 호스트와 포트의 ‘/ingredients/{id}’ 경로로 GET 요청을 보내게 된다.