第 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. 【赵渝强老师】Oracle数据库的内存结构

    首先,我们通过一张图片来了解一下Oracle数据库的内存结构,如下: 每个数据库实例有两个关联的内存结构-系统全局区(SGA),程序全局区(PGA). 系统全局(SGA):一组共享的内存结构(称为SG ...

  2. 聊聊 HTAP 的前世今生

    随着现代社会大型实时分析应用的逐渐流行,关系型数据库已经难以处理高并发的事务请求.商业层面上,当全球进入数字化时代,数字化技术渗透到各行各业,同时产生了海量数据,数据的存储和应用是企业决策的重要依据之 ...

  3. 我们如何在 vue 应用我们的权限

    权限可以分为用户权限和按钮权限: 用户权限,让不同的用户拥有不同的路由映射 ,具体实现方法: 1. 初始化路由实例的时候,只把静态路由规则注入 ,不要注入动态路由规则 : 2. 用户登录的时候,根据返 ...

  4. kotlin协程——>异常处理

    异常处理 本节内容涵盖了异常处理与在异常上取消.我们已经知道取消协程会在挂起点抛出 CancellationException 并且它会被协程的机制所忽略.在这⾥我们会看看在取消过程中抛出异常或同 ⼀ ...

  5. Nginx UI:全新的 Nginx 在线管理平台

    前言 Nginx在程序部署中扮演着至关重要的角色,其高性能.高安全性.易于配置和管理的特点,使得它成为现代Web应用部署中不可或缺的一部分.今天大姚给大家分享一款实用的 Nginx Web UI 工具 ...

  6. 国内空白,AI将文字搜索转化为交互数据图表,融资4000万,已与Perplexity整合

    2024年10月17日.产品为利用生成式AI将文字搜索转化为数据图表的美国初创公司Tako,种子轮融资575万美元,折合人民币4000万元. 国外AI搜索主导者Perplexity,其创始人也参与了这 ...

  7. 3D数学基础:图形和游戏开发(第二版)--读书笔记(1)

    简介: 本书是关于3D数学.三维空间的几何和代数的入门教材.它旨在告诉你如何使用数学描述三维中的物体及其位置.方向和轨迹.这不是一本关于计算机图形学.模拟,甚至计算几何的书,但是,如果读者打算研究这些 ...

  8. house of cat

    调用方法 调用链1 house of cat调用链 __malloc_assert 在 2.35 的 glibc 中源码如下 static void __malloc_assert (const ch ...

  9. Multi-Patch Prediction Adapting LLMs for Time Series Representation Learning

    这篇论文是出自2024ICML的一篇论文,作者成功将大语言模型应用到时序模型之中,并在时序领域取得了很好的效果,不仅如此,作者还设置了多种下游任务,从论文结果得知,作者的模型在下游任务处都取得了很好的 ...

  10. 2个月搞定计算机二级C语言——真题(9)解析

    1. 前言 本篇我们讲解2个月搞定计算机二级C语言--真题9 2. 程序填空题 2.1 题目要求 2.2 提供的代码 #include <stdio.h> double f1(double ...