spring netflix zuul routing

08 December 2019

spring-cloud-starter-netflix-zuul을 활용하여, routing을 하는 방법을 알아보도록 하겠습니다.
먼저 아래와 같이 gradle을 통해 dependency를 정의해줍니다.

plugins {
    id 'java'
}

group 'org.shashaka.io'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.2.0.RELEASE'
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '2.2.0.RELEASE'
    compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-zuul', version: '2.2.0.RELEASE'
    compile group: 'org.projectlombok', name: 'lombok', version: '1.18.10'
}
그리고 routing을 할 endpoint를 아래와 같이 restController로 생성해주도록 합니다.
기본적인 routing만 사용할 것이므로 @EnableZuulProxy가 아니라, @EnableZuulServer로 설정해주었습니다.
package org.shashaka.io;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableZuulServer
@RestController
public class RouteApplication {
    public static void main(String[] args) {
        SpringApplication.run(RouteApplication.class, args);
    }

    @RequestMapping(path = "/get")
    public String get(@RequestParam String route) {
        return route;
    }
}

이제 아래와 같이 routeFilter를 정의해주도록 합니다.
route filter는 webClient를 활용하여 진행하도록 하겠습니다.
queryParam에서 route의 값이 true이면 http://httpbin.org 으로 routing하여 호출하고,
false인 경우는 그대로 app에서 처리하도록 설정합니다.
package org.shashaka.io;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;

@Component
@Slf4j
public class RoutingFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return FilterConstants.ROUTE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.SIMPLE_HOST_ROUTING_FILTER_ORDER;
    }

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

    @Override
    public Object run() {

        RequestContext context = RequestContext.getCurrentContext();

        Map> queryParams = context.getRequestQueryParams();

        if (queryParams != null && queryParams.containsKey("route") && queryParams.get("route").contains("true")) {
            ClientResponse response = WebClient.create("http://httpbin.org" + context.getRequest().getRequestURI())
                    .get()
                    .exchange()
                    .block();

            ProxyRequestHelper helper = new ProxyRequestHelper();

            try {
                helper.setResponse(response.rawStatusCode(),
                        new ByteArrayInputStream(response.bodyToMono(String.class).block().getBytes()),
                        new HttpHeaders());
            } catch (IOException e) {
                e.printStackTrace();
            }

            RequestContext.getCurrentContext().setSendZuulResponse(false);
        } else {
            RequestContext.getCurrentContext()
                    .set(FilterConstants.FORWARD_TO_KEY, RequestContext.getCurrentContext().getRequest().getRequestURI());
        }
        return null;
    }
}
아래와 같이 routing이 정상 동작하는 것을 알 수 있습니다.
$ http :8080/get?route=true
HTTP/1.1 200
Date: Sun, 08 Dec 2019 10:14:52 GMT
Transfer-Encoding: chunked

{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip",
    "Host": "httpbin.org",
    "User-Agent": "ReactorNetty/0.9.0.RELEASE"
  },
  "origin": "###########",
  "url": "https://httpbin.org/get"
}


$ http :8080/get?route=false
HTTP/1.1 200
Content-Length: 5
Content-Type: text/plain;charset=UTF-8
Date: Sun, 08 Dec 2019 10:14:56 GMT

false