<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          海量訂單系統(tǒng)微服務(wù)開發(fā):訂單接口管理后臺微服務(wù)開發(fā)、集成測試

          共 9454字,需瀏覽 19分鐘

           ·

          2022-04-10 18:15


          訂單接口微服務(wù)開發(fā)

          在對數(shù)據(jù)庫進行單元測試之后,我們就可以開始微服務(wù)接口的開發(fā)了。完成數(shù)據(jù)庫的開發(fā)之后,接口的開發(fā)就很簡單了。下面的代碼展示了訂單數(shù)據(jù)查詢和訂單生成的設(shè)計實例:

          @RestController
          CRequestMapping("/order")@slf4j
          publicclass OrderRestController {
          @Autowired
          private orderService orderService;
          @GetMapping (value="/{id] ")
          public Mono<0rder> fnidById(@Pathvariable string id){
          return orderService.findById(id);
          )
          GetMapping()
          public Flux findAll(Integer index,Integer size,Long userid, Longmerchantid,
          Integer status, String start, String end){
          try{
          OrderQo orderQ0 = new OrderQo();
          if(CommonUtils.isNotNul1(index)){
          orderQo.setPage (index);
          if(CommonUtils.isNotNull(size)){
          orderQo .setsize(size);
          if(CommonUtils.isNotNull (userid)){
          orderQo.setUserid(userid);
          }
          if(CommonUtils.isNotNull (merchantid)){
          orderQo.setMerchantid (merchantid);
          }
          if(CommonUtils.isNotNull(status)){
          orderQo.setStatus(status);
          if(CommonUtils.isNotNull (start)){
          SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd
          HH:mm: ss"
          );
          orderQo .setstart(sdf.parse(start));
          }
          if(CommonUtils.isNotNull (end)){
          SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd
          HH:mm:sS"
          );
          orderQo .setEnd(sdf.parse(end));
          }
          return orderService.findAll (orderQo);}catch (Exception e){
          e.printStackTrace();)
          return null;
          CPostMapping()
          public Mono save (CRequestBody Ordergo orderQo) throws Exception{
          Order order = CopyUtil.copy(orderQo,Order.class);
          ListdetailList=
          CopyUtil.copyList(orderQo.get0rderDetails(),OrderDetail.class);
          order.setOrderDetails(detailList);
          Mono response = orderService.save (order).then();
          log.info("新增="+ response);
          return response;
          }
          }

          由于使用反應(yīng)式的編程方法,所以不管是哪一種接口的設(shè)計,最后都必須返回一個異步序列,例如 Mono或者Flux。

          對于分頁查詢,則必須檢測每個查詢參數(shù),只有非空,才提供相關(guān)的查詢條件。注意日期類型的參數(shù),因為在傳輸過程中只能使用文本方式,所以最后在提交查詢條件時必須將其轉(zhuǎn)換為日期類型的數(shù)據(jù)。

          在訂單生成的設(shè)計中,因為接收的參數(shù)是查詢對象OrderQo,所以最終必須將其轉(zhuǎn)換成文檔Order。每一個訂單明細(xì)都必須進行轉(zhuǎn)換。

          訂單的分布式事務(wù)管理

          集中式的數(shù)據(jù)管理可以在一個事務(wù)中完成,所以能保證數(shù)據(jù)的高一致性。微服務(wù)的多服務(wù)架構(gòu),使得數(shù)據(jù)可以由不同的微服務(wù)進行分散管理,所以想要保證數(shù)據(jù)的一致性,就必須有合理的設(shè)計。

          對于訂單來說,訂單的狀態(tài)變化與庫存、物流、評價等各個服務(wù)息息相關(guān),所以訂單的狀態(tài)變化,會涉及分布式事務(wù)管理的問題。比如當(dāng)買家撤銷訂單時,庫存服務(wù)的商品存量必須改變。

          對于分布式事務(wù)管理,我們可以依據(jù)CAP原理的BASE理論實現(xiàn)數(shù)據(jù)最終一致性設(shè)計。

          CAP (Consistency,Availability,Partition Tolerance)即一致性、可用性和分區(qū)容錯性,三者不可兼得。

          BASE(Basically Available,Soft State,Eventually Consistent)即基本可用、軟狀態(tài)和最終一致性。BASE是對CAP中一致性和可用性進行權(quán)衡的結(jié)果。

          在微服務(wù)設(shè)計中,數(shù)據(jù)最終一致性設(shè)計主要使用兩種方法實現(xiàn),一種是通過接口調(diào)用實現(xiàn)實時同步操作,另一種是使用消息通道以事件響應(yīng)的方式進行異步處理。

          這里,我們以訂單取消為例,使用異步消息傳輸,實現(xiàn)分布式事務(wù)管理的數(shù)據(jù)最終一致性設(shè)計。

          訂單取消的消息生成

          首先,在order-restapi模塊的項目對象模型配置中引入AMQP的消息組件依賴,代碼如下所示:

          <dependency>
          <groupId>org.springframework.cloudgroupId>
          <artifactId>spring-cloud-starter-bus-amapartifactId>dependency>

          其次,在配置文件中,設(shè)置連接RabbitMQ服務(wù)器的配置,代碼如下所示:

          spring:
          rabbitmq:
          addresses: amap://localhost:5672username: develop
          password: develop

          服務(wù)地址、端口、用戶名和密碼等參數(shù)需根據(jù)RabbitMQ的安裝和配置進行設(shè)定。

          下面,我們創(chuàng)建一個消息發(fā)送器MessageSender,用來發(fā)送 MQ (Message Queue),代碼如下所示:

          @service
          public class MessageSender {
          private static Logger logger =
          LoggerFactory.getLogger (MessageSender.class);
          @Autowired
          private AmqpTemplate amqpTemplate;
          public void OrderUpdateMsg (0rderQo orderQo){
          MessageProperties messageProperties = new MessageProperties();
          messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);messageProperties.setContentType("UTF-8");
          String json = new Gson().toJson(orderQo);
          Message message = new Message (json.getBytes(),messageProperties);
          amapTemplate.send ("ordermsg.update", message);//接收端必須使用相同隊列
          logger.info("發(fā)送訂單變更消息:{}", json) ;
          }
          }

          這里使用查詢對象 OrderQo發(fā)送了一個完整的訂單信息。

          注意,這里的消息隊列的名稱設(shè)定為“ordermsg.update”,所以,消息接收方必須使用這個隊列名稱才能接收訂單狀態(tài)變化的相關(guān)信息。

          最后,我們在訂單修改這個接口中,使用消息發(fā)送器生成一條異步消息,代碼如下所示:

          @RestController
          @RequestMapping ("/order")@Slf4j
          public class OrderRestController {
          @Autowired
          private OrderService orderService;
          @Autowired
          private MessageSender messageSender;
          CPutMapping()
          public Mono<Void> update (@RequestBody OrderQo orderQo) throws Exception{
          Order order = CopyUtil.copy(orderQo, Order.class);
          order.setModify(new Date());
          List<0rderDetail> detailList =
          CopyUtil.copyList(orderQo.get0rderDetails(),OrderDetail.class);
          order.setorderDetails(detailList);
          Mono<Void> response = orderService.save(order).then();
          //發(fā)送 MQ消息,通知訂單修改
          messageSender.OrderUpdateMsg(orderQo);
          log.info("修改="+ response);
          return response;
          }
          }

          上面就是在分布式事務(wù)設(shè)計中消息生產(chǎn)方的一個設(shè)計實例。接下來,我們看看消息消費者是如何接收消息的,以完成一次分布式事務(wù)的過程。

          訂單取消的庫存變化處理

          接收訂單取消的消息處理是在庫存管理項目goods-microservice的goods-restapi模塊中實現(xiàn)的。其中,MQ的組件依賴和服務(wù)器連接的配置與前面的基本相同。

          在庫存接口微服務(wù)應(yīng)用中,我們只需設(shè)計一個訂單消息接收器MessageOrderReceiver,就可以完成消息監(jiān)聽、消息接收、消息處理的相關(guān)操作,代碼如下所示:

          @component
          public class MessageOrderReceiver {
          private static Logger logger=
          LoggerFactory.getLogger (MessageOrderReceiver.class);
          @Autowired
          private GoodsService goodsService;
          @RabbitListener(queuesToDeclare = CQueue (value = "ordermsg.update"))public void orderUpdate (byte[] body) {
          logger.info("----------訂單變更消息處理----------");try {
          OrderQo orderQo =new Gson ().fromJson (new String (body,"UTF-8"),
          OrderQo.class);
          if(orderQo != null) {
          logger.info("接收訂單更新消息,訂單編號="+orderQo.getorderNo());
          if (orderQo.getStatus() !=null && orderQo.getStatus() <0){
          List<0rderDetailQ0> list = orderQo.getorderDetails();
          for (orderDetailQo orderDetailQ0 : list) {
          Goods goods =
          goodsService.getById(orderDetailQo.getGoodsid());
          if(goods != null){
          Integer num= goods.getBuynum() != null &&
          goods.getBuynum() >0?
          goods.getBuynum() - 1 :0;
          goods.setBuynum (num) ;
          goodsservice.update(goods);
          logger.info("更新了商品購買數(shù)量,商品名稱="+
          goods.getName ());
          }
          }
          }
          }
          }
          catch (Exception e) {
          e.printStackTrace();
          }
          }
          }

          這里我們監(jiān)聽了消息隊列“ordermsg.update”,將接收的消息轉(zhuǎn)換成查詢對象OrderQo,這樣,即可根據(jù)訂單狀態(tài)和訂單明細(xì)中的商品數(shù)據(jù),決定是否執(zhí)行商品庫存存量減少的操作,完成一次分布式事務(wù)管理的整個流程。

          訂單管理后臺微服務(wù)開發(fā)

          訂單管理后臺微服務(wù)是為商家提供的一個PC端的Web微服務(wù)應(yīng)用,它的設(shè)計在訂單微服務(wù)項目的order-web模塊中。在這個模塊中,含有微服務(wù)接口調(diào)用和頁面設(shè)計等內(nèi)容,與前面章節(jié)中類目管理和庫存管理項目的設(shè)計大同小異。這里只針對一些不同點進行詳細(xì)介紹,其他方面可以參照前面章節(jié)。

          訂單查詢主頁設(shè)計

          訂單后臺主頁控制器 OrderController的設(shè)計代碼如下所示:

          @RestController
          @RequestMapping(" /order")
          public class OrderController {
          private static Logger logger =
          LoggerFactory.getLogger(0rderController.class);
          @Autowired
          private OrderRestService orderRestservice;
          CRequestMapping(value="/index")
          public ModelAndView index (ModelMap model) throws Exceptionl
          //訂單狀態(tài)枚舉集合
          StatusEnum[] statuses
          = StatusEnum.values();model .addAttribute( "statuses", statuses);
          return new ModelAndview( "order/index");
          @RequestMapping (value="/{id}")
          public ModelAndview findById (@PathVariable String id,ModelMap model)
          String json
          = orderRestService.findById(id);
          OrderQo orderQo =new Gson().fromJson(json,OrderQo.class);
          model .addAttribute ("status",
          StatusEnum.valueOf(orderQo. getStatus()).getName ());
          model.addAttribute( " order",orderQo);
          return new ModelAndView("order/show");
          }
          @RequestMapping(value = "/list")
          public Page0bject>> findAll(Ordergo orderQo) throws Exception {
          String json = orderRestService.findPage (orderQo);
          Pageable pageable = PageRequest.of (orderQo.getPage(),orderQo.getSize (),
          null);
          List<0rderQo> list = new Gson() .fromJson(json,new
          TypeToken0rderQ0>>(){}.getType());
          for(0rderQo order : list){
          order.setStatusStr (StatusEnum. valueOf (order.getStatus()) .getName ());
          }
          String count =orderRestService.getCount();
          return new PageImpl(list, pageable, new Long (count));
          }
          }

          這幾個方法中都用到了訂單狀態(tài)的枚舉類型集合StatusEnum,使用這個集合,為我們在訂單查詢和參數(shù)轉(zhuǎn)換中提供很多方便。

          其中在分頁查詢中,調(diào)用了兩次訂單接口,一次用來取得訂單列表,另一次用來取得訂單總數(shù)。通過這個總數(shù),才能計算出總的頁數(shù)。另外,對于列表中訂單狀態(tài)的顯示,在這里提前進行了轉(zhuǎn)換處理,這樣在后面的頁面設(shè)計中,就可以直接使用。

          基于訂單狀態(tài)的枚舉集合,主頁頁面設(shè)計中的查詢條件設(shè)計代碼如下所示:

        2. <label class="preInpTxt f-left">訂單狀態(tài)label><select id="status" name="status">
          <option value="">全部option>
          <option th:each="status:${statuses]"
          th:value="$ {status.code}" th:text="${status.name}">
          option>
          select>
          li>

          通過引用訂單狀態(tài)的枚舉集合變量statuses,使用一個th:each循環(huán)語句,即可生成一個訂單狀態(tài)的下拉列表框。

          訂單狀態(tài)修改設(shè)計

          下面再來看看訂單狀態(tài)的修改設(shè)計。在控制器OrderController 的設(shè)計中,使用如下所示的實現(xiàn)方法:

          @RestController
          @RequestMapping ("/order")
          public class OrderController {
          private static Logger logger =
          LoggerFactory.getLogger (0rderController.class);
          @Autowired
          private OrderRestService orderRestService;
          @GetMapping (" /edit/{id}")
          public ModelAndView update (@PathVariable String id, HttpServletRequestregquest, Mode lMap model){
          String json = orderRestService.findById(id);
          OrderQo orderQo = new Gson().fromJson(json, OrderQo.class);
          //訂單狀態(tài)枚舉集合
          StatusEnum[I] statuses = StatusEnum.values();
          model.addAttribute( "statuses", statuses);
          model .addAttribute ( "order", orderQo);
          return new ModelAndView("order/edit");
          }
          CPostMapping(value="/update")
          public String update (0rderQo orderQo,HttpServletRequest request)[
          String json
          = orderRestService.findById(orderQo.getId());
          OrderQo newOrder = new Gson().fromJson(json, OrderQo.class);
          newOrder.setStatus (orderQo.getStatus());
          neworder.setModify (new Date());
          orderRestService. update (newOrder);
          logger.info("修改=" +orderQo.getId());return "1";
          }
          }

          這里,先從OrderRestService 中取得一個訂單數(shù)據(jù),然后在頁面edit.html 中展示出來。當(dāng)用戶在頁面上選擇一個訂單狀態(tài)并提交之后,就會調(diào)用OrderRestService的update方法,請求數(shù)據(jù)庫更新數(shù)據(jù)。

          在頁面edit.html的設(shè)計中,使用了一個彈出窗口,其完整代碼如下所示:

          <html xmlns:th="http://www.thymeleaf.org">
          <script th:src="@{/scripts/order/edit.js}">script><form id="saveForm" method-"post">
          <input type="hidden" name="id" id="id" th:value="$ {order.id} "/><div class="addInfBtn">
          <h3 class="itemTit"><span>子類信息span>h3><table class="addNewInfList">
          <tr>
          <th>訂單號th><td width="200">
          <input class="inp-list w-200 clear-mr f-left" type="text"
          th:value="${order.orderNo}" readonly="true" id="orderNo" name="orderNo"maxlength="16"/>

          td>
          <th>狀態(tài)th><td>
          <select id="status" name="status">
          <option th:each="status:${statuses]"
          th:value="${status.code] "
          th:text="${status.name } "
          th:selected="${status.code == order.status}"
          >
          /option>
          select>
          td>
          tr>
          table>
          <div class="bottomBtnBox">
          <a class="btn-93x38 saveBtn" href="javascript:void(0)">確定a>
          <a class="btn-93x38 backBtn" href="javascript:closeDialog (0)">返回a>div>
          div>
          form>

          其中,訂單編號設(shè)定為只讀狀態(tài),即不能被修改,而訂單的狀態(tài)使用一個下拉列表框來顯示。剛打開頁面時,原有的訂單狀態(tài)會處于已經(jīng)選定的狀態(tài)。這樣當(dāng)用戶在頁面上選擇另一個狀態(tài)進行提交時,就可以對訂單狀態(tài)進行修改操作了。

          集成測試

          在開發(fā)完成之后,需要進行一個集成測試。在這個集成測試中,會用到消息隊列,所以必須保證 RabbitMQ服務(wù)器已經(jīng)啟動,并且程序與服務(wù)器的連接配置都正確無誤。

          按下列順序啟動各個微服務(wù)模塊:

          (1)庫存管理微服務(wù)API應(yīng)用:goods-restapi。(2)訂單微服務(wù)應(yīng)用接口設(shè)計: order-restapi。(3)訂單后臺管理應(yīng)用: order-web。

          上面各個模塊啟動成功之后,通過瀏覽器打開如下鏈接地址,即訂單Web應(yīng)用的后臺管理首頁:

          http: //Localhost:8095

          如果打開成功,并且已有訂單數(shù)據(jù),則可以看到如圖8-3所示頁面。


          現(xiàn)在我們編輯一下訂單,選擇圖8-3中第一個已付款的訂單,單擊“編輯”選項,在彈出的編輯窗口中,將狀態(tài)修改為已撤銷,如圖8-4 所示。


          單擊“確定”按鈕,如果頁面上返回了編輯成功的提示,則說明修改操作已經(jīng)完成。查看訂單接口和庫存接口的控制臺輸出日志,看看分布式事務(wù)的消息是否已經(jīng)處理完成。如果一切正常,則可以在訂單接口的控制臺中看到如下所示的輸出日志:

          MessageSender -發(fā)送訂單變更消息:

          ("id":"5d6b777971d7ad663e4ac3dd" , "orderNo": "1567324025207", "userid":11111235,"merchantid":123456,"amount":11.2,"status":-1, "created" :"Sep 1, 2019 3:47:05PM","modify" : "Sep 1,20193:50:27
          PM"
          , "orderDetails":[{ "goodsid":1, "goodsname":"測試商品
          1"
          , "photo": " /images/demol.png" , "nums " : 1 , "price":11.2,"money":11.21], "page":0,"size" :10}

          如果能看到上面所示的日志,則說明訂單狀態(tài)變更的消息已經(jīng)發(fā)送到了消息隊列上。

          再切換到庫存接口的控制臺,查看庫存接口的輸出日志。如果能看到如下所示的輸出日志,則說明消息已經(jīng)處理成功,同時也說明分布式事務(wù)已經(jīng)處理完畢。

          接收到訂單更新消息,訂單編號=1567324025207
          ...
          更新了商品購買數(shù)量,商品名稱=測試商品1
          ...

          這時,在訂單管理后臺的首頁上,可以看到訂單的狀態(tài)已修改成功,如圖8-5所示。在圖8-5中,我們還可以按各種查詢條件和不同的參數(shù),實現(xiàn)各種不同需求的分頁數(shù)據(jù)的列表查詢操作。


          小結(jié)

          本章我們使用MongoDB開發(fā)了一個可以支持海量數(shù)據(jù)的訂單系統(tǒng),并且使用Spring 5的反應(yīng)式編程設(shè)計,實現(xiàn)了支持非阻塞異步調(diào)用的高并發(fā)微服務(wù)訂單接口,所以這是一個高性能的訂單微服務(wù)應(yīng)用系統(tǒng)。有關(guān)反應(yīng)式編程設(shè)計,由于其異步調(diào)用的特性,使得其只能支持無事務(wù)管理的數(shù)據(jù)庫設(shè)計。而對于微服務(wù)設(shè)計來說,其本身就是一種分布式的應(yīng)用,所以有關(guān)事務(wù)管理的設(shè)計,只能使用分布式的事務(wù)管理來實現(xiàn)。在本章訂單狀態(tài)變更所引起的事務(wù)管理實例中,我們使用消息隊列實現(xiàn)了分布式事務(wù)管理中數(shù)據(jù)最終一致性的設(shè)計。

          本文給大家講解的內(nèi)容

          SpringCloud微服務(wù)架構(gòu)實戰(zhàn):海量訂單系統(tǒng)微服務(wù)開發(fā),訂單接口微服務(wù)開發(fā)、訂單的分布式事務(wù)管理、 訂單管理后臺微服務(wù)開發(fā)、集成測試

          1. 下篇文章給大家講解的是SpringCloud微服務(wù)架構(gòu)實戰(zhàn):移動商城的設(shè)計和開發(fā);

          2. 覺得文章不錯的朋友可以轉(zhuǎn)發(fā)此文關(guān)注小編;

          3. 感謝大家的支持!


        3. 本文就是愿天堂沒有BUG給大家分享的內(nèi)容,大家有收獲的話可以分享下,想學(xué)習(xí)更多的話可以到微信公眾號里找我,我等你哦。

          瀏覽 26
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品久久久久久久久AV竹菊 | 欧美A∨| 人妻精品久久久久中国字幕 | 国产有码视频 | 亚洲日韩一区 |