javascript
Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用
文章目錄
- Spring Cloud【Finchley】專欄
- 概述
- HTTP方式之RestTemplate
- 方式一 (直接使用restTemplate訪問URL,url寫死)
- 方式二 (使用LoadBalancerClient通過應用名獲取url,拼裝請求地址,然后再使用restTemplate)
- 方式三 (使用@LoadBalanced注解)
- Fegin 的使用
- 商品微服務獲取商品列表功能開發
- Product微服務查詢商品列表功能開發
- DAO層
- Service層
- Controller層
- Order微服務調用接口查詢商品列表
- 增加接口方法
- 調用商品微服務扣庫存功能開發
- Product微服務減庫存功能開發
- 減庫存的參數 DTO封裝
- Service
- Controller層
- Order微服務調用接口扣減庫存
- 增加接口方法
- 整合
- 測試
- 知識點小結
- 點1
- 點2
- Github
Spring Cloud【Finchley】專欄
如果還沒有系統的學過Spring Cloud ,先到我的專欄去逛逛吧
Spring Cloud 【Finchley】手札
概述
還記得上篇博文的TODO吧
這里我們先循序漸進的了解下,微服務之間調用的幾種方式
先了解下應用之間的通行的主要兩種方式
- RPC – 代表 Dubbo (可以基于TCP協議,也可以基于HTTP協議)
- HTTP —代表 Spring Cloud (基于HTTP協議)
HTTP方式之RestTemplate
我們在order微服務調用product微服務。
product作為服務端,先對外暴露個測試接口
order作為客戶端調用該接口
方式一 (直接使用restTemplate訪問URL,url寫死)
訪問 http://localhost:8081/order/getServerInfoFromClient
寫死的地址,并且只能請求一個,如果有多個地址就比較麻煩了。而且還是IP地址。
方式二 (使用LoadBalancerClient通過應用名獲取url,拼裝請求地址,然后再使用restTemplate)
package com.artisan.order.controller;import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;@RestController @Slf4j @RequestMapping("/order") public class ClientController {@Autowiredprivate LoadBalancerClient loadBalancerClient;@GetMapping("/getServerInfoFromClient")public String requestServer(){// 方式二 (使用LoadBalancerClient通過應用名獲取url,拼裝請求地址,然后再使用restTemplate)RestTemplate restTemplate2 = new RestTemplate();ServiceInstance serviceInstance = loadBalancerClient.choose("ARTISAN-PRODUCT");// 獲取ip port 組裝urlString url = String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort() + "/product/serverMsg");log.info("url:{}",url);String msg = restTemplate2.getForObject(url,String.class);log.info("msg from server : {}", msg);return msg;} }loadBalancerClient.choose("ARTISAN-PRODUCT"); 通過loadBalancerClient 選擇 注冊到Eurek Server上的ip . 需要填寫注冊到注冊中心的名字ARTISAN-PRODUCT。
訪問 http://localhost:8081/order/getServerInfoFromClient
方式三 (使用@LoadBalanced注解)
先初始化RestTemplate , 標注 @LoadBalanced 注解
package com.artisan.order.config;import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate;@Configuration public class RestTemplateConfig {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();} } package com.artisan.order.controller;import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;@RestController @Slf4j @RequestMapping("/order") public class ClientController {@Autowiredprivate RestTemplate restTemplate;@GetMapping("/getServerInfoFromClient")public String requestServer(){// 方式三 (使用@LoadBalanced注解)String msg = restTemplate.getForObject("http://ARTISAN-PRODUCT/product/serverMsg",String.class);log.info("msg from server : {}", msg);return msg;} }請求的地址 http://ARTISAN-PRODUCT/product/serverMsg 注冊到服務中心上的服務
訪問 http://localhost:8081/order/getServerInfoFromClient
Fegin 的使用
Spring Cloud【Finchley】-06服務消費者整合Feign
總體來說,在作為客戶端的order微服務中, 步驟如下
pom.xml 添加依賴
添加注解@EnableFeignClients
編寫client接口
package com.artisan.order.client;import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping;// name為注冊在注冊中心上的名稱 @FeignClient(name="ARTISAN-PRODUCT") public interface ProductClient {// ARTISAN-PRODUCT微服務接口的訪問路徑@GetMapping("/product/serverMsg")String getServerInfo(); }調用
package com.artisan.order.controller;import com.artisan.order.client.ProductClient; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;@RestController @Slf4j @RequestMapping("/order") public class FeginClientController {@Autowiredprivate ProductClient productClient;@GetMapping("/getServerInfoByFeign")public String requestServer(){String msg = productClient.getServerInfo();log.info("msg from server : {}", msg);return msg;} }訪問 http://localhost:8081/order/getServerInfoByFeign
OK。
商品微服務獲取商品列表功能開發
熟悉了基本使用后,剛開始說了,我們有幾個TODO要做,那開始吧
Product微服務查詢商品列表功能開發
我們看下前臺會傳遞什么給我們
關于商品的信息,productId是個集合,那么我們就需要提供一個根據傳入的productId列表來返回product集合的功能。
DAO層
老規矩,DAO層先
單元測試下,
結合庫表中的數據
單元測試通過
Service層
緊接著Service層
實現類
單元測試
單元測試通過
Controller層
/*** 根據productIdList 查詢商品列表* 提供給Order微服務用* @param productIdList* @return*/@PostMapping("/productListForOrder")private List<Product> getProductForOrder(@RequestBody List<String> productIdList){return productService.getProductList(productIdList);}Order微服務調用接口查詢商品列表
增加接口方法
返回的類型是個Product集合,我們先從product微服務那邊將Product copy一份過來。 后續會優化這些地方。
我們寫個方法來測試下這個功能, 那就在剛才的用作測試的FeginClientController類中寫個方法吧
當參數中標注了@RequestBody , 則必須使用POST方法
啟動服務,測試下 http://localhost:8081/order/getProductList
可見功能是OK的。
調用商品微服務扣庫存功能開發
Product微服務減庫存功能開發
減庫存的參數 DTO封裝
我們看下前臺會傳遞什么給我們
肯定是 某個產品 扣除多少個數量。 []可以傳遞多個,對于后臺來講是個集合 。
Product微服務需要兩個參數 productId 和 productQuantity
Service
分析下,扣減庫存,直接使用JPA內置的方法即可,DAO層可以省略了。
直接來Service吧 ,直接寫實現類中的方法你把
@Override// 因為是對List操作,所以加個事務控制@Transactionalpublic void decreaseProduct(List<CartDTO> cartDTOList) {// 遍歷CartDTOfor (CartDTO cartDTO : cartDTOList) {// 根據productId查詢ProductOptional<Product> productOptional = productRepository.findById(cartDTO.getProductId());// 商品是否存在if (!productOptional.isPresent()) {throw new ProductException(ResultEnum.PRODUCT_NOT_EXIST);}// 是否庫存充足Product product = productOptional.get();int leftStock = product.getProductStock() - cartDTO.getProductQuantity();if (leftStock < 0 ){throw new ProductException(ResultEnum.PRODUCT_STOCK_ERROR);}// 將剩余庫存設置到product,并更新數據庫product.setProductStock(leftStock);productRepository.save(product);}}因為是對List操作,所以加個事務控制 @Transactional
單元測試
@Testpublic void decreaseProductTest() {CartDTO cartDTO = new CartDTO();cartDTO.setProductId("3");cartDTO.setProductQuantity(2);productService.decreaseProduct(Arrays.asList(cartDTO));}測試前數據
Controller層
Order微服務調用接口扣減庫存
增加接口方法
ProductClient接口新增方法
測試下 ,在 FeginClientController 新增個方法 (這個Controller和工程無關哈,僅僅是用來測試用的)
訪問 http://localhost:8081/order/decreseProduct
整合
Product微服務要提供的功能及Order微服務調用都開發完了,那整合到業務邏輯中吧
@Overridepublic OrderDTO createOrder(OrderDTO orderDTO) {String orderId = KeyUtil.genUniqueKey();// 查詢商品信息(調用商品微服務)List<String> productIdList = orderDTO.getOrderDetailList().stream().map(OrderDetail::getProductId).collect(Collectors.toList());List<Product> productList = productClient.getProductForOrder(productIdList);// 計算訂單總價BigDecimal orderAmout = new BigDecimal(BigInteger.ZERO);for (OrderDetail orderDetail: orderDTO.getOrderDetailList()) {for (Product product: productList) {if (product.getProductId().equals(orderDetail.getProductId())) {//單價*數量orderAmout = product.getProductPrice().multiply(new BigDecimal(orderDetail.getProductQuantity())).add(orderAmout);BeanUtils.copyProperties(product, orderDetail);orderDetail.setOrderId(orderId);orderDetail.setDetailId(KeyUtil.genUniqueKey());//訂單詳情入庫orderDetailRepository.save(orderDetail);}}}// 扣減庫存(調用商品微服務)List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream().map(e -> new CartDTO(e.getProductId(), e.getProductQuantity())).collect(Collectors.toList());productClient.decreseProduct(cartDTOList);//訂單入庫Order order = new Order();orderDTO.setOrderId(orderId);// 復制屬性BeanUtils.copyProperties(orderDTO, order);// 設置其他屬性order.setOrderAmount(orderAmout);order.setOrderStatus(OrderStatusEnum.NEW.getCode());order.setPayStatus(PayStatusEnum.WAIT.getCode());orderRepository.save(order);return orderDTO;}測試
[{"productId": "1","productQuantity": 2 }, {"productId": "2","productQuantity": 5 }, {"productId": "3","productQuantity": 10 }]買 1號商品 2個 ,金額 20.99乘以2 = 41.98
買 2號商品 5個 ,金額 7.5乘以5 = 37.5
買 3號商品 10個 ,金額 15乘以10 = 150
總金額 229.48
原始庫存:
使用POSTMAN測試一把
檢查下總金額,庫存扣減,及order_detail中的數據
artisan_order
order_detail 3條記錄
庫存:
OK
知識點小結
點1
Spring MVC在接收集合請求參數時,需要在Controller方法的集合參數里前添加@RequestBody
List<Product> getProductForOrder(@RequestBody List<String> productIdList)點2
當參數中標注了@RequestBody , 則必須使用POST方法
Github
artisan-product: https://github.com/yangshangwei/springcloud-o2o/tree/master/artisan_order
artisan_order: https://github.com/yangshangwei/springcloud-o2o/tree/master/artisan-product
總結
以上是生活随笔為你收集整理的Spring Cloud【Finchley】实战-03订单微服务与商品微服务之间的调用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Cloud【Finchle
- 下一篇: Spring Cloud【Finchle