实例上下文模式:单例模式

       单例模式效果可以用下面这张图表示,服务端的服务实例只有一个,任何一个客户端访问的服务端都是相同的服务实例。意味着服务端可以留下不同客户端的脚印。

                                   

      使用也很简单,只需要将ServiceBehavior的上下文模式InstanceContextMode设置为Single即可。可以参照上一篇介绍实例上下文模式:单调模式

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
    public class Calculator : ICalculator,IDisposable
    {
       ......
    }

    

      上篇介绍实例上下文模式时有些概念没有讲清楚。这里来补一下。

      ServiceHost有两个有参数的构造函数,分别是

 public ServiceHost(object singletonInstance, params Uri[] baseAddresses);
 public ServiceHost(Type serviceType, params Uri[] baseAddresses);

   第一个构造函数,singletonInstance是指服务实例,即new Calculator()创建的实例对象。

   第二个构造函数是指服务实例的类型,即typeof(Calculator)获得。

   baseAddresses均指终结点的基地址,这里需要注意,每一种Binding类型只能有一个基地址。例如下面寄宿代码将会报错,因为baseAddrs中含有两个http的基地址。

  

  回到正题,通过观察ServiceHost的构造函数代码。会将服务实例放入singletoninstance,类型放入serviceType。  这两个字段在ServiceHost中都是私有的。Servciehost启动的时候先检查singletonInstance是否存在,若不存在则根据serviceType反射创建一个服务实例。

了解单例的机制需要知道一下几点: 

   1.当服务启动时候会调用服务行为(ServiceBehavior)初始化ServiceHost。而服务行为实现了IServiceBehavior接口,它定义了一个ApplyDispatchBehavior接口。

public interface IServiceBehavior
{
void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters);
void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
}

 此接口的作用是将创建的实例上下文(InstanceContext)附加到终点分发器(EndpointDispatcher)的DispatchRuntime的SingletonInstance上。

 

   2.实例上下文提供者实现了接口IInstanceContextProvider,实例上下文提供者附加在终点分发器(EndpointDispatcher)的DispatchRuntime的InstanceContextProvider上。

public interface IInstanceContextProvider
{
InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel);
void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel);
bool IsIdle(InstanceContext instanceContext);
void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext);
}

     当服务端接受到一个请求消息时,根据消息地址报头和Action选好终结点分发器(EndpointDispatcher)后,首先会调用终结点分发器中的运行时(DispatchRuntime)获取服务上下文提供者(IInstanceContextProvider),再调用服务上下文提供者的GetExistingInstanceContext获取InstanceContext,再从上下文中获取服务实例,调用实例的方法。流程处理完毕后调用IsIdle方法,如果返回为true则标识当前实例上下文生命周期结束,GC回收。

      上一篇介绍单调时,直接将GetExistingInstanceContext返回null,WCF会创建一个新的实例上下文,使用完毕后再调用Isdle,直接返回true,则让GC回收。因此就有了单调模型。

     实例模型只需要保证在GetExistingInstanceContext时返回一个相同的实例上下文,并且不能被GC回收,因此IsIdle返回false即可。而这个相同的实例上下文,需要到终结点分发器中取出来。

     下面自定义一个SingleServiceProvider,如前面所述,IsIdle返回false,确保不让GC回收。GetExistingInstanceContext返回内部的runtime中的SingletonInstanceContext。而这个runtime是在构造函数中指定。

public class SingleServiceProvider : IInstanceContextProvider
    {
        private DispatchRuntime runtime;
        public SingleServiceProvider(DispatchRuntime runtime)
        {
            this.runtime = runtime;
        }
        public InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel)
        {
            return runtime.SingletonInstanceContext;
        }
        public void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel)
        {}
        public bool IsIdle(InstanceContext instanceContext)
        {
            return false;
        }
        public void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext)
        {}
    }

 自定义SingleAttribute,遍历终结点分发器,若终ServiceHost中的服务实例SingletonInstance存在,则直接获取用来构造InstanceContext;若不存在则用serviceType反射形式创建一个服务实例,再构造InstanceContext。

public class SingleAttribute : Attribute, IServiceBehavior
    {
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {}
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher channel in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher endpoint in channel.Endpoints)
                {
                    DispatchRuntime runtime = endpoint.DispatchRuntime;
                    ServiceHost host = (ServiceHost)serviceHostBase;
                    object Instance = null;
                    if (null != host.SingletonInstance)
                    {
                        runtime.SingletonInstanceContext = new InstanceContext(serviceHostBase, host.SingletonInstance);
                        Instance = host.SingletonInstance;
                    }
                    else
                    {
                        Instance = Activator.CreateInstance(serviceDescription.ServiceType);
                        runtime.SingletonInstanceContext = new InstanceContext(serviceHostBase, Instance);
                    }
                    endpoint.DispatchRuntime.InstanceContextProvider = new SingleServiceProvider(runtime);
                    if (serviceDescription.ServiceType.GetInterfaces().Contains(typeof(IDisposable)))
                    {
                        FieldInfo field = typeof(ServiceHost).GetField("disposableInstance", BindingFlags.Instance | BindingFlags.NonPublic);
                        field.SetValue(host, Instance);
                    }
                   
                }
            }
        }
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {}
    }

   当ServiceHost结束时会调用onClosed方法,里面会释放private的disposableInstance字段。所以这里也将创建的服务实例通过反射放到这里,这样ServiceHost关闭时也能让服务实例也能回收。

 

 

最后使用自定义的SingleAttribute,可以看到同样实现了单例效果,并没有针对每次调用都创建一个实例上下文。

    [Single]
    public class Calculator : ICalculator,IDisposable
    {
        ......
    }    

 

 

posted @ 2015-05-27 21:53  lh_cn  阅读(1056)  评论(0编辑  收藏  举报