2020. 1. 11. 23:31ㆍSpring Micro Services/Netflix Hystrix
- discovery-service: Eureka Server
- hystrix-dashboard: Hystrix DashBoard
- order-service
- payment-service
- product-service
Eureka Server 로서 제공되는 서비스 등록
server:
port: ${PORT:8761}
eureka:
client:
registerWithEureka: false
fetchRegistry: false
server:
waitTimeInMsWhenSyncEmpty: 0
- 서비스 포트 8761로 설정
package com.roopy.services.discovery; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class DiscoveryApplication { public static void main(String[] args) { SpringApplication.run(DiscoveryApplication.class, args); } }
- @SpringBootApplication, @EnableEurekaServer 어노테이션 설정
서비스 모니터링을 위한 DashBoard 화면 제공 및 환경 설정
spring: application: name: hystrix-turbine server: port: ${PORT:9090} eureka: client: serviceUrl: defaultZone: ${EUREKA_URL:http://localhost:8761/eureka/} turbine: aggregator: clusterConfig: ORDER-SERVICE,PAYMENT-SERVICE,PRODUCT-SERVICE appConfig: order-service,payment-service,product-service
spring: application: name: hystrix-turbine server: port: ${PORT:9090} eureka: client: serviceUrl: defaultZone: ${EUREKA_URL:http://localhost:8761/eureka/} turbine: appConfig: order-service,payment-service,product-service clusterNameExpression: "'default'"
package com.roopy.services.hystrix; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.netflix.turbine.EnableTurbine; @SpringBootApplication @EnableDiscoveryClient @EnableHystrixDashboard @EnableTurbine public class HystrixApplication { public static void main(String[] args) { SpringApplication.run(HystrixApplication.class, args); } }
- @SpringBootApplication, @EnableDiscoveryClient, @EnableHystrixDashboard, @EnableTurbine 어노테이션 설정
주문서비스에서 사용자가 주문요청을 하였는데 만약 결제서비스에 장애가 생겨서 정상적인 주문처리가 안되었을 경우 사용자에게는 주문이 실패하였다는 내용만 알려주면 되겠지만
server: port: ${PORT:8060} eureka: client: serviceUrl: defaultZone: ${EUREKA_URL:http://localhost:8761/eureka/} spring: application: name: order-service datasource: hikari: connection-test-query: SELECT 1 minimum-idle: 1 maximum-pool-size: 5 driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:5432/postgres username: postgres password: admin!@34 jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect show-sql: true properties: hibernate: format_sql: true temp.use_jdbc_metadata_defaults: false hibernate: naming: implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl management: endpoints: web: exposure: include: "*"
package com.roopy.services.order; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } @LoadBalanced @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } }
- @SpringBootApplication, @EnableDiscoveryClient, @EnableCircuitBreaker 어노테이션 설정
package com.roopy.services.order.controller; import java.util.HashMap; import java.util.LinkedHashMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.http.HttpStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.roopy.services.order.domain.Order; import com.roopy.services.order.helper.IDGeneratorHelper; import com.roopy.services.order.service.IOrderService; import com.roopy.services.order.service.IPaymentService; import com.roopy.services.order.service.IProductService; @RestController public class OrderController { @Autowired private IOrderService orderService; @Autowired private IPaymentService paymentService; @Autowired private IProductService productService; @Autowired private IDGeneratorHelper idGenerator; @RequestMapping(value = "/order", method = RequestMethod.POST) public HashMap<String,Object> order(HttpServletRequest request, HttpServletResponse response, @RequestBody Order order) throws Exception { // 주문결과 HashMap<String,Object> retObj = new LinkedHashMap<String,Object>(); // 주문ID생성 String orderId = idGenerator.getOrderId(); // 주문ID설정 order.setOrderId(orderId); // 주문날짜설정 order.setOrderDtm(orderId.substring(1)); // 결제처리결과 HashMap<String,Object> paymentRetObj = paymentService.payment(order); // 결제처리결과코드 int paymentResultCode = (int) paymentRetObj.get("code"); // 주문정보저장 if (paymentResultCode == 200) { // 상품재고수량 업데이트 HashMap<String,Object> productRetObj = productService.updateProductQty(order); // 상품재고수량처리 결과 코드 int productStockQtyUpdateResultCode = (int) productRetObj.get("code"); if (productStockQtyUpdateResultCode == -1) { // 결제처리시 오류가 발생하더라도 이력을 남기기 위해서 주문대기상태로 저장한다. orderService.save(order); retObj.put("code", -1); retObj.put("msg", productRetObj.get("msg")); retObj.put("data", order); return retObj; } // 결제처리, 상품재고수량 업데이트 처리가 정상적으로 된경우 주문상태코드를 완료 변경 처리 order.setOrderStatus("C"); // 주문정보저장 orderService.save(order); } else { // 결제처리시 오류가 발생하더라도 이력을 남기기 위해서 주문대기상태로 저장한다. orderService.save(order); retObj.put("code", -1); retObj.put("msg", paymentRetObj.get("msg")); retObj.put("data", order); return retObj; } // 최종주문정보 조회 retObj.put("code", HttpStatus.SC_OK); retObj.put("msg", "[" + order.getOrderId() + "] 주문처리가 정상적으로 처리 되었습니다."); return retObj; } @RequestMapping(value = "/order/{orderId}", method = RequestMethod.POST) public Order findOrder(HttpServletRequest request, HttpServletResponse response, @PathVariable String orderId, @RequestParam HashMap<String, String> param) throws Exception { return orderService.find(orderId); } }
- 서비스 호출 부분은 서비스 클래스에 호출 한다.
package com.roopy.services.order.service.impl; import java.util.HashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.roopy.services.order.domain.Order; import com.roopy.services.order.service.IPaymentService; @Service public class PaymentServiceImpl implements IPaymentService { private static final Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class); @Autowired private RestTemplate restTemplate; @Override @HystrixCommand(commandKey = "order-service.payment", fallbackMethod = "paymentFallBack") public HashMap<String,Object> payment(Order order) throws Exception { HashMap<String,Object> retObj = new HashMap<String,Object>(); HttpEntity<Order> ordRequest = new HttpEntity<>(order); ResponseEntity<String> response = null; try { response = restTemplate.postForEntity("http://payment-service/payment", ordRequest, String.class); } catch (Exception e) { e.printStackTrace(); } retObj.put("code", response.getStatusCodeValue()); retObj.put("data", response.getBody()); return retObj; } /** * 결재 오류 시 호출되는 메소드 * * @param order * @return */ public HashMap<String,Object> paymentFallBack(Order order) { LOGGER.error("[" + order.getOrderId() + "] 주문 결제 서비스 처리 중 오류가 발생하였습니다."); HashMap<String,Object> retObj = new HashMap<String,Object>(); retObj.put("code", -1); retObj.put("data", order); retObj.put("msg", "[" + order.getOrderId() + "] 주문 결제 서비스 처리 중 오류가 발생하였습니다."); return retObj; } }
- payment 메소드의 @HystrixCommand를 설정하여서 장애 발생 처리를 할 수 있다.
package com.roopy.services.order.service.impl; import java.util.HashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.roopy.services.order.domain.Order; import com.roopy.services.order.service.IProductService; @Service public class ProductServiceImpl implements IProductService { private static final Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class); @Autowired private RestTemplate restTemplate; @Override @HystrixCommand(commandKey = "order-service.updateProductQty", fallbackMethod = "productFallBack") public HashMap<String,Object> updateProductQty(Order order) throws Exception { HashMap<String,Object> retObj = new HashMap<String,Object>(); HttpEntity<Order> ordRequest = new HttpEntity<>(order); ResponseEntity<Void> response = null; try { response = restTemplate.postForEntity("http://product-service/products", ordRequest, Void.class); } catch (Exception e) { e.printStackTrace(); } retObj.put("code", response.getStatusCodeValue()); retObj.put("data", order); return retObj; } /** * 결재 오류 시 호출되는 메소드 * * @param order * @return */ public HashMap<String,Object> productFallBack(Order order) { LOGGER.error("[" + order.getOrderId() + "] 상품재고 수량 업데이트 중 오류가 발생하였습니다."); HashMap<String,Object> retObj = new HashMap<String,Object>(); retObj.put("code", -1); retObj.put("data", order); retObj.put("msg", "[" + order.getOrderId() + "] 상품재고 수량 업데이트 중 오류가 발생하였습니다."); return retObj; } }
- 04.PaymentServiceImpl과 동일하게 처리된다.
01. application.yml
server: port: ${PORT:8070} eureka: client: serviceUrl: defaultZone: ${EUREKA_URL:http://localhost:8761/eureka/} spring: application: name: payment-service datasource: hikari: connection-test-query: SELECT 1 minimum-idle: 1 maximum-pool-size: 5 driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:5432/postgres username: postgres password: admin!@34 jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect show-sql: true properties: hibernate: format_sql: true temp.use_jdbc_metadata_defaults: false hibernate: naming: implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl management: endpoints: web: exposure: include: "*"
02. PaymentApplication.java
package com.roopy.services.payment; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker public class PaymentApplication { public static void main(String[] args) { SpringApplication.run(PaymentApplication.class, args); } }
- @SpringBootApplication, @EnableDiscoveryClient, @EnableCircuitBreaker 어노테이션 설정
01. application.yml
server: port: ${PORT:8090} eureka: client: serviceUrl: defaultZone: ${EUREKA_URL:http://localhost:8761/eureka/} spring: application: name: product-service datasource: hikari: connection-test-query: SELECT 1 minimum-idle: 1 maximum-pool-size: 5 driver-class-name: org.postgresql.Driver url: jdbc:postgresql://localhost:5432/postgres username: postgres password: admin!@34 jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect show-sql: true properties: hibernate: format_sql: true temp.use_jdbc_metadata_defaults: false hibernate: naming: implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl management: endpoints: web: exposure: include: "*"
02. ProductApplication.java
package com.roopy.services.product; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker public class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); } }
- @SpringBootApplication, @EnableDiscoveryClient, @EnableCircuitBreaker 어노테이션 설정
- Application 영역에 4개의 서비스가 보이면 정상
- 위와 같은 화면이 로딩 되면 정상
'Spring Micro Services > Netflix Hystrix' 카테고리의 다른 글
Spring Cloud: Netflix Hystrix - 테스트 (0) | 2020.01.11 |
---|