第 8 章 创建连接器

上一章向您展示了各种类型的连接器,以及它们对于请求的同步和异步处理过程。到目前为止,我一直忽略的一个最为重要的步骤是:初始化连接器和连接器链。连接器通常既不是直接待代码中创建,也不是在配置文件中定义出来。相反,是通过链接器链来设置的。它最终在后台返回连接器链。本章将向您展示构建自己的连接器链的基础。这些定制的连接器将在第 9 章中实现。

8.1 理解连接器提供器

如你在第 4 章中所见,你可以使用如下所示的方式,通过 .NET 配置文件来定义连接器链。( 该示例用于客户端的配置文件,对于服务器端连接器链,你需要将 clientProviders 替换为 serverProviders )

 <configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http">
<clientProviders>
<provider type="MySinks.SomeMessageSinkProvider, Client" />
<formatter ref="soap" />
<provider type="MySinks.SomeClientChannelSinkProvider, Client" />
</clientProviders>
</channel>
</channels>
</application>
</system.runtime.remoting>
<configuration>

为了更为透彻地说明该示例,我将其中的 \<formatter ref="soap" /> 替换为使用 machine.config 中真实的值 ( 包括其中必须的强签名部分 )

这样,完整的链现在看起来是这样的:

<provider type="MySinks.SomeMessageSinkProvider, Client" />
<formatter type="System.Runtime.Remoting.Channels.SoapClientFormatterSinkProvider,
System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"/>
<provider type="MySinks.SomeClientChannelSinkProvider, Client" />

如你所见,在这些示例中,该连接器链 不是 通过 sinks 的名/值对来定义的 ( 也许应该是这样,例如 SoapClientFormatterSink )。相反,是使用连接器 提供器 来完成的,提供器既可以在客户端,也可以在服务器端使用,它至少需要实现如下一个接口:

public interface IClientChannelSinkProvider {
IClientChannelSinkProvider Next { get; set; }
IClientChannelSink CreateSink(IChannelSender channel,
string url,
object remoteChannelData);
} public interface IServerChannelSinkProvider {
IServerChannelSinkProvider Next { get; set; }
IServerChannelSink CreateSink(IChannelReceiver channel);
void GetChannelData(IChannelDataStore channelData);
}

你可以在接口定义中看到,每个提供器中使用了 Next 属性来构建真实的调用链。

8.1.1 创建客户端的连接器

在加载了前面的示例配置文件之后,提供器链将被如图 8-1 所示的对象调用形式构建出来。前面 3 个提供器将通过配置文件加载,而最后一个 (HttpClientTransportSinkProvider) 则被 HTTP 通道默认初始化出来。



图 8-1 提供器链

在客户端,这些连接器提供器被关联到客户端的通道。你可以使用如下代码来访问这个通道对象:

IChannel chnl = ChannelServices.GetChannel("http");

注意到代码中的 "http" 表示通道的唯一名称。对于 HTTP 与二进制通道来说,名称是在 machine.config 中设置的。

该通道对象创建的相关连接器如图 8-2 所示:



图 8-2 使用提供的连接器提供器创建的 IChannel

当一个远程的服务器端激活对象 (SAO ) 被创建的时候 ( 对于 SAO,额外的 ConstructionCall 消息被发送到服务器,并使用代理的标识对象填充 ),在幕后会发生一系列事情。在使用 new 操作符的时候,或者调用 Activator.GetObject() 的时候,方法 RemotingServices.Connect() 被调用,在调用之后发生的事情如图 8-3 所示:



图 8-3 通过连接器提供器链创建连接器

在通过 RemotingServices 调用之后,ChannelServices 在每个注册的通道上调用 CreateMessageSink() ,直到其中之一接受作为参数之一传递的 URL 地址。比如说是 HTTP 通道,它工作在任何以 http: 或者以 https: 开头的 URL 地址上,而 TCP 通道只接受使用 tcp: 协议。

当通道识别了指定的 URL 地址,它就会在客户端通道上调用 CreateMessageSink() 方法。

