본문 바로가기
ETC

MSA를 구축하는 간단한 방법

by brilliant-growth 2023. 8. 2.

 

깃 : https://github.com/kdh11112/brilliant-growth/tree/main/springbootex19_springcloud

 

@SpringBootApplication
@EnableEurekaServer //유레카 서버 활성화
public class Springbootex14DiscoveryApplication {

	public static void main(String[] args) {
		SpringApplication.run(Springbootex14DiscoveryApplication.class, args);
	}

}

유레카 서버를 활성화 하는 코드 이다

 

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>

폼에 유레카 서버에 관한 정보를 줘야한다

 

server:
  port: 8761
 
  
spring:
  application:
    name: discoveryservice
    
eureka:
  client:
    register-with-eureka: false #eureka 서버에 등록할지 여부를 설정 (내가 서버가 될거라 등록이 안됨)
    fetch-registry: false #eureka 서버로 부터 정보를 가져올지를 선택

포트는 8761을 사용하는데 관습적인 이유이다

 

 

zuul을 을 사용한 게이트웨이

		<dependency>
		    <groupId>org.springframework.cloud</groupId>
		    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
		    <version>2.2.10.RELEASE</version>
		</dependency>

@SpringBootApplication
@EnableZuulProxy
public class Springbootex18ZuulserviceApplication {

	public static void main(String[] args) {
		SpringApplication.run(Springbootex18ZuulserviceApplication.class, args);
	}

}

zuul을 사용하기 위해서 @EnableZuulProxy를 사용해야한다

 

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

 

yml에 게이트웨이에 등록할 서버를 설정해준다 

현재 여기선 로컬로 2개를 만들어줬다

@Slf4j
@Component
public class zuulLogginFilter extends ZuulFilter{

	@Override
	public boolean shouldFilter() {
		return true; //필터 사용
	}

	@Override
	public Object run() throws ZuulException {
		//필터가 동작 된다면 이부분이 실행됨
		log.info("로그 시작 ===============>");
		//요청 URI 를 로그로 기록
		
		//1번방식
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		String uri = request.getRequestURI();
		log.info("요청 uri : {}",uri);
		
		//2번방식  ==> 1번과 똑같은 거임
		log.info("요청 uri : {}",RequestContext.getCurrentContext().getRequest().getRequestURI());
		log.info("로그 끝 ===============>");
		return null;
	}

	@Override
	public String filterType() {
		// TODO Auto-generated method stub
		return "pre"; //진입 쪽 필터
	}

	@Override
	public int filterOrder() {
		// TODO Auto-generated method stub
		return 0;
	}

}

zuul 필터를 사용하는 간단한 방법

 

 

spring cloud gateway에 대해 알아보자

현재 스프링에서는 zuul대신 scg를 권고하고 있다

 

		<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>

 

1번째껀 scg를 사용하기 위해 쓰고

2번째껀 유레카 서버에 클라이언트 등록을 위해 사용해야한다

 

scg 는 Application에 어노테이션을 따로 지정하지 않아도 된다.

 

server:
  port: 8000
  
  
