한마디로 정리하면 Api gateway인 동시에 load balancer(ribbon) 역활을 한다고 보면된다.
장점
인증 및 보안 — 각 리소스에 대한 인증 요구 사항을 식별하고이를 만족시키지 않는 요청을 거부합니다.
통찰력 및 모니터링 — 의미있는 데이터 및 통계를 가장자리에서 추적하여 정확한 생산보기를 제공합니다.
동적 라우팅 — 요청을 다른 백엔드 클러스터로 동적으로 라우팅합니다.
스트레스 테스팅 (Stress Testing) — 성능 측정을 위해 점차적으로 클러스터 트래픽을 증가시킵니다.
Load Shedding — 각 유형의 요청에 대해 용량을 할당하고 제한을 초과하는 요청을 삭제합니다.
정적 응답 처리 — 내부 클러스터로 전달하는 대신 가장자리에서 일부 응답을 직접 작성합니다.
다중 지역 복원력 — ELB 사용을 다양 화하고 회원에게 더 가까이 나아갈 수 있도록 AWS 지역에서 요청을 라우팅합니다.
build.gradle
buildscript {
ext {
springBootVersion = '2.0.3.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.zuul'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
ext {
springCloudVersion = 'Finchley.RELEASE'
}
dependencies {
compile('org.springframework.cloud:spring-cloud-starter-config')
compile group: 'org.springframework.cloud', name:'spring-cloud-starter-netflix-zuul'
compile group: 'org.springframework.cloud', name:'spring-cloud-starter-netflix-eureka-client'
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-hystrix'
testCompile('org.springframework.boot:spring-boot-starter-test')
compile('org.springframework.boot:spring-boot-starter-actuator')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
bootstrap.yml
zuul:
threadPool:
useSeparateThreadPools: true
routes:
prefix: /api
ribbon:
eager-load:
enabled: true
sensitiveHeaders: Cookie,Set-Cookie
ignored-services: "*"
gateway:
path: /test/**
serviceId: gateway
stripPrefix: false
serviceA:
url: http://localhost:8888
serviceId: serviceA
ribbonIsolationStrategy: THREAD
zuul properties
serviceId(eureka 연동) 및 물리적인 url로 매칭시킬수 있다.
예
url: http://example.com 로 매칭시
http://localhost:8899/api/ 로 접속시 http://example.com/users_service이 호출되는구조
http://localhost:8899/api/1 => http://example.com/1
stripPrefix : false
prefix를 그대로 전달할지 여부
true
http://127.0.0.1:8899/api/test/
=>
false
http://127.0.0.1:8899/api/test/
=>
zuul.retryable
리본 클라이언트가 실패한 요청을 자동으로 다시 시도합니다
zuul.addProxyHeaders = true
X-Forwarded-Host 자동으로 추가
sensitiveHeaders : Cookie,Set-Cookie,Authorization // 기본값
해당값이 기본값이며 즉 블랙리스트 값이다
sensitiveHeaders : 로 비워두면 쿠키등이 그대로 다운스트림에 전달된다.
forceOriginalQueryStringEncoding: true
쿼리 문자열 원본 그대로 가져오기
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
ribbon:
ConnectTimeout: 3000
ReadTimeout: 60000
해당 설정 2개가 세트다
hystrix를 쓰는경우 리본의 타임아웃설정은 63000 가 될것이고 hystrix는 60000 초이다
리본에서는 retry 횟수가 1이면
총 2회 (63000 x2) 126000 초 이상의 hystrix 타임아웃 시간이 필요하다.
application.java
package com.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
@EnableEurekaClient
@EnableHystrix
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
Hystrix fallback provider 구성
import com.netflix.hystrix.exception.HystrixTimeoutException;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
public class MyFallBackProvider implements FallbackProvider {
@Override
public String getRoute() {
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
System.out.println(cause);
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private ClientHttpResponse response(final HttpStatus status) {
System.out.println("status::"+status);
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
}
@Override
public int getRawStatusCode() throws IOException {
return status.value();
}
@Override
public String getStatusText() throws IOException {
return status.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("fallback".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
@Bean
public MyFallBackProvider getMyFallbackProvider() {
return new MyFallBackProvider();
}
참조
Originally published at https://erea.tistory.com on July 3, 2018.