《解剖PetShop》系列之三
《解剖PetShop》系列之三
三、PetShop數據訪問層之消息處理
??? 在進行系統設計時,除了對安全、事務等問題給與足夠的重視外,性能也是一個不可避免的問題所在,尤其是一個B/S結構的軟件系統,必須充分地考慮訪問量、數據流量、服務器負荷的問題。解決性能的瓶頸,除了對硬件系統進行升級外,軟件設計的合理性尤為重要。
??? 在前面我曾提到,分層式結構設計可能會在一定程度上影響數據訪問的性能,然而與它給設計人員帶來的好處相比,幾乎可以忽略。要提供整個系統的性能,還可以從數據庫的優化著手,例如連接池的使用、建立索引、優化查詢策略等等,例如在PetShop中就利用了數據庫的Cache,對于數據量較大的訂單數據,則利用分庫的方式為其單獨建立了Order和Inventory數據庫。而在軟件設計上,比較有用的方式是利用多線程與異步處理方式。
??? 在PetShop4.0中,使用了Microsoft Messaging Queue(MSMQ)技術來完成異步處理,利用消息隊列臨時存放要插入的數據,使得數據訪問因為不需要訪問數據庫從而提供了訪問性能,至于隊列中的數據,則等待系統空閑的時候再進行處理,將其最終插入到數據庫中。
??? PetShop4.0中的消息處理,主要分為如下幾部分:消息接口IMessaging、消息工廠MessagingFactory、MSMQ實現MSMQMessaging以及數據后臺處理應用程序OrderProcessor。
從模塊化分上,PetShop自始自終地履行了“面向接口設計”的原則,將消息處理的接口與實現分開,并通過工廠模式封裝消息實現對象的創建,以達到松散耦合的目的。
??? 由于在PetShop中僅對訂單的處理使用了異步處理方式,因此在消息接口IMessaging中,僅定義了一個IOrder接口,其類圖如下:
??? 在對消息接口的實現中,考慮到未來的擴展中會有其他的數據對象會使用MSMQ,因此定義了一個Queue的基類,實現消息Receive和Send的基本操作:
?2?{
?3???????try
?4??????{
?5???????????using?(Message?message?=?queue.Receive(timeout,?transactionType))
?6??????????????return?message;
?7???????}
?8???????catch?(MessageQueueException?mqex)
?9??????{
10???????????if?(mqex.MessageQueueErrorCode?==?MessageQueueErrorCode.IOTimeout)
11??????????????throw?new?TimeoutException();
12?????????????????throw;
13???????}
14?}
15?public?virtual?void?Send(object?msg)
16?{
17???????queue.Send(msg,?transactionType);
18?}
19?
??? 其中queue對象是System.Messaging.MessageQueue類型,作為存放數據的隊列。MSMQ隊列是一個可持久的隊列,因此不必擔心用戶不間斷地下訂單會導致訂單數據的丟失。在PetShopQueue設置了timeout值,OrderProcessor會根據timeout值定期掃描隊列中的訂單數據。
??? MSMQMessaging模塊中,Order對象實現了IMessaging模塊中定義的接口IOrder,同時它還繼承了基類PetShopQueue,其定義如下:
???
2?
??? 方法的實現代碼如下:
???
?2?????{
?3?????????//?This?method?involves?in?distributed?transaction?and?need?Automatic?Transaction?type
?4?????????base.transactionType?=?MessageQueueTransactionType.Automatic;
?5?????????return?(OrderInfo)((Message)base.Receive()).Body;
?6?????}?????public?OrderInfo?Receive(int?timeout)
?7?????{
?8?????????base.timeout?=?TimeSpan.FromSeconds(Convert.ToDouble(timeout));
?9?????????return?Receive();
10?????}
11?
12?????public?void?Send(OrderInfo?orderMessage)
13?????{
14?????????//?This?method?does?not?involve?in?distributed?transaction?and?optimizes?performance?using?Single?type
15?????????base.transactionType?=?MessageQueueTransactionType.Single;
16?????????base.Send(orderMessage);
17?????}
18?
19?
??? 所以,最后的類圖應該如下:?
??? 注意在Order類的Receive()方法中,是用new關鍵字而不是override關鍵字來重寫其父類PetShopQueue的Receive()虛方法。因此,如果是實例化如下的對象,將會調用PetShopQueue的Receive()方法,而不是子類Order的Receive()方法:
2?????queue.Receive();
3?
??? 從設計上來看,由于PetShop采用“面向接口設計”的原則,如果我們要創建Order對象,應該采用如下的方式:
2?????order.Receive();
??? 考慮到IOrder的實現有可能的變化,PetShop仍然利用了工廠模式,將IOrder對象的創建用專門的工廠模塊進行了封裝:?
??? 在類QueueAccess中,通過CreateOrder()方法利用反射技術創建正確的IOrder類型對象:
???
2?????{
3?????????string?className?=?path?+?“.Order”;
4?????????return?PetShop.IMessaging.IOrder)Assembly.Load(path).CreateInstance(className);
5?????}
6?????path的值通過配置文件獲取:
7?????private?static?readonly?string?path?=?ConfigurationManager.AppSettings[”OrderMessaging”];
8?
??? 而配置文件中,OrderMessaging的值設置如下:
???
2?
??? 之所以利用工廠模式來負責對象的創建,是便于在業務層中對其調用,例如在BLL模塊中OrderAsynchronous類:
2?{???????
3?????private?static?readonly?PetShop.IMessaging.IOrder?asynchOrder?=?PetShop.MessagingFactory.QueueAccess.CreateOrder();
4?????public?void?Insert(PetShop.Model.OrderInfo?order)
5?{
6?????????asynchOrder.Send(order);
7?????}
8?}
9?
??? 一旦IOrder接口的實現發生變化,這種實現方式就可以使得客戶僅需要修改配置文件,而不需要修改代碼,如此就可以避免程序集的重新編譯和部署,使得系統能夠靈活應對需求的改變。例如定義一個實現IOrder接口的SpecialOrder,則可以新增一個模塊,如PetShop.SpecialMSMQMessaging,而類名則仍然為Order,那么此時我們僅需要修改配置文件中OrderMessaging的值即可:
???
??? OrderProcessor是一個控制臺應用程序,不過可以根據需求將其設計為Windows Service。它的目的就是接收消息隊列中的訂單數據,然后將其插入到Order和Inventory數據庫中。它利用了多線程技術,以達到提高系統性能的目的。
??? 在OrderProcessor應用程序中,主函數Main用于控制線程,而核心的執行任務則由方法ProcessOrders()實現:
???
?2?????{
?3?????????//?the?transaction?timeout?should?be?long?enough?to?handle?all?of?orders?in?the?batch
?4?????????TimeSpan?tsTimeout?=?TimeSpan.FromSeconds(Convert.ToDouble(transactionTimeout?*?batchSize));
?5?
?6?????????Order?order?=?new?Order();
?7?????????while?(true)
?8?????????{
?9?????????????//?queue?timeout?variables
10?????????????TimeSpan?datetimeStarting?=?new?TimeSpan(DateTime.Now.Ticks);
11?????????????double?elapsedTime?=?0;
12?
13?????????????int?processedItems?=?0;
14?
15?????????????ArrayList?queueOrders?=?new?ArrayList();
16?
17?????????????using?(TransactionScope?ts?=?new?TransactionScope(TransactionScopeOption.Required,?tsTimeout))
18?????????????{
19?????????????????//?Receive?the?orders?from?the?queue
20?????????????????for?(int?j?=?0;?j?<?batchSize;?j++)
21?????????????????{
22?????????????????????try
23?????????????????????{
24?????????????????????????//only?receive?more?queued?orders?if?there?is?enough?time
25?????????????????????????if?((elapsedTime?+?queueTimeout?+?transactionTimeout)?<?tsTimeout.TotalSeconds)
26?????????????????????????{
27?????????????????????????????queueOrders.Add(order.ReceiveFromQueue(queueTimeout));
28?????????????????????????}
29?????????????????????????else
30?????????????????????????{
31?????????????????????????????j?=?batchSize;???//?exit?loop
32?????????????????????????}
33?
34?????????????????????????//update?elapsed?time
35?????????????????????????elapsedTime?=?new?TimeSpan(DateTime.Now.Ticks).TotalSeconds?-?datetimeStarting.TotalSeconds;
36?????????????????????}
37?????????????????????catch?(TimeoutException)
38?????????????????????{
39?????????????????????????//exit?loop?because?no?more?messages?are?waiting
40?????????????????????????j?=?batchSize;
41?????????????????????}
42?????????????????}
43?????????????????//process?the?queued?orders
44?????????????????for?(int?k?=?0;?k?<?queueOrders.Count;?k++)
45?????????????????{
46?????????????????????order.Insert((OrderInfo)queueOrders[k]);
47?????????????????????processedItems++;
48?????????????????????totalOrdersProcessed++;
49?????????????????}
50?
51?????????????????//batch?complete?or?MSMQ?receive?timed?out
52?????????????????ts.Complete();
53?????????????}
54?
55?????????????Console.WriteLine("(Thread?Id?"?+?Thread.CurrentThread.ManagedThreadId?+?")?batch?finished,?"?+?processedItems?+?"?items,?in?"?+?elapsedTime.ToString()?+?"?seconds.");
56?????????}
57?????}
58?
59?
??? 首先,它會通過PetShop.BLL.Order類的公共方法ReceiveFromQueue()來獲取消息隊列中的訂單數據,并將其放入到一個ArrayList對象中,然而再調用PetShop.BLL.Order類的Insert方法將其插入到Order和Inventory數據庫中。
??? 在PetShop.BLL.Order類中,并不是直接執行插入訂單的操作,而是調用了IOrderStrategy接口的Insert()方法:
?2?{
?3?????//?Call?credit?card?procesor
?4?????ProcessCreditCard(order);
?5?
?6?????//?Insert?the?order?(a)synchrounously?based?on?configuration
?7?????orderInsertStrategy.Insert(order);
?8?}
?9?
10?
??? 在這里,運用了一個策略模式,類圖如下所示:?
??? 在PetShop.BLL.Order類中,仍然利用配置文件來動態創建IOrderStategy對象:
?2?private?static?PetShop.IBLLStrategy.IOrderStrategy?LoadInsertStrategy()
?3?{
?4?????//?Look?up?which?strategy?to?use?from?config?file
?5?????string?path?=?ConfigurationManager.AppSettings[”OrderStrategyAssembly”];
?6?????string?className?=?ConfigurationManager.AppSettings[”OrderStrategyClass”];
?7?
?8?????//?Using?the?evidence?given?in?the?config?file?load?the?appropriate?assembly?and?class
?9?????return?(PetShop.IBLLStrategy.IOrderStrategy)Assembly.Load(path).CreateInstance(className);
10?}
11?????由于OrderProcessor
12?
是一個單獨的應用程序,因此它使用的配置文件與PetShop不同,是存放在應用程序的App.config文件中,在該文件中,對IOrderStategy的配置為:
????
2?????<add?key=”OrderStrategyClass”?value=”PetShop.BLL.OrderSynchronous”?/>?
??
??? 因此,以異步方式插入訂單的流程如下圖所示:?
??? Microsoft Messaging Queue(MSMQ)技術除用于異步處理以外,它主要還是一種分布式處理技術。分布式處理中,一個重要的技術要素就是有關消息的處理,而在System.Messaging命名空間中,已經提供了Message類,可以用于承載消息的傳遞,前提上消息的發送方與接收方在數據定義上應有統一的接口規范。
??? MSMQ在分布式處理的運用,在我參與的項目中已經有了實現。在為一個汽車制造商開發一個大型系統時,分銷商Dealer作為.Net客戶端,需要將數據傳遞到管理中心,并且該數據將被Oracle的EBS(E-Business System)使用。由于分銷商管理系統(DMS)采用的是C/S結構,數據庫為SQL Server,而汽車制造商管理中心的EBS數據庫為Oracle。這里就涉及到兩個系統之間數據的傳遞。
??? 實現架構如下:
???? 首先Dealer的數據通過MSMQ傳遞到MSMQ Server,此時可以將數據插入到SQL Server數據庫中,同時利用FTP將數據傳送到專門的文件服務器上。然后利用IBM的EAI技術(企業應用集成,Enterprise Application Itegration)定期將文件服務器中的文件,利用接口規范寫入到EAI數據庫服務器中,并最終寫道EBS的Oracle數據庫中。
??? 上述架構是一個典型的分布式處理結構,而技術實現的核心就是MSMQ和EAI。由于我們已經定義了統一的接口規范,在通過消息隊列形成文件后,此時的數據就已經與平臺無關了,使得在.Net平臺下的分銷商管理系統能夠與Oracle的EBS集成起來,完成數據的處理。
轉載于:https://www.cnblogs.com/sapronlee/archive/2007/07/11/813341.html
總結
以上是生活随笔為你收集整理的《解剖PetShop》系列之三的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java中的sql语句代码拼接问题
- 下一篇: cad计算机绘图实操视频,cad工程制图