第 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. Linux 进程调度之schdule主调度器

    考虑到文章篇幅,在这里我只讨论普通进程,其调度算法采用的是CFS(完全公平)调度算法. 至于CFS调度算法的实现后面后专门写一篇文章,这里只要记住调度时选择一个优先级最高的任务执行 一.调度单位简介 ...

  2. 精彩回顾|【2023 ACDU 中国行·深圳站】数据库主题交流活动成功举办!

    6月30日下午,[ACDU 中国行·深圳站]在深圳回酒店圆满落下帷幕.本次活动由中国数据库联盟(ACDU)联合墨天轮社区主办,围绕「数据库前沿技术揭秘及应用」这一主题,七位数据库行业的领军人物从数据库 ...

  3. 如何实现高效运维?来谈谈性能优化那些事(含直播回顾 Q&A)

    数据库性能问题,常常是困扰DBA高效运维的难题之一.如何多角度地帮助DBA,找到"数据库慢"的原因,保证系统高效.稳定.安全地运行? 2021年10月14日,云和恩墨技术顾问,拥有 ...

  4. 2022年10月中国数据库排行榜:达梦冲刺IPO热度不减,PolarDB立足创新夺锦才

    秋风萧瑟,洪波涌起. 2022年10月的 墨天轮中国数据库流行度排行榜 火热出炉,本月共有245个数据库参与排名,相比上月新增七个数据库,本月排行榜前十名变动较大:达梦反超openGauss重摘探花: ...

  5. [Java/日志] 日志框架打印应用程序日志代码的执行情况

    0 引言 我常以为 INFO 日志级别的 应用程序日志代码,不会被执行(比如,实验1中的printTestLog函数).但今天线上的问题,证实了这个思路是错的. 1 验证实验 版本信息 jdk : 1 ...

  6. 解决数据库表的字段id中间自增断层问题(删除自增主键其中的任意一条数据后,再次插入数据发现id排序出现问题)

    万能解决办法: 先将该表的id字段删除,然后再重新按照见表需求创建该字段 注意!!!!!!!!!!!!! 注意!!!!!!!!!!!!! 注意!!!!!!!!!!!!! 删除之前一定要复制建表时候的S ...

  7. KubeSphere 社区双周报|2024.02.29-03.14

    KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书.新增的讲师证书以及两周内提交过 commit 的贡献者,并对近期重要的 PR 进行解析,同时还包含了线上/线下活动和布道推广等一系列 ...

  8. 云原生爱好者周刊 | 使用 WASM 来写博客是什么感觉?

    开源项目推荐 zzhack zzhack 是一个静态博客框架,是一个纯正的 WASM 应用,它由 Rust & Yew 来作为技术栈进行搭建,UI 设计比较美观,大家也可以直接使用该项目的设计 ...

  9. typescript 编译报错 不能用于索引类型

    Element implicitly has an 'any' type because expression of type 'any' can't be used to index type 't ...

  10. Chapter12 动态内存与智能指针

    目录 12.1.0 介绍 12.1.1 shared_ptr类 make_shared函数 shared_ptr的拷贝和引用 shared_ptr自动销毁所管理的对象- -shared_ptr还会自动 ...