Tec-Blog

DK 기술 블로그

Technology Blog .Dong Ki

Spring Boot MSA 적용기- 2탄

08 Jan 2022

Author - Kim, DongKi

TL;DR

안녕하세요, 벌써 2022년 새해네요 🙆‍♂️ 다들 새해 복 많이 받으세요 🙇

이번 포스팅은 이전과 이어서 MSA 관련 포스팅을 주제로 가겠습니다 😃


Zuul Filtering

  • Zuul Gateway를 통한 요청 처리 시 미인증 요청 혹은 각종 이슈 상황 발생 시 우선 대응을 도와주는 기능

  • Zuul Filter Type

    • Pre Filter
      • 서비스 처리 전 실행되는 필터 (Security, 인증 처리 등)
    • Post Filter
      • 서비스 처리 이후 실행되는 필터 (API 처리 통계 처리 등)
    • Route Filter
      • 각 서비스 컴포넌트 내 라우팅 제어를 위한 필터 (Ribbon, 동적 라우팅 관리)
    • Error Filter
      • 필터 영역 내 발생되는 이슈 상황 처리 실행을 위한 필터

Filter 구현

  • ZuulFilter를 상속받는 각 필터 처리 커스텀 클래스를 구현
    • 각 커스텀 필터 클래스 내 처리 로직 작성

[PreFilter.java]

public class PreFilter extends ZuulFilter {

  private static final Logger logger = LoggerFactory.getLogger(PreFilter.class);

  @Override
  public String filterType() {
    return "pre";
  }

  @Override
  public int filterOrder() {
    return 0;
  }

  @Override
  public boolean shouldFilter() {
    return true;
  }

  @Override
  public Object run() {
    RequestContext requestContext = RequestContext.getCurrentContext();
    HttpServletRequest httpServletRequest = requestContext.getRequest();
    logger.info("===== ZUUL::PRE =====");
    logger.info("Request URL :: {} Request Method :: {}", 
        httpServletRequest.getRequestURI(),
        httpServletRequest.getMethod());

    return null;
  }
}

[PostFilter.java]

public class PostFilter extends ZuulFilter {

  private static final Logger logger = LoggerFactory.getLogger(RouteFilter.class);

  @Override
  public String filterType() {
    return "post";
  }

  @Override
  public int filterOrder() {
    return 0;
  }

  @Override
  public boolean shouldFilter() {
    return true;
  }

  @Override
  public Object run() {
    logger.info("===== ZUUL::POST =====");
    return null;
  }
}

[RouteFilter.java]

public class RouteFilter extends ZuulFilter {

  private static final Logger logger = LoggerFactory.getLogger(RouteFilter.class);

  @Override
  public String filterType() {
    return "route";
  }

  @Override
  public int filterOrder() {
    return 0;
  }

  @Override
  public boolean shouldFilter() {
    return true;
  }

  @Override
  public Object run() {
    logger.info("===== ZUUL::ROUTE =====");
    return null;
  }
}

[ErrorFilter.java]

public class ErrorFilter extends ZuulFilter {

  private static final Logger logger = LoggerFactory.getLogger(RouteFilter.class);
  
  @Override
  public String filterType() {
    return "error";
  }

  @Override
  public int filterOrder() {
    return 0;
  }

  @Override
  public boolean shouldFilter() {
    return true;
  }

  @Override
  public Object run() {
    logger.error("===== ZUUL::ERROR =====");
    return null;
  }
}
  • 구현 한 커스텀 클래스를 Bean 파일로 등록

[DefaultServerApplication.java]

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class DefaultServerApplication {

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

  @Bean
  public PreFilter preFilter() {
    return new PreFilter();
  }

  @Bean
  public PostFilter postFilter() {
    return new PostFilter();
  }

  @Bean
  public ErrorFilter errorFilter() {
    return new ErrorFilter();
  }

  @Bean
  public RouteFilter routeFilter() {
    return new RouteFilter();
  }
}

Filter Test

2022-01-08-msa-start-2-1.jpg

2022-01-08-msa-start-2-2.jpg

  • 테스트 결과 지정한 Filter 클래스를 수행하는걸 확인할 수 있습니다.

추가로 이전 포스팅에 적었던 바와 같이 Python 기반 컴포넌트도 Eureka 서비스로 붙이는 예시를 보여드리고 오늘 포스팅은 마무리 하도록 하겠습니다 :)

Python Flask - Netflex Eureka

필요한 Python Lib

  • flask
  • flask_restx
  • py_eureka_client

2022-01-08-msa-start-2-3.jpg

Python 이용한 Eureka 컴포넌트 구현

먼저 간단한 컴포넌트로 구성을 할 생각이기 때문에 Flask를 이용한 크롤링을 구현해 보겠습니다.

  • 컴포넌트 내 메인이 되는 클래스

[app.py]

from flask import Flask, request
from flask_restx import Api
from flask_cors import CORS
import py_eureka_client.eureka_client as eureka_client
import crawling
import config.server_config as server

app = Flask(__name__)
api = Api(app)
CORS(app)

eureka_client.init(eureka_server=server.EUREKA_SERVER,
                   app_name=server.SERVICE_NAME,
                   instance_host=server.SERVICE_HOST,
                   instance_port=server.SERVICE_PORT)

@app.route("/craw/url", methods=['GET'])
def get_crawling_url():
  return crawling.crawler('https://github.com/kdkrkwhr')

@app.route("/craw/get_data", methods=['POST'])
def post_crawling_data():
  params = request.get_json()
  return crawling.crawler_parsing(params['url'], params['tag'])

@app.route("/craw/test", methods=['GET'])
def test():
  args = request.args
  param = args['param']
  return {"message" : "Hi", "param" : param}

if __name__ == "__main__": 
  app.run(host=server.SERVICE_HOST, port=server.SERVICE_PORT, debug=True)
  • Eureka 설정 파일

[server_config.py]

SERVICE_NAME = 'kdk-eureka-zuul-service-flask'
SERVICE_PORT = 8102
SERVICE_HOST = '127.0.0.1'
EUREKA_SERVER = 'http://localhost:8081/eureka/'
  • 실제 크롤링 서비스 로직
    • 호출한 Url html을 가져오도록 구현
    • 호출한 Url html 내 특정 태그 값을 가져오도록 구현

[crawling.py]

import requests
from bs4 import BeautifulSoup

def crawler(url):
  html = requests.get(url)
  return html.text

def crawler_parsing(url, tag):
  html = requests.get(url)
  soup = BeautifulSoup(html.text, 'html.parser')
  select = soup.select_one(tag)

  return select.text

Test

  • 정상적으로 Discovery 된 것을 확인

2022-01-08-msa-start-2-4.jpg

  • 정상적으로 Zuul을 이용한 Routing이 완수 된 것을 확인

2022-01-08-msa-start-2-5.jpg

마무리

MSA는 계속 흥미를 가지고 있는 주제로 아직까지도 틈틈히 공부를 하고 있습니다.

그래서 앞으로도 관련된 포스팅을 작성할 수가 있을 것 같습니다 😊

다시 한 번 새해 복 많이 받으시고, 편안한 한 해 되길 바랍니다 ~

Thanks .