- 클라우드 네이티브 시스템은 동적이다.(필요에 따라 그때그때 올라가고, 내려가기도 함)
- 어떤 서비스가 내려가고 올라가더라도 그 서비스를 이용하는 다른 서비스는 영향을 받으면 안된다.
- 해당 서비스를 아래와 같이 설정하면 클라이언트 -> 서버를 고정적으로 결합하므로 동적인 클라우드 네이티브 시스템에는 적합하지 않다.
member-service:
endpoints:
- http://172.12.35.65:8080/members
- http://172.12.35.67:8080/members
- DNS를 이용할 수도 있지만, 클라우드 환경에서는 가장 좋은 방법이라고 볼 수 없다.
- 클라이언트 내부에 캐시를 둘 수 있다는 것이 장점인데, 사용되지 않는 IP 주소를 캐시하고 있을 수 있다.
- TTL(time to live) 값을 짧게 잡으면 DNS 레코드를 더 자주 받아오는데 많은 시간을 쓰게 된다.
- DNS 로드밸런싱을 적용하면 스티키 세션(sticky session)은 지원하지만 그 이상의 기능이 필요하면 감당하기 어렵다.(예를들어 OAuth2나 JWT의 access token 기반 밸런싱)
- 프록시 역할을 하는 가상 로드밸런서는 시스템의 상태나 요청 처리에 필요한 작업량을 알지 못한다.
- 초기 연결을 서비스에 분산할 뿐, 연결에 포함된 요청 처리에 필요한 작업량을 분산하지는 못한다.
- 인프라가 아니라 프로그래밍을 통해 더 나은 라우팅 전략을 구현할 수 있다.(작업량은 어떻게 분산..?)
- 서비스 레지스트리는 서비스 인스턴스와 서비스가 제공하는 API를 내용으로 하는 테이블이다.
- 서비스 레지스트리는 CAP(Consistency 일관성, Availability 가용성, Partition tolerance 분리 내구성) 정리의 제약도 받는다.
- 스프링 클라우드는 DiscoveryClient 추상화를 통해 클라이언트가 다양한 유형의 서비스 레지스트리를 쉽게 이용할 수 있게 해준다.
- 다양한 구현체들을 Plugin 하여 사용할 수 있다.
- 클라우드 파운드리, 아파치 주키퍼, 해시코프 컨설, 넷플릭스 유레카, CoreOS etcd
- 일부 서비스 레지스트리는 클라이언트도 레지스트리에 등록해야 한다.
- 많은 DiscoveryClient 추상화 구현체는 애플리케이션 기동 시 자동으로 등록해준다.
DiscoveryClient.java
package org.springframework.cloud.client.discovery;
import java.util.List;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.Ordered;
public interface DiscoveryClient extends Ordered {
/**
* Default order of the discovery client.
*/
int DEFAULT_ORDER = 0;
/**
* A human-readable description of the implementation, used in HealthIndicator.
* @return The description.
*/
String description();
/**
* Gets all ServiceInstances associated with a particular serviceId.
* @param serviceId The serviceId to query.
* @return A List of ServiceInstance.
*/
List<ServiceInstance> getInstances(String serviceId);
/**
* @return All known service IDs.
*/
List<String> getServices();
/**
* Default implementation for getting order of discovery clients.
* @return order
*/
@Override
default int getOrder() {
return DEFAULT_ORDER;
}
}
간단하게 org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient
살펴보면 com.netflix.discovery.EurekaClient
를 이용하여
Spring cloud의 ServiceInstance
로 컨버팅 해주고 있다.
package org.springframework.cloud.netflix.eureka;
...
public class EurekaDiscoveryClient implements DiscoveryClient {
...
@Override
public List<ServiceInstance> getInstances(String serviceId) {
List<InstanceInfo> infos = this.eurekaClient.getInstancesByVipAddress(serviceId,
false);
List<ServiceInstance> instances = new ArrayList<>();
for (InstanceInfo info : infos) {
instances.add(new EurekaServiceInstance(info));
}
return instances;
}
@Override
public List<String> getServices() {
Applications applications = this.eurekaClient.getApplications();
if (applications == null) {
return Collections.emptyList();
}
List<Application> registered = applications.getRegisteredApplications();
List<String> names = new ArrayList<>();
for (Application app : registered) {
if (app.getInstances().isEmpty()) {
continue;
}
names.add(app.getName().toLowerCase());
}
return names;
}
...
}
ServiceInstance
는 Spring Cloud에서 정의한 인터페이스로 host,port 등의 아래와 같은 정보들을 담고있다.
org.springframework.cloud.client.ServiceInstance.java
/**
* Represents an instance of a service in a discovery system.
*/
public interface ServiceInstance {
/**
* @return The unique instance ID as registered.
*/
default String getInstanceId() {
return null;
}
/**
* @return The service ID as registered.
*/
String getServiceId();
/**
* @return The hostname of the registered service instance.
*/
String getHost();
/**
* @return The port of the registered service instance.
*/
int getPort();
/**
* @return Whether the port of the registered service instance uses HTTPS.
*/
boolean isSecure();
/**
* @return The service URI address.
*/
URI getUri();
/**
* @return The key / value pair metadata associated with the service instance.
*/
Map<String, String> getMetadata();
/**
* @return The scheme of the service instance.
*/
default String getScheme() {
return null;
}
}
- 위와 같이 Eureka Server와 Application에 포함된 Eureka Client가 존재한다.
- Application Service는 Service discovery에 대상이 되기 위해 Eureka Server에 등록을 하고 있다.
- Application Client는 Service discovery에 대상은 아니지만 Fetch registry를 통해 다른 서비스 정보를 가져오고 있다.
- Application Service를 살펴보면 Eureka Client를 통해 Eureka Server와 아래와 같은 상호작용을 하고 있다.
Eureka wiki:Understanding-eureka-client-server-communication
Register
;Client -> Server에게 등록 요청을 보낸다.
Renew
30초마다 heatbeat를 보내서 lease를 갱신한다. Server는 마지막 heatbeat를 보낸 시간보다 90초가 지났으면 해당 서비스를 Registry에서 제거한다.
Fetch Registry
Client는 Server로부터 registry 정보를 가져와 로컬 캐시에 담아둔다. 이러한 registry 정보는 service discovery하는데 사용된다.
아래의 com.netflix.discovery.DiscoveryClient
를 살펴보면 delta를 조회하여 client의 이전 delta와 해시코드(문자열)를 비교하여 변경사항이 있으면 갱신한다.
private void getAndUpdateDelta(Applications applications) throws Throwable {
long currentUpdateGeneration = fetchRegistryGeneration.get();
Applications delta = null;
// /apps/delta?regions=... 호출
EurekaHttpResponse<Applications> httpResponse = eurekaTransport.queryClient.getDelta(remoteRegionsRef.get());
if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
delta = httpResponse.getEntity();
}
if (delta == null) {
logger.warn("The server does not allow the delta revision to be applied because it is not safe. "
+ "Hence got the full registry.");
getAndStoreFullRegistry();
} else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
logger.debug("Got delta update with apps hashcode {}", delta.getAppsHashCode());
String reconcileHashCode = "";
if (fetchRegistryUpdateLock.tryLock()) {
try {
// client의 delta 갱신
updateDelta(delta);
// {instance_name}:{count}_{instance_name}_{count} ...
reconcileHashCode = getReconcileHashCode(applications);
} finally {
fetchRegistryUpdateLock.unlock();
}
} else {
logger.warn("Cannot acquire update lock, aborting getAndUpdateDelta");
}
...
}
...
}
Cancel
Client는 애플리케이션 종료 시 Server에게 Cancel 요청을 보내 registry에서 제거된다.
Eureka interacts
위와 같은 예를 들어보자.
- Service1 : 애플리케이션 시작 시 Eureka 서버에서 Register 요청을 보낸 뒤 서버는 Registry에 추가한다.
- Service2 : 주기적으로 Heatbeat를 보낸다.(Renew)
- Service3 : 애플리케이션 종료 시 Eureka 서버에게 Cancel 요청을 보낸 뒤 서버는 Registry에서 제거한다.
- Service4 : 애플리에키션 종료 시 Cancel 요청을 보내지 않은 상태이다.
- Eureka Server : Service4에 대하여 마지막 Heatbeat로 부터 90초가 지나서 Registry에서 제거한다.
더 자세한 Endpoint는 WIKI-Eureka-REST-operations 를 살펴보자.
간단하게 Standalone Mode의 Eureka서버를 실행하고, Eureka Client를 포함하고 있는 account service를 실행해보자.
application.yaml
eureka:
# dashboard에 대한 설정으로, http://localhost:3000/eureka-ui 를 통해 확인할 수 있다.
dashboard:
path: /eureka-ui
instance:
hostname: localhost
statusPageUrlPath: /info
healthCheckUrlPath: /health
# 등록된 인스턴스 중 많은 수가 정해진 시간 내에 Heatbeat를 보내지 않으면 Eureka는 이를 인스턴스 문제가 아닌
# 네트워크 문제라고 간주하고 Registry를 그대로 유지한다. Example 실행을 위해 false로 설정
server:
enableSelfPreservation: false
client:
# Eureka client -> Eureka server로 등록 여부
# standalone mode이므로 자기 자신을 등록할 필요가 없다.
registerWithEureka: false
# Eureka Client -> Eureka server로 Registry fetch 여부
fetchRegistry: false
@EnableEurekaServer
package server;
import java.util.Arrays;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.function.Predicate;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(EurekaServerApplication.class)
.web(WebApplicationType.SERVLET).run(args);
}
}
bootstrap.yaml
spring:
application:
name: account-service
eureka:
instance:
# 랜덤값을 이용하여 instance id를 고유하게 재정의
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
application.yaml
eureka:
# service instance에 대한 설정
instance:
statusPageUrlPath: /actuator/info
healthCheckUrlPath: /actuator/health
# eureka client 설정
client:
serviceUrl:
defaultZone: http://localhost:3000/eureka/
# /actuator/info 호출 시 출력 된 Application 정보
info:
app:
name: Account Example Application
version: 1.0.0
discription: This is a demo project for eurkea
미리 정의한 스크립트를 기반으로 Server와 AccountServer(EurekaClient)를 실행해보자.
start server/client
// eureka 서버 시작하기
$ ./tools/script/start.sh server
// account 서버1 시작하기(profile:default, port:3100)
$ ./tools/script/start.sh account default 3100
// account 서버2 시작하기(profile:default, port:3101)
$ ./tools/script/start.sh server default 3101
http://localhost:3000/eureka-ui
를 접속하면 아래와 같은 화면을 확인할 수 있다.
account service에 아래와 같은 코드를 추가해서 /discovery/services
를 호출하면 모든 인스턴스를 반환하는 API를 추가하자.
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/discovery/services")
public Map<String, List<ServiceInstance>> discoveryServices() {
return discoveryClient.getServices()
.stream()
.collect(Collectors.toMap(s -> s, s -> discoveryClient.getInstances(s)));
}
아래와 같이 요청하면 Spring cloud의 ServiceInstance
구현체인 EurekaServiceInstance
의 응답 결과를 확인할 수 있다.
(instanceId ~ metadata는 ServiceInstance 스펙이고 instanceInfo는 eureka의 InstanceInfo 스펙이다.)
$ curl -XGET http://localhost:3100/discovery/services | jq .
{
"account-service": [
{
"instanceId": "account-service:b12e61ad1432e7da69d396f443e8b1bf",
"serviceId": "ACCOUNT-SERVICE",
"host": "192.168.0.2",
"port": 3101,
"secure": false,
"scheme": "http",
"uri": "http://192.168.0.2:3101",
"metadata": {
"management.port": "3101"
},
"instanceInfo": {
"instanceId": "account-service:b12e61ad1432e7da69d396f443e8b1bf",
"app": "ACCOUNT-SERVICE",
"appGroupName": null,
"ipAddr": "192.168.0.2",
"sid": "na",
"homePageUrl": "http://192.168.0.2:3101/",
"statusPageUrl": "http://192.168.0.2:3101/actuator/info",
"healthCheckUrl": "http://192.168.0.2:3101/actuator/health",
"secureHealthCheckUrl": null,
"vipAddress": "account-service",
"secureVipAddress": "account-service",
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"hostName": "192.168.0.2",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 1598537803756,
"lastRenewalTimestamp": 1598537923714,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1598537803756
},
"isCoordinatingDiscoveryServer": false,
"metadata": {
"management.port": "3101"
},
"lastUpdatedTimestamp": 1598537803756,
"lastDirtyTimestamp": 1598537803712,
"actionType": "ADDED",
"asgName": null
}
},
{...}
]
}
- (1) Client는
GET /eureka//apps/?
를 호출하여 Registry 정보를 가져온다.
// Client log
2020-08-27 23:36:57.663 INFO 17140 --- [ main] com.netflix.discovery.DiscoveryClient : Getting all instance registry info from the eureka server
...
2020-08-27 23:36:57.841 DEBUG 17140 --- [ main] n.d.s.t.j.AbstractJerseyEurekaHttpClient : Jersey HTTP GET http://localhost:3000/eureka//apps/?; statusCode=200
2020-08-27 23:36:57.841 DEBUG 17140 --- [ main] c.n.d.s.t.d.RedirectingEurekaHttpClient : Pinning to endpoint null
2020-08-27 23:36:57.841 INFO 17140 --- [ main] com.netflix.discovery.DiscoveryClient : The response status is 200
2020-08-27 23:36:57.841 DEBUG 17140 --- [ main] com.netflix.discovery.DiscoveryClient : Got full registry with apps hashcode
2020-08-27 23:36:57.842 DEBUG 17140 --- [ main] com.netflix.discovery.DiscoveryClient : The total number of all instances in the client now is 0
// Server log
2020-08-27 23:36:57.797 DEBUG 17119 --- [nio-3000-exec-1] c.n.e.registry.AbstractInstanceRegistry : Fetching applications registry with remote regions: false, Regions argument []
2020-08-27 23:36:57.803 DEBUG 17119 --- [nio-3000-exec-1] c.n.eureka.registry.ResponseCacheImpl : New application cache entry {name=ALL_APPS, type=Application, format=JSON} with apps hashcode
2020-08-27 23:36:57.940 DEBUG 17119 --- [nio-3000-exec-2] c.n.d.util.DeserializerStringCache : clearing global-level cache with size 1
2020-08-27 23:36:57.940 DEBUG 17119 --- [nio-3000-exec-2] c.n.d.util.DeserializerStringCache : clearing app-level serialization cache with size 8
- (2) Client는
POST /eureka//apps/ACCOUNT-SERVICE
를 호출하여 자신의 인스턴스를 Register 후 204 Status code로 성공
// client log
2020-08-27 23:36:57.843 INFO 17140 --- [ main] com.netflix.discovery.DiscoveryClient : Starting heartbeat executor: renew interval is: 30
...
2020-08-27 23:36:57.852 INFO 17140 --- [ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application ACCOUNT-SERVICE with eureka with status UP
...
2020-08-27 23:36:57.855 INFO 17140 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff: registering service...
...
2020-08-27 23:36:57.954 DEBUG 17140 --- [nfoReplicator-0] n.d.s.t.j.AbstractJerseyEurekaHttpClient : Jersey HTTP POST http://localhost:3000/eureka//apps/ACCOUNT-SERVICE with instance account-service:a389e2d6e5bf85f9e1544048fbbf8eff; statusCode=204
2020-08-27 23:36:57.954 DEBUG 17140 --- [nfoReplicator-0] c.n.d.s.t.d.RedirectingEurekaHttpClient : Pinning to endpoint null
2020-08-27 23:36:57.954 INFO 17140 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff - registration status: 204
// server log
2020-08-27 23:36:57.940 DEBUG 17119 --- [nio-3000-exec-2] c.n.e.resources.ApplicationResource : Registering instance account-service:a389e2d6e5bf85f9e1544048fbbf8eff (replication=null)
2020-08-27 23:36:57.940 DEBUG 17119 --- [nio-3000-exec-2] o.s.c.n.eureka.server.InstanceRegistry : register ACCOUNT-SERVICE, vip account-service, leaseDuration 90, isReplication false
2020-08-27 23:36:57.941 DEBUG 17119 --- [nio-3000-exec-2] c.n.e.registry.AbstractInstanceRegistry : No previous lease information found; it is new registration
2020-08-27 23:36:57.941 DEBUG 17119 --- [nio-3000-exec-2] c.n.e.registry.AbstractInstanceRegistry : Processing override status using rule: [com.netflix.eureka.registry.rule.DownOrStartingRule, com.netflix.eureka.registry.rule.OverrideExistsRule, com.netflix.eureka.registry.rule.LeaseExistsRule, com.netflix.eureka.registry.rule.AlwaysMatchInstanceStatusRule]
2020-08-27 23:36:57.941 DEBUG 17119 --- [nio-3000-exec-2] c.n.e.r.r.AlwaysMatchInstanceStatusRule : Returning the default instance status UP for instance account-service:a389e2d6e5bf85f9e1544048fbbf8eff
2020-08-27 23:36:57.942 DEBUG 17119 --- [nio-3000-exec-2] c.n.eureka.registry.ResponseCacheImpl : Invalidating the response cache key : Application ACCOUNT-SERVICE V1 JSON, full
2020-08-27 23:36:57.943 DEBUG 17119 --- [nio-3000-exec-2] c.n.eureka.registry.ResponseCacheImpl : Invalidating the response cache key : Application ACCOUNT-SERVICE V1 JSON, compact
...
- (3) Client는
PUT /eureka//apps/ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff
를 호출하여 Heartbeat를 보내 Renew 작업이 이루어진다.
// client log
2020-08-27 23:37:27.878 DEBUG 17140 --- [tbeatExecutor-0] n.d.s.t.j.AbstractJerseyEurekaHttpClient : Jersey HTTP PUT http://localhost:3000/eureka//apps/ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff; statusCode=200
2020-08-27 23:37:27.879 DEBUG 17140 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff - Heartbeat status: 200
2020-08-27 23:37:27.897 DEBUG 17140 --- [freshExecutor-0] c.n.d.shared.MonitoredConnectionManager : Released connection is reusable.
...
2020-08-27 23:37:27.897 DEBUG 17140 --- [freshExecutor-0] n.d.s.t.j.AbstractJerseyEurekaHttpClient : Jersey HTTP GET http://localhost:3000/eureka//apps/?; statusCode=200
2020-08-27 23:37:27.897 INFO 17140 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient : The response status is 200
// server log
2020-08-27 23:37:27.876 DEBUG 17119 --- [nio-3000-exec-4] o.s.c.n.eureka.server.InstanceRegistry : renew ACCOUNT-SERVICE serverId account-service:a389e2d6e5bf85f9e1544048fbbf8eff, isReplication {}false
2020-08-27 23:37:27.876 DEBUG 17119 --- [nio-3000-exec-4] c.n.e.registry.AbstractInstanceRegistry : Fetching applications registry with remote regions: false, Regions argument []
- (4) Client는
DELETE /eureka//apps/ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff
를 호출하여 Cancel 요청을 보낸 후 서버는 Registry에서 제거한다.
// client log
2020-08-28 00:02:33.149 INFO 19325 --- [extShutdownHook] o.s.c.n.e.s.EurekaServiceRegistry : Unregistering application ACCOUNT-SERVICE with eureka with status DOWN
...
2020-08-28 00:02:33.149 INFO 19325 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff: registering service...
...
2020-08-28 00:02:36.187 DEBUG 19325 --- [extShutdownHook] n.d.s.t.j.AbstractJerseyEurekaHttpClient : Jersey HTTP DELETE http://localhost:3000/eureka//apps/ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff; statusCode=200
2020-08-28 00:02:36.187 INFO 19325 --- [extShutdownHook] com.netflix.discovery.DiscoveryClient : DiscoveryClient_ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff - deregister status: 200
// server log
2020-08-28 00:02:36.184 DEBUG 19308 --- [nio-3000-exec-6] o.s.c.n.eureka.server.InstanceRegistry : cancel ACCOUNT-SERVICE, serverId account-service:a389e2d6e5bf85f9e1544048fbbf8eff, isReplication false
2020-08-28 00:02:36.185 DEBUG 19308 --- [nio-3000-exec-6] c.n.eureka.registry.ResponseCacheImpl : Invalidating the response cache key : Application ACCOUNT-SERVICE V1 JSON, full
...
2020-08-28 00:02:36.186 INFO 19308 --- [nio-3000-exec-6] c.n.e.registry.AbstractInstanceRegistry : Cancelled instance ACCOUNT-SERVICE/account-service:a389e2d6e5bf85f9e1544048fbbf8eff (replication=false)
2020-08-28 00:02:36.186 DEBUG 19308 --- [nio-3000-exec-6] c.n.eureka.resources.InstanceResource : Found (Cancel): ACCOUNT-SERVICE - account-service:a389e2d6e5bf85f9e1544048fbbf8eff
Eureka Server Cluster는 아래와 같이 동작한다.
- Eureka cluster에서 Eureka Server를
peer
라고 표현하고 아래와 같은 행위를Peer Awareness
라고 부른다. - 각각의 Eureka Server는 Eureka Client를 이용하여
Register
와Fetch Registry
작업이 이루어진다. - 또한
Register
,Renew
등 모든 Operation이 발생하면 다른 Peer에게 동일한 요청을 시도한다.
아래와 같이 PeerAwareInstanceRegistryImpl를 살펴보자.
Register
요청에 대하여 Registry를 업데이트하고replicateToPeers()
를 통해서 다른 Peer에게Register with replica
요청을 보낸다.
(즉POST /eureka/v2/apps/appID
요청과 헤더에 Replica 속성을 true)
@Singleton
public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {
...
@Override
public void register(final InstanceInfo info, final boolean isReplication) {
int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
leaseDuration = info.getLeaseInfo().getDurationInSecs();
}
super.register(info, leaseDuration, isReplication);
replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
}
...
}
Spring의 Profile 속성을 이용하여 아래와 같이 peer1-3을 구성해보자.
application.yaml
---
spring:
profiles: peer-1
application:
name: eureka-server-clustered
server:
port: 3001
eureka:
instance:
hostname: peer-1-server.com
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:3002/eureka/,http://localhost:3003/eureka/
---
spring:
profiles: peer-2
application:
name: eureka-server-clustered
server:
port: 3002
eureka:
instance:
hostname: peer-2-server.com
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:3001/eureka/,http://localhost:3003/eureka/
---
spring:
profiles: peer-3
application:
name: eureka-server-clustered
server:
port: 3003
eureka:
instance:
hostname: peer-3-server.com
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:3001/eureka/,http://localhost:3002/eureka/
docker-compose.yaml
version: '3.1'
services:
eureka-peer-1:
build:
context: ../../eureka-server
dockerfile: Dockerfile
image: service-discovery/eureka-server
hostname: eureka-peer-1
container_name: eureka-peer-1
ports:
- 3001:3001
environment:
- SPRING_PROFILES_ACTIVE=peer-1
- EUREKA_CLIENT_SERVICE-URL_DEFAULT-ZONE=http://eureka-peer-2:3002/eureka/,http://eureka-peer-3:3003/eureka/
eureka-peer-2:
image: service-discovery/eureka-server
hostname: eureka-peer-2
container_name: eureka-peer-2
ports:
- 3002:3002
environment:
- SPRING_PROFILES_ACTIVE=peer-2
- EUREKA_CLIENT_SERVICE-URL_DEFAULT-ZONE=http://eureka-peer-1:3001/eureka/,http://eureka-peer-3:3003/eureka/
eureka-peer-3:
image: service-discovery/eureka-server
hostname: eureka-peer-3
container_name: eureka-peer-3
ports:
- 3003:3003
environment:
- SPRING_PROFILES_ACTIVE=peer-3
- EUREKA_CLIENT_SERVICE-URL_DEFAULT-ZONE=http://eureka-peer-1:3001/eureka/,http://eureka-peer-2:3002/eureka/
account-service:
build:
context: ../../account
dockerfile: Dockerfile
image: service-discovery/account
container_name: account-service
restart: always
environment:
- STARTUP_WAIT=5
- EUREKA_CLIENT_SERVICE-URL_DEFAULT-ZONE=http://eureka-peer-1:3001/eureka/,http://eureka-peer-2:3002/eureka/,http://eureka-peer-3:3003/eureka/
ports:
- 3100:3100
depends_on:
- eureka-peer-1
- eureka-peer-2
- eureka-peer-3
article-service:
build:
context: ../../article
dockerfile: Dockerfile
image: service-discovery/article
container_name: article-service
restart: always
environment:
- STARTUP_WAIT=5
- EUREKA_CLIENT_SERVICE-URL_DEFAULT-ZONE=http://eureka-peer-1:3001/eureka/,http://eureka-peer-2:3002/eureka/,http://eureka-peer-3:3003/eureka/
ports:
- 3200:3200
depends_on:
- eureka-peer-1
- eureka-peer-2
- eureka-peer-3
- account-service
Run with script
// gradle + docker build
$ ./tools/script/compose.sh build
// run docker-compose
$ ./tools/script/compose.sh up
- Check eureka server dash boards
- Check article api (
curl -XGET http://localhost:3200/articles
)