《解剖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的基本操作:

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);
}

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

注意在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对象的创建用专门的工厂模块进行了封装:

在类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);
}

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

在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" />

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

  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集成起来,完成数据的处理。

以上就是PetShop数据访问层消息处理部分的全部内容,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 《解剖PetShop》之六:PetShop之表示层设计

    六 PetShop之表示层设计 表示层(Presentation Layer)的设计可以给系统客户最直接的体验和最十足的信心.正如人与人的相交相识一样,初次见面的感觉总是永难忘怀的.一件交付给客户使用的产品,如果在用户界面(User Interface,UI)上缺乏吸引人的特色,界面不友好,操作不够体贴,即使这件产品性能非常优异,架构设计合理,业务逻辑都满足了客户的需求,却仍然难以讨得客户的欢心.俗语云:"佛要金装,人要衣装",特别是对于Web应用程序而言,Web网页就好比人的衣装,代

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

    二.PetShop数据访问层之数据库访问设计 在系列一中,我从整体上分析了PetShop的架构设计,并提及了分层的概念.从本部分开始,我将依次对各层进行代码级的分析,以求获得更加细致而深入的理解.在PetShop 4.0中,由于引入了ASP.Net 2.0的一些新特色,所以数据层的内容也更加的广泛和复杂,包括:数据库访问.Messaging.MemberShip.Profile四部分.在系列二中,我将介绍有关数据库访问的设计. 在PetShop中,系统需要处理的数据库对象分为两类:一是数据实体,

  • 《解剖PetShop》之五:PetShop之业务逻辑层设计

    五 PetShop之业务逻辑层设计 业务逻辑层(Business Logic Layer)无疑是系统架构中体现核心价值的部分.它的关注点主要集中在业务规则的制定.业务流程的实现等与业务需求有关的系统设计,也即是说它是与系统所应对的领域(Domain)逻辑有关,很多时候,我们也将业务逻辑层称为领域层.例如Martin Fowler在<Patterns of Enterprise Application Architecture>一书中,将整个架构分为三个主要的层:表示层.领域层和数据源层.作为领

  • 学会sql数据库关系图(Petshop)

    很久以前就知道微软的Petshop的很经典,昨天抽出时间去学习,一开始还真的不适应,什么成员资格,还真的看不太懂,运行petshop想从登陆学起,但是用户名和密码都不知道,后来发现有更注册的页面,自己注册了一个页面,才发现还得从数据库出发.花了这么多时间最终还是回到了数据库,但是数据库中一张一张的表格找不到脚本,也不是自己设计的数据库,完全没有一点头绪,后来突然想起来sql有个数据库关系图,可以很快的适合数据库程序员很快的掌握数据库表之间的关系.于是开始了我的百度之旅,关于数据库的关系图的文章还

  • 《解剖PetShop》之四:PetShop之ASP.NET缓存

    四 PetShop之ASP.NET缓存 如果对微型计算机硬件系统有足够的了解,那么我们对于Cache这个名词一定是耳熟能详的.在CPU以及主板的芯片中,都引入了这种名为高速缓冲存储器(Cache)的技术.因为Cache的存取速度比内存快,因而引入Cache能够有效的解决CPU与内存之间的速度不匹配问题.硬件系统可以利用Cache存储CPU访问概率高的那些数据,当CPU需要访问这些数据时,可以直接从Cache中读取,而不必访问存取速度相对较慢的内存,从而提高了CPU的工作效率.软件设计借鉴了硬件设

  • 《解剖PetShop》之一:PetShop的系统架构设计

    前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有许多.Net与J2EE之争,许多数据是从微软的PetShop和Sun的PetStore而来.这种争论不可避免带有浓厚的商业色彩,对于我们开发人员而言,没有必要过多关注.然而PetShop随着版本的不断更新,至现在基于.Net 2.0的PetShop4.0为止,整个设计逐渐变得成熟而优雅,却又很多可以借鉴之处.PetShop是一个小型的项目,系统架构与代码都比较简单,却也凸现了许多颇有价值的设计与开发理念.本系列试图对

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

    三.PetShop数据访问层之消息处理 在进行系统设计时,除了对安全.事务等问题给与足够的重视外,性能也是一个不可避免的问题所在,尤其是一个B/S结构的软件系统,必须充分地考虑访问量.数据流量.服务器负荷的问题.解决性能的瓶颈,除了对硬件系统进行升级外,软件设计的合理性尤为重要. 在前面我曾提到,分层式结构设计可能会在一定程度上影响数据访问的性能,然而与它给设计人员带来的好处相比,几乎可以忽略.要提供整个系统的性能,还可以从数据库的优化着手,例如连接池的使用.建立索引.优化查询策略等等,例如在P

  • java 使用JDBC构建简单的数据访问层实例详解

    本教程的目的是使用Java编写的分离的层去访问数据库中的表,这一层通常称为数据访问层(DAL) 使用DAL的最大好处是通过直接使用一些类似insert()和find()的方法简化了数据库的访问操作,而不是总是先做链接,再执行一些查询. 该层在其内部处理所有与数据库相关的调用和查询. 创建数据库 我们希望为用户创造一个简单的表,我们可以使用这些字段来创建 id        int name      varchar(200) password  varchar(200) age       in

  • asp.net SqlHelper数据访问层的使用

    本文章主要介绍SqlHelper使用. 每个项目都要用到数据访问层,我做的也不例外,但是我把数据访问层做成独立项目,没有什么太大的目的,数据访问层,仅仅做数据访问用,不包含任何逻辑. 为什么要使用数据访问层? 如果不使用数据访问层,那么你的代码里会出现很多SqlConnection.SqlCommand.SqlDataReader.Open. Close--这些类和方法,而且代码量很大,让你不胜其烦,而且代码写起来,其实都是体力活,没有技术含量.因此我们要把数据访问层封装起来,方便重用.微软已经

  • 在ASP.NET 2.0中操作数据之一:创建一个数据访问层

    导言 作为web开发人员,我们的生活围绕着数据操作.我们建立数据库来存储数据,写编码来访问和修改数据,设计网页来采集和汇总数据.本文是研究在ASP.NET 2.0中实现这些常见的数据访问模式之技术的长篇系列教程的第一篇.我们将从创建一个软件框架开始,这个框架的组成部分包括一个使用强类型的DataSet的数据访问层(DAL),一个实施用户定义的业务规则的业务逻辑层(BLL),以及一个由共享页面布局的ASP.NET网页组成的表现层.在打下这个后端的基础工作之后,我们将开始转向报表,示范如何显示,汇总

  • asp.net 数据访问层 存储过程分页语句

    所以最好在数据访层分页,如果这样就要使用存储过程来分页.以下是以pubs 数据库中的employee表为例来进行数据分页的存储过程,你可以参考它根据实际情况来创建自己的存储过程. 注:@pageindex 数据页的索引,@dataperpage 每页的记录数目,@howmanyrecords 用来获取总的记录数. 复制代码 代码如下: create proc getdata @pageindex int,@dataperpage int,@howmanyrecords int output as

  • asp.net 数据访问层基类

    部分代码: 复制代码 代码如下: using System; using System.Collections; using System.Collections.Specialized; using System.Data; using System.Data.SqlClient; using System.Configuration; using System.Data.Common; using System.Collections.Generic; namespace sosuo8.DB

  • SpringBoot中Mybatis + Druid 数据访问的详细过程

    目录 1.简介 2.JDBC 3.CRUD操作 4.自定义数据源 DruidDataSource 1.配置 Druid 数据源监控 2.配置 Druid web 监控 filter 5.SpringBoot 整合mybatis 1. 导入mybatis所需要的依赖 2.配置数据库连接信息 3,创建实体类 4.配置Mapper接口类 6.SpringBoot 整合 1.简介 ​ 对于数据访问层,无论是SQL(关系型数据库) 还是NOSQL(非关系型数据库),SpringBoot 底层都是采用 Sp

  • Spring Boot深入学习数据访问之Spring Data JPA与Hibernate的应用

    目录 前言 Spring Boot的支持 前言 Hibernate是一个开源的对象关系映射框架,它对JDBC及进行了非常轻量级的对象封装,它将POJO简单的java对象与数据库表建立映射关系,是一个全自动的ORM框架,Hibernate可以自动生成SQL语句自动执行. JPA是官方提出的Java持久化规范,JPA通过注解或XML描述对象一关系表的映射关系,并将内存中的实体对象持久化到数据库 Spring Data JPA通过提供基于JPA的Repository极大的简化了JPA的写法,在几乎不写

  • 使用JDBC实现数据访问对象层(DAO)代码示例

    JAVA是面向对象的语言,开发者在操作数据的时候,通常更习惯面对一个特定类型的对象,如一个用户就是一个User类的对象.DAO层需要做的,就是为上层提供充分的对象支持,让上层再也看不到具体的数据,而是一个个活生生的对象. 增加,删除,查询和修改操作是DAO需要做的最基本的4项操作.查询一般需要提供遍历查询和id查询,对于遍历查询,DAO需要提供User泛型的list对象,对于id查询则提供已经装配好数据的User对象,至于增加和修改操作,上层一般会提供一个User对象,DAO把User对象中的数

随机推荐