`
灵雨飘零
  • 浏览: 35143 次
  • 性别: Icon_minigender_1
  • 来自: 唐山
文章分类
社区版块
存档分类
最新评论

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的基本操作:
public virtual object Receive()
{
try
{
using (Message message = queue.Receive(timeout, transactionType))
return message;
}
catch (MessageQueueException mqex)
{
if (mqex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
throw new TimeoutException();
throw;
}
}
public virtual void Send(object msg)
{
queue.Send(msg, transactionType);
}
其中queue对象是System.Messaging.MessageQueue类型,作为存放数据的队列。MSMQ队列是一个可持久的队列,因此不必担心用户不间断地下订单会导致订单数据的丢失。在PetShopQueue设置了timeout值,OrderProcessor会根据timeout值定期扫描队列中的订单数据。
MSMQMessaging模块中,Order对象实现了IMessaging模块中定义的接口IOrder,同时它还继承了基类PetShopQueue,其定义如下:
public class Order:PetShopQueue, PetShop.IMessaging.IOrder
方法的实现代码如下:
public new OrderInfo Receive()
{
// This method involves in distributed transaction and need Automatic Transaction type
base.transactionType = MessageQueueTransactionType.Automatic;
return (OrderInfo)((Message)base.Receive()).Body;
}

public OrderInfo Receive(int timeout)
{
base.timeout = TimeSpan.FromSeconds(Convert.ToDouble(timeout));
return Receive();
}

public void Send(OrderInfo orderMessage)
{
// This method does not involve in distributed transaction and optimizes performance using Single type
base.transactionType = MessageQueueTransactionType.Single;
base.Send(orderMessage);
}
所以,最后的类图应该如下:
ps02.gif
注意在Order类的Receive()方法中,是用new关键字而不是override关键字来重写其父类PetShopQueue的Receive()虚方法。因此,如果是实例化如下的对象,将会调用PetShopQueue的Receive()方法,而不是子类Order的Receive()方法:
PetShopQueue queue = new Order();
queue.Receive();
从设计上来看,由于PetShop采用“面向接口设计”的原则,如果我们要创建Order对象,应该采用如下的方式:
IOrder order = new Order();
order.Receive();
考虑到IOrder的实现有可能的变化,PetShop仍然利用了工厂模式,将IOrder对象的创建用专门的工厂模块进行了封装:
ps03.gif
在类QueueAccess中,通过CreateOrder()方法利用反射技术创建正确的IOrder类型对象:
public static PetShop.IMessaging.IOrder CreateOrder()
{
string className = path + ".Order";
return PetShop.IMessaging.IOrder)Assembly.Load(path).CreateInstance(className);
}
path的值通过配置文件获取:
private static readonly string path = ConfigurationManager.AppSettings["OrderMessaging"];
而配置文件中,OrderMessaging的值设置如下:
<add key="OrderMessaging" value="PetShop.MSMQMessaging"/>
之所以利用工厂模式来负责对象的创建,是便于在业务层中对其调用,例如在BLL模块中OrderAsynchronous类:
public class OrderAsynchronous : IOrderStrategy
{
private static readonly PetShop.IMessaging.IOrder asynchOrder = PetShop.MessagingFactory.QueueAccess.CreateOrder();
public void Insert(PetShop.Model.OrderInfo order)
{
asynchOrder.Send(order);
}
}
一旦IOrder接口的实现发生变化,这种实现方式就可以使得客户仅需要修改配置文件,而不需要修改代码,如此就可以避免程序集的重新编译和部署,使得系统能够灵活应对需求的改变。例如定义一个实现IOrder接口的SpecialOrder,则可以新增一个模块,如PetShop.SpecialMSMQMessaging,而类名则仍然为Order,那么此时我们仅需要修改配置文件中OrderMessaging的值即可:
<add key="OrderMessaging" value="PetShop.SpecialMSMQMessaging"/>
OrderProcessor是一个控制台应用程序,不过可以根据需求将其设计为Windows Service。它的目的就是接收消息队列中的订单数据,然后将其插入到Order和Inventory数据库中。它利用了多线程技术,以达到提高系统性能的目的。
在OrderProcessor应用程序中,主函数Main用于控制线程,而核心的执行任务则由方法ProcessOrders()实现:
private static void ProcessOrders()
{
// the transaction timeout should be long enough to handle all of orders in the batch
TimeSpan tsTimeout = TimeSpan.FromSeconds(Convert.ToDouble(transactionTimeout * batchSize));

Order order = new Order();
while (true)
{
// queue timeout variables
TimeSpan datetimeStarting = new TimeSpan(DateTime.Now.Ticks);
double elapsedTime = 0;

int processedItems = 0;

ArrayList queueOrders = new ArrayList();

using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, tsTimeout))
{
// Receive the orders from the queue
for (int j = 0; j < batchSize; j++)
{
try
{
//only receive more queued orders if there is enough time
if ((elapsedTime + queueTimeout + transactionTimeout) < tsTimeout.TotalSeconds)
{
queueOrders.Add(order.ReceiveFromQueue(queueTimeout));
}
else
{
j = batchSize; // exit loop
}

//update elapsed time
elapsedTime = new TimeSpan(DateTime.Now.Ticks).TotalSeconds - datetimeStarting.TotalSeconds;
}
catch (TimeoutException)
{
//exit loop because no more messages are waiting
j = batchSize;
}
}
//process the queued orders
for (int k = 0; k < queueOrders.Count; k++)
{
order.Insert((OrderInfo)queueOrders[k]);
processedItems++;
totalOrdersProcessed++;
}

//batch complete or MSMQ receive timed out
ts.Complete();
}

Console.WriteLine("(Thread Id " + Thread.CurrentThread.ManagedThreadId + ") batch finished, " + processedItems + " items, in " + elapsedTime.ToString() + " seconds.");
}
}
首先,它会通过PetShop.BLL.Order类的公共方法ReceiveFromQueue()来获取消息队列中的订单数据,并将其放入到一个ArrayList对象中,然而再调用PetShop.BLL.Order类的Insert方法将其插入到Order和Inventory数据库中。
在PetShop.BLL.Order类中,并不是直接执行插入订单的操作,而是调用了IOrderStrategy接口的Insert()方法:
public void Insert(OrderInfo order)
{
// Call credit card procesor
ProcessCreditCard(order);

// Insert the order (a)synchrounously based on configuration
orderInsertStrategy.Insert(order);
}
在这里,运用了一个策略模式,类图如下所示:
ps05.gif
在PetShop.BLL.Order类中,仍然利用配置文件来动态创建IOrderStategy对象:
private static readonly PetShop.IBLLStrategy.IOrderStrategy orderInsertStrategy = LoadInsertStrategy();
private static PetShop.IBLLStrategy.IOrderStrategy LoadInsertStrategy()
{
// Look up which strategy to use from config file
string path = ConfigurationManager.AppSettings["OrderStrategyAssembly"];
string className = ConfigurationManager.AppSettings["OrderStrategyClass"];

// Using the evidence given in the config file load the appropriate assembly and class
return (PetShop.IBLLStrategy.IOrderStrategy)Assembly.Load(path).CreateInstance(className);
}
由于OrderProcessor是一个单独的应用程序,因此它使用的配置文件与PetShop不同,是存放在应用程序的App.config文件中,在该文件中,对IOrderStategy的配置为:
<add key="OrderStrategyAssembly" value="PetShop.BLL" />
<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集成起来,完成数据的处理。

分享到:
评论

相关推荐

    《解剖PetShop》之三:PetShop数据访问层之消息处理

    三、PetShop数据访问层之消息处理  在进行系统设计时,除了对安全、事务等问题给与足够的重视外,性能也是一个不可避免的问题所在,尤其是一个B/S结构的软件系统,必须充分地考虑访问量、数据流量、服务器负荷的...

    《解剖PetShop》之二:PetShop数据访问层数之据库访问设计

    二、PetShop数据访问层之数据库访问设计  在系列一中,我从整体上分析了PetShop的架构设计,并提及了分层的概念。从本部分开始,我将依次对各层进行代码级的分析,以求获得更加细致而深入的理解。在PetShop 4.0中,...

    pet shop 4.0 有详细架构文档 代码 三层解析

    《解剖PetShop》系列之三 PetShop数据访问层之消息处理 14 《解剖PetShop》系列之四 PetShop之ASP.NET缓存 22 《解剖PetShop》系列之五 PetShop之业务逻辑层设计 35 《解剖PetShop》系列之六 PetShop之表示层设计 44

    解剖PetShop4.0

    通过阅读《解剖PetShop4.0》系列PDF,开发者不仅可以学习到.NET技术的实际应用,还能了解到如何构建可扩展、可维护的Web应用,以及如何在实际项目中应用设计模式和架构策略。因此,对于想要提升.NET开发技能或者研究...

    解剖PetShop4

    紧接着,《PetShop数据访问层之消息处理》这部分,可能是关于服务间的通信。PetShop可能使用了消息队列(如MSMQ)来处理异步通信,这种方式可以提高系统的响应速度和并发处理能力,同时减少服务之间的耦合度。 然后...

    《解剖PetShop》系列 word格式

    2. **PetShop架构设计**:PetShop的架构设计采用了三层架构模式,包括表示层(Presentation Layer)、业务逻辑层(Business Logic Layer)和数据访问层(Data Access Layer)。表示层负责用户交互,业务逻辑层处理...

    解剖PetShop4.doc

    《解剖PetShop4——深入理解ASP.NET应用实践》 微软的PetShop4是一个经典的Web应用程序示例,它被广泛用于展示和教学ASP.NET技术。这个项目为开发者提供了丰富的学习资源,帮助他们理解和掌握如何构建高效、可扩展...

    解剖PetShop

    - **工厂模式**:在数据访问层和消息处理层广泛采用工厂模式,为创建对象提供了一种抽象的方法。 - **多数据库支持**:通过IDAL接口和具体的数据库实现类,可以灵活切换数据库。 - **缓存机制**:引入了缓存依赖类和...

    解剖petshop_ASP.NET开发B S系统

    B/S模式是一种三层架构,包括表现层(客户端,通常为浏览器)、业务逻辑层(中间件,负责处理业务规则和数据验证)以及数据访问层(服务器端,主要负责数据库操作)。PetShop项目就很好地展示了这种架构,通过ASP...

    全面解剖PetShop4.0

    1. **分层架构**:PetShop4.0采用了常见的三层架构,包括表现层(UI)、业务逻辑层(BLL)和数据访问层(DAL)。这种架构有助于代码的组织和复用,同时降低各层之间的耦合度。 2. **依赖注入**:为了提高代码的可...

    解剖PetShop.rar

    6、SQLServerDAL=SQLServer数据访问层 / OracleDAL=Oracle数据访问层 DBUtility 数据库访问组件基础类 二、项目引用关系 1、Web 引用 BLL。 2、BLL 引用 IDAL,Model,使用DALFactory创建实例。 3、IDAL 引用 ...

    Petshop4.0 解剖

    2. **三层架构**:PetShop4.0采用了经典的三层架构设计,包括表现层(UI)、业务逻辑层(BLL)和数据访问层(DAL)。这种架构有助于代码的组织和重用,同时也便于维护和扩展。 3. **ADO.NET**:在数据访问层,...

    深入解剖petshop

    《深入解剖PetShop》是一本专注于探讨微软经典示例应用PetShop的详细教程,它以其深入的技术解析和丰富的实践指导,在ASP.NET开发者社区中享有很高的声誉。PetShop作为一款基于早期ASP.NET技术构建的网上宠物商店...

    petshop4源代码及相关文档

    源代码中包含了业务逻辑层(Business Logic Layer, BLL)、数据访问层(Data Access Layer, DAL)以及用户界面层(Presentation Layer)的实现,这些层次结构清晰地展示了企业级应用程序的设计模式。通过分析这些...

    PETSHOP4.0 详细分析

    PetShop4.0版本在前几版的基础上进行了优化,引入了更多的现代Web开发理念和技术,如使用设计模式、三层架构以及数据库访问策略等。其中,标签“ASP.NET”表明这个项目是用微软的ASP.NET技术构建的,这是一种用于...

Global site tag (gtag.js) - Google Analytics