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

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

使用也很简单,只需要将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
    {
        ......
    }

时间: 2024-12-13 17:49:43

实例上下文模式:单例模式的相关文章

WCF实例上下文模式与并发模式对性能的影响

实例上下文模式 InstanceContextMode 控制在响应客户端调用时,如何分配服务实例.InstanceContextMode 可以设置为以下值: •Single – 为所有客户端调用分配一个服务实例. •PerCall – 为每个客户端调用分配一个服务实例. •PerSession – 为每个客户端会话分配一个服务实例. InstanceContextMode 的默认设置为 PerSession 并发模式ConcurrencyMode 控制一次允许多少个线程进入服务.Concurre

实例上下文模式:会话模式

一.会话模式简介与示例代码   会话模式下,客户端和服务实例上下文.服务实例是一一对应关系,每一个客户端都在服务端都有自己对应的服务实例上下文.如下图所示                  服务端使用会话模式的条件: 1.使用支持会话模式的绑定,如WSHttpBinding.WS2007HttpBinding.NetTcpBinding.NetNamedPipeBinding. 不支持会话的绑定有 BasicHttpBinding.NetMsmqBinding.Msmq属于离线状态的绑定,交互使

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

在创建一个服务端的时候都会依托于ServiceHost对象,里面存在一个SingletonInstance指向一个服务实例.而ServiceHost最终都是寄在InstanceContext中.对于单调模式,客户端的每次调用服务端都会创建一个全新的上下文和服务实例. 为了验证上述流程,写了下面这个例子.ServiceBehavior中的InstanceContextMode可以设置服务类型的上下文模式,公三种PerCall.PerSession.Single分别对应单调.回话.单例.这里设置为P

WCF实例上下文与并发

一.实例上下文模式(InstanceContextMode)可以简单地理解为服务端的服务实例与客户端的服务代理之间的关联方式.WCF具有单调(Per-Call).会话(Per-Session).和单例(Single) 单调(Per-Call) [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class TestService : ITest 会话(Per-Session) [ServiceBe

PHP-设计模式-单例模式

实用场景 编号 场景 1 数据库连接 2 <?php /**  * PHP设计模式-单例模式  * @author chengtao3  */ class Singleton{ private static $Instance = null; /**  * 公共静态方法获取实例  * @return Singleton  */ public function getInstance(){ if(self::$Instance == null){ self::$Instance = new Sin

6 创建型模式-----单例模式

模式动机:对于系统中的某些类而言,确保只有一个实例运行很重要,例如一个系统只能有一个计时器或者ID生成器. 模式定义(Singleton Pattern):确保一个类只有一个实例,并且该类自己负责创建它自己的唯一实例,而且还必须可以向系统提供这个实例. 模式结构图: 模式分析:单例类拥有一个私有构造函数,确保用户无法通过new关键字创建它:模式中包括一个静态工厂方法和一个静态(确保唯一性)私有变量,该静态方法: 1> 负责实例化自己,然后存储到静态变量中: 2> 提供系统可访问的接口.   模

MVC实例应用模式

要了解mvc的具体的实例应用模式,首先要了解一下mvc模式,这样才能知道他要如何应用到具体的实例中. l  mvc模式 mvc的全名是Model -View- Controller,是模型-视图-控制器的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑. MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式.

JDBC - 开发实例 - MVC模式

JDBC - 开发实例 - MVC模式  1. 在web.xml中配置连接数据库的信息 web.xml: <context-param> <param-name>server</param-name> //主机名 <param-value>localhost</param-value> </context-param> <context-param> <param-name>db</param-name&

[Js-设计模式]单例模式(饿汉,懒汉,登记式)

·单例模式的特点: 1. 单例类只能有一个实例. 2. 单例类必须自己创建自己的唯一实例. 3. 单例类必须给所有其他对象提供这一实例. ·各种单例模式的特点: ·懒汉式是延时加载,在需要的时候才创建对象,避免内存浪费,但存在线程安全问题. ·饿汉式线程安全,类一加载就实例化对象,所以要提前占用系统资源. ·登记式单例模式克服了饿汉以及懒汉单例的不可继承问题,其子类在登记时才被创建,并且子类的实例化方式只能是饿汉式. ·各种单例模式的类图: ·饿汉式单例模式 ·懒汉式单例模式: ·登记式单例模式