SpringBoot 를 이용한 Spring Cloud Eureka & Zuul
Service Register & Service Discovery
대량의 Traffic 이 발생하는 MSA와 같은 분산 환경은 서비스 간의 원격 호출로 서버의 오토 스케일링등에 의해서 동적으로 생성되거나 변경되는 케이스들이 발생한다. DNS + Load Balancer 기반으로는 한계가 있고 이를 해결하기 위해 여러 소프트웨어적인 솔루션들 역시 이미 나와 있다.
https://www.consul.io/ 나 https://zookeeper.apache.org/ 도 유명하고, 이 챕터에서 소개하려고 하는 Eureka 와 Zuul 역시 Netflix의 대규모 분산 시스템을 구축 하기 위한 고가용성 서버의 구축 생태계중 하나로 Spring Cloud Project 의 공식 starter로 제공되고 있다.
https://www.consul.io/ 나 https://zookeeper.apache.org/ 도 유명하고, 이 챕터에서 소개하려고 하는 Eureka 와 Zuul 역시 Netflix의 대규모 분산 시스템을 구축 하기 위한 고가용성 서버의 구축 생태계중 하나로 Spring Cloud Project 의 공식 starter로 제공되고 있다.
Spring Cloud Eureka는 넷플릭스에서 만든 서비스로, 서버 들을 중앙 레지스트리에 등록하고 장애가 발생할 때 자동으로 제외, 새로운 서비스가 발견되면 자동으로 실시간으로 반영하는 도구이다.
Eureka Server 와 Eureka Client 구조를 통해 Service Registry & Service Discovery 를 가능케 한다.
Eureka Server 와 Eureka Client 구조를 통해 Service Registry & Service Discovery 를 가능케 한다.
클라이언트가 Eureka 서버에 등록되면 호스트, 포트, 상태 표시 URL, 기타 세부 정보와 같은 자체에 대한 메타 데이터를 제공 (Registry) 하게 된다. Eureka Server에서 클라이언트 측 에서 받은 정보를 Service Discovery를 통해 다른 클라이언트에게 전파하는 형태로 서비스가 된다.
이는 서비스는 호스트 이름과 포트를 하드 코딩하지 않고도 서로 검색하고 통신함으로써 서비스의 투입과 제거가 빠르게 이루어질 수 있다는 의미이다.
Eureka 서버는 서비스에 속한 각 인스턴스로부터 Heartbeat 메시지를 수신한다. (클라이언트의 연결 여부를 알기 위해서 Heartbeat 신호를 서버 레지스트리에 보내야 함)
Heartbeat가 시간 설정(기본 30초) 에 초과 된다거나 누락된다면 인스턴스는 레지스트리에서 제거하게 된다. 아래의 그림을 참고하자.
이는 서비스는 호스트 이름과 포트를 하드 코딩하지 않고도 서로 검색하고 통신함으로써 서비스의 투입과 제거가 빠르게 이루어질 수 있다는 의미이다.
Eureka 서버는 서비스에 속한 각 인스턴스로부터 Heartbeat 메시지를 수신한다. (클라이언트의 연결 여부를 알기 위해서 Heartbeat 신호를 서버 레지스트리에 보내야 함)
Heartbeat가 시간 설정(기본 30초) 에 초과 된다거나 누락된다면 인스턴스는 레지스트리에서 제거하게 된다. 아래의 그림을 참고하자.
서비스 레지스트리로 사용하기 위해 유레카 서버를 구현하는 것은 다음과 같다
- spring-cloud-starter-eureka-server dependency 추가
- @EnableEurekaServer 어노테이션 추가
- application.yml 혹은 application.properties 에 레지스트리 등록 옵션 설정
실제 서버가 동작할 때의 기능을 도식화 해놓으면 아래와 같다.
클라이언트 측 그룹들이 존재한다면 해당 정보를 등록하고 전파하는 기능은 Eureka Server 에서 담당을 하게 된다.
Eureka Server 프로젝트 구성하기
다운로드 받은 파일을 압축을 풀고 IntelliJ로 open 한다.
먼저 유레카 서비스 레지스트리를 만든다.
org.springframework.cloud의 의 spring-cloud-starter-netflix-eureka-server
를 pom.xml에 추가하고 application에서 @EnableEurekaServer를 사용하여 다른 애플리케이션이 등록할 수 있는 레지스트리를 작성할 수 있다.
먼저 pom.xml 을 보자
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.springcloud</groupId>
<artifactId>eureka-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>eureka-service</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RC2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
다음은 Spring Boot Application에
@EnableEurekaServer
어노테이션을 등록하여 서비스 레지스트리가 가능하게 한다.package com.example.springcloud.eurekaservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServiceApplication.class, args);
}
}
다음은 resources 하위에 application.properties 파일을 만든다.
spring.application.name=eureka-service
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
#eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF
#eureka.instance.hostname=localhost
application.yml 로 작성할 경우 아래와 같다.
spring:
application:
name: eureka-service
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
기본적값으로 레지스트리도 자체 등록을 시도하므로 관련 옵션은 비활성화 했다. 또한 로깅 레벨도 비활성화 해두었다.
EurekaServiceApplication 을 스타트 한다.
EurekaServiceApplication 을 스타트 한다.
http://localhost:8761 에 접근하면 정상적으로 Eureka server 가 동작하는 것을 확인할 수 있다.
이제 서비스 레지스트리를 시작 했으므로 레지스트리에 자신을 등록하고 Spring Cloud DiscoveryClient 추상화를 통해 자체 호스트 및 포트에 대한 레지스트리를 조사하는 클라이언트를 올려보자.
@EnableDiscoveryClient
는 Netflix Eureka DiscoveryClient 구현을 활성화 시킨다.Hashicorp 's Consul(https://www.consul.io) 이나 Apache Zookeeper(https://zookeeper.apache.org)와 같은 비슷한 역할을 하는 다른 구현체도 있으니 참고하기 바란다.
Eureka Client (DB 연동 API) 프로젝트 구성하기
여기서 만들 프로젝트는 Stock Option을 사용자에게 부여하는 프로젝트로, 화면에서 (화면은 만들지 않을 예정이다) 사용자 의 userId와 Stock의 갯수를 API로 전송하면 DB 에 저장하는 (CRUD) 간단한 어플리케이션이다.
전반적인 아키텍쳐는 아래와 같다.
전반적인 아키텍쳐는 아래와 같다.
stock-db-server 는 MySQL 과 연동하여 Stock option을 부여 받은 사용자와 주식 수 등을 저장하는 어플리케이션으로Web, JPA, MySQL, Eureka Discovery 로 구성할 예정이다. http://start.spring.io 에서 해당 사항을 선택한 후 다운받고 압축을 푼다.
두번째 stock-service는 앞에서 전달받은 REST API를 DB Service에 전달하는 하거나 다른 Micro Service들을 호출하여 데이터를 Aggregation 하는 역할을 한다고 가정하자. Web과 Eureka Discovery 번들로 구성한다.
먼저 stock-db-service 를 오픈 한다.
com.example.springcloud 패키지 밑에 StockDbServiceApplication 이 위치한다.
com.example.springcloud 패키지 밑에 StockDbServiceApplication 이 위치한다.
package com.example.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class StockDbServiceApplication {
public static void main(String[] args) {
SpringApplication.run(StockDbServiceApplication.class, args);
}
}
resources 하위에 application.properties 는 아래와 같이 작성하도록 한다.
# ===============================
# = DATA SOURCE
# ===============================
# Set here configurations for the database connection
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/test?useSSL=false
# Username and password
spring.datasource.username = root
spring.datasource.password = pass
# Keep the connection alive if idle for a long time (needed in production)
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1
# ===============================
# = JPA / HIBERNATE
# ===============================
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update): with "update" the database
# schema will be automatically updated accordingly to java entities found in the project
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# Allows Hibernate to generate SQL optimized for a particular DBMS
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
db 스키마는 test 를 이용하며 username 과 password 는 각자의 상황에 맞게 수정한다.
이제 com.example.springcloud 패키지 밑에 stockdb 패키지를 만들고 StockDbController 를 작성한다.
package com.example.springcloud.stockdb;
import com.example.springcloud.stockdb.entity.StockUser;
import com.example.springcloud.stockdb.repository.StockUserRepository;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/v1")
public class StockDbController {
private StockUserRepository stockUserRepository;
public StockDbController(StockUserRepository stockUserRepository) {
this.stockUserRepository = stockUserRepository;
}
@GetMapping("/user")
public List<StockUser> stockUserList(@RequestParam String userId) {
return stockUserRepository.findByUserId(userId);
}
@PostMapping("/user")
public StockUser addStockUser(@RequestBody StockUser stockUser) {
stockUser.setCreatedAt(new Date());
StockUser stockUserEntity = stockUserRepository.save(stockUser);
return stockUserEntity;
}
@GetMapping("/user/{id}")
public Optional<StockUser> stockUser(@PathVariable("id") Integer id) {
return stockUserRepository.findById(id);
}
@DeleteMapping("/user/{id}")
public boolean deleteStockUser(@PathVariable("id") Integer id) {
Optional<StockUser> user = stockUserRepository.findById(id);
if (user.isPresent()) {
stockUserRepository.deleteById(id);
return true;
} else {
return false;
}
}
}
stockdb 패키지 밑에 entity 패키지와 repository 패키지를 생성한다.
먼저 Stock User entity 의 테이블을 생성하고 entity 클래스를 작성해보자.
먼저 Stock User entity 의 테이블을 생성하고 entity 클래스를 작성해보자.
CREATE TABLE `test`.`stock_user` (
`id` INT NOT NULL AUTO_INCREMENT,
`user_id` VARCHAR(45) NOT NULL COMMENT '사용자 아이디',
`stock` INT NOT NULL COMMENT '주식수 ',
`created_at` DATETIME NULL COMMENT '생성일 ',
PRIMARY KEY (`id`));
Dummy data Insert
INSERT INTO `test`.`stock_user` (`id`, `user_id`, `stock`, `created_at`) VALUES (1, 'edell', 100, now());
INSERT INTO `test`.`stock_user` (`id`, `user_id`, `stock`, `created_at`) VALUES (2, 'quick', 500, now());
INSERT INTO `test`.`stock_user` (`id`, `user_id`, `stock`, `created_at`) VALUES (3, 'kakao', 18000, now());
Entity 클래스는 아래와 같다.
package com.example.springcloud.stockdb.entity;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.util.Date;
@Entity
@Table(name="stock_user", catalog = "test")
public class StockUser {
public StockUser() {
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "user_id")
private String userId;
@Column(name = "stock")
private Integer stock;
@Column(name = "created_at")
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
// setter, getter 생성
}
ID GeneratedValue 가 IDENTITY인 이유는 Spring boot 2.0 이후 부터 id 생성 전략을 그대로 따라갈지 말지를 결정하는 값이 false 에서 true 로 변경되고, Hibernate 5.0부터 MySQL의 AUTO는 IDENTITY가 아닌 테이블 기본 시퀀스로 선택되므로 AUTO로 지정할 경우 아래와 같이 (미리 강제로 데이터를 넣었으므로) key 에러를 만나게 될것이다.
Hibernate: select stockuser0_.id as id1_0_, stockuser0_.created_at as created_2_0_, stockuser0_.stock as stock3_0_, stockuser0_.user_id as user_id4_0_ from test.stock_user stockuser0_ where stockuser0_.user_id=?
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into test.stock_user (created_at, stock, user_id, id) values (?, ?, ?, ?)
여기서 sequence 값이 기본 1이므로 insert 구문으로 넣은 id 값과 충돌이 나므로 IDENTITY 로 변경하였다.
마지막으로 Repository 를 작성한다. (비교적 간단한 어플리케이션이므로 service layer는 따로 두지 않는다)
package com.example.springcloud.stockdb.repository;
import com.example.springcloud.stockdb.entity.StockUser;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface StockUserRepository extends CrudRepository<StockUser, Integer> {
List<StockUser> findByUserId(String userId);
}
마지막으로 StockDbServiceApplication 클래스에 Jpa repository 베이스 패키지를 지정해준다.
package com.example.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories(basePackages = "com.example.springcloud.stockdb")
@SpringBootApplication
public class StockDbServiceApplication {
public static void main(String[] args) {
SpringApplication.run(StockDbServiceApplication.class, args);
}
}
application.yml은 DB properties와 겹치지 않게 따로 구성한다.
spring:
application:
name: stock-db-service
server:
port: 8083
서버를 기동하고 REST API 테스트 툴 (여기서는 IntelliJ의 내장 툴인 HTTP Client 를 사용했으나 Post Man 등의 어플리케이션을 사용해도 무방하다) 을 이용하여 각각의 API 들을 호출해보자.
POST 방식으로 RequestBody 에 json 으로 userId, stock 을 전송하면 저장되는 것을 확인할 수 있을 것이다.
{ "userId" : "testUser", "stock": 50}
마찬가지로 상세 조회 및 삭제도 잘 동작하는 것을 확인할 수 있다.
삭제의 경우 ID값이 존재할 경우 삭제 후 true를, 없으면 false를 리턴한다.
이제 API를 통해 DB를 연동하는 작업은 끝났고, 실제 stock-db-service를 호출하는 rest client 를 만들어보자. (위의 아키텍쳐 그림상으로 8082 로 서비스 하는 stock-service 어플리케이션)
Stock-Service 를 이용한 Eureka Client 작성
Eureka Client 영역은 Spring Boot의 실행 Application 에 @EnableEurekaClient 어노테이션을 이용하는 점이 다르다.
아래의 소스를 보자.
아래의 소스를 보자.
package com.example.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class StockServiceApplication {
public static void main(String[] args) {
SpringApplication.run(StockServiceApplication.class, args);
}
}
application 속성은 resources 하위에 application.yml 로 작성하였다.
spring:
application:
name: stock-service
server:
port: 8082
eureka:
instance:
hostname: localhost
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:8761/eureka/
stock-service 는 Rest Client 를 이용하여 stock-db-service 를 호출하게 될 것이다.
controller 를 작성해보자.
package com.example.springcloud.stock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/stock")
public class StockController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user")
public List<Stock> getUserStock(@RequestParam("userId") String userId) {
String url = "http://localhost:8083/api/v1/user?userId=" + userId;
ResponseEntity<List<Stock>> stockResponse =
restTemplate.exchange(url,
HttpMethod.GET, null, new ParameterizedTypeReference<List<Stock>>() {
});
return stockResponse.getBody();
}
}
config 패키지 하위에 RestTemplate Bean을 작성한다.
package com.example.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class Config {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
화면에 필드를 리턴할 DTO를 작성한다.
package com.example.springcloud.stock;
import java.util.Date;
public class Stock {
private Integer Id;
private String userId;
private Integer stock;
private Date createdAt;
// Constructor & Setter, Getter
작성이 마무리 되었으면 eureka-service 를 기동하여 http://localhost:8761 에 접근해본다.
현재는 등록된 서버가 없을 것이다.
stock-db-service 의 application.yml 을 아래와 같이 작성하고 StockDbServiceApplication에
@EnableEurekaClient
어노테이션을 붙여본 후 각각의 서버들을 기동시켜 보자.spring:
application:
name: stock-db-service
server:
port: 8083
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:8761/eureka/
stock-service
어플리케이션과 stock-db-service
어플리케이션이 모두 eureka client 로 구성되었고 직접 연결하는 서버의 주소도 Eureka Server 이므로 다시 Eureka 서버에 접근해보면 아래와 같이 서버가 등록된 것을 확인할 수 있을 것이다.
현재 Eureka에 등록되어 있는 서버로 둘다 표시가 되면 정상적으로 동작하게 된것이다.
이제 stock-service 의 /stock/user API를 호출해보자.
이제 IP와 Port 로 할당된 서비스가 아닌 Application의 네임으로 접근이 가능하다.
stock-service 의 controller 를 아래와 같이 수정한다.
package com.example.springcloud.stock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/stock")
public class StockController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user")
public List<Stock> getUserStock(@RequestParam("userId") String userId) {
String url = "http://stock-db-service/api/v1/user?userId=" + userId;
ResponseEntity<List<Stock>> stockResponse =
restTemplate.exchange(url,
HttpMethod.GET, null, new ParameterizedTypeReference<List<Stock>>() {
});
return stockResponse.getBody();
}
}
IP와 Port (8083) 부분은 stock-db-servie 로 대체 하고 Config 클래스에
이 어노테이션으로 인해 eureka-client 로 등록된 모든 클라이언트에 대해 Load Balancing 이 가능 하다.
개발자는 어플리케이션 명만 알고 있으면 된다.
고전적인 RestTemplate 에 Client Discovery 와 Load Balance 가 결합된 것으로 보면 될거 같다.
@LoadBalanced
어노테이션을 추가 한다.이 어노테이션으로 인해 eureka-client 로 등록된 모든 클라이언트에 대해 Load Balancing 이 가능 하다.
개발자는 어플리케이션 명만 알고 있으면 된다.
고전적인 RestTemplate 에 Client Discovery 와 Load Balance 가 결합된 것으로 보면 될거 같다.
package com.example.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class Config {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
서버를 재기동하고 stock-service 를 호출하면 변경된 stock-db-service 라는 어플리케이션 명으로 통신을 하는 것을 확인할 수 있다.
만약 다른 IP 혹은 다른 Port 로 몇개의 stock-db-service 를 띄워서 서비스 한다고 가정해보자. 실제 서비스 환경에서는 서버가 10 대 이상으로 운용되는 경우가 많을 것이다. 이런 경우에도 Eureka Client 설정만 제대로 한다면 Eureka Server 에 등록이 될 것이고 stock-service 에 의해서 로드밸런싱이 일어나며 자동으로 새 어플리케이션에도 호출이 되는 것을 확인할 수 있다.
새 Application (DB 용) 을 복사한 후 간단히 설정을 바꿔주자 . 8084 포트로 stock-db-service 명을 하나 더 붙여보았다.
spring:
application:
name: stock-db-service
server:
port: 8084
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:8761/eureka/
eureka-service 에는 application.yml 에 waitTimeInMsWhenSyncEmpty 항목을 아래와 같이 추가한다.
spring:
application:
name: eureka-service
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
waitTimeInMsWhenSyncEmpty: 0 // 레지스트리 정보를 가져 오지 못할 경우 바로 재시도
stock-db-service 라는 이름으로 2개의 서비스가 붙은것을 확인할 수 있고 API를 호출해보면 양쪽 서버 로그가 번갈아 가면서 올라가는 것을 확인할 수 있다.
자동으로 로드밸런싱이 되는 것이다!
자동으로 로드밸런싱이 되는 것이다!
Tip
Instances currently registered with Eureka 란에 보면 status 목록이 링크가 되어있는 것을 확인할 수 있다.
바로 Actuator 의 info 엔드포인트로 걸리는데, 이 부분에 서버의 정보를 남겨 확인하고자 한다면 pom.xml 에 actuator 디펜던시를 추가한 후 아래와 같이 하면 된다.
바로 Actuator 의 info 엔드포인트로 걸리는데, 이 부분에 서버의 정보를 남겨 확인하고자 한다면 pom.xml 에 actuator 디펜던시를 추가한 후 아래와 같이 하면 된다.
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
그리고 InfoContributor 인터페이스를 구현한다.
package com.example.springcloud.stockdb.meta;
import org.apache.commons.lang.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class MetaDataContributor implements InfoContributor {
@Autowired
private ApplicationContext ctx;
@Override
public void contribute(Info.Builder builder) {
Map<String, Object> collects = new HashMap<>();
collects.put("bean-definition-count", ctx.getBeanDefinitionCount());
collects.put("startup-date", DateFormatUtils.format(ctx.getStartupDate(), "yyyy-MM-dd HH:mm:ss"));
builder.withDetail("context", collects);
}
}
마지막으로 application.yml 에 아래의 메타 정보 옵션들을 추가해주면 정상적으로 레지스트리에 올라온 서버측의 정보들이 출력되는 것을 확인할 수 있다.
# Spring Boot Actuator Info Endpoint setup
info:
app: name: Stock-db-service
description: Spring Cloud Eureka DB Service
build:
groupId: @project.groupId@
artifact: @project.artifactId@
name: @project.name@
version: @project.version@
env:
java: vendor: ${java.specification.vendor}
vm-name: ${java.vm.name}
runtime-version: ${java.runtime.version}
= FIN =
읔 정리하시는거 힘드셨겠다.~~ 잘보겠습니다. ^^b
답글삭제