[SpringCloud MSA]API Gateway Service(Netflix Zuul)

zzarbttoo·2021년 9월 5일
0

이 글은 인프런 강의 "Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)"를 정리한 글입니다. 문제/오류가 있을 시 댓글로 알려주시면 감사드리겠습니다


API Gateway 는 클라이언트 대신 요청, 응답을 해주는 proxy 역할을 한다
시스템 내부 구조는 숨기고 외부 요청에 대해서 적절한 형태로 가공해서 응답할 수 있다는 장점이 있다

API Gateway Service란

  • microservice에서 수정/배포가 이루어질 때 client 측에서 직접 요청을 한다면 client측도 수정/배포가 이루어져야 한다는 문제가 있었음
    -> 단일 진입점을 가지고 있는 형태로 개발이 되어야 한다
  • client는 gateway만 상대하면 되므로, gateway만 수정하면 된다
인증 권한 부여, 
서비스 검색 통합, 
응답 캐싱,
정책/회로 차단기 및 Qos 다시 시도,
속도 제한,
로드밸런싱,
로깅/추적/상관관계(ELK를 사용할 수도 있지만 APIGateway를 사용해도 된다), 
헤더/쿼리 문자열 및 청구 변환,
IP 허용 목록에 추가 

| Netflix Ribbon

Spring Cloud에서는 MSA간 통신을 할 수 있다
1. RestTemplate

  • 필요한 외부 서비스에 연동
  • 직접적인 서버, 포트번호를 명시해야함
  1. Feign Client
@FeignClient("stores")
public interface StoreClient{
	@RequestMapping(method = RequestMethod.GET, value = "/stores")
    	List<Store> getStores();
}
  • 서버의 주소, 포트번호 없이 마이크로서비스의 이름을 이용해 호출할 수 있다

이전에는 Netflix Ribbon을 이용해 서비스 이름으로 호출, Health Check를 했다

  • client api Gateway가 Client Rebbon 안으로 들어갔다고 생각하면 된다
  • 하지만 현재는 잘 이용하지 않는다(react 등의 비동기 방식과 호환이 잘 되지 않기 때문에 maintenance 상태)

| Netflix Zuul


Netflix Zuul

| Zuul Service

pom.xml
<?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.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>zuul-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>zuul-service</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>Hoxton.SR11</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-zuul</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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </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>
  • Zuul을 이용하기 위해 SpringBoot 2.4 이하의 버전을 선택함
  • spring-cloud-starter-netflix-zuul 추가

SpringBootApplication 부분
package com.example.zuulservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@EnableZuulProxy
@SpringBootApplication
public class ZuulServiceApplication {

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

}
  • @EnableZuulProxy 어노테이션을 붙여준다

Filter
package com.example.zuulservice.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;


@Slf4j
@Component
public class ZuulLoggingFilter extends ZuulFilter {

    //해당 필터가 실행될 때 수행하는 메소드 
    //log, 인증정보, 변환작업 등을 여기서 처리할 수 있다
    @Override
    public Object run() throws ZuulException {

        log.info("---------------printing logs: ");

        //web 프로젝트에 request/response 정보를 가지고 있는 최상위 객체
        RequestContext ctx= RequestContext.getCurrentContext();
        //사용자 request 정보 출력
        HttpServletRequest request = ctx.getRequest();

        log.info("---------------------------: " + request.getRequestURI());
        return null;
    }

    // 이 필터가 무슨 필터인지 반환하는 메소드 
    @Override
    public String filterType() {
        return "pre";
    }

    // 필터의 우선순위를 반환하는 메소드 
    @Override
    public int filterOrder() {
        return 1;
    }

    //필터로 쓸거니까 true 	
    @Override
    public boolean shouldFilter() {
        return true;
    }


}

application.yml
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
  • path를 입력하면 url로 요청할 수 있도록 zuul.routes를 설정함

| Service 1, 2

pom.xml
<?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.3.12.RELEASE</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>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>Hoxton.SR11</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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </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>
  • client 쪽 역시 springboot 2.4 이하로 설정
  • spring-cloud-starter-netflix-eureka-client 를 추가

application.yml
//Service2의 경우에는 8082로 설정 
server:
  port: 8081 
spring:
  application:
    name : my-first-service
eureka:
  client:
    fetch-registry: false
    register-with-eureka: false

ServiceController.java
package com.example.firstservice;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/")
public class FirstServiceController {

    @GetMapping("/welcome")
    public String welcome(){
        return "Welcome to the First Service";
    }
}
  • Service1에서 /welcome을 요청하면 "Welcome to the First Service" 응답
  • Service2에서는 "Welcome to the Second Service" 응답

| 결과


  • localhost:8000번으로 요청했음에도 first-service/** 는 service1에 요청이 되고, second-service/**는 service2로 요청이 된다
Zuul Service console

  • Zuul의 콘솔에 filter을 이용한 로그가 찍히는 것을 확인할 수 있다
profile
나는야 누워있는 개발머신

0개의 댓글