Spring Eureka가 해주는 역할 ⇒ 포트 여러개를 하나에 등록 시켜서 사용
외부에서 마이크로서비스를 검색해서 사용하기 위해 사용
key // value로 지정
client ↔ loadbalancer ↔ discoveryservice ↔ msa1, msa2, ms3
@SpringBootApplication
@EnableEurekaServer //유레케 서버로서 지정하기 위해 설정
public class Msa1Application {
public static void main(String[] args) {
SpringApplication.run(Msa1Application.class, args);
}
}
server:
port: 8761
spring:
application:
name: discoveryservice //서비스의 고유한 아이디를 지정
eureka:
client:
register-with-eureka: false //유레카에 등록하는 여부(그런데 이건 서비스로 사용하는것임으로 true로 놓게되면 자기 자신을 서비스로 등록한다는 뜻)
fetch-registry: false //서버로부터 무언가를 주고 받을것이 아니기 때문에, 서버로서만 상용함으로 false
<properties>
<java.version>11</java.version>
<spring-cloud.version>2020.0.4</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-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
package com.example.userservice1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class UserService1Application {
public static void main(String[] args) {
SpringApplication.run(UserService1Application.class, args);
}
}
server:
port: 9001
spring:
application:
name: user-service
eureka:
client:
register-with-eureka: true # eurkea서버로부터 인스턴스들의 정보를 주기적으로 가져올지 설정하는 속성, true시 받겠다는 뜻
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka #disoveryservice 서버 지정
작동중인걸 확인할 수 있다.
새로운 서비스 등록함 + 버튼 위치의 3번째 버튼 클릭해서 같은 서비스를 카피한 후 그것을 이름 만 바꿔서 저장
그런데 포트번호 중복임으로
environtment 눌러서
VM에 다음과 같이 포트번호 추가(-D는 추가 명령 내릴 때 사용)
mvn spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=9003'
target파일안의 jar파일을 실행함으로서 또다른 포트로 실행
java -jar -Dserver.port=9004 ./target/user-service1-0.0.1-SNAPSHOT.jar
server:
port: 0 #포트번호를 랜덤하게 사용한다 라는 뜻
spring:
application:
name: user-service
eureka:
client:
register-with-eureka: true # eurkea????? ?????? ??? ????? ???? ???? ??, true? ???? ?
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka #disoveryservice ?? ??
그런데 이럴거면 위에서 설정한 applciation 에서 2번째는 포트번호가 설정되어있음으로 이거 삭제하고 해야됨
terminal에서도 mvn spring-boot:run이라고만 처도 알아서 실행됨
그런데
확인해보면 한개만 뜸 왜?
:0은 포트번호가 곳인데, 이건 yml 파일에 써있는 포트번호를 나타냄 따라서 랜덤하게 생성되면 0이 뜨지 않음 그래서 이걸 보여주기 위해
yml 파일을 수정하겠음
server:
port: 0 #포트번호를 랜덤하게 사용한다 라는 뜻
spring:
application:
name: user-service
eureka:
instance:
유레카에 출력하기위해 아래와 같이 설정
instance-id: ${spring.cloud.client.hostname}:${spring.application.instance-id:${random.value}}
client:
register-with-eureka: true # eurkea????? ?????? ??? ????? ???? ???? ??, true? ???? ?
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka #disoveryservice ?? ??
이렇게 호스트 네임과 아이디, 랜덤아이디가 출력된것을 볼 수 있다.
Proxy 역할을 함
단일 진입로를 통해 일관적으로 작업을 처리하고자 필요성이 증가하게됨
따라서 api gateway만 상대하면 됨
할 수 있는 것들
spring cloude 에서의 msa간 통신
RestTemplate
RestTemplate resTemplate = new RestTemplate();
restTemplate.getForObject("http://localhost:8080/", User.class, 200);
Feign Cleint → 마이크로 서비스 이름으로 호출이 가능함(springcloud가 사용하는 것)
@FeignClient("stores")
publci interface StoreClient{
@RequestMapping(method= RequestMEthod.GET ,value="/stores")
Lists<Store>getSTroe();
Ribbon : 스프링클라우드 가 로드밸런서로 사용하는 서비스→ 그러나 비동기화 방식이 안맞음 따라서 잘 안사용함(client side loadbalancer)
Netflix Zuul: api게이트웨이 역할을 함
Spring gateay → 이것을 씀
server:
port: 8000
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/** #조건절로 이 주소로 접속하면 8081로 보낸다
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/** #조건절로 이 주소로 접속하면 8082로 보낸다
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.11</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>apigateway-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>apigateway-service</name>
<description>apigateway-service</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>2020.0.4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
비동기 방식으로 netty서버가 실행됨을 알 수 있다.
first service, seconde service 셋팅 (second는 8082, second로만 바꾸면된다)
@RestController
@RequestMapping("/first-service") -> 게이트웨이 yml파일에 적은 path를 써줘야함
public class Controller {
@GetMapping("/welcome")
public String welcome()
{
return "Welcome first service";
}
}
server:
port: 8081
spring:
application:
name: my-first-service
eureka:
client:
fetch-registry: false
register-with-eureka: false
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>first-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>first-service</name>
<description>first-service</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>2020.0.4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Spring Cloud Gateway - Filter 적용
Gateway Handler Mapping(요정 정보들어옴) → predicate(조건분기) → prefilter(작업이 일어나기전 사전필터), postFilter(처리 끝난후 호출되는 필터) → Gateway Handler Mapping으로 반환
property, javacode로 작업 설정 할 수 있음
람다 → 익명 클래스 → 인스턴스를 바로 생성하고 소멸 시킬 수 있음
람다식(익명의 클래스, 함수를 호출해서 객체를 생성하고 소멸시킴)
메소드체이닝 → 비슷한 메소드를 연속으로 호출
yml 파일 설정을 javacode로
@Configuration
public class FilterConfig {
//yml 파일에 있는 설정을 자바 코드로 바꿈
@Bean
public RouteLocator gatewayRoutes(RouteLocatorBuilder builder){
return builder.routes() //라우터 등록
.route(r -> r.path("/first-service/**") //r이라는 값이 전달되면 path를 확인하고
//filter를 적용시킴
.filters(f -> f.addRequestHeader("first-request","first-request-header") //request 헤더가 삽입됨
.addResponseHeader("first-response","first-response-header")) //response헤더가 삽입됨
.uri("http://localhost:8081")) //이 uri로 전달된다
.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();
}
}
first, second, service(localhost:8000/first-service/message)
@GetMapping("/message")
public String message(@RequestHeader("first-request") String header){
log.info(header);
return "Hello first service";
}
위에 javcode로 설정했던 방법을 yml 파일로 설정할 수 있다.
이때 config 파일은 @Configuraiton, @Bean은 주석처리해야함
server:
port: 8000
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-reqeust-header2
- AddResponseHeader=first-request, first-response-header2
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:<-추가된 부분!!!!!!!!!!!!!!!!!!!!
- AddRequestHeader=second-request, second-reqeust-header2
- AddResponseHeader=second-request, second-response-header2
package com.example.apigatewayservice.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
//reactive -> webflux를 사용하는 클래스에 포함됨
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> { // AbstractGatewayFilterFactory<데이터타입>
//여기서 데이터 타입은 우리가 작성한 CustomFilter이고
public CustomFilter(){ //디폴트 생성자에, Config 상위 클래스만 호출한다.
super(Config.class);
}
@Override //우리가 구현해야할 Config를 오버라이드 한다.
public GatewayFilter apply(Config config) {
return (exchange, chain)-> { //람다에서 여기부터는 GatewayFilter의 반환값을 작성한다
ServerHttpRequest request = exchange.getRequest(); //비동기화된 webflux을 사용할 경우 동기화된 톰캣과 다르게
ServerHttpResponse response = exchange.getResponse(); //serveletrequest가 아닌 severrequest를 받아야 한다
//pre필터 적용
log.info("Custom PRE filter: request id-> {}", request.getId());
//post 필터 적용
return chain.filter(exchange).then(Mono.fromRunnable(()-> { //Mono는 webflux처럼 비동기 방식에서 단일값을 전달한다고 지정해주기 위해 작성하는 것
log.info("Custom POST filter: response code -> {}", response.getStatusCode()); //단일값 하나 전달
}));
};
}
public static class Config {
//
}
}
server:
port: 8000
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-reqeust-header2
# - AddResponseHeader=first-request, first-response-header2
- CustomFilter #우리가 작성한 필터를 여기에 등록
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
# - AddRequestHeader=second-request, second-reqeust-header2
# - AddResponseHeader=second-request, second-response-header2
- CustomFilter #우리가 작성한 필터를 여기에 등록
firstservice, secondservice
@GetMapping("/check")
public String check(){
return "HI This is first customservice";
}
커스텀 필터는 개별적인 라우팅 정보마다 다 등록 해놓아야 함.(id별 등록), 공통적인 부분은 글로벌 필터로 적용 시키면 편하다.
private boolean preLogger처럼 불리언 타입일 경우
isPreLogger() 처럼 is를 앞에 붙여서 메소드 호출 한다 .
server:
port: 8000
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters:
- name: GlobalFilter #우리가 작성한 클래스 이름
args:
baseMessage: Spring Cloud Gateway Global Filter #우리가 클래스 파일에 적은 baseMessaege파라미터값에 들어갈 말을 적음
preLogger: true
postLogger: true
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
# - AddRequestHeader=first-request, first-reqeust-header2
# - AddResponseHeader=first-request, first-response-header2
- CustomFilter #우리가 작성한 필터를 여기에 등록
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
# - AddRequestHeader=second-request, second-reqeust-header2
# - AddResponseHeader=second-request, second-response-header2
- CustomFilter #우리가 작성한 필터를 여기에 등록
package com.example.apigatewayservice.filter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
//reactive -> webflux를 사용하는 클래스에 포함됨
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
public GlobalFilter(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain)-> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//pre필터 적용
log.info("Custom PRE filter baseMessage: request id-> {}", config.getBaseMessage());
if (config.isPreLogger()){
log.info("Global Filter start: request id -> {}", request.getId());
}
//post 필터 적용
return chain.filter(exchange).then(Mono.fromRunnable(()-> {
if (config.isPostLogger()){
log.info("Global Filter start: request id -> {}", response.getStatusCode());
}
}));
};
}
@Data
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
포스트맨으로 보내게 되면 apigateway 인텔리제이 터미널에 다음과 같이 로그가 찍힌다. 보면 글로벌 필터가 먼저 적용되고 그다음 커스텀 필터 그리고 종료 때는 커스텀 필터 먼저 마지막에 글로벌 필터가 적용됨을 알 수 있다. 즉
즉 여기까지의 내용 정리
요청이 들어오면
gatway client → gateway handler → globla filter → customfilter → logginfilter → proixed service(우리가 구현한 firts, second service같은것) 응답은 이거 반대 순서로 감
람다식
package com.example.apigatewayservice.filter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
//reactive -> webflux를 사용하는 클래스에 포함됨
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
public LoggingFilter(){
super(Config.class);
}
//구현시켜야할 객체 : apply
//반환타입 : gatewayFilter
@Override
public GatewayFilter apply(Config config) {
// return (exchange, chain)-> {
// ServerHttpRequest request = exchange.getRequest();
// ServerHttpResponse response = exchange.getResponse();
//
// //pre필터 적용
// log.info("Custom PRE filter baseMessage: request id-> {}", config.getBaseMessage());
//
// if (config.isPreLogger()){
// log.info("Global Filter start: request id -> {}", request.getId());
// }
//
//
// //post 필터 적용
// return chain.filter(exchange).then(Mono.fromRunnable(()-> {
// if (config.isPostLogger()){
// log.info("Global Filter start: request id -> {}", response.getStatusCode());
// }
// }));
// };
GatewayFilter filter = new OrderedGatewayFilter((exchange, chain)->{
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//pre필터 적용
log.info("Logging filter baseMessage: request id-> {}", config.getBaseMessage());
if (config.isPreLogger()){
log.info("Logging PRE Filter start: request id -> {}", request.getId());
}
//post 필터 적용
return chain.filter(exchange).then(Mono.fromRunnable(()-> {
if (config.isPostLogger()){
log.info("Logging POST Filter start: response code -> {}", response.getStatusCode());
}
}));
}, Ordered.HIGHEST_PRECEDENCE);//gatewayFilter의 두번재 인자값은 필터의 우선순위를 지정하는 인자값을 넣는다.
return filter;
}
//configuration정보는 내가 자유롭게 지정할 수 있다.
//내가 여기서 메소드를 지정하면 필터에서 해당 메소드를 지정할 수 있다.
@Data
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
//private String sayHello;
}
}
특히 gatewayfilter는 객체 생성시
oredredGAtewayFilter를 사용해야 한다.
이건 gatewayfilter를 구현하는 자식 클래스 역할을 한다.
public class OrderedGatewayFilter implements GatewayFilter, Ordered {
private final GatewayFilter delegate;
private final int order;
//우리가 구현함
public OrderedGatewayFilter(GatewayFilter delegate, int order) {
this.delegate = delegate;
this.order = order;
}
public GatewayFilter getDelegate() {
return this.delegate;
}
//필터가 해야할 역할을 재정의 할 수 있다.
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
public int getOrder() {
return this.order;
}
public String toString() {
return "[" + this.delegate + ", order = " + this.order + "]";
}
}
Webflux에서는 serveltrequest, response를 더이상 지원하지 않는다.
여기서는
ServerWebExchange를 사용한다 (serverRequest, serverResponse 사용) 이걸로 얻어옴
GateWayFilterChain : pre, post체인 필터를 연결 시켜줌
server:
port: 8000
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters:
- name: GlobalFilter #우리가 작성한 클래스 이름
args:
baseMessage: Spring Cloud Gateway Global Filter #우리가 클래스 파일에 적은 baseMessaege파라미터값에 들어갈 말을 적음
preLogger: true
postLogger: true
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
# - AddRequestHeader=first-request, first-reqeust-header2
# - AddResponseHeader=first-request, first-response-header2
- CustomFilter #우리가 작성한 필터를 여기에 등록
# 비교를 위해 pre필터는 Logging 필터를 넣지 않는다
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
# - AddRequestHeader=second-request, second-reqeust-header2
# - AddResponseHeader=second-request, second-response-header2
- name: CustomFilter
- name: LoggingFilter
args:
baseMessage: Logging, filter.
preLogger: true
postLogger: true
로깅필터가 적용된 second service를 호출하면 나오는 로거이다.
보면 우리가 기대했던 순서와 다르게 나온다. 이유는?
orderedGateWayFilter를 설정할 시 LogginFilter를 Ordered.HIGHEST_PRECDENCE로 설정했기 때문에 가장 먼저 실행이 되었기 때문이다.
만일 Ordered.LOWEST_PRECEDENCE로 설정하면 우리가 기대했던 순서로 작동된다.
//post 필터 적용
return chain.filter(exchange).then(Mono.fromRunnable(()-> {
if (config.isPostLogger()){
log.info("Logging POST Filter start: response code -> {}", response.getStatusCode());
}
}));
}, Ordered.HIGHEST_PRECEDENCE);//gatewayFilter의 두번재 인자값은 필터의 우선순위를 지정하는 인자값을 넣는다.
return filter;
클라이언트 호출 → api gateway → eureka server → 서버 확인 → api gateway → 해당서버로 이동
server:
port: 8000
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters:
- name: GlobalFilter
args:
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
routes:
- id: first-service
uri: lb://MY-FIRST-SERVICE //lb//등록한 네임으로 uri 지정
predicates:
- Path=/first-service/**
filters:
# - AddRequestHeader=first-request, first-reqeust-header2
# - AddResponseHeader=first-request, first-response-header2
- CustomFilter #우리가 작성한 필터를 여기에 등록
- id: second-service
uri: lb://MY-SECOND-SERVICE
predicates:
- Path=/second-service/**
filters:
# - AddRequestHeader=second-request, second-reqeust-header2
# - AddResponseHeader=second-request, second-response-header2
- name: CustomFilter
- name: LoggingFilter
args:
baseMessage: Logging, filter.
preLogger: true
postLogger: true
first, second-service의 yml파일도 다음과 같이 수정
server:
port: 8082
spring:
application:
name: my-second-service
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8761/eureka
여기서는 총 4개의 파일이 실행된다.
각 서비스를 2개씩 띄워서 총 4개 돌게끔 실행
그러나 포스트맨으로 요청해도 어느 서비스로 가는지 알 수가 없음
first-service만 랜덤포트 구현
server:
port: 0#랜덤포트로 설정
spring:
application:
name: my-first-service
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
2번째는 터미널로 기동
만일 mvn 컴파일시 버전 안맞아서 에러 난다면
참고하여 mvn컴파일 버전을 추가하도록 함
first service controller를 다음과 같이 수정
package com.example.firstservice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
@RequestMapping("/first-service")
@Slf4j
public class Controller {
//변수에 바로 Autowired를 쓰지말고
//이건 yml 파일의 값을 갖고오는 방법 중 하나이다.
Environment env;
//생성자를 통해 주입 후 @Autowired를 통해 빈 등록
@Autowired
public Controller(Environment env){
this.env = env;
}
@GetMapping("/welcome")
public String welcome()
{
return "Welcome first service";
}
@GetMapping("/message")
public String message(@RequestHeader("first-request") String header){
log.info(header);
return "Hello first service";
}
@GetMapping("/check")
public String check(HttpServletRequest request){
log.info("Server port= {}", request.getServerPort());
return String.format("This message indicate First service PORT %S",
env.getProperty("local.server.port"));//getProperty()안에는
//우리가 갖고오고 싶은 값을 명시해주면 된다. 우리는 포트 번호를 갖고오고 싶음
}
}
라운드 로빈 방식으로 호출 되는 것을 알 수 있다.
broker 1↔zookeeper ↔ broker0
broker 2
→ 즉 : 브로커 : 메시지를 전달하기위한 저장공간(3대이상 권장)
주키퍼 : 브로커를 중재하는 역할(브로커의 존재 유무, 컨트롤러, 등등의 정보)
여러개의 브로커중 하나는 리더 역할을 해야 한다 이게 메인이 되며, 문제가 생길시 이 리더를 중지 시키고 다른 브로커에서 리더를 선출해서 사용해야함
C:\kafka\kafka_2.13-3.0.0\kafka_2.13-3.0.0
bin : 명령어 모아놓음(window는 bin 안에 windows파일안에 있음 .bat파일로 실행해야함)
config : zooper, broker 등의 서버 설정파일