《解剖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接口,其类图如下:

ps01.gif

    在对消息接口的实现中,考虑到未来的扩展中会有其他的数据对象会使用MSMQ,因此定义了一个Queue的基类,实现消息Receive和Send的基本操作:

 1 public virtual object Receive()
 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,其定义如下:
   

1 public class Order:PetShopQueue, PetShop.IMessaging.IOrder
2 

    方法的实现代码如下:
   

 1 public new OrderInfo Receive()
 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 

    所以,最后的类图应该如下: 

ps02.gif

    注意在Order类的Receive()方法中,是用new关键字而不是override关键字来重写其父类PetShopQueue的Receive()虚方法。因此,如果是实例化如下的对象,将会调用PetShopQueue的Receive()方法,而不是子类Order的Receive()方法:

1     PetShopQueue queue = new Order();
2     queue.Receive();
3 

    从设计上来看,由于PetShop采用“面向接口设计”的原则,如果我们要创建Order对象,应该采用如下的方式:

1     IOrder order = new Order();
2     order.Receive();


    考虑到IOrder的实现有可能的变化,PetShop仍然利用了工厂模式,将IOrder对象的创建用专门的工厂模块进行了封装: 

ps03.gif

    在类QueueAccess中,通过CreateOrder()方法利用反射技术创建正确的IOrder类型对象:
   

1 public static PetShop.IMessaging.IOrder CreateOrder()
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的值设置如下:
   

1 <add key=”OrderMessaging” value=”PetShop.MSMQMessaging”/>
2 

    之所以利用工厂模式来负责对象的创建,是便于在业务层中对其调用,例如在BLL模块中OrderAsynchronous类:

1 public class OrderAsynchronous : IOrderStrategy
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的值即可:
   

1 <add key=”OrderMessaging” value=”PetShop.SpecialMSMQMessaging”/>


    OrderProcessor是一个控制台应用程序,不过可以根据需求将其设计为Windows Service。它的目的就是接收消息队列中的订单数据,然后将其插入到Order和Inventory数据库中。它利用了多线程技术,以达到提高系统性能的目的。
    在OrderProcessor应用程序中,主函数Main用于控制线程,而核心的执行任务则由方法ProcessOrders()实现:
   

 1 private static void 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()方法:

 1 public void Insert(OrderInfo order)
 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 

    在这里,运用了一个策略模式,类图如下所示: 

ps05.gif

    在PetShop.BLL.Order类中,仍然利用配置文件来动态创建IOrderStategy对象:

 1 private static readonly PetShop.IBLLStrategy.IOrderStrategy orderInsertStrategy = LoadInsertStrategy();
 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的配置为:
    

1 <add key=”OrderStrategyAssembly” value=”PetShop.BLL” />
2     <add key=”OrderStrategyClass” value=”PetShop.BLL.OrderSynchronous” /> 

  
    因此,以异步方式插入订单的流程如下图所示: 

ps06.gif

    Microsoft Messaging Queue(MSMQ)技术除用于异步处理以外,它主要还是一种分布式处理技术。分布式处理中,一个重要的技术要素就是有关消息的处理,而在System.Messaging命名空间中,已经提供了Message类,可以用于承载消息的传递,前提上消息的发送方与接收方在数据定义上应有统一的接口规范。
    MSMQ在分布式处理的运用,在我参与的项目中已经有了实现。在为一个汽车制造商开发一个大型系统时,分销商Dealer作为.Net客户端,需要将数据传递到管理中心,并且该数据将被Oracle的EBS(E-Business System)使用。由于分销商管理系统(DMS)采用的是C/S结构,数据库为SQL Server,而汽车制造商管理中心的EBS数据库为Oracle。这里就涉及到两个系统之间数据的传递。
    实现架构如下:

ps07.gif

     首先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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/411903.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux下带密码的scp,linux下带密码的scp

from linux下带密码的scp (2014-03-10 12:35:46)背景&#xff1a; 需要将一台机器的数据传输到另外一台机器上&#xff0c;两台机器没有信任关系&#xff0c;没有数据源一方没有ftp或者http&#xff0c;无法进行wget 解决方法&#xff1a; 在其中一台机器安装expect&#xff0c…

@RestControllerAdvice注解使用

在spring 3.2中&#xff0c;新增了ControllerAdvice&#xff0c;RestControllerAdvice 注解&#xff0c;可以用于定义ExceptionHandler、InitBinder、ModelAttribute&#xff0c;并应用到所有RequestMapping中。参考帮助文档。RestControllerAdvice 是组件注解&#xff0c;他使…

[vue] vue组件会在什么时候下被销毁?

[vue] vue组件会在什么时候下被销毁&#xff1f; 页面关闭、路由跳转、v-if和改变key值个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

简单理解bash和常规操作

1. 什么是bash&#xff1f; Bash shell是一个命令解释器&#xff0c;它是操作系统的外壳程序&#xff0c;负责处理用户命令与操作系统内核之间的交互&#xff0c;当用户输入一个命令并执行时&#xff0c;shell会把命令解释并传递给内核&#xff0c;然后再把内核输出返回给用户&…

linux虚拟机dhcp启动失败,Linux 的dhcp启动异常之No subnet declaration for eth1 (192.168.48.134)...

最近折腾Centos自动化安装&#xff0c;需要的dhcp服务&#xff0c;使用yum -y install dhcp安装dhcp、dhcp-common的rpm包&#xff0c;启动dhcp时异常报错&#xff0c;由于dhcp是及服务&#xff0c;关于dhcp启动失败可以查看操作系统日志/var/log/messages&#xff0c;使用serv…

