<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ā):使用MongoDB支持海量數(shù)據(jù)

          共 8223字,需瀏覽 17分鐘

           ·

          2022-04-10 18:15


          海量訂單系統(tǒng)微服務(wù)開發(fā)

          訂單系統(tǒng)是電商平臺(tái)中一個(gè)非常重要的組成部分,而且它還是一個(gè)具有巨大流量和高并發(fā)訪問的系統(tǒng),與訂單相關(guān)的服務(wù)涉及庫存、支付、物流等。在設(shè)計(jì)訂單系統(tǒng)時(shí),我們選擇使用支持海量數(shù)據(jù)的NoSQL 數(shù)據(jù)庫MongoDB,配合使用反應(yīng)式的Spring Data MongoDB,實(shí)現(xiàn)高并發(fā)設(shè)計(jì)。

          本章實(shí)例項(xiàng)目代碼可從本書源代碼中下載,在IDEA 中檢出,或通過頁面直接下載使用。檢出后請(qǐng)獲取分支版本V2.1。在這個(gè)分支中包含以下幾個(gè)模塊:

          • order-object:訂單公共對(duì)象設(shè)計(jì)。

          • order-restapi:訂單微服務(wù)接口應(yīng)用設(shè)計(jì)。

          • order-web:訂單后臺(tái)管理應(yīng)用設(shè)計(jì)。

          使用MongoDB支持海量數(shù)據(jù)

          MongoDB是一個(gè)分布式數(shù)據(jù)庫,對(duì)于開發(fā)調(diào)試,我們只需一個(gè)單機(jī)版即可。

          使用 Mongo插件

          如果使用的是IDEA開發(fā)工具,則為了方便查詢數(shù)據(jù)庫,也可以安裝一個(gè)Mongo客戶端插件。打開 IDEA 設(shè)置,在插件上搜索Mongo進(jìn)行安裝即可,安裝完成后,如圖8-1所示。


          安裝插件之后,就可以在設(shè)置中通過Other Settings連接 MongoDB,使用客戶端來查詢數(shù)據(jù)。圖8-2是一個(gè)本地?cái)?shù)據(jù)庫連接的配置實(shí)例。


          MongoDB數(shù)據(jù)源相關(guān)配置

          我們?cè)谀K order-restapi中進(jìn)行MongoDB的設(shè)計(jì),首先在項(xiàng)目對(duì)象模型pom.xml中引入相關(guān)依賴引用,代碼如下所示:

          <dependency>
          <groupId>org.springframework.bootgroupId>
          <artifactId>spring-boot-starter-data-mongodb-reactiveartifactId>
          dependency>

          這里引用的是反應(yīng)式Spring Data MongoDB組件,它可以支持無事務(wù)的高并發(fā)非阻塞的異步請(qǐng)求調(diào)用。

          在模塊的配置文件 applicaption.yml 中,設(shè)定連接MongoDB服務(wù)器的數(shù)據(jù)源配置,代碼如下所示:

          #datasourcespring:
          data:
          mongodb:
          host: localhostport: 27017
          #矯正Mongo查詢時(shí)間jackson:
          timezone: GMT+8

          這里是開發(fā)環(huán)境的一個(gè)本地連接的簡(jiǎn)單配置,如果是生產(chǎn)環(huán)境,則可以設(shè)置用戶名和密碼,并且指定使用的數(shù)據(jù)庫名稱。

          這里是開發(fā)環(huán)境的一個(gè)本地連接的簡(jiǎn)單配置,如果是生產(chǎn)環(huán)境,則可以設(shè)置用戶名和密碼,并且指定使用的數(shù)據(jù)庫名稱。

          因?yàn)镸ongoDB使用了格林尼治時(shí)間(GMT),所以為了顯示東八區(qū)的正確時(shí)間,我們?cè)跀?shù)據(jù)查詢時(shí)做了“GMT+8”的配置。


          訂單文檔建模

          訂單數(shù)據(jù)主要由訂單及其明細(xì)數(shù)據(jù)組成,由于訂單從生成開始到交易結(jié)束,會(huì)發(fā)生一系列狀態(tài)變化,而這些狀態(tài)一般可以固定下來,所以可以使用一個(gè)枚舉類來實(shí)現(xiàn)。

          訂單及其明細(xì)數(shù)據(jù)

          訂單文檔的建模由Order類實(shí)現(xiàn),代碼如下所示:

          @Document
          @Data
          @NoArgsConstructorpublic class Order {
          //訂單ID
          @Id
          private String id;//訂單號(hào)
          @Indexed (name = "index orderNo")
          private String orderNo;//用戶編號(hào)
          private Long userid;//商家編號(hào)
          private Long merchantid;//訂單金額
          private Double amount;
          //訂單狀態(tài)(0:未付款,1:已付款,2:已發(fā)貨,3:已收貨,4:已評(píng)價(jià),-1:已撤銷,-2:已退款)
          private Integer status;//創(chuàng)建時(shí)間
          @DateTimeFormat (pattern= "yyyy-MM-dd HH:mm : ss")private Date created;
          //操作員
          private string operator;//修改時(shí)間
          @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date modify;
          //訂單明細(xì)
          private List orderDetails = new ArrayList<>();
          }

          在上面的代碼中,各個(gè)字段的屬性已經(jīng)有注釋說明。注解@Data為各個(gè)字段自動(dòng)生成getter/setter 方法。另外,注解@Id可由數(shù)據(jù)庫自動(dòng)生成ID,并且是文檔的唯一索引;注解@Indexed為訂單編號(hào)創(chuàng)建了一個(gè)索引,從而提高了以訂單號(hào)進(jìn)行查詢的性能。

          訂單明細(xì)的定義在類 OrderDetail中,代碼如下所示:

          @ Data
          public class OrderDetail {
          //商品編號(hào)
          private Long goodsid;//商品名稱
          private String goodsname;//商品圖片
          private String photo;//購買數(shù)量
          private Integer nums;//單價(jià)
          private Double price;//金額
          private Double money;//時(shí)間戳
          @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
          private Date created;
          }

          在訂單明細(xì)設(shè)計(jì)中,對(duì)于商品名稱和圖片數(shù)據(jù)等字段,使用冗余設(shè)計(jì)的方法,可以減少對(duì)庫存管理中商品接口的調(diào)用。

          訂單明細(xì)雖然是一個(gè)獨(dú)立的類,但它不是一個(gè)獨(dú)立的文檔。訂單明細(xì)將與訂單一起組成一個(gè)文檔。這一點(diǎn)與關(guān)系數(shù)據(jù)庫的設(shè)計(jì)不同,如果是MySQL,則訂單明細(xì)會(huì)使用另一個(gè)表結(jié)構(gòu),在查詢時(shí)再使用關(guān)聯(lián)關(guān)系獲取數(shù)據(jù),這樣一來必將是很耗性能的。

          訂單狀態(tài)枚舉

          訂單狀態(tài)在訂單文檔中保存時(shí)是一個(gè)整型字段,它對(duì)應(yīng)訂單的一個(gè)狀態(tài)信息。一般來說,這種狀態(tài)都較為固定,所以我們使用一個(gè)枚舉定義StatusEnum來實(shí)現(xiàn),這樣在訂單的查詢?cè)O(shè)計(jì)中,就可以對(duì)各個(gè)訂單狀態(tài)進(jìn)行轉(zhuǎn)換,同時(shí)在訂單的編輯中也可以列舉出所有狀態(tài)進(jìn)行選擇。代碼如下所示:

          public enum StatusEnum {
          UNPAID(Integer.valueOf(0),"未付款"),
          PAID(Integer.valueOf(1),"已付款"),
          SHIPPED(Integer.valueOf(2),"已發(fā)貨"),
          RECEIVED(Integer.valueOf(3), "已收貨"),
          EVALUATED(Integer.valueof(4),"已評(píng)價(jià)"),
          REVOKED(Integer.valueOf(-1),"已撤銷"),
          REFUNDED(Integer.valueOf(-2),"已退款");
          private Integer code;
          private String name;
          StatusEnum(Integer code, String name) {
          this.code = code;
          this.name = name;
          public static boolean contains (Integer code) throws NullPointerException {
          if(null -= code){
          throw new NullPointerException ("constant code is null");
          ]else {
          StatusEnum[] varl = values();int var2=var1. length;
          for(int var3 =0; var3 < var2; ++var3) {
          StatusEnum eum = varl[var3];
          if(code.equals(eum.getCode())) {
          return true;
          return false;
          public static StatusEnum valueof(Integer code) throws NullPointerException,EnumConstantNotPresentException {
          if(null == code) {
          throw new NullPointerException ("constant code is null");]else {
          StatusEnum[] var1 =values();int var2 = var1 . length;
          for(int var3 =0;var3 < var2; ++var3) {
          StatusEnum statusEnum= var1 [var3];
          if(code.equals(statusEnum.getCode())) {
          return statusEnum;
          throw new EnumConstantNotPresentException (StatusEnum.class,
          code.toString())
          ;
          )
          public Integer getCode( {
          return this.code;
          }
          public string getName()
          {
          return this.name;
          }
          }

          反應(yīng)式 MongoDB編程設(shè)計(jì)

          反應(yīng)式編程設(shè)計(jì)是Spring Boot 2.0及以上版本提供的一個(gè)新功能,這是一個(gè)非阻塞的異步調(diào)用設(shè)計(jì),可以適應(yīng)高并發(fā)的請(qǐng)求調(diào)用。在反應(yīng)式編程中有兩個(gè)基本概念:Flux和 Mono。Flux表示的是包含0到N個(gè)元素的異步序列,在該序列中可以包含三種不同類型的消息通知:正常的包含元素的消息、序列結(jié)束的消息和序列出錯(cuò)的消息。當(dāng)消息通知產(chǎn)生時(shí),訂閱者中對(duì)應(yīng)的方法 onNext()、onComplete()和 onError()會(huì)被調(diào)用。Mono表示的是包含0或者1個(gè)元素的異步序列,在該序列中,包含的消息通知的類型與Flux相同。

          基于Spring Data的存儲(chǔ)庫接口設(shè)計(jì)

          Spring Data MongoDB和Spring Data一樣,有一個(gè)統(tǒng)一的規(guī)范設(shè)計(jì)。前面我們?cè)赟pring DataJPA中使用過這種規(guī)范,所以接下來的代碼,讀者會(huì)覺得很熟悉。

          訂單的存儲(chǔ)庫接口是 OrderRepository,實(shí)現(xiàn)代碼如下所示:

          @Repository
          ePrimary
          public interface OrderRepository extends ReactiveMongoRepository<Order, String>{
          Mono<0rder> findByOrderNo (String orderNo);
          }

          動(dòng)態(tài)分頁查詢?cè)O(shè)計(jì)

          在存儲(chǔ)庫接口設(shè)計(jì)中,可以使用注解@Query靈活地定義復(fù)雜的查詢。對(duì)于訂單的分頁查詢,我們使用了如下所示的動(dòng)態(tài)查詢?cè)O(shè)計(jì):

          @Query ("I 'userid':?#(([0] == null)?{$exists:true}:[0]},"+
          " 'merchantid':?#{([1] == null)?{$exists:true}:[1]},"+" 'status' :?#{([2] == null)?{$exists:true} :[2]1,"+
          " 'created':?#{([3] == null) and ([4] == null)?{Sexists:true}:( $gte:
          [3],$lte: [4]}}}"
          )
          Flux findAll (Long userid, Long merchantid, Integer status, Date start,
          Date end, Sort sort) ;

          這里我們提供了幾個(gè)查詢條件,它們分別是:用戶編號(hào)(userid) 、商家編號(hào)(merchantid)、訂單狀態(tài)(status)和訂單創(chuàng)建日期(created)。這些查詢條件如果值為空,則忽略不計(jì),否則按提供的數(shù)值進(jìn)行限定查詢。其中,對(duì)于訂單的創(chuàng)建日期的條件查詢,使用了大于或等于(Sgte)開始日期和小于或等于($Ite)結(jié)束日期的條件限制。最后,還可以對(duì)查詢結(jié)果進(jìn)行排序。

          針對(duì)分頁的查詢接口聲明,我們?cè)诜?wù)類OrderService中使用了如下所示的設(shè)計(jì):

          @service
          public class OrderService {
          @Autowired
          private OrderRepository orderRepository;
          public Flux<0rder> findAll (0rderQo orderQo){
          try{
          Sort sort = Sort.by (new Sort.Order(Sort.Direction.DESC, "created"));
          return orderRepository. findAll(orderQo.getUserid(),
          orderQo-getMerchantid(),
          orderQo.getStatus(),orderQo.getStart(),orderQo.getEnd(),
          sort)
          .skip(orderQo.getPage() * orderQo.getSize()).limitRequest (orderQo.getSize());
          }catch(Exception e){
          e.printstackTrace();return null;
          public Mono<Long> getCount(){
          return orderRepository. count();
          }
          }

          首先對(duì)訂單創(chuàng)建日期進(jìn)行倒序排序,然后使用查詢對(duì)象OrderQo傳輸查詢參數(shù),并對(duì)查詢結(jié)果使用分頁方式輸出。需要注意的是,這里的輸出結(jié)果是一個(gè)異步序列Flux,它包含了訂單的列表數(shù)據(jù)。如果是單個(gè)對(duì)象的數(shù)據(jù)輸出,則可以使用異步序列Mono,如上面代碼中對(duì)訂單總數(shù)查詢的輸出使用了Mono序列。

          Mongo單元測(cè)試

          針對(duì)前面的純數(shù)據(jù)庫方面的設(shè)計(jì),我們可以使用一個(gè)單元測(cè)試進(jìn)行驗(yàn)證。一個(gè)生成訂單數(shù)據(jù)的測(cè)試用例如下所示:

          @RunWith(SpringRunner.class)
          @ContextConfiguration(classes =(0rderRestApiApplication.class))@SpringBootTest
          @Sl4j
          public class OrderTest {
          @Autowired
          private orderService orderService;
          @Test
          public void insertData(){
          OrderDetail orderDetail1 =new OrderDetail();orderDetail1.setGoodsname("測(cè)試商品1");
          orderDetail1.setGoodsid(1L);
          orderDetaill.setPrice(12.20D);orderDetail1.setNums (1);
          orderDetail1.setMoney(12.20D);
          orderDetail1 .setPhoto( "../images/demo1 .png") ;
          OrderDetail orderDetail2 = new OrderDetail();orderDetail2.setGoodsname("測(cè)試商品2");
          orderDetail2.setGoodsid(2L);
          orderDetail2 .setPrice(20.00D);orderDetail2.setNums (2);
          orderDetail2.setMoney(40.00D);
          orderDetai12.setPhoto ("../images/demo2.png");
          Order order = new Order();
          order.setorderNo ( "123456");order.setUserid(1213L);
          order. setMerchantid(2222L);order.setAmount (52.20D);order.setStatus(1);
          order.setCreated (new Date());
          List<0rderDetail> orderDetails = new ArrayEist<>();orderDetails.add(orderDetail1);
          orderDetails.add(orderDetail2);
          order.setOrderDetails(orderDetails);
          Mono<0rder> response = orderService.save (order);
          Assert.notNull(response, "save erro");
          log.info("返回結(jié)果:{}",new Gson ().toJson(response.block()));
          }}

          在這個(gè)測(cè)試用例設(shè)計(jì)中生成了一個(gè)訂單,并為這個(gè)訂單的明細(xì)數(shù)據(jù)生成了兩個(gè)記錄。如果打開MongoDB的調(diào)試日志,就可以從控制臺(tái)中看到如下輸出:

          Inserting Document containing fields:[orderNo,userid,merchantid,amount,status,created,orderDetails, class]in collection: order

          另外,為了更加清晰地看到測(cè)試結(jié)果,我們還在日志輸出中通過“返回結(jié)果:0}”將這條生成的訂單信息打印出來。

          這時(shí),也可以借助MongoDB的客戶端查詢測(cè)試的結(jié)果。

          因?yàn)闇y(cè)試是在線程中執(zhí)行反應(yīng)式的數(shù)據(jù)操作,所以對(duì)于異步序列,必須在最后執(zhí)行類似block()這樣的阻塞處理,才能完成反應(yīng)式的調(diào)用過程,否則不可能達(dá)到預(yù)期的結(jié)果。

          在接下來的各種增刪改查的測(cè)試用例設(shè)計(jì)中,最后都進(jìn)行了阻塞處理設(shè)計(jì)。例如,對(duì)分頁查詢的測(cè)試,我們使用如下所示的設(shè)計(jì):

          @Test
          public void findAl1() throws Exception
          {
          OrderQo orderQo = new OrderQo();
          List<0rder> list = orderService.findAll(orderQo) . collectList().block();
          Assert.notEmpty(list, "list is empty");
          log.info("總數(shù):{;列表:{}",list.size(),new Gson() .toJson (list));
          }

          執(zhí)行這個(gè)測(cè)試用例后,可以在控制臺(tái)日志中看到 MongoDB的日志輸出,如下所示:

          find using query:{ "userid" :{ "Sexists" :true }, "merchantid":{ "$exists":true }, "status":{"Sexists" : true ], "created":( "$exists" : true }I fields:
          Document{{} for class: class com.demo.order.restapi.domain.0rder in collection:order

          因?yàn)檫@里沒有提供查詢參數(shù)的數(shù)值,所示這是一個(gè)沒有條件限制的查詢,它會(huì)按分頁結(jié)果查出訂單的所有記錄。

          當(dāng)我們?yōu)檫@些查詢參數(shù)指定數(shù)據(jù)時(shí),即可看到如下所示的查詢?nèi)罩据敵?

          find using query: "userid" : 1213, "merchantid" :2222, "status" :1, "created":["$gte":{"$date" :1564538018885 }, "$lte":( "$date" : 1567130018886]HIfields: Document{{ for class: class com.demo,order.restapi.domain.0rder incollection: order

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

          SpringCloud微服務(wù)架構(gòu)實(shí)戰(zhàn):海量訂單系統(tǒng)微服務(wù)開發(fā),使用MongoDB支持海量數(shù)據(jù)、 訂單文檔建模、反應(yīng)式MongoDB編程設(shè)計(jì)、Mongo單元測(cè)試

          1. 下篇文章給大家講解的是SpringCloud微服務(wù)架構(gòu)實(shí)戰(zhàn):海量訂單系統(tǒng)微服務(wù)開發(fā),訂單接口微服務(wù)開發(fā)、訂單的分布式事務(wù)管理、 訂單管理后臺(tái)微服務(wù)開發(fā)、集成測(cè)試;

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

          3. 感謝大家的支持!


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

          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  91乱子伦国产乱子伦www.sz-sd.cn | 日韩少妇在线 | 美女操逼的网站 | 日韩免费高清无码 | 三级片软件 |