Debugging Sparrow

API Gateway with Zuul 2.0 (2)

2018/08/26 Zuul API Gateway Zuul Filters

Zuul 2.0 Filter

늦었지만 Zuul 2.0의 Architecture입니다. 대략적으로 Netty Server가 Request를 받아 세 가지 Filter를 거쳐 Response 하는 구조를 되어 있는데요. Filter는 이렇듯 Zuul에서 핵심이 되는 요소입니다. Zuul은 Filter들을 동적으로 컴파일 하고 불러올 수 있기 때문에 장애에 빠른 대처가 가능해집니다. 또한 Filter 간에는 context를 이용하여 상태를 공유합니다. Filter는 적용되는 시점에 따라 크게 세 가지로 나눌 수 있습니다.

  • Inbound Filters Routing 이전에 실행되는 Filter들입니다. 인증, 동적 라우팅 혹은 request를 decorating 하는데 이용할 수 있습니다.
  • Endpint Filters Inbound Filter의 요청에 따라 응답을 처리합니다. 정적 응답(static response)을 주거나 Origin으로 라우팅 하여 각각의 서비스에 요청을 보낼 수 있습니다.
  • Outbound Filters Backend로부터 응답을 받은 후에 실행되는 필터입니다. 통계를 저장하거나 response를 decorating 하는데 이용합니다.

Filter는 또한 다음의 요소들을 가집니다.

  • Type Inbound, Endpoint, Outbound Filters에 따라 나뉩니다. 그뿐만 아니라 사용자가 직접 이름을 정의하여 지정할 수 있습니다.
  • Async Filter가 sync 한 가 async 한가에 대한 정의입니다. Zuul은 event loop를 사용하기 때문에 필터 안에서 block이 일어나는 것은 치명적일 수 있습니다. 로직에 따라 sync와 async를 다르게 선택해야 합니다. 필터가 많은 작업을 수행하지 않고 Block이 없는 경우 Sync Filter를 외부의 서비스에서 데이터를 가져오거나 작업량이 많은 경우 Async Filter를 사용해야 합니다.
  • Execution Order Filter들 사이에 실행 순서를 정의합니다.
  • Criteria 필터를 실행하기 위해 필요한 조건들을 정의합니다.
  • Action Criteria가 충족되었을 때 실행할 로직입니다.

실제로 Filter의 Source를 살펴보아야 제대로 감이 올 것 같습니다. Filter들은 이전에 살펴본 소스의 다음의 경로에 있습니다. zuul-sample/src/main/groovy/com/netflix/zuul/sample/filters
경로 안에는 inbound, endpoint, outbound 세 가지 디렉터리가 있는데요. inbound 디렉터리 안에 Routes.groovy를 살펴봅시다.

Routes.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Routes extends HttpInboundSyncFilter {

@Override
int filterOrder() {
return 0
}

@Override
boolean shouldFilter(HttpRequestMessage httpRequestMessage) {
return true
}

@Override
HttpRequestMessage apply(HttpRequestMessage request) {
SessionContext context = request.getContext()
String path = request.getPath()
String host = request.getOriginalHost()

// Route healthchecks to the healthcheck endpoint.;
if (path.equalsIgnoreCase("/healthcheck")) {
context.setEndpoint(Healthcheck.class.getCanonicalName())
}
else {
context.setEndpoint(ZuulEndPointRunner.PROXY_ENDPOINT_FILTER_NAME);
context.setRouteVIP("api")
}

return request
}
}

inbound direcotry에 있기 때문에 Type은 Inbound, HttpInboundSyncFilter를 상속하므로 Sync 필터입니다. filterOrder 메서드에에 return이 0이며 shouldFilter의 return이 항상 true인 걸 보아 항상 가장 먼저 실행되는 필터임을 알 수 있습니다. apply 메서드은 필터의 실제 동작을 나타냅니다.

path가 healthcheck인 경우 Endpoint Filter 중 하나인 Healthcheck를 endpoint로 지정해줍니다. $ ./gradlew run을 통해 Zuul을 실행시킨 후 브라우저를 통해 http://localhost:7001/healthcheck로 접속하면 healthy라는 텍스트가 나옵니다. zuul-sample/src/main/groovy/com/netflix/zuul/sample/filters/endpoint/Healthcheck.groovy파일을 통해 소스를 확인할 수 있습니다.

path가 healthy가 아닌 경우 ZuulEndPointRunner.PROXY_ENDPOINT_FILTER_NAME를 endpoint로 지정해줍니다. ZuulEndPointRunner.PROXY_ENDPOINT_FILTER_NAME는 ProxyEndpoint를 나타내는데 ProxyEndpoint는 zuul-core에서 기본 제공하는 build-in 프록시 필터입니다. setRouteVIP는 eureka에서 사용하는 virtual address를 지정해주는 메서드입니다. 이 과정은 context를 통해 이루어져 Filter들 간의 통신/상태 공유를 가능하게 합니다.

Filter File Manager

앞서 zuul은 filter를 동적으로 로딩할 수 있다고 말씀드린 것 기억하시나요?

zuul-sample/src/main/groovy/com/netflix/zuul/sample/filters/endpoint/Healthcheck.groovy를 변경해봅시다. healthy가 아닌 다른 텍스트로 바꿔보고 브라우저를 통해 접속하면 변경된 텍스트가 나타납니다.

Zuul에는 Filter File Manager가 있습니다. Filter File Manager는 필터 파일들을 관리하는 역할을 하고 Poller가 있어 일정 주기마다 file의 변경을 체크합니다.

FilterFileManager.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void startPoller() {
poller = new Thread("GroovyFilterFileManagerPoller") {
public void run() {
while (bRunning) {
try {
sleep(config.getPollingIntervalSeconds() * 1000);
manageFiles();
}
catch (Exception e) {
LOG.error("Error checking and/or loading filter files from Poller thread.", e);
}
}
}
};
poller.start();
}

다음은 Filter들의 사용 예시를 살펴봅니다.

Author: dbgsprw

Link: https://dbgsprw.github.io/2018/08/26/zuul2/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
JWT RS256
NextPost >
API Gateway with Zuul 2.0 (1)
CATALOG
  1. 1. Zuul 2.0 Filter
  2. 2. Filter File Manager