Self Registration#
Self Registration은 각 마이크로서비스 인스턴스가 자신의 정보를 서비스 레지스트리에 직접 등록하고 관리하는 패턴이다. 서비스가 시작될 때 자동으로 등록되고, 종료될 때 해제되는 방식으로 동작한다.
Self Registration 패턴은 마이크로서비스 환경에서 동적으로 변화하는 서비스 인스턴스를 효과적으로 관리할 수 있게 해주는 중요한 패턴이다. 하지만 구현의 복잡성과 유지보수 측면에서 주의가 필요하며, 프로젝트의 규모와 요구사항에 따라 적절히 선택해야 한다.
주요 특징#
- 자동 등록: 서비스 인스턴스가 시작될 때 자신의 정보(호스트, IP 주소, 포트 등)를 레지스트리에 등록한다.
- 자동 해제: 서비스가 종료될 때 레지스트리에서 자신의 정보를 제거한다.
- 헬스체크: 주기적으로 레지스트리에 헬스체크 신호를 보내 자신이 살아있음을 알린다.
- 상태 관리: 서비스 인스턴스가 자신의 상태를 가장 잘 알기 때문에, UP/DOWN 외에도 STARTING, AVAILABLE 등 더 복잡한 상태 모델을 구현할 수 있다.
구현 방법#
- 서비스 레지스트리 설정: Eureka, Consul, ZooKeeper 등의 도구를 사용하여 중앙 레지스트리를 구축한다.
- 서비스 등록 코드 구현: 각 마이크로서비스에 자신을 레지스트리에 등록하는 코드를 추가한다.
- 헬스체크 메커니즘 구현: 주기적으로 레지스트리에 헬스체크 신호를 보내는 로직을 구현한다.
- 서비스 디스커버리 클라이언트 구현: 다른 서비스들이 등록된 서비스를 찾고 통신할 수 있도록 한다.
- 구현이 비교적 간단하다.
- 추가적인 시스템 컴포넌트가 필요하지 않다.
- 서비스가 자신의 상태를 가장 잘 알기 때문에 정확한 정보를 제공할 수 있다.
- 서비스와 레지스트리 간의 결합도가 높아진다.
- 각 프로그래밍 언어와 프레임워크마다 등록 로직을 구현해야 한다.
- 서비스가 비정상적으로 종료될 경우 레지스트리에서 자동으로 제거되지 않을 수 있다.
구현 예시#
Netflix Eureka는 셀프 등록 패턴의 대표적인 예시이다.
Eureka 클라이언트는 다음과 같은 방식으로 동작한다:
- 서비스 등록: 서비스 인스턴스는 시작 시 Eureka 서버에 자신의 네트워크 위치를
POST
요청으로 등록한다. - 하트비트 전송: 등록 후, 서비스 인스턴스는 주기적으로
PUT
요청을 보내어 등록 정보를 갱신한다. - 서비스 해제: 서비스 인스턴스가 종료되거나 장애가 발생하면,
DELETE
요청을 통해 등록을 해제하거나, 일정 시간 동안 하트비트가 수신되지 않으면 Eureka 서버에서 자동으로 등록이 제거된다.
이러한 방식으로 Eureka 클라이언트는 서비스 레지스트리와의 통신을 통해 자신의 상태를 관리한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| // 서비스 등록 클라이언트 구현
class ServiceRegistration {
constructor(options = {}) {
this.serviceName = options.serviceName;
this.serviceHost = options.serviceHost;
this.servicePort = options.servicePort;
this.registryUrl = options.registryUrl || 'http://service-registry:8500';
this.healthCheckEndpoint = options.healthCheckEndpoint || '/health';
this.registrationInterval = options.registrationInterval || 30000;
this.isRegistered = false;
}
async start() {
try {
// 초기 서비스 등록
await this.register();
// 주기적인 등록 갱신
this.registrationTimer = setInterval(
() => this.renewRegistration(),
this.registrationInterval
);
// 프로세스 종료 시 등록 해제
process.on('SIGTERM', () => this.deregister());
process.on('SIGINT', () => this.deregister());
this.isRegistered = true;
} catch (error) {
console.error('Service registration failed:', error);
throw error;
}
}
async register() {
const registrationData = {
name: this.serviceName,
id: `${this.serviceName}-${this.serviceHost}-${this.servicePort}`,
address: this.serviceHost,
port: this.servicePort,
check: {
http: `http://${this.serviceHost}:${this.servicePort}${this.healthCheckEndpoint}`,
interval: '10s',
timeout: '5s'
}
};
await fetch(`${this.registryUrl}/v1/agent/service/register`, {
method: 'PUT',
body: JSON.stringify(registrationData)
});
}
async deregister() {
if (this.isRegistered) {
clearInterval(this.registrationTimer);
const serviceId = `${this.serviceName}-${this.serviceHost}-${this.servicePort}`;
await fetch(`${this.registryUrl}/v1/agent/service/deregister/${serviceId}`, {
method: 'PUT'
});
this.isRegistered = false;
}
}
}
|
참고 및 출처#