对于 HttpClientChannel 来说,随后在第一个通道提供器上调用 CreatSink() 方法 ( 如图 8-3 所示 )。不同的接收器提供器现在执行的操作,对应下面的代码:

public class SomeClientChannelSinkProvider: IClientChannelSinkProvider {
private IClientChannelSinkProvider next = null;
public IClientChannelSink CreateSink(IChannelSender channel,
string url, object remoteChannelData)
{
IClientChannelSink nextSink = null; // checking for additional sink providers
if (next != null)
{
nextSink = next.CreateSink(channel,url,remoteChannelData);
} // returning first entry of a sink chain
return new SomeClientChannelSink(nextSink);
}
}

每个接收器提供器首先在提供器链上的下一个条目上调用 CreateSink() 方法,通过返回的结果,然后在其上返回自己的提供器。在调用链的最开始放置一个新的接收器的显式语法并没有指出。但是在此示例中,SomeClientSink 提供器的构造函数接受一个 IClientChannelSink 对象作为参数,并设置到它的 _nextChnlSink 实例变量中,如下面代码片段所示 ( 重复一下,这里只有部分代码 )

public class SomeClientChannelSink: IClientChannelSink {

    private IClientChannelSink _nextChnlSink;

    public SomeClientChannelSink (IClientChannelSink next)
{
_nextChnlSink = next as IClientChannelSink;
}
}

通过调用 ChannelServices.CreateMessageSink() 方法返回的完整的连接器链,随后被连接到一对新的 TransparentProxy/RealProxy 成对标识对象上,如图 8-4 所示



图 8-4 连接到 TransparentProxy 的第一个 IMessageSink

8.1.2 创建服务器端连接器

创建服务器端的连接器与创建客户端连接器有一点不同。如你前面所见,在客户端,当引用一个要求的远程对象的时候,需要的连接器被创建。与此相反,服务器端的对象是在通道被注册的时候立即创建。

当服务器端的通道通过配置文件中的定义创建的时候,使用如下的构造函数:

public HttpServerChannel(IDictionary properties, IServerChannelSinkProvider sinkProvider)

第一个参数 IDictionary 来自 <channel> 配置节的定义。例如,当你的配置文件如下所示,包含下述内容:

<channel ref="http" port="1234" />

那么,properties 字典中将会包含一个键为 port 值为 1234 的条目。

而在构造函数的 sinkProvider 参数,来自连接器链的第一个将会被传递给通道。该链使用配置文件中的 <serverProvider> 配置节构造。

在通道设置过程中,它从 HTTP 通道的构造函数开始,现在会发生两件事中的之一。如果没有 <serverProvider> 配置,默认的连接器链将被创建,如图 8-5 所示。



图 8-5 HttpServerChannel 的默认连接器链

当在配置文件中指定了 <serverProviders> 的时候,连接器链将通过这些定义创建,不会使用默认的连接器提供器。

