对于 MBR,我们可以指定不同的激活模式。
- 服务器激活(Server-Activated Objects / SAO):只有在客户端调用代理对象第一个方法时才创建,区分为 Singleton 和 SingleCall 两种模式。Singleton 一如设计模式中的名称,无论有多少客户端都只有一个实例存在;而 SingleCall 则为每次调用创建一个新对象,因此它是无状态的。SingleCall 在方法调用完成后立即失效,不会参与生存期租约系统。
- 客户端激活(Client-Activated Objects / CAO):在客户端调用 new 或 Activator.CreateInstance 时立即创建。
通常情况下服务器激活模式只能使用默认构造函数,客户端激活模式则没有此限制。当然,可以通过其他方法来绕开这个限制,比如动态发布,我们会在后面的章节解决这个问题。
在 Remoting 中,我们使用 RemotingConfiguration 类型来完成远程类型的注册。RegisterWellKnownServiceType()、RegisterWellKnownClientType() 用于注册服务器激活对象,通过 WellKnownObjectMode 枚举参数可以指定 Singleton 或 SingleCall 模式。RegisterActivatedServiceType()、RegisterActivatedClientType() 用于注册客户端激活对象。
注册方式演示
1. SAO / Singleton
// Server
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Data), "data", WellKnownObjectMode.Singleton);
...
// Client1
RemotingConfiguration.RegisterWellKnownClientType(typeof(Data), "tcp://localhost:801/data");
Data data = new Data(); // 必须使用默认构造方法。
...
// Client2
Data data = (Data)Activator.GetObject(typeof(Data), "tcp://localhost:801/data");
2. SAO / SingleCall
// Server
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Data), "data", WellKnownObjectMode.SingleCall);
...
// Client1
RemotingConfiguration.RegisterWellKnownClientType(typeof(Data), "tcp://localhost:801/data");
Data data = new Data(); // 必须使用默认构造方法。
...
// Client2
Data data = (Data)Activator.GetObject(typeof(Data), "tcp://localhost:801/data");
3. CAO
由于 RegisterActivatedServiceType 中不能为远程对象指定 URI,因此我们需要使用 ApplicationName 属性。
// Server
RemotingConfiguration.ApplicationName = "test"; //
RemotingConfiguration.RegisterActivatedServiceType(typeof(Data));
...
// Client1
RemotingConfiguration.RegisterActivatedClientType(typeof(Data), "tcp://localhost:801/test");
Data data = new Data(123); // 可以使用非默认构造方法
...
// Client2
RemotingConfiguration.RegisterActivatedClientType(typeof(Data), "tcp://localhost:801/test");
Data data = (Data)Activator.CreateInstance(typeof(Data), 123); // 可以使用非默认构造方法
创建时间的区别
我们在远程对象创建和方法调用代码之间进行一些延时来观察创建时间的异同。
1. 服务器激活
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Messaging;
namespace Learn.Library.Remoting
{
public class RemotingTest2
{
/// <summary>
/// 远程类型
/// </summary>
public class Data : MarshalByRefObject
{
public Data()
{
Console.WriteLine(DateTime.Now);
}
public void Test()
{
Console.WriteLine(DateTime.Now);
}
}
/// <summary>
/// 服务器端代码
/// </summary>
static void Server()
{
// 创建新的应用程序域,以便模拟分布结构。
AppDomain server = AppDomain.CreateDomain("server");
server.DoCallBack(delegate
{
// 创建并注册信道
TcpServerChannel channel = new TcpServerChannel(801);
ChannelServices.RegisterChannel(channel, false);
// 注册远程类型
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Data), "data",
WellKnownObjectMode.SingleCall);
});
}
/// <summary>
/// 客户端代码
/// </summary>
static void Client()
{
// 创建并注册信道
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, false);
// 创建远程对象并调用其方法
Data data = (Data)Activator.GetObject(typeof(Data), "tcp://localhost:801/data");
Thread.Sleep(3000);
data.Test();
}
static void Main()
{
Server();
Client();
}
}
}
输出:
2007-2-23 14:22:06
2007-2-23 14:22:06
输出结果表明,对象在方法调用时才被创建。
2. 客户端激活
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Messaging;
namespace Learn.Library.Remoting
{
public class RemotingTest2
{
/// <summary>
/// 远程类型
/// </summary>
public class Data : MarshalByRefObject
{
public Data()
{
Console.WriteLine(DateTime.Now);
}
public void Test()
{
Console.WriteLine(DateTime.Now);
}
}
/// <summary>
/// 服务器端代码
/// </summary>
static void Server()
{
// 创建新的应用程序域,以便模拟分布结构。
AppDomain server = AppDomain.CreateDomain("server");
server.DoCallBack(delegate
{
// 创建并注册信道
TcpServerChannel channel = new TcpServerChannel(801);
ChannelServices.RegisterChannel(channel, false);
// 注册远程类型
RemotingConfiguration.ApplicationName = "test";
RemotingConfiguration.RegisterActivatedServiceType(typeof(Data));
});
}
/// <summary>
/// 客户端代码
/// </summary>
static void Client()
{
// 创建并注册信道
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, false);
// 创建远程对象并调用其方法
RemotingConfiguration.RegisterActivatedClientType(typeof(Data), "tcp://localhost:801/test");
Data data = new Data();
Thread.Sleep(3000);
data.Test();
}
static void Main()
{
Server();
Client();
}
}
}
输出:
2007-2-23 14:25:07
2007-2-23 14:25:10
输出结果证实远程对象在执行 new 语句时被创建。
Singleton 和 SingleCall 的区别
Singleton
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Messaging;
namespace Learn.Library.Remoting
{
public class RemotingTest2
{
/// <summary>
/// 远程类型
/// </summary>
public class Data : MarshalByRefObject
{
private Guid id = Guid.NewGuid();
public void Test()
{
Console.WriteLine(id.ToString());
}
}
/// <summary>
/// 服务器端代码
/// </summary>
static void Server()
{
// 创建新的应用程序域,以便模拟分布结构。
AppDomain server = AppDomain.CreateDomain("server");
server.DoCallBack(delegate
{
// 创建并注册信道
TcpServerChannel channel = new TcpServerChannel(801);
ChannelServices.RegisterChannel(channel, false);
// 注册远程类型
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Data), "data",
WellKnownObjectMode.Singleton);
});
}
/// <summary>
/// 客户端代码
/// </summary>
static void Client()
{
// 创建并注册信道
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, false);
// 注册远程对象
RemotingConfiguration.RegisterWellKnownClientType(typeof(Data), "tcp://localhost:801/data");
// 创建远程对象并调用其方法
Data data1 = new Data();
Data data2 = new Data();
data1.Test();
data2.Test();
}
static void Main()
{
Server();
Client();
}
}
}
输出:
94cc586b-a24f-4633-9c88-c98a93246453
94cc586b-a24f-4633-9c88-c98a93246453
两个不同的代理对象引用了同一个远程对象,符合 Singleton 的定义。
SingleCall
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Messaging;
namespace Learn.Library.Remoting
{
public class RemotingTest2
{
/// <summary>
/// 远程类型
/// </summary>
public class Data : MarshalByRefObject
{
private Guid id = Guid.NewGuid();
public void Test()
{
Console.WriteLine(id.ToString());
}
}
/// <summary>
/// 服务器端代码
/// </summary>
static void Server()
{
// 创建新的应用程序域,以便模拟分布结构。
AppDomain server = AppDomain.CreateDomain("server");
server.DoCallBack(delegate
{
// 创建并注册信道
TcpServerChannel channel = new TcpServerChannel(801);
ChannelServices.RegisterChannel(channel, false);
// 注册远程类型
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Data), "data",
WellKnownObjectMode.SingleCall);
});
}
/// <summary>
/// 客户端代码
/// </summary>
static void Client()
{
// 创建并注册信道
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, false);
// 创建远程对象并调用其方法
Data data = (Data)Activator.GetObject(typeof(Data), "tcp://localhost:801/data");
data.Test();
data.Test();
}
static void Main()
{
Server();
Client();
}
}
}
输出:
6141870b-43db-4d2f-a991-73ae4097ffd3
8e28bf26-5aab-4975-a36a-da61a4bcb93e
即便是同一个代理对象,也会为每次方法调用创建一个新的远程对象。