自动匹配未认领订单编号_海量订单系统微服务开发:使用MongoDB支持海量数据...
海量訂單系統微服務開發
訂單系統是電商平臺中一個非常重要的組成部分,而且它還是一個具有巨大流量和高并發訪問的系統,與訂單相關的服務涉及庫存、支付、物流等。在設計訂單系統時,我們選擇使用支持海量數據的NoSQL 數據庫MongoDB,配合使用反應式的Spring Data MongoDB,實現高并發設計。
本章實例項目代碼可從本書源代碼中下載,在IDEA 中檢出,或通過頁面直接下載使用。檢出后請獲取分支版本V2.1。在這個分支中包含以下幾個模塊:
- order-object:訂單公共對象設計。
- order-restapi:訂單微服務接口應用設計。
- order-web:訂單后臺管理應用設計。
使用MongoDB支持海量數據
MongoDB是一個分布式數據庫,對于開發調試,我們只需一個單機版即可。
使用 Mongo插件
如果使用的是IDEA開發工具,則為了方便查詢數據庫,也可以安裝一個Mongo客戶端插件。打開 IDEA 設置,在插件上搜索Mongo進行安裝即可,安裝完成后,如圖8-1所示。
安裝插件之后,就可以在設置中通過Other Settings連接 MongoDB,使用客戶端來查詢數據。圖8-2是一個本地數據庫連接的配置實例。
MongoDB數據源相關配置
我們在模塊 order-restapi中進行MongoDB的設計,首先在項目對象模型pom.xml中引入相關依賴引用,代碼如下所示:
org.springframework.bootspring-boot-starter-data-mongodb-reactive這里引用的是反應式Spring Data MongoDB組件,它可以支持無事務的高并發非阻塞的異步請求調用。
在模塊的配置文件 applicaption.yml 中,設定連接MongoDB服務器的數據源配置,代碼如下所示:
#datasourcespring:data:mongodb:host: localhostport: 27017#矯正Mongo查詢時間jackson:timezone: GMT+8這里是開發環境的一個本地連接的簡單配置,如果是生產環境,則可以設置用戶名和密碼,并且指定使用的數據庫名稱。
這里是開發環境的一個本地連接的簡單配置,如果是生產環境,則可以設置用戶名和密碼,并且指定使用的數據庫名稱。
因為MongoDB使用了格林尼治時間(GMT),所以為了顯示東八區的正確時間,我們在數據查詢時做了“GMT+8”的配置。
訂單文檔建模
訂單數據主要由訂單及其明細數據組成,由于訂單從生成開始到交易結束,會發生一系列狀態變化,而這些狀態一般可以固定下來,所以可以使用一個枚舉類來實現。
訂單及其明細數據
訂單文檔的建模由Order類實現,代碼如下所示:
@Document@Data@NoArgsConstructorpublic class Order {//訂單ID@Idprivate String id;//訂單號@Indexed (name = "index orderNo")private String orderNo;//用戶編號private Long userid;//商家編號private Long merchantid;//訂單金額private Double amount;//訂單狀態(0:未付款,1:已付款,2:已發貨,3:已收貨,4:已評價,-1:已撤銷,-2:已退款)private Integer status;//創建時間@DateTimeFormat (pattern= "yyyy-MM-dd HH:mm : ss")private Date created;//操作員private string operator;//修改時間@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date modify;//訂單明細private List orderDetails = new ArrayList<>();}在上面的代碼中,各個字段的屬性已經有注釋說明。注解@Data為各個字段自動生成getter/setter 方法。另外,注解@Id可由數據庫自動生成ID,并且是文檔的唯一索引;注解@Indexed為訂單編號創建了一個索引,從而提高了以訂單號進行查詢的性能。
訂單明細的定義在類 OrderDetail中,代碼如下所示:
@ Datapublic class OrderDetail {//商品編號private Long goodsid;//商品名稱private String goodsname;//商品圖片private String photo;//購買數量private Integer nums;//單價private Double price;//金額private Double money;//時間戳@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date created;}在訂單明細設計中,對于商品名稱和圖片數據等字段,使用冗余設計的方法,可以減少對庫存管理中商品接口的調用。
訂單明細雖然是一個獨立的類,但它不是一個獨立的文檔。訂單明細將與訂單一起組成一個文檔。這一點與關系數據庫的設計不同,如果是MySQL,則訂單明細會使用另一個表結構,在查詢時再使用關聯關系獲取數據,這樣一來必將是很耗性能的。
訂單狀態枚舉
訂單狀態在訂單文檔中保存時是一個整型字段,它對應訂單的一個狀態信息。一般來說,這種狀態都較為固定,所以我們使用一個枚舉定義StatusEnum來實現,這樣在訂單的查詢設計中,就可以對各個訂單狀態進行轉換,同時在訂單的編輯中也可以列舉出所有狀態進行選擇。代碼如下所示:
public enum StatusEnum {UNPAID(Integer.valueOf(0),"未付款"),PAID(Integer.valueOf(1),"已付款"),SHIPPED(Integer.valueOf(2),"已發貨"),RECEIVED(Integer.valueOf(3), "已收貨"),EVALUATED(Integer.valueof(4),"已評價"),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;}}反應式 MongoDB編程設計
反應式編程設計是Spring Boot 2.0及以上版本提供的一個新功能,這是一個非阻塞的異步調用設計,可以適應高并發的請求調用。在反應式編程中有兩個基本概念:Flux和 Mono。Flux表示的是包含0到N個元素的異步序列,在該序列中可以包含三種不同類型的消息通知:正常的包含元素的消息、序列結束的消息和序列出錯的消息。當消息通知產生時,訂閱者中對應的方法 onNext()、onComplete()和 onError()會被調用。Mono表示的是包含0或者1個元素的異步序列,在該序列中,包含的消息通知的類型與Flux相同。
基于Spring Data的存儲庫接口設計
Spring Data MongoDB和Spring Data一樣,有一個統一的規范設計。前面我們在Spring DataJPA中使用過這種規范,所以接下來的代碼,讀者會覺得很熟悉。
訂單的存儲庫接口是 OrderRepository,實現代碼如下所示:
@RepositoryePrimarypublic interface OrderRepository extends ReactiveMongoRepository{Mono<0rder> findByOrderNo (String orderNo);}動態分頁查詢設計
在存儲庫接口設計中,可以使用注解@Query靈活地定義復雜的查詢。對于訂單的分頁查詢,我們使用了如下所示的動態查詢設計:
@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) ;這里我們提供了幾個查詢條件,它們分別是:用戶編號(userid) 、商家編號(merchantid)、訂單狀態(status)和訂單創建日期(created)。這些查詢條件如果值為空,則忽略不計,否則按提供的數值進行限定查詢。其中,對于訂單的創建日期的條件查詢,使用了大于或等于(Sgte)開始日期和小于或等于($Ite)結束日期的條件限制。最后,還可以對查詢結果進行排序。
針對分頁的查詢接口聲明,我們在服務類OrderService中使用了如下所示的設計:
@servicepublic class OrderService {@Autowiredprivate 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 getCount(){return orderRepository. count();}}首先對訂單創建日期進行倒序排序,然后使用查詢對象OrderQo傳輸查詢參數,并對查詢結果使用分頁方式輸出。需要注意的是,這里的輸出結果是一個異步序列Flux,它包含了訂單的列表數據。如果是單個對象的數據輸出,則可以使用異步序列Mono,如上面代碼中對訂單總數查詢的輸出使用了Mono序列。
Mongo單元測試
針對前面的純數據庫方面的設計,我們可以使用一個單元測試進行驗證。一個生成訂單數據的測試用例如下所示:
@RunWith(SpringRunner.class)@ContextConfiguration(classes =(0rderRestApiApplication.class))@SpringBootTest@Sl4jpublic class OrderTest {@Autowiredprivate orderService orderService;@Testpublic void insertData(){OrderDetail orderDetail1 =new OrderDetail();orderDetail1.setGoodsname("測試商品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("測試商品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("返回結果:{}",new Gson ().toJson(response.block()));}}在這個測試用例設計中生成了一個訂單,并為這個訂單的明細數據生成了兩個記錄。如果打開MongoDB的調試日志,就可以從控制臺中看到如下輸出:
Inserting Document containing fields:[orderNo,userid,merchantid,amount,status,created,orderDetails, class]in collection: order另外,為了更加清晰地看到測試結果,我們還在日志輸出中通過“返回結果:0}”將這條生成的訂單信息打印出來。
這時,也可以借助MongoDB的客戶端查詢測試的結果。
因為測試是在線程中執行反應式的數據操作,所以對于異步序列,必須在最后執行類似block()這樣的阻塞處理,才能完成反應式的調用過程,否則不可能達到預期的結果。
在接下來的各種增刪改查的測試用例設計中,最后都進行了阻塞處理設計。例如,對分頁查詢的測試,我們使用如下所示的設計:
@Testpublic void findAl1() throws Exception{OrderQo orderQo = new OrderQo();List<0rder> list = orderService.findAll(orderQo) . collectList().block();Assert.notEmpty(list, "list is empty");log.info("總數:{;列表:{}",list.size(),new Gson() .toJson (list));}執行這個測試用例后,可以在控制臺日志中看到 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因為這里沒有提供查詢參數的數值,所示這是一個沒有條件限制的查詢,它會按分頁結果查出訂單的所有記錄。
當我們為這些查詢參數指定數據時,即可看到如下所示的查詢日志輸出:
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本文給大家講解的內容
SpringCloud微服務架構實戰:海量訂單系統微服務開發,使用MongoDB支持海量數據、 訂單文檔建模、反應式MongoDB編程設計、Mongo單元測試
總結
以上是生活随笔為你收集整理的自动匹配未认领订单编号_海量订单系统微服务开发:使用MongoDB支持海量数据...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kubuntu Focus Xe Gen
- 下一篇: 注册资本金如何实缴 注册资本金实缴需要怎