注意:有意思的是,在本场景下,你将不能通过在你的 SAO 对象上在 URL 上使用 ?WSDL 参数来生成 WSDL 定义,除非在 ` 配置节中显式指定了 SdlChannelSinkProvider

在提供器链被创建之后,ChannelServices.CreateServerChannelSinkChain() 方法被调用。该方法将连接器提供器链作为参数。它会随后遍历该链条,并在调用 CreateSink() 方法之前,添加 DispatchChannelSinkProvider 对象到该链的最后。最后,它返回生成的连接器链条。当通过 ChannelServices 收到这个对象之后,HttpServerChannel 对象将添加一个 HttpServerTransportSink 作为第一个元素。最后的服务器端通道对象如图 8-6 所示。



图 8-6 完整的服务器端 HTTP 通道连接器栈

使用动态连接器

如你在上一章所见,客户端和服务器端的连接器链都可以调用动态连接器。在服务器端是通过 CrossContextChannel 而客户端是通过 ClientContextTerminatorSink 来完成的。

动态连接器关联到特定的 上下文 ( 你可以在第 11 章了解更多关于上下文内容 ),进而被传递了上下文边界的所有调用所调用。它们不能被赋予特定的通道,甚至可以被本地跨上下文,或者跨应用程序域所调用。连接器通过动态上下文属性创建,这是实现了 IDynamicProperty 和 IContributeDynamicSink 接口的类。

注意:IContributeDynamicSink 可以看作是动态连接器的连接器提供器。

相关的接口如下所示:

public interface IDynamicProperty {
string Name { get; }
} public interface IContributeDynamicSink {
IDynamicMessageSink GetDynamicSink();
}

动态连接器本身,通过 GetDynamicSink() 方法返回,实现如下的 IDynamicMessageSink 接口:

public interface IDynamicMessageSink {
void ProcessMessageStart(IMessage reqMsg, bool bCliSide, bool bAsync);
void ProcessMessageFinish(IMessage replyMsg, bool bCliSide, bool bAsync);
}

如名称所暗示,在消息穿过接收器链之前,ProcessMessageStart() 方法将被调用,在消息被连接器处理之后,ProcessMessageFinish() 方法被调用。

下面的简化的动态连接器将会在控制台写出一行,只要消息穿越 remoting 边界。

public class MyDynamicSinkProvider: IDynamicProperty, IContributeDynamicSink
{
public string Name
{
get { return “MyDynamicSinkProvider"; }
} public IDynamicMessageSink GetDynamicSink()
{
return new MyDynamicSink();
}
} public class MyDynamicSink: IDynamicMessageSink
{
public void ProcessMessageStart(IMessage reqMsg, bool bCliSide, bool bAsync)
{
Console.WriteLine("--> MyDynamicSink: ProcessMessageStart");
} public void ProcessMessageFinish(IMessage replyMsg, bool bCliSide, bool bAsync)
{
Console.WriteLine("--> MyDynamicSink: ProcessMessageFinish");
}
}

为了使用正确的上下文注册 IDynamicProperty,你可以使用如下代码:

Context ctx = Context.DefaultContext;
IDynamicProperty prp = new MyDynamicSinkProvider();
Context.RegisterDynamicProperty(prp, null, ctx);

小结

在本章中,你了解了创建各种类型的连接器。与上一章一起,你已经完全具备了实现你自己的连接器来扩展 .NET Remoting 功能。你还与动态上下文连接器有了第一次碰面,更详细内容将会在第 11 章中介绍。

我承认随后的两章内容有点多,但是在下一章,我将会为你的耐心奖励一些使用这里介绍的技术所创建的真实世界的连接器。

原文链接

Advanced .NET Remoting: 第 8 章 创建连接器的更多相关文章

  1. [Effective Java]第二章 创建和销毁对象

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  2. 第一章 创建WEB项目

    第一章   创建WEB项目 一.Eclipse创建WEB项目 方法/步骤1 首先,你要先打开Eclipse软件,打开后在工具栏依次点击[File]>>>[New]>>&g ...

  3. 《Effective Java 2nd》第2章 创建和销毁对象

    目录 第1条:考虑使用静态工厂方法代替构造器 第2条:遇到多个构造器参数时考虑用构建器 第3条:用私有构造器或者枚举类型强化Singleton属性 第4条:通过私有构造器强化不可实例化的能力 第5条: ...

  4. Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏

    Unity 2D游戏开发高速入门第1章创建一个简单的2D游戏 即使是如今,非常多初学游戏开发的同学.在谈到Unity的时候.依旧会觉得Unity仅仅能用于制作3D游戏的. 实际上.Unity在2013 ...

  5. Rxjava2实战--第三章 创建操作符

    Rxjava2实战--第三章 创建操作符 Rxjava的创建操作符 操作符 用途 just() 将一个或多个对象转换成发射这个或者这些对象的一个Observable from() 将一个Iterabl ...

  6. [Effective Java 读书笔记] 第二章 创建和销毁对象 第一条

    第二章  创建和销毁对象 第一条 使用静态工厂方法替代构造器,原因: 静态工厂方法可以有不同的名字,也就是说,构造器只能通过参数的不同来区分不同的目的,静态工厂在名字上就能表达不同的目的 静态工厂方法 ...

  7. [ABP教程]第三章 创建、更新和删除图书

    Web应用程序开发教程 - 第三章: 创建,更新和删除图书 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以 ...

  8. [ABP教程]第一章 创建服务端

    Web应用程序开发教程 - 第一章: 创建服务端 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发 ...

  9. suse 12 二进制部署 Kubernetets 1.19.7 - 第01章 - 创建CA证书和kubectl集群管理命令

    文章目录 1.kubernetes集群部署 1.0.创建CA证书和秘钥 1.0.0.安装cfssl工具 1.0.1.创建根证书 1.0.2.创建证书签名请求文件 1.0.3.生成CA证书和秘钥 1.0 ...

  10. 《Linux命令行与shell脚本编程大全》第十七章 创建函数

    可以将shell脚本代码放进函数中封装起来,这样就能在脚本中的任何地方多次使用它了. 17.1 基本的脚本函数 函数:是一个脚本代码块,可以为其命名并在代码中任何位置重用. 17.1.1 创建函数 有 ...

随机推荐

  1. Spark集群的安装及高可用配置

    spark官网学习文档 Spark集群的安装及高可用配置 前期需求:Hadoop和Scala必须已经安装完成 步骤: ①进入spark下载网站中https://spark.apache.org/dow ...

  2. 谷歌浏览器页面乱码问题在浏览器端解决,charset下载安装;

    一   下载插件(百度网盘) 链接:https://pan.baidu.com/s/1o9Zuo2m 密码:rrcz 二    将下载好的插件拖到谷歌浏览器中 三    如果谷歌浏览器右下角出现如下图 ...

  3. 66.有没有碰到过数组响应丢失(问的是ref和reactive的用法,什么情况下用)

    由于vue3使用proxy,对于对象和数组都不能直接整个赋值.  直接赋值丢失了响应性 只有push或者根据索引遍历赋值才可以保留reactive数组的响应性  : 可以使用 toRefs 解决这个问 ...

  4. 使用 KubeKey v3.1.1 离线部署原生 Kubernetes v1.28.8 实战

    今天,我将为大家实战演示,如何基于操作系统 openEuler 22.03 LTS SP3,利用 KubeKey 制作 Kubernetes 离线安装包,并实战离线部署 Kubernetes v1.2 ...

  5. 揭秘!KubeSphere 背后的“超级大脑”:etcd 的魅力与力量

    作者:尹珉,KubeSphere Ambassador & Contributor,KubeSphere 社区用户委员会杭州站站长. 1. 开篇:揭开神秘面纱,etcd 如何驱动 KubeSp ...

  6. 云原生爱好者周刊:买个蓝牙打印机实时打印新提交的 PR 吧 | 2022-10-24

    开源项目推荐 blue 这个项目非常有意思,利用树莓派.蓝牙热敏打印机和 GitHub Actions 自动将新提交的 PR 或者 Issue 通过打印机打印出来,非常适合各个项目的维护者使用 Kub ...

  7. Linux系统管理-yum源配置

    一.本地光盘yum源配置 1.创建挂载点 [root@localhost ~]# mkdir /mnt/cdrom 2.配置自动挂载本地光盘 [root@localhost ~]# vim /etc/ ...

  8. 实现.NET 4.0下的Task类相似功能组件

    实现 .NET 4.0 下的 Task 类相似功能:TaskExCum 组件详解 引言 随着 .NET 技术的发展,异步编程模型逐渐成为现代应用程序开发中的标准实践之一..NET 4.5 引入了 Ta ...

  9. [解决方案] 几种通过 iproute2 来打通不同节点间容器网络的方式

    几种通过 iproute2 来打通不同节点间容器网络的方式 几种通过 iproute2 来打通不同节点间容器网络的方式 host-gw ipip vxlan 背景 之前由于需要打通不同节点间的容器网络 ...

  10. Clickhouse SQL语法

    Insert 基本与标准 SQL(MySQL)基本一致 (1)标准 insert into [table_name] values(-),(-.) (2)从表到表的插入 insert into [ta ...