Developer Express .Net 2005 V7.2.1 crack

Developer Express .Net 2005 V7.2.1破解及下载 http://www.sofitcn.net/ 破解需要注册后才能下载。不要广泛外传&#xff01; 转载于:https://www.cnblogs.com/ainima/archive/2007/07/14/6331513.html

[vue] 使用vue渲染大量数据时应该怎么优化?说下你的思路!

[vue] 使用vue渲染大量数据时应该怎么优化&#xff1f;说下你的思路&#xff01; 1.如果需要响应式&#xff0c;考虑使用虚表&#xff08;只渲染要显示的数据&#xff09;&#xff1b; 2.如果不考虑响应式&#xff0c;变量在beforeCreated或created中声明&#xff08;Object.f…

linux u盘 uid pid,linux下的pid文件的作用

在Linux系统的目录/var/run下面一般我们都会看到很多的*.pid文件。而且往往新安装的程序在运行后也会在/var/run目录下面产生自己的pid文件。那么这些pid文件有什么作用呢&#xff1f;它的内容又是什么呢&#xff1f;(1) pid文件的内容&#xff1a;pid文件为文本文件&#xff0…

学习进度第4周

时间30小时代码量2000行左右博客园2知识点 知识点&#xff1a;从最基本的jsp连接数据库学习。 List<类名> listnew ArrayList<>()&#xff1a;这就像是一个容器。能把数据储存在 list里。 ResultSet rs&#xff1a;创建rs结果集可以用来接受储存数据。 rs…

昨夜的雨图片

昨天趟着淹没到屁股的雨水回家的&#xff0c;今天才知道&#xff0c;这是一场非常可怕的暴雨.本来就吓的够戗,现在直接两腿发软&#xff0c;几欲要哭了。想想真是后怕. 朋友说&#xff0c;你可真大胆,那么大雨你也敢趟.其实不是胆大,而是我走着走着水没的越来越往上了.幸亏个子…

[vue] 你有使用过JSX吗?说说你对JSX的理解

[vue] 你有使用过JSX吗&#xff1f;说说你对JSX的理解 jsx不是一门新的语言&#xff0c;是一种新的语法糖。让我们在js中可以编写像html一样的代码。 允许XML语法直接加入到JavaScript代码中&#xff0c;让你能够高效的通过代码而不是模板来定义界面个人简介 我是歌谣&#x…

linux qtopia-2.2.0编译,qtopia-2.2.0在linux上的安装(基于mini2440)

环境&#xff1a;fedora9、ubuntu10.04x86-qtopia、arm-qtopiagcc-4.3.x、g-4.3.xarm-linux-4.3.2一、x86-qtopia的安装x86-qtopia的安装不管在哪一版本中的linux中&#xff0c;必须采用gcc-4.3.x、g-4.3.x的编译器(或以下&#xff0c;没测试过)&#xff0c;编译过程才不会莫名…

微信支付开发流程

记录下微信JSAPI支付的流程 1、判断是微信浏览器则直接请求微信授权的链接&#xff0c;需要传递给微信重定向回的页面&#xff0c;及订单id // 微信浏览器直接调用 if (this.isWeixin) {let redirectUri http://192.168.1.6/weChatwindow.location.href https://open.weixin.…

vb对数据库操作用存储过程

存储过程如下create proc sp_recordset bbb int as select * from aaa where bbb bbbvb代码如下&#xff1a; Dim cmd As ADODB.Command Dim conn As ADODB.Connection Dim rs As ADODB.Recordset Dim ADOPrm Set ADOPrm New ADODB.Parameter Set rs New AD…

[vue] `<template></template>`有什么用?

[vue] <template></template>有什么用&#xff1f; 包裹嵌套其它元素&#xff0c;使元素具有区域性&#xff0c;自身具有三个特点&#xff1a; *隐藏性&#xff1a;不会显示在页面中 *任意性&#xff1a;可以写在页面的任意地方 *无效性&#xff1a; 没有一个根元…

linux域文件夹权限设置密码,如何配置Linux 文件权限(经典详细版本: rwxst)

在Linux中&#xff0c;Linux文件权限一共分为三种。本文将向读者展示如何正确配置Linux文件权限。三种特殊权限简介SUID当一个设置了SUID 位的可执行文件被执行时&#xff0c;该文件将以所有者的身份运行&#xff0c;也就是说无论谁来执行这个文件&#xff0c;他都有文件所有者…

ASP如何限定中英文混合的文字输出字数?

1<%2字符串截取函数&#xff0c;用于信息标题 3strWord需要截取的字符串 4intByteLength显示的字节长度&#xff0c;1个汉字两个字节 5intPadDotAmount背截取后尾部补充点的个数 6字符串截取函数&#xff0c;用于信息标题 7Function FixString()Function FixString(ByVal st…

【译】索引进阶(十一):SQL SERVER中的索引碎片【上篇】

原文链接&#xff1a;传送门。 第十章节我们分析了索引的内部结构。有了这些关于索引结构的知识&#xff0c;我们便可以分析索引碎片了&#xff1a;其产生的原因&#xff0c;如何防止&#xff0c;以及何时可以不去关注它们。 一些背景知识 / 复习 以下知识对于理解索引碎片来说…

linux备份mysql部分表数据,linux mysql 数据按表名称备份

1、按表名称备份#!/bin/bash#user#数据库用户dbuser"root"#数据库密码dbpassword"chenyong"#时间date$(date %Y%m%d)#数据库名称dbnamesungrowv3#备份数据保存的路劲url/home/test/sql/#指定数据库的所以表tables$(mysql -u$dbuser -p$dbpassword -ss -e &…