spring:
  application:
    name: apigateway-service
  cloud:
    gateway:
      default-filters:
      - name: GlobalFilter
        args:
          baseMessage: Hello Spring Cloud Global Filter
          preLogger: true
          postLogger: true
    
      routes:
      - id: first-service
        predicates:
        - Path=/first-service/**
        uri:
          lb://MY-FIRST-SERVICE
#          http://localhost:8081 로드밸런싱을 위하여 포트번호를 지정하면 안됨
        filters:
         - CustomFilter
#        - AddRequestHeader=first-request, first-request-header-from-yml
#        - AddResponseHeader=first-response, first-response-header-from-yml
      - id: second-service
        predicates:
        - Path=/second-service/**
        uri:
          lb://MY-SECOND-SERVICE
#          http://localhost:8082
        filters:
         - CustomFilter
#        - AddRequestHeader=second-request, second-request-header-from-yml
#        - AddResponseHeader=second-response, second-response-header-from-yml
#   여기가다 설정해도 되고 FilterConfig에다가 설정해도 됨
          
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka

 

yml에 설정한 내용은 2개의 서버를 지정하였고 로드밸런싱을 위하여 포트번호는 랜덤으로 부여

필터는 커스텀 필터를 사용하였다는 뜻이다

서버는 유레카 서버로 지정

 

필터는 야믈에ㅁ

# - AddRequestHeader=second-request, second-request-header-from-yml

# - AddResponseHeader=second-response, second-response-header-from-yml

이런식으로 지정해도 되지만 Config파일을 따로 만들어서 설정해줘도된다

 

// 빈 정의할때 스는 어노테이션
@Configuration
public class FilterConfig {

	@Bean
	public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
		
		//여기다가 설정을 하게되면 야뮬 파일에서 설정을 한거랑 같은거임
		return builder	.routes()
                        .route(r -> r.path("/first-service/**")
                        .filters(f -> f.addRequestHeader("first-service", "first-request-header")
                                        .addResponseHeader("first-service", "first-response-header")
                                )
                        .uri("http://localhost:8081")
                        )
                        .route(r -> r.path("/second-service/**")
                                .filters(f -> f.addRequestHeader("second-service", "second-request-header")
                                                .addResponseHeader("second-service", "second-response-header")
                                        )
                                .uri("http://localhost:8082")
                        )

                        .build();
	}

 

아래는 커스텀필터이다

 

@Slf4j
//커스텀 필터
@Component
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config>{

	//설정 정보를 제공하는 클래스
	public static class Config{
		
	}
	
	public CustomFilter() {
		super(Config.class);
	}

	//필터의 동작을 정의한 메소드
	@Override
	public GatewayFilter apply(Config config) {

//		GatewayFilter gf = new GatewayFilter() {//인터페이스 이기에 이너 클래스로 바로 만듬
//			
//			@Override
//			public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//				ServerHttpRequest request = exchange.getRequest();
//				ServerHttpResponse response = exchange.getResponse();
//				
//				log.info("Customer pre Filter : request id = {}",request.getId());
//				Mono<Void> m = chain.filter(exchange).then(Mono.fromRunnable(() ->{
//					log.info("Custom POST filter : response code = {}",response.getStatusCode());
//				}));
//				
//				return m;
//			}
//		};
//		
//		return gf;
		
		//위의 코드를 람다식으로 정의 하는방법
		return (exchange,chain) -> {
			ServerHttpRequest request = exchange.getRequest();
			ServerHttpResponse response = exchange.getResponse();
			log.info("Customer pre Filter : request id {}",request.getId());
			return chain.filter(exchange).then(Mono.fromRunnable(() ->{
				log.info("Customer POST filter : response code = {}",response.getStatusCode());
			}));
		};
		
	}

}

 

아래는 글로벌 필터이다

 

@Slf4j
@Component
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config>{
	
	
	@Data
	public static class Config{
		private String baseMessage;
		private boolean preLogger;
		private boolean postLogger;
		
	}
	
	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 필터 메시지 : {}",config.getBaseMessage());
			if(config.isPreLogger()) {
				log.info("Global 필터 시작 request id = {}",request.getId());
			}
			return chain.filter(exchange).then(Mono.fromRunnable(() ->{
				if(config.isPostLogger()) {
					log.info("Global Post 필터 끝부분 response code = {}",response.getStatusCode());
				}
			}));
		};
	}

}

 

 

 

서버1 의 컨트롤러

@RequestMapping부분은 필수로 있어아야 한다 

 

@Slf4j
@RestController
@RequestMapping("/first-service")// zuul는 없어도 됬는데 cloud일때는 잇어야함
public class FirstServiceController {

	@GetMapping("/welcome")
	public String welcome() {
		return "welcom !!! first Service";
	}
	
	@GetMapping("message")
	public String msg(@RequestHeader(value = "first-service",defaultValue = "NONE") String header) {
		
		log.info("apigateway에서 추가한 헤더 정보"+header);
		
		return header;
	}
	
	@GetMapping("/check")
	public String check(HttpServletRequest request) {
		log.info("check 호출됨");
		log.info("요청 포트 : {}",request.getServerPort());
		return "/check : "+request.getServerPort();
	}
}
server:
   port: 0
#  port: 8081 포트 0번으로 하는 이유는 한곳에만 과부하가 걸리면 안되기때문에 로드밸런싱을 적용하기 위하여 포트번호를 랜덤으로 지정

  
spring:
  application:
    name: my-first-service
    
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}}

 

폼에 추가해줘야한다

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>

서버2이다 내용은 1번과 동일하게 되어있다 굳이 볼필요는없다

@Slf4j
@RestController
@RequestMapping("/second-service")// zuul는 없어도 됬는데 cloud일때는 잇어야함
public class SecondServiceController {

	@GetMapping("welcome")
	public String welcom() {
		return "welcom !!! second Service";
	}
	
	@GetMapping("message")
	public String msg(@RequestHeader(name = "second-service",defaultValue = "NONE")String header) {
		
		log.info("second"+header);
		return header;
	}
}

 

server:
  port: 0
#  port: 8082
  
spring:
  application:
    name: my-second-service
    
    
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}}
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>

 

유레카 서버를 키게 되면 나오는 화면이다

 

 

게이트웨이 1개 서버1 서버1 총 3개가 접속되어있고

 

서버1 같은경우는 2개를 켜놨다

 

http://localhost:8000/first-service/check로 들어가게되면 
랜덤으로 지정된 포트번호가 나온다

 

 

자세히 보면 위의 번호가 2개가 다르다

포트번호를 랜덤으로 지정해서 로드밸런싱을 구현했다

 

유레카

유레카가 구동중이고 변경이 있는지 계속 확인하고 있는 로그

 

필터부분이 적용되고 있는지 확인할수있는 부분

 

 

위에서 말했다 시피

유레카 서버 1개

게이트웨이 1개

1서버 2개

2서버 1개

가 켜져있다

'ETC' 카테고리의 다른 글

젠킨스로 CI/CD구축  (0) 2023.08.21
젠킨스 구축방법  (0) 2023.08.09
oracle,mysql 타입 차이  (0) 2023.07.14
도커 설정  (0) 2023.07.10
파일질라 및 푸티 사용법  (0) 2023.07.10