티스토리 뷰
1. Overview
출처 : https://medium.com/netflix-techblog/announcing-zuul-edge-service-in-the-cloud-ab3af5be08ee
만약 GATEWAY를 직접 개발해야 한다면 ...
- 요청 메소드(GET/POST/PUT/DELETE)에 따라 값을 처리하는 것을 구현해야 한다.
- 요청할 때 값은 KEY, VALUE의 파라미터 형태일 수도 있고 REQUEST BODY 형태일 수도 있다.
- 요청에 따라 전달 하는 API 서버에 주소로 전달을 해야 한다.
- API 주소는 "/USER/{USER_ID}" 이런식으로 동적으로 만들어야 할 수도 있다.
- 요청/결과에 대한 로그를 관리해야 할 수 있다.
- 에러에 대한 처리가 필요하다.
- 인증 처리, 모니터링, 서비스 제어 등등을 모두 구현해야 한다.
직접 처음부터 개발할려면 할게 참 많습니다.
ZUUL PROXY가 하는 일
- URL 패턴에 따라 자동으로 API SERVER에 전달해준다.
- 전처리, 후처리, 추가처리, 에러처리에 대해 손쉽게 개발할 수 있게 도와준다.
2. DEPENDENCY 설정(Gradle)
1 2 3 4 5 6 | dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.cloud:spring-cloud-starter-netflix-zuul' providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' testImplementation 'org.springframework.boot:spring-boot-starter-test' } | cs |
3. Application 설정
@EnableZuulProxy 및 필터 추가
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 | @SpringBootApplication @EnableZuulProxy public class ZuulDemoApplication { public static void main(String[] args) { SpringApplication.run(ZuulDemoApplication.class, args); } @Bean public PreFilter preFilter() { return new PreFilter(); } @Bean public PostFilter postFilter() { return new PostFilter(); } @Bean public ErrorFilter errorFilter() { return new ErrorFilter(); } @Bean public RouteFilter routeFilter() { return new RouteFilter(); } } | cs |
4. 필터 설정
출처 : https://medium.com/netflix-techblog/announcing-zuul-edge-service-in-the-cloud-ab3af5be08ee
<Filter>
Filter | Description |
---|---|
Pre filter |
요청 전 처리 필터 인증 처리, 헤더에서 토큰을 읽어서 유효성 체크를 한다. 요청 로그, 서비스 제한 등 처리 |
Custem filter |
추가 처리 필터 |
Routing filter | 라우팅 필터 조건에 따라 특정 서버로 전달하고자 할 때 이곳에 로직 구현. |
Post filter |
후 처리 필터 결과 로그, 서비스 모니터링 처리 |
Error filter |
에러 처리 필터 에러에 따른 공통 처리 |
샘플 코드
PreFilter.java
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 31 32 33 34 35 36 37 38 39 40 41 42 | public class PreFilter extends ZuulFilter { private final Logger log = LoggerFactory.getLogger(getClass()); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.debug("Request Method : " + request.getMethod()); log.debug("Request URL : " + request.getRequestURL().toString()); String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION); if (!validateToken(authorizationHeader)) { ctx.setSendZuulResponse(false); ctx.setResponseBody("API key not authorized"); ctx.getResponse().setHeader("Content-Type", "text/plain;charset=UTF-8"); ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); } return null; } private boolean validateToken(String tokenHeader) { // do something to validate the token return true; } } | cs |
RouteFilter.java
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 | public class RouteFilter extends ZuulFilter { private final Logger log = LoggerFactory.getLogger(getClass()); @Override public String filterType() { return "route"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { log.debug("Route Filter"); return null; } } | cs |
PostFilter.java
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 | public class PostFilter extends ZuulFilter { private final Logger log = LoggerFactory.getLogger(getClass()); @Override public String filterType() { return "post"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { log.debug("Post Filter"); return null; } } | cs |
ErrorFilter.java
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 | public class ErrorFilter extends ZuulFilter { private final Logger log = LoggerFactory.getLogger(getClass()); @Override public String filterType() { return "error"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return RequestContext.getCurrentContext().getThrowable() != null; } @Override public Object run() { Throwable throwable = RequestContext.getCurrentContext().getThrowable(); log.error("Exception was thrown in filters: ", throwable); return null; } } | cs |
5. application.properties 설정
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #Zuul routes. "/user-service" 이 경로로 유입되는 것은 http://localhost:8083 서버로 연결한다. zuul.routes.user-service.path=/user-service/** zuul.routes.user-service.url=http://localhost:8083 # 예외, 무시 처리 zuul.ignored-services='*' zuul.ignored-patterns=/**/admin/** # 타임아웃 설정 zuul.host.connect-timeout-millis=3000 zuul.host.socket-timeout-millis=3000 #Ribbon 사용 안함. ribbon.eureka.enabled=false # 서버 포트 설정 server.port=8080 | cs |
6. 에러 페이지 처리
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @RestController public class CustomErrorController implements ErrorController { private static final String ERROR_PATH = "/error"; @Override public String getErrorPath() { return ERROR_PATH; } @RequestMapping(ERROR_PATH) public Map<String, String> handleError(HttpServletRequest request, HttpServletResponse response) { Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); HttpStatus httpStatus = HttpStatus.valueOf(Integer.valueOf(status.toString())); Map<String, String> errorMsg = new HashMap<>(); errorMsg.put("code", status.toString()); errorMsg.put("msg", httpStatus.getReasonPhrase()); return errorMsg; } } | cs |
404 에러 페이지 호출 시 결과 화면
{"msg":"Not Found","code":"404"}
7. API 서비스 개발 : User 서비스
Spring Starter Project로 간단한 서비스 개발
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 31 32 33 | @RestController public class UserTestController { private final Logger log = LoggerFactory.getLogger(getClass()); @Autowired private UserService userService; @GetMapping("/users/{userId}") public User getUser(User pUser) { return userService.getUser(pUser); } @PostMapping("/users") public User addUser(User pUser) { userService.addUser(pUser); return pUser; } @PutMapping("/users/{userId}") public String modifyUser(@RequestBody String jsonStr, User pUser) { log.debug(jsonStr); //userService.modifyUser(pUser); return jsonStr; } @DeleteMapping("/users/{userId}") public User removeUser(User pUser) { userService.removeUser(pUser); return pUser; } } | cs |
1 2 | #web server server.port=8083 | cs |
8. 테스트
User 서비스 Run
http://localhost:8083/users/1234
1234 고객 정보 조회 확인
Gateway 서비스를 실행 및 호출
http://localhost:8080/user-service/users/1234
user-service : http://localhost:8083 서버로 연결 된다.
참고
넷플릿스 GITHUB
- https://github.com/Netflix/zuul
Announcing Zuul: Edge Service in the Cloud
- https://medium.com/netflix-techblog/announcing-zuul-edge-service-in-the-cloud-ab3af5be08ee
'Spring Frameworks' 카테고리의 다른 글
[spring boot]@RestController json null 값 처리 및 결과 타입 변환 (2) | 2020.04.14 |
---|---|
Spring framework legacy 시스템 redis 연동 예제 (0) | 2020.01.03 |
Spring boot - RestTemplate 설정(Timeout, socketTimeOut) (2) | 2019.03.07 |
Spring boot - Assert 사용 예제 (0) | 2019.03.05 |
Spring boot - Object Mapper (0) | 2019.03.04 |
- Total
- Today
- Yesterday
- springboot
- 스프링부트
- listToMap
- 스프링
- java
- UI
- 샘플
- lombok
- 메시지
- AG-GRID
- restful서비스
- REST
- mapToList
- Spring Boot
- 그리드
- 예제
- thymeleaf
- 설정
- SHEETJS
- example
- Javascript
- oracle
- cache
- 엑셀
- ag grid
- RESTful
- 타임리프
- sample
- mybatis
- spring
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |