본 게시물은 스스로의 공부를 위한 글입니다.
틀린 내용이 있을 수 있습니다.
Eureka
는 Service Registry
서비스 중 하나이다.Eureka
와 API Gateway
프로세스는 아래 이미지와 같다.스프링에서 사용하는 API Gateway로는 다음과 같은 서비스들이 있다.
Spring Cloud Loadbalancer
로 대체Spring Cloud Gateway
로 대체Maintenance mode란?
더 이상 지원 서비스를 하지 않는 보류 상태.
@RestController
@RequestMapping("/first-service")
public class FirstServiceController {
@GetMapping("/welcome")
public String welcome(){
return "Welcome to the First service";
}
}
server:
port: 8081 # second service는 8082
spring:
application:
name: my-first-service
Test
http://localhost:8081/welcome 에 Welcome to the First service
출력 확인
http://localhost:8082/welcome 에 Welcome to the Second service
출력 확인
@EnableZullProxy
추가zuul.routes
추가path
요청이 들어오면 url
로 포워딩 시켜줌@SpringBootApplication
@EnableZuulProxy
public class ZuulServiceApplication {
public static void main(String[] args) {
SpringApplication.run(FirstServiceApplication.class, args);
}
}
server:
port: 8000
spring:
application:
name: my-zuul-service
zuul:
routes:
first-service:
path: /first-service/**
url: http://localhost:8081
second-service:
path: /second-service/**
url: http://localhost:8082
테스트
first service, second service, zuul serivce를 모두 실행 후..
http://localhost:8000/first-service/welcom으로 접속하면 first-service로 연결되는거 확인 가능
http://localhost:8000/second-service/welcom으로 접속하면 second-service로 연결되는거 확인 가능
이로써 gateway 기능을 사용할 수 있다.
@Component
@SLF4J
public class ZullLoggingFilter extends ZuulFilter{
@Override
//실제 동작
public Object run() throws ZullException {
log.info("사전필터입니다!");
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info("Request url = "+ request.getRequestURL());
return null;
}
@Override
public String filterType(){
return "pre"; //사전 처리 필터로 결정
}
@Override
public int filterOrder(){
return 1; //필터가 여러개인경우 순서 결정
}
@Override
public boolean shouldFilter(){
return true; //필터를 사용할지, 사용 안할지
}
}
@RestController
@RequestMapping("/first-service")
@Slf4j
public class FirstServiceController {
@GetMapping("/message")
public String message(@RequestHeader("first-request") String header){
log.info(header);
return "Hello world in First Service";
}
}
server:
port: 8081 # Second Service는 8082
spring:
application:
name: my-first-service
아래에서 Eureka 서비스와 연동해서 사용할 예정이므로 미리 Eureka 관련 서비스도 추가
Dependencies : Spring Boot DevTools, Eureka Discovery Client, Gateway
application.yml 또는 자바 코드로 작성 가능
4-1. application.yml로 설정
server:
port: 8000
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-header2
- AddResponseHeader=first-response, first-response-header2
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- AddRequestHeader=second-request, second-request-header2
- AddResponseHeader=second-response, second-response-header2
predicates
에 있는 path
로 접속한다면 해당하는 url
로 포워딩 시켜준다.url
+접속한 path
)이다.filters
로 requestHeader
과 responseHeader
을 추가할 수 있다. (key, value 형식) 4-2. 자바 파일로 설정
@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();
}
}
pre filter은 service 호출 전 실행
post filter은 service 호출 후 실행
CustomFilter을 위해선 AbstractGatewayFilterFactory
을 상속 받아야함
@Component
@Slf4j
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {
public CustomFilter(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config){
// Custom pre Filter
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("Custom PRE filter: request id -> {}", request.getId());
// Custom post filter
return chain.filter(exchange).then(Mono.fromRunnable(()->{
log.info("Custom POST filter: response code -> {}", response.getStatusCode());
}));
};
}
public static class Config{
//put the configuration properties
}
}
application.yml
에서 설정한 args
를 받아와 사용할 수 있다.Ordered.HIGHEST_PRECEDENCE
, Ordered.LOWEST_PRECEDENCE
로 커스텀 필터의 실행 우선순위를 정해줄 수 있다.@Component
@Slf4j
public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
public LoggingFilter(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config){
GatewayFilter filter= new OrderedGatewayFilter((exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("Logging Filter baseMessage: {}", config.getBaseMessage());
if(config.isPreLogger()){
log.info("Logginf Pre Filter: request id-> {}", request.getId());
}
// Custom post Filter
return chain.filter(exchange).then(Mono.fromRunnable(()-> {
if (config.isPostLogger()) {
log.info("Log Filter End: response code -> {}", response.getStatusCode());
}
}));
}, Ordered.HIGHEST_PRECEDENCE); //필터 체인 방식에서 우선순위를 정해줄 수 있다.
return filter;
}
@Data
public static class Config{
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
server:
port: 8000
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- CustomFilter # Custom Filter만 적용
- id: second-service
uri: http://localhost:8081/
predicates:
- Path=/second-service/**
filters:
- name: CustomFilter # filter을 여러개 적용가능
- name: LogginFilter
args:
baseMessage: ~
preLogger: ~
~
name
을 적어줘야 한다.args
는 위 코드와 같이 사용하면 된다.Custom Filter와 Global Filter의 작동 순서
@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();
log.info("Global Filter baseMessage: {}", config.getBaseMessage());
if(config.isPreLogger()){
log.info("Global Filter start: request id -> {}" , request.getId());
}
return chain.filter(exchange).then(Mono.fromRunnable(()->{
if(config.isPostLogger()){
log.info("Global Filter End: response code -> {}", response.getStatusCode());
}
}));
};
}
@Data
public static class Config{ // application.yml에서 설정한 args를 받아올 수 있음
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
application.yml
에 코드 추가spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters: # default-filters가 글로벌 필터이다.
- name: GlobalFilter
args: # 인자 전달 가능
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
...
server:
port: 8000
eureka: # 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
predicates:
- Path=/first-service/**
filters:
- CustomFilter
- id: second-service
uri: lb://MY-SECOND-SERVICE
predicates:
- Path=/second-service/**
filters:
- CustomFilter
uri
에는 eureka
에 등록되어 있는 서비스 이름으로 작성한다. lb://{등록된 서비스 이름}
형식server:
port: 0 # 랜덤 포트
spring:
application:
name: my-first-service
eureka: # eureka에 등록
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
@RestController
@RequestMapping("/first-service")
@Slf4j
public class FirstServiceController {
Environment env;
@Autowired
public FirstServiceController(Environment env){
this.env=env;
}
@GetMapping("/check")
public String check(HttpServletRequest request){
log.info("server port={}", request.getServerPort());
return String.format("Hi, there. This is a message from First Service on Port %s",
env.getProperty("local.server.port"));
}
}
인프런의 'Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)(Dowon Lee)'을 스스로 정리한 글입니다.
자세한 내용은 해당 강의를 참고해주세요.