외부의 다른 서비스들이 마이크로 서비스를 검색하기 위한 개념
Key, Value로 저장된다고 가정할 때
Key | Value |
---|---|
서비스 명들... | 서비스의 위치들... |
각각의 마이크로서비스가 어디에 누가 저장되어있는지, 요청 정보에 따라 서비스의 위치를 알려주는 역할이다.
스프링에서는 Spring Cloud Netflix Eureka가 있다.
클라이언트가 요청한 것을 load balancer나 api gateway로 전달되고, 요청 정보가 서비스 디스커버리에 전달되어 필요한 서비스가 어디있는지 확인한 후, 해당 마이크로 서비스에 전달한다.
spring:
application:
name: xxxservice
서비스 디스커버리는 마이크로서비스가 아닌, 요청이 왔을 경우 어느 마이크로서비스로 가야하는지 알려주는 역할이다. register-with-eureka와 fetch-registry가 디폴트로 true로 설정이 되어있는데, 이걸 false로 선언해야한다. 만약 true로 설정할 경우 이 서비스 디스커버리도 마이크로서비스로 인식이 되기 때문에 굳이 할 이유가 없는 작업이어서 false로 설정해두자.
eureka:
client:
register-with-eureka: false
fetch-registry: false
EnableDiscoveryClient는 유레카 이외에 consul, zookeeper들이 구현되어있으며, spring-cloud-commons에 기반을 두고 있다. EnableEurekaClient는 유레카 관련만 의존해 있으며, spring-cloud-netflix에 기반을 두고 있다. 만약 msa를 Eureka 기반으로 구성한다면 EnableEurekaClient를 사용하면 되고, 그 외에는 EnableDiscoveryClient를 사용하면 된다.(EnableDiscoveryClient가 좀 더 많은 것들을 구현하고 있으니깐)
eureka:
client:
# 유레카 서비스로 등록register-with-eureka: true
# 해당 서비스가 검색 되도록 true 설정fetch-registry: true
# 서비스 디스커버리에 내가 만든 서비스 등록하기service-url:
defaultZone: http://127.0.0.1:8761/eureka
인텔리제이에서 실행하기
이렇게 확인이 가능하다.
인텔리제이 내의 터미널에서 실행하기
마찬가지로 확인하기!
외부 터미널로 실행하기
eureka:
instance:
instance-id: ${spring.cloud.client.hostname}:${spring.application.instance_id:${random.value}}
리버스 프록시는 요청을 대신해서 보내는 것이다. 클라이언트의 요청을 받아 적절한 웹 서버로 요청을 전송한다. 요청을 받은 서버는 응답을 전송할 떄, 다시 이 리버스 프록시에게 전달하여 그 응답을 클라이언트에게 제공한다.
대표적으로는 nginx가 우리가 잘 알고있는 리버스 프록시 서버이다.
RestTemplate restTemplate = new RestTemplate();
restTemplate.getForObject("http://localhost:8080/", User.class, 200);
@FeignClient("stores")// 호출하고 싶은 마이크로 서비스 명 작성public interface StoreClient {
@GetMapping("/stores")
List<Store> getStores();
}
@Configuration
public class FilterConfig {
@Bean
public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.path("/first-service/**")
.filters(f -> f.addRequestHeader("first-request", "first-request-header")
.addResponseHeader("first-response", "first-response-header"))
.uri("http://localhost:8081"))
.route(r -> r.path("/second-service/**")
.filters(f -> f.addRequestHeader("second-request", "second-request-header")
.addResponseHeader("second-response", "second-response-header"))
.uri("http://localhost:8082"))
.build();
}
}
server:
port: 8080
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- AddRequestHeader=first-request, first-request-header-withyml
- AddResponseHeader=first-response, first-response-header-withyml
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- AddRequestHeader=second-request, second-request-header-withyml
- AddResponseHeader=second-response, second-response-header-withyml
개인적으로는 자바코드로 설정하는 것이 훨씬 나아보인다. 컴파일로 내가 잘못 작성했을 경우 에러가 발생하기 때문에!
@Component
@Slf4j
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {
public CustomFilter() {
super(Config.class);
}
public static class Config {
// Put the configuration properties
}
@Override
public GatewayFilter apply(Config config) {
// Custom Prefilterreturn (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("Custom Pre filter : request id -> {}", request.getId());
// Custom Postfilterreturn chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
log.info("Custom Post filter : response statusCode -> {}", response.getStatusCode());
}));
};
}
}
각 라우팅 정보마다 커스터마이징한 필터를 등록해주어야 한다. 앞에서 설정했던 Custom Filter가 그렇다. 하지만 Global Filter의 경우 라우팅 정보들 마다 공통적으로 필요한 처리들을 한번의 설정으로 해결해준다.
둘 다 적용되었을 경우의 순서 : Global Filter PRE -> Custom Filter PRE -> Custom Filter POST -> Global Filter POST
server:
port: 8080
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: apigateway-service
cloud:
gateway:
# Global Filter 설정하기default-filters:
- name: GlobalFilter# 클래스명args:
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- CustomFilter
# - AddRequestHeader=first-request, first-request-header-withyml# - AddResponseHeader=first-response, first-response-header-withyml- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- CustomFilter
# - AddRequestHeader=second-request, second-request-header-withyml# - AddResponseHeader=second-response, second-response-header-withyml
Api Gateway에서는 로깅을 위한 필터도 존재하는데, 여기서 로깅 필터도 Custom Filter라서 마이크로 서비스 마다 등록을 해주어야 한다. 강의 내용 처럼 second-service에 등록하자.
이 Logging Filter를 등록하게 되면 요청에 대한 흐름은 다음과 같다.
기본적으로 라운드 로빈 방식으로 각 마이크로서비스를 한번씩 호출한다.
API Gateway에서uri에 lb://서비스명 이라고 설정해두었는데, 이 때 랜덤포트로 설정한 경우는 서비스 명으로 게이트웨이에서 인스턴스를 찾아 각 서비스를 한번씩 호출한다.
설정에 따라 랜덤하게 서버 인스턴스를 호출할 수 있다.
출처