• Distributed Application Applying Remoting & Enterprise Service


    Start with Database Connection Pool一文中把DAL从Client转移到了Server,从而获得更好的scalability。此时我们迈出了分布式的第一步,如果更进一步,把BLL也分布到Server上,对我们又将产生怎样的影响呢?

             CS3 

    从图中可以看到一个显而易见的优点,Client非常的Thin,这样的结构特别适合于那种Client端处理能力弱的应用,比如最近很流行的Mobile应用。把复杂的业务逻辑全部交由Server端完成,Client仅仅通过远程接口传入参数并得到结果,这样Client的所需计算能力将非常的小。虽然这也算是分布式带来的好处,但是在本系列中关注的主要不是这一方面的内容。

    其实将BLL分布到Server上,也会产生一个明显的缺点,原来的UIL对BLL的访问全部由Local Call变成了Remote Call, 本地访问和远程访问的性能可不是一个数量级,应用的性能将大打折扣,那么是什么原因使得我们仍然想把BLL分布到其他机器上呢?

    在继续本话题的讨论之前,先要了解一个在分布式应用的重要概念--- Stateless。

    如果你对J2EE有一些了解的话,你肯定听说过session beans和entity bean这两个概念(还有一个用于异步通讯的message bean)。而在所有介绍j2ee的书籍中都对Stateless Session Bean大为推崇,entity bean基本上都是被批判的角色(也是导致O/R M工具的兴起原因之一)。 那么Stateless的具体含义是什么呢?Stateless 和Stateful的区别又在何处?

    Stateless从字面上是说对象没有状态。何谓状态?说白了就是对象中的成员变量。那么Stateless是说得对象没有成员变量,只有成员函数吗? 答案是否定的。这里的状态是争对object的使用者而言。任意一个客户对object的任意一次方法调用都不受之前调用的影响,也就是说每次调用object方法都是相互独立行为,彼此不产生任何影响,这样object称之为Stateless object。 这里的状态指的是对象的使用者的状态,没有状态即对象中不保存特定的某个对象的使用者的状态。 那么我们为什么要定义出这么一种类型的对象,而使它与其他对象区别开来呢?

    对object方法的调用相互独立,可以想象该类型的对象可以服务于任何一个客户,服务于任何一个请求。Client A可以使用它来完成工作,Client B同样可以使用它, Client A在时间T1使用它,也可以在T2时使用它来完成工作。同一个对象可以在不同的时间服务于多个客户,这就是Stateless 对象所具有的最大优势。

    那么使用Stateless object究竟能给我带来什么好处。在C/S结构中如果BLL和UIL部署在一起,那么使用Stateless 或者Stateful对象效果完全一样,因为他们都处于Client端,完全不能被其他Client重用。当BLL被部署在Server端时,Stateless对象和Stateful对象的差别就大了,Stateless对象可以被任意一个Client重用。和DB Connection Pool类似,一个Stateless对象可以为多个Client提供服务。

    但是以上仅仅证明了Stateless对象在分布式的应用中优于Stateful对象,并没有很好的解释为什么要将BLL分布。

    确实在C/S应用中,将BLL分布仅仅为Client减负(计算能力和内存),但是由于远程调用的反而使得应用的整体性能下降,并且将BLL分布也并未获得更强的Scalability(方便的支持更多的用户)。

    但是如果将应用背景转移到B/S结构的应用中,分布BLL的优势就凸现出来了。

        BS2

    如上图所示, UIL和BLL一起部署在Web Server中,作为Web Server的服务器,一台机器的负载是有限的,当并发Client数量激增时,Web Server将受限于有限的计算能力和存储空间,而无法满足越来越多的Client的服务请求。

    而当我们把BLL从Web Server中分布到Application Server后,由于设计时考虑主要使用Stateless 的业务对象充当提供服务的Facade,此时为了满足越来越多的Client请求,一方面我们可以使用Stateless object pool来减少所需创建的对象数量,另一方面可以对分布出来的Application Server做集群,而Stateless 的业务对象做集群时不需要考虑状态的复制,这样就可以很好的满足系统对Scalability的要求。        

        BS1    

         BS3

    上述的Stateless 业务对象,在EJB中对应了SLSB(Stateless Session Bean)的概念。EJB作为应用服务器,为SLSB提供了大量的服务,如远程调用,实例池,资源池,线程管理,声明性事务,集群等等。而在.Net的世界,就需要依靠COM,DCOM,Remoting以及EnterpriseService来帮助我们实现同样的目的。下面将通过一个小例子来展示这些功能,主要集中在远程调用,实例池,资源池。

    分布式中最基础的一个功能就是实现远程调用,既然对象被分布了,那么首先你必须还能访问到它。如果能象本地对象那样访问远程对象,那就更方便了。我想搞.Net的或多或少都听说过Remoting这个概念,不过真正用到的人可能不多,大家可能觉得它比较神秘,其实这项技术就是用于实现远程方法调用的,是为分布式服务的。

    何时采用Remoting?

    当你需要访问远程对象(跨应用域,跨进程,跨机器)时,并且应用的两端都是采用的.Net环境,即.Net---.Net。

    限于篇幅,在此不准备对Remoting做详细介绍,本文假设读者已经对Remoting有一定了解。

    如果说EJB中的SLSB,是Stateless Object在Java下的具体实现的话,那么在.Net下,与之比较相近的就是Remoting中的SAO(Server Activation Object)。不过Remoting提供的服务和EJB可不能相提并论,可以说Remoting仅仅提供了远程调用的功能,不过结合EnterpriseService,在.Net的环境中你也可以享受到EJB容器所提供的诸多服务。

    下面,让我们从一个Remoting的例子开始.Net分布式应用的演示。

     

        1     public interface IGetInfo

        2     {

        3         string GetInfo();

        4     }

     

     

     

       1     public class SimpleService : MarshalByRefObject, IGetInfo, IDisposable

        2     {

        3         private bool alreadyDisposed = false;

        4         private string id;

        5         public SimpleService()

        6         {

        7             id = Guid.NewGuid().ToString().Substring(20);

        8             string logMsg = string.Format("Construct Object: {0}",

        9                                         id);

       10             Console.WriteLine("SimpleService.Construct {0}", logMsg);

       11         }

       12 

       13         public string GetInfo()

       14         {

       15             Thread.Sleep(1000);

       16             return "i am simpleservice";

       17         }

       18 

       19         ~SimpleService()

       20         {

       21             Console.WriteLine(string.Format("SimpleService.Finalize {0}",id));

       22             Dispose(false);

       23         }

       24 

       25         void IDisposable.Dispose()

       26         {

       27             Dispose(true);

       28             GC.SuppressFinalize(true);

       29         }

       30 

       31         protected virtual void Dispose(bool isDisposing)

       32         {

       33             // Don't dispose more than once.

       34             if (alreadyDisposed)

       35                 return;

       36             if (isDisposing)

       37             {

       38                 // TODO: free managed resources here.

       39             }

       40             Console.WriteLine(string.Format("Disposing unmanaged resources {0}",id));

       41 

       42             // TODO: free unmanaged resources here.

       43             // Set disposed flag:

       44             alreadyDisposed = true;

       45         }

       46     }

     

     

    服务端提供了一个GetInfo的方法供客户端使用,注意SimpleService实现了IDisposable接口,之所以实现它是用于演示Remoting对对象生命周期的管理。

    如前所述,我们希望使用Stateless object pool来减少所需创建的对象数量,但是Remoting本身仅仅提供了远程访问的能力,它并没有支持对象池。Remoting对Stateless object 的生命周期的管理主要有Singleton和SingleCall两者,并且通常情况下会使用SingCall方式,即Client每来一个请求,Server创建出一个新的对象,请求被处理后,销毁对象。

         RemotingSingleCall

    具体过程如图所示,Client在对远程对象发出请求前,对象并不存在,当Client请求到达Server后,Server创建出相应的对象,然后利用它处理请求,请求处理完之后,返回结果并立即Dispose对象,在.Net Framework下一次垃圾回收时再彻底销毁对象。由于当前虚拟机创建销毁对象的效率已经相当高,当创建对象所需资源不多(不耗过多时间,不占过多内存)时,利用该方法已经可以很好的满足需求。

    下面是Server端和Client端使用Remoting来完成请求的实例代码及结果

        1     class Server

        2     {

        3         static void Main()

        4         {

        5             // Create a channel specifying the port #

        6             HttpChannel channel = new HttpChannel(13101);

        7             // Register the channel with the runtime remoting services

        8             ChannelServices.RegisterChannel(channel, false);

        9 

       10             // Register a type as a well-known type

       11             RemotingConfiguration.RegisterWellKnownServiceType(

       12                typeof(SimpleService), // The type to register

       13                "SimpleService.soap",                   // The well-known name

       14                WellKnownObjectMode.SingleCall   // SingleCall or Singleton

       15             );

       16 

       17             // Keep the server alive until Enter is pressed.

       18             Console.WriteLine("Server started. Press Enter to end");

       19             Console.ReadLine();

       20         }

       21     }

        1     class Client

        2     {

        3         static void Main(string[] args)

        4         {

        5             // Create and register the channel. The default channel ctor

        6             // does not open a port, so we can't use this to receive messages.

        7             HttpChannel channel = new HttpChannel();

        8             ChannelServices.RegisterChannel(channel, false);

        9 

       10             // Get a proxy to the remote object

       11             object remoteObj = Activator.GetObject(

       12                 typeof (IGetInfo),

       13                 "http://localhost:13101/SimpleService.soap"

       14                 );

       15             IGetInfo getInfoSvc = remoteObj as IGetInfo;

       16             string info = getInfoSvc.GetInfo();

       17             Console.WriteLine(info);

       18 

       19             //Test for multiple creat and dispose object

       20             getInfoSvc.GetInfo();

       21             getInfoSvc.GetInfo();

       22             getInfoSvc.GetInfo();

       23 

       24             Console.ReadLine();

       25         }

       26     }

     

        SingleCallConsole 

    可以看出运行结果和文中描述的是一致的。

    .Net世界的开发者总是盼望着什么时候也有一个象EJB那样的容器,确实EJB为java开发者提供了很多方便的服务,但是如果你仔细研究.Net下的COM,DCOM,EnterpriseService,MSMQ,你会发现.Net的世界同样精彩。至少EJB为Stateless Session Bean提供的服务,.Net大多也提供了方便支持,如远程调用,实例池,资源池,声明性事务。下面将介绍一下如何用ES来实现实例池,其他内容限于篇幅可能在以后做进一步介绍。

    远程调用这部分的使用方法仍旧采用Remoting,代码不需要做任何改变。为了支持对象池,主要修改服务的实现,不过改动也是非常的小。下面是新的SimpleService 的实现代码:

        1     [ClassInterface(ClassInterfaceType.None)]

        2     [ComVisible(true)]

        3     [JustInTimeActivation()]

        4     [ObjectPooling(3, 3)]

        5     public class SimpleService : ServicedComponent, IGetInfo, IDisposable

        6     {

        7         private bool alreadyDisposed = false;

        8         private string id;

        9 

       10         // Serviced components require a default constructor.

       11         public SimpleService()

       12         {

       13             id = Guid.NewGuid().ToString().Substring(20);

       14             string logMsg = string.Format("Construct Object: {0}",

       15                                         id);

       16             Console.WriteLine("SimpleService.Construct {0}", logMsg);

       17         }

       18 

       19         public string GetInfo()

       20         {

       21             Thread.Sleep(1000);

       22             ContextUtil.DeactivateOnReturn = true;

       23             return "i am simpleservice using object pool";

       24         }

       25 

       26         protected override void Activate()

       27         {

       28             string logMsg = string.Format("Activated Object: {0}",

       29                                         id);

       30             Console.WriteLine("SimpleService.Activate {0}", logMsg);

       31         }

       32 

       33         protected override void Deactivate()

       34         {

       35             string logMsg = string.Format("Deactivated Object: {0}",

       36                                         id);

       37             Console.WriteLine("SimpleService.Deactivate {0}", logMsg);

       38         }

       39 

       40         protected override bool CanBePooled()

       41         {

       42             return true;

       43         }

       44 

       45         // finalizer:

       46         // Call the virtual Dispose method.

       47         ~SimpleService()

       48         {

       49             Console.WriteLine("SimpleService.Finalize");

       50             Dispose(false);

       51         }

       52 

       53         // Implementation of IDisposable.

       54         // Call the virtual Dispose method.

       55         // Suppress Finalization.

       56         void IDisposable.Dispose()

       57         {

       58             Dispose(true);

       59             GC.SuppressFinalize(true);

       60         }

       61 

       62         // Virtual Dispose method

       63         protected new virtual void Dispose(bool isDisposing)

       64         {

       65             // Don't dispose more than once.

       66             if (alreadyDisposed)

       67                 return;

       68             if (isDisposing)

       69             {

       70                 // TODO: free managed resources here.

       71             }

       72             Console.WriteLine("Disposing unmanaged resources");

       73 

       74             // TODO: free unmanaged resources here.

       75             // Set disposed flag:

       76             alreadyDisposed = true;

       77         }

       78     }

    
    

    我们通过Attribute使用了JustInTimeActivation和ObjectPooling两个COM+服务,其中设定了ObjectPool的大小为3个对象。客户端和服务器端使用Remoting的代码不变,运行结果如下:

         objectpoolConsole

    可以看出对象的创建是在调用方法时发生的,而不是一开始就在池中连续创建3个对象。并且可以发现尽管Client发出了4次请求,但是总共创建了3个对象,并仅仅使用了2个。处理请求的时候Activate对象,处理完后Deactivate。最后三次请求都是使用的同一个对象,那是因为在示例中的请求是顺序的,而不是并发的。下面用多线程模拟一下多客户并发请求时,对象池的使用情况。

        1     class Client

        2     {

        3         delegate string GetInfoDelegate();

        4         static void Main(string[] args)

        5         {

        6             // Create and register the channel. The default channel ctor

        7             // does not open a port, so we can't use this to receive messages.

        8             HttpChannel channel = new HttpChannel();

        9             ChannelServices.RegisterChannel(channel, false);

       10 

       11             // Get a proxy to the remote object

       12             object remoteObj = Activator.GetObject(

       13                 typeof (IGetInfo),

       14                 "http://localhost:13101/SimpleService.soap"

       15                 );

       16             IGetInfo getInfoSvc = remoteObj as IGetInfo;

       17             string info = getInfoSvc.GetInfo();

       18             Console.WriteLine(info);

       19 

       20             //Test for concurrent invoke

       21             GetInfoDelegate getInfoHandle = new GetInfoDelegate(getInfoSvc.GetInfo);

       22             getInfoHandle.BeginInvoke(null, null);

       23             getInfoHandle.BeginInvoke(null, null);

       24             getInfoHandle.BeginInvoke(null, null);

       25 

       26             Console.ReadLine();

       27         }

       28     }

     

         objectpoolConsole2 


    从中可以看出最后三次调用分别使用了不同的对象。 



    总结:

    本文主要介绍了Stateless Object的概念,并提出将BLL分布,通过对Application Server做Cluster,以获得更好的Scalability。之后通过一个例子演示了如何在.Net环境中利用Remoting和EnterpriseService实现类似EJB中SLSB的功能。

    参考资料:

    Microsoft .Net Distributed Application
    Expert One-on-One J2EE Design and Develop
    Expert One on one J2EE Development Without EJB
    Pattern Oriented Software Architecture-Patterns for Resource Management
    Distributed .NET Programming in C#

    相关文章:

    Applying Distributed Application in .Net系列文章

    Webservice 的设计和模式

    Web Services Security

    WS-Addressing 从理论到实践 --- SOA基础规范介绍

  • 相关阅读:
    fastJson
    关于mybatis “org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)” 错误的问题。
    JVM——自定义类加载器
    NumberFormat数字格式化
    Java BigDecimal详解
    NumberUtils、ArrayUtils和RandomUtils工具类用法
    itext 生成 PDF(二)
    C语言中的可变参数列表
    归并排序--c语言实现
    机器学习实战学习笔记 一 k-近邻算法
  • 原文地址:https://www.cnblogs.com/chorrysky/p/766411.html
Copyright © 2020-2023  润新知