[Spring Cloud Gateway] Building a Gateway 따라하기

MEUN·2022년 2월 18일
0

이 게시글은 Spring Cloud Gateway 공식 문서의 Building a Gateway 페이지를 실습한 내용을 담고 있습니다.
(자세한 내용은 하단 링크 참고)

목표

  • Spring Cloud Gateway를 사용하여 Gateway 구축하기

준비물

  • 약 15분
  • 좋아하는 텍스트 편집기나 IDE
  • JDK 1.8 이상
  • Gradle 4 이상 또는 Maven 3.2 이상

실습을 진행하는 방법

  • 처음부터 시작하려면 Spring Initializr를 이용합니다.
  • 기본 사항을 건너뛰려면 아래와 같이 진행합니다.
    • git clone https://github.com/spring-guides/gs-gateway.git 경로의 소스를 복제하거나 다운로드하여 압축을 해제합니다.
    • gs-gateway/initial 폴더로 이동합니다.
    • 실습 진행 후 gs-gateway/comlete 폴더에서 결과 비교가 가능합니다.

Spring Initializr로 프로젝트 생성하기


간단한 라우트 생성하기

1. 첫 라우트 추가

1) SpringCloudGatewayApplication.java

package com.meun2.scg.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SpringCloudGatewayApplication {

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

	@Bean
	public RouteLocator myRoutes(RouteLocatorBuilder builder) {
		return builder.routes().build();
	}
}
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
	return builder.routes()
			.route(p -> p
					.path("/get")
					.filters(f -> f.addRequestHeader("Hello", "World"))
					.uri("http://httpbin.org:80"))
			.build();
}

2) CURL로 데이터 확인

$ curl http://localhost:7070/get	

터미널에 위 명령어 입력 후 정상적으로 동작한 경우 아래 사진과 같이 결과값이 조회됩니다.


2. Circuit Breaker 추가

1) build.gradle

implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j'

2) SpringCloudGatewayApplication.java

@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
	return builder.routes()
			.route(p -> p
					.path("/get")
					.filters(f -> f.addRequestHeader("Hello", "World"))
					.uri("http://httpbin.org:80"))
			.route(p -> p
					.host("*.circuitbreaker.com")
					.filters(f -> f.circuitBreaker(config -> config.setName("mycmd")))
					.uri("http://httpbin.org:80"))
			.build();
}

3) CURL 테스트 진행

$ curl --dump-header - --header 'Host: www.circuitbreaker.com' http://localhost:7070/delay/3


3. FallBack 추가

1) SpringCloudGatewayApplication.java

package com.meun2.scg.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@RestController
@SpringBootApplication
public class SpringCloudGatewayApplication {

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

	@Bean
	public RouteLocator myRoutes(RouteLocatorBuilder builder) {
		return builder.routes()
				.route(p -> p
						.path("/get")
						.filters(f -> f.addRequestHeader("Hello", "World"))
						.uri("http://httpbin.org:80"))
				.route(p -> p
						.host("*.circuitbreaker.com")
						.filters(f -> f.circuitBreaker(config -> config
								.setName("mycmd")
								.setFallbackUri("forward:/fallback")))
						.uri("http://httpbin.org:80"))
				.build();
	}

	@RequestMapping("/fallback")
	public Mono<String> fallback() {
		return Mono.just("fallback");
	}
}

3) CURL 테스트 진행

$ curl --dump-header - --header 'Host: www.circuitbreaker.com' http://localhost:8080/delay/3

HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: text/plain;charset=UTF-8

fallback

4. HttpBin 의존성 분리

1) SpringCloudGatewayApplication.java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@SpringBootApplication
@RestController
@EnableConfigurationProperties(UriConfiguration.class)
public class SpringCloudGatewayApplication {

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

	@Bean
	public RouteLocator myRoutes(RouteLocatorBuilder builder, UriConfiguration uriConfiguration) {
		String httpUri = uriConfiguration.getHttpbin();
		return builder.routes()
				.route(p -> p
						.path("/get")
						.filters(f -> f.addRequestHeader("Hello", "World"))
						.uri(httpUri))
				.route(p -> p
						.host("*.circuitbreaker.com")
						.filters(f -> f
								.circuitBreaker(config -> config
										.setName("mycmd")
										.setFallbackUri("forward:/fallback")))
						.uri(httpUri))
				.build();
	}

	@RequestMapping("/fallback")
	public Mono<String> fallback() {
		return Mono.just("fallback");
	}
}

@ConfigurationProperties
class UriConfiguration {
	private String httpbin = "http://httpbin.org:80";

	public String getHttpbin() {
		return httpbin;
	}

	public void setHttpbin(String httpbin) {
		this.httpbin = httpbin;
	}
}

5. 테스트 코드 작성

1) SpringCloudGatewayApplication.java

package com.meun2.scg.study;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.test.web.reactive.server.WebTestClient;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.assertj.core.api.Assertions.*;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {"httpbin=http://localhost:${wiremock.server.port}"})
@AutoConfigureWireMock(port = 0)
class SpringCloudGatewayApplicationTests {

	@Autowired
	private WebTestClient webClient;

	@Test
	void contextLoads() throws Exception {
		// Stubs
		stubFor(get(urlEqualTo("/get"))
				.willReturn(aResponse()
						.withBody("{\"headers\":{\"Hello\":\"World\"}}")
						.withHeader("Content-Type", "application/json")));
		stubFor(get(urlEqualTo("/delay/3"))
				.willReturn(aResponse()
						.withBody("no fallback")
						.withFixedDelay(3000)));

		webClient
				.get().uri("/get")
				.exchange()
				.expectStatus().isOk()
				.expectBody()
				.jsonPath("$.headers.Hello").isEqualTo("World");

		webClient
				.get().uri("/delay/3")
				.header("Host", "www.circuitbreaker.com")
				.exchange()
				.expectStatus().isOk()
				.expectBody()
				.consumeWith(
						response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes()));
	}

}

레퍼런스

1개의 댓글

comment-user-thumbnail
2022년 6월 30일

@configurationproperties 에서 prefix must be specifed 에러가 뜨는데 혹시 어떻게 해결하는지 아시나요??

답글 달기