지난번 포스팅에서는 이론에 관한 정리를 했고 이번 포스팅에는 구현에 관해 포스팅 하도록 하겠습니다.
1. Eureka Server
패키지 구조는 아래와 같습니다.
1-1. 의존성 추가
이전에 작성한 cloud config와 연동하기 위해서 추가적으로 의존성을 넣었습니다.
dependencies {
// cloud config
implementation 'org.springframework.boot:spring-boot-starter-amqp'
implementation 'org.springframework.cloud:spring-cloud-bus'
implementation 'org.springframework.security:spring-security-rsa'
implementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp'
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-stream-binder-rabbit'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.amqp:spring-rabbit-test'
// eureka
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
}
1-2. EurekaServerApplication.java
@EnableEurekaServer을 추가합니다.
package com.otrodevym.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
1-3. bootstrap.yml
config의 위치를 지정합니다.
spring:
application:
name: eurekaserver
# profiles:
# active: default
# config:
# import: optional:configserver:http://localhost:8888
cloud:
config:
uri: http://localhost:8888 # Config Server 위치
encrypt:
key: my_config_key
1-4. cloud config -> eurekaserver/eurekaserver.yml
eureka ha(고가용성)을 위해서 peer 구분과 port 구분을 했습니다.
my-config.name: default eureak
server:
port: 8760
spring:
profiles: default
rabbitmq:
host: localhost
port: 5672
username: guest
password: "{cipher}08fde35c4f79cc080854c47871e241fc293fa157b5d9151be5a05fc5aed1d2bb"
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
eureka:
# instance:
# hostname: localhost
server:
wait-time-in-ms-when-sync-empty: 5 # Eureka Server 가 시작되고 Peer nodes 로부터 Instance 들을 가져올 수 없을 때 얼마나 기다릴지 (디폴트 3000ms, 운영 환경에선 삭제 필요)
# 일시적인 네트워크 장애로 인한 서비스 해제 막기 위한 보호모드 해제 (디폴트 60초, 운영에선 삭제 필요)
# 원래는 해당 시간안에 하트비트가 일정 횟수 이상 들어오지 않아야 서비스 해제하는데 false 설정 시 하트비트 들어오지 않으면 바로 서비스 제거
enable-self-preservation: false
client:
register-with-eureka: false # 유레카 서비스에 (자신을) 등록하지 않는다. (클러스터 모드가 아니므로) -> false 로 해도 피어링이 된다.
fetch-registry: false # 레지스트리 정보를 로컬에 캐싱하지 않는다. (클러스터 모드가 아니므로)
# serviceUrl:
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 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
---
my-config.name: peer1 eureak
server:
port: 8762
spring:
profiles: peer1
rabbitmq:
host: localhost
port: 5672
username: guest
password: "{cipher}08fde35c4f79cc080854c47871e241fc293fa157b5d9151be5a05fc5aed1d2bb"
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
eureka:
instance:
hostname: peer1
server:
wait-time-in-ms-when-sync-empty: 5 # Eureka Server 가 시작되고 Peer nodes 로부터 Instance 들을 가져올 수 없을 때 얼마나 기다릴지 (디폴트 3000ms, 운영 환경에선 삭제 필요)
# 일시적인 네트워크 장애로 인한 서비스 해제 막기 위한 보호모드 해제 (디폴트 60초, 운영에선 삭제 필요)
# 원래는 해당 시간안에 하트비트가 일정 횟수 이상 들어오지 않아야 서비스 해제하는데 false 설정 시 하트비트 들어오지 않으면 바로 서비스 제거
enable-self-preservation: false
client:
register-with-eureka: false # 유레카 서비스에 (자신을) 등록하지 않는다. (클러스터 모드가 아니므로) -> false 로 해도 피어링이 된다.
fetch-registry: false # 레지스트리 정보를 로컬에 캐싱하지 않는다. (클러스터 모드가 아니므로)
serviceUrl:
defaultZone: http://peer2:8763/eureka/
---
my-config.name: peer2 eureak
server:
port: 8763
spring:
profiles: peer2
rabbitmq:
host: localhost
port: 5672
username: guest
password: "{cipher}08fde35c4f79cc080854c47871e241fc293fa157b5d9151be5a05fc5aed1d2bb"
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
eureka:
instance:
hostname: peer2
server:
wait-time-in-ms-when-sync-empty: 5 # Eureka Server 가 시작되고 Peer nodes 로부터 Instance 들을 가져올 수 없을 때 얼마나 기다릴지 (디폴트 3000ms, 운영 환경에선 삭제 필요)
# 일시적인 네트워크 장애로 인한 서비스 해제 막기 위한 보호모드 해제 (디폴트 60초, 운영에선 삭제 필요)
# 원래는 해당 시간안에 하트비트가 일정 횟수 이상 들어오지 않아야 서비스 해제하는데 false 설정 시 하트비트 들어오지 않으면 바로 서비스 제거
enable-self-preservation: false
client:
register-with-eureka: false # 유레카 서비스에 (자신을) 등록하지 않는다. (클러스터 모드가 아니므로) -> false 로 해도 피어링이 된다.
fetch-registry: false # 레지스트리 정보를 로컬에 캐싱하지 않는다. (클러스터 모드가 아니므로)
serviceUrl:
defaultZone: http://peer1:8762/eureka/
1-5. 인텔리제이 실행 설정
VM options : -DSpring.profiles.active=default
VM options : -DSpring.profiles.active=peer1
VM options : -DSpring.profiles.active=peer2
2. Eureka Client
Client는 1개로 만들어서 같은 요청과 응답을 하는 하나의 서비스이지만
포트를 다르게 주어 요청과 응답이 서로 다른것을 확인하도로 구성합니다.
패키지 구성은 아래와 같습니다.
2-1. 의존성 추가
eureka Client도 Server와 마찬가지고 cloud config를 사용하기 위해 추가로 의존성을 넣었습니다.
dependencies {
// cloud config
implementation 'org.springframework.security:spring-security-rsa'
implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap'
implementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp'
implementation 'org.springframework.cloud:spring-cloud-starter-config'
implementation 'org.springframework.cloud:spring-cloud-bus'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// eureka client
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
}
2-2. EurekaClientApplication.java
@EnableDiscoveryClient 유레카 클라이언트를 사용하기 위한 설정입니다.
EurekaClient와의 차이는 위쪽에 설명되어 있습니다.
@EnableFeignClients, @LoadBalanced 두 bean은 서비스 검색을 위해 추가했습니다.
유레카 레지스트레에서 등록되어 있는 정보를 가져와 이벤트 서비스의 위치를 알지 못해도 호출이 가능하게 합니다.
- 스프링 디스커버리 클라이언트
스프링 클라우드 표준 방식으로 @EnableDiscoveryClient를 사용하며
@LoadBalanced로 RestTempate를 생성하여 사용합니다. - 넷플릭스 Feign 클라이언트
@EnableFeignClients를 사용
package com.otrodevym.eurekaclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
@LoadBalanced // 스프링 클라우드가 리본이 지원하는 RestTemplate 클래스 생성하도록 지시
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
2-3. bootstrap.yml
cloud config 설정을 잡아줍니다.
spring:
application:
name: eureka-service
profiles:
active: default
# config:
# import: optional:configserver:http://localhost:8888
cloud:
config:
# enabled: false
uri: http://localhost:8888 # Config Server
encrypt:
key: my_config_key
#eureka:
# instance:
# # 랜덤값을 이용하여 instance id를 고유하게 재정의
# instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
2-4. cloud config -> eurekaserver/eurekaserver-service.yml
eureka client도 여러개의 서비스를 분산하여 호출하기 위해 서로 다른 포트로 설정을 합니다.
my-config.name: default eureak
server:
port: 8880
spring:
profiles : default
rabbitmq:
host: localhost
port: 5672
username: guest
password: "{cipher}08fde35c4f79cc080854c47871e241fc293fa157b5d9151be5a05fc5aed1d2bb"
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
eureka: ## 추가
instance:
prefer-ip-address: true ## 서비스 이름 대신 IP 주소 등록
lease-renewal-interval-in-seconds: 3 # 디스커버리한테 3초마다 하트비트 전송 (디폴트 30초)
lease-expiration-duration-in-seconds: 2 # 디스커버리는 서비스 등록 해제 하기 전에 마지막 하트비트에서부터 설정된 시간(second) 기다린 후 서비스 등록 해제 (디폴트 90초)
client:
register-with-eureka: true ## Eureka Server 에 서비스 등록
fetch-registry: true ## 레지스트리 정보를 로컬에 캐싱
service-url:
defaultZone : http://localhost:8760/eureka/
---
my-config.name: service eureak
server:
port: 8881
spring:
profiles : peer1
rabbitmq:
host: localhost
port: 5672
username: guest
password: "{cipher}08fde35c4f79cc080854c47871e241fc293fa157b5d9151be5a05fc5aed1d2bb"
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
eureka: ## 추가
instance:
prefer-ip-address: true ## 서비스 이름 대신 IP 주소 등록
lease-renewal-interval-in-seconds: 3 # 디스커버리한테 3초마다 하트비트 전송 (디폴트 30초)
lease-expiration-duration-in-seconds: 2 # 디스커버리는 서비스 등록 해제 하기 전에 마지막 하트비트에서부터 설정된 시간(second) 기다린 후 서비스 등록 해제 (디폴트 90초)
client:
register-with-eureka: true ## Eureka Server 에 서비스 등록
fetch-registry: true ## 레지스트리 정보를 로컬에 캐싱
service-url:
defaultZone : http://localhost:8762/eureka/,http://localhost:8763/eureka/
---
my-config.name: service eureak
server:
port: 8882
spring:
profiles : peer2
rabbitmq:
host: localhost
port: 5672
username: guest
password: "{cipher}08fde35c4f79cc080854c47871e241fc293fa157b5d9151be5a05fc5aed1d2bb"
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
shutdown:
enabled: true
eureka: ## 추가
instance:
prefer-ip-address: true ## 서비스 이름 대신 IP 주소 등록
lease-renewal-interval-in-seconds: 3 # 디스커버리한테 3초마다 하트비트 전송 (디폴트 30초)
lease-expiration-duration-in-seconds: 2 # 디스커버리는 서비스 등록 해제 하기 전에 마지막 하트비트에서부터 설정된 시간(second) 기다린 후 서비스 등록 해제 (디폴트 90초)
client:
register-with-eureka: true ## Eureka Server 에 서비스 등록
fetch-registry: true ## 레지스트리 정보를 로컬에 캐싱
service-url:
defaultZone : http://localhost:8762/eureka/,http://localhost:8763/eureka/
2-5. EurekaResClientContoller.java
요청에 대한 응답을 하는 컨트롤러입니다.
package com.otrodevym.eurekaclient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletRequest;
@RestController
public class EurekaResClientContoller {
@GetMapping("/res")
public String resClient() {
return "resClienbt1";
}
@GetMapping("/feign/res/{nick}")
public String feignResClient(ServletRequest req, @PathVariable("nick") String nick) {
return "Feign Nick : " + nick + " port is : " + req.getServerPort();
}
}
2-6. EurekaReqClientController.java
요청을 하는 컨트롤러입니다.
package com.otrodevym.eurekaclient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@Slf4j
public class EurekaReqClientController {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
private static final String EUREKA_SERVICE_NAME = "eureka-service";
@Autowired
private EurekaReqFeignClient eurekaReqFeignClient;
@GetMapping("/ping")
public List<ServiceInstance> ping() {
List<ServiceInstance> instances = discoveryClient.getInstances("");
log.info("INSTANCES: count ={}", instances.size());
instances.forEach(it -> log.info("INSTANCES: id={}, port={}", it.getServiceId(), it.getPort()));
return instances;
}
@GetMapping("/req")
public String reqMessage() {
String apiPath = "/res";
ResponseEntity<String> ackMessage = restTemplate.getForEntity("http://" + EUREKA_SERVICE_NAME + apiPath,
String.class);
return "Service-A: inst001 호출" + " > " + ackMessage.getBody();
}
@GetMapping("/feign/{nick}")
public String getNick(@PathVariable("nick") String nick) {
return eurekaReqFeignClient.getYourNick(nick);
}
}
2-7. EurekaReqFeignClient.java
Feign으로 요청하기 위한 인터페이스입니다.
package com.otrodevym.eurekaclient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
//@FeignClient("${eureka.instance.instance-id}")
@FeignClient("${spring.application.name}")
public interface EurekaReqFeignClient {
@GetMapping(value = "/feign/res/{nick}")
String getYourNick(@PathVariable("nick") String nick);
}
2-8. 인텔리 제이 설정
spring.profiles.active를 cloud config에 맞게 설정합니다.
-DSpring.profiles.active=default
-DSpring.profiles.active=peer1
-DSpring.profiles.active=peer2
3. 테스트 및 결과
3-1. docker 실행
rebbitmq를 위한 설정 전파를 위해 실행합니다.
3-2. cloud config 서버 실행
3-3. eureka server 실행
default와 peer1, peer2를 순차적으로 실행하고
cloud conifg 서버를 제대로 읽어왔는지 확인합니다.
3-4. eureka client 실행
eureka client도 순차적으로 실행하고
cloud config 서버 설정을 제대로 가져오는지 확인합니다.
3-5. eureka 기본 설정 : localhost:8760
eureka client 중 localhost:8880이 붙은걸 확인할 수 있습니다.
3-5. eureka peer 설정 : localhost:8762,localhost:8763
아래 결과를 보면 서로 다른 peer의 정보를 참고하고 있는걸 볼 수 있습니다.
'개발(합니다) > Java&Spring' 카테고리의 다른 글
[spring boot 설정하기-20] spring cloud gateway(1) 설정 및 테스트 소스 (0) | 2021.05.23 |
---|---|
[spring boot 설정하기-19] spring properties(+jasypt) 암호화 설정 및 테스트 소스 (0) | 2021.05.20 |
[spring boot 설정하기-17] spring cloud eureka(1) 설정 및 테스트 소스 (0) | 2021.05.18 |
[spring boot 설정하기-16] spring cloud config(3) git 연동 설정 및 테스트 소스 (0) | 2021.05.08 |
[spring boot 설정하기-15] spring cloud config(2) 암호화 설정 및 테스트 소스 (0) | 2021.05.07 |