Advanced .NET Remoting: 第 9 章 3.在 Remoting 中传递额外的运行时信息
Advanced .NET Remoting:第 9 章
3.传递运行时信息
前面使用的接收器 ( Sink ) 是 IClientChannelSinks 与 IServerChannelSinks。这意味着它们工作在格式化器已经序列化 IMessage 对象 之后 。而对于 IMessageSink 来说,不同的是,可以直接工作在消息的内容被格式化 之前 。这意味着,你对 IMessage 内容的任何修改都将被序列化,进而反映到最终的流中。
注意:即使你试图在 IClienntChannelSink 中修改 IMessage 对象的内容,需要注意的是,这些修改 不会 被传播到服务器端,因为序列化的流已经通过 IMessage 对象生成了。
基于该区别,客户端 IMessageSink 可以用来从客户端将运行时信息传递到服务器端。在随后的示例中,我将向您展示如何将客户端的线程 ( thread ) 的当前优先级别 ( priority ) 传递到服务器端,以便远程方法可以运行在同样的优先级上。
为了从客户端向服务器端发送任意数据,你需要将它加入到 Message 对象的逻辑调用上下文 ( logical call context ) 中。通过这种方式,你可以传递可序列化对象 ( serializable ) 或者扩展了 MarshalByRefObject 的对象。例如,为了在对服务器上任何方法的调用中,传递客户端的线程的当前上下文,你可以实现如下的 SyncProcessMessage() 方法:
public IMessage SyncProcessMessage(IMessage msg)
{
if (msg as IMethodCallMessage != null)
{
LogicalCallContext lcc =
(LogicalCallContext) msg.Properties["__CallContext"];
lcc.SetData("priority",Thread.CurrentThread.Priority);
return _nextMsgSink.SyncProcessMessage(msg);
}
else
{
return _nextMsgSink.SyncProcessMessage(msg);
}
}
对于 AsyncProcessMessage() 方法也同样处理。
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
if (msg as IMethodCallMessage != null)
{
LogicalCallContext lcc =
(LogicalCallContext) msg.Properties["__CallContext"];
lcc.SetData("priority",Thread.CurrentThread.Priority);
return _nextMsgSink.AsyncProcessMessage(msg,replySink);
}
else
{
return _nextMsgSink.AsyncProcessMessage(msg,replySink);
}
}
在服务器端,你也必须实现一个 IServerChannelSink 来从 IMessage 对象中提取调用上下文信息 ( call context ),然后设置到 Thread.CurrentThread.Priority 上来应用该值。
public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,
IMessage requestMsg,
ITransportHeaders requestHeaders,
Stream requestStream,
out IMessage responseMsg,
out ITransportHeaders responseHeaders,
out Stream responseStream)
{
LogicalCallContext lcc =
(LogicalCallContext) requestMsg.Properties["__CallContext"];
// storing the current priority
ThreadPriority oldprio = Thread.CurrentThread.Priority;
// check if the logical call context contains "priority"
if (lcc != null && lcc.GetData("priority") != null)
{
// fetch the priority from the call context
ThreadPriority priority =
(ThreadPriority) lcc.GetData("priority");
Console.WriteLine(" -> Pre-execution priority change {0} to {1}",
oldprio.ToString(),priority.ToString());
// set the priority
Thread.CurrentThread.Priority = priority;
}
// push on the stack and pass the call to the next sink
// the old priority will be used as "state" for the response
sinkStack.Push(this,oldprio);
ServerProcessing spres = _next.ProcessMessage (sinkStack,
requestMsg, requestHeaders, requestStream,
out responseMsg,out responseHeaders,out responseStream);
// restore priority if call is not asynchronous
if (spres != ServerProcessing.Async)
{
if (lcc != null && lcc.GetData("priority") != null)
{
Console.WriteLine(" -> Post-execution change back to {0}",oldprio);
Thread.CurrentThread.Priority = oldprio;
}
}
return spres;
}
用于服务器端的接收器的接收器提供器非常简单。它看起来多少与前面的 IServerChannelSink 是相同的。
在客户端,这种方式有一点不太方便。请记住,你现在是在实现 IMessageSink 而不是 IClientChannelSing。如果寻找 IMessageSinkProvider 的话不会有 任何结果,所以,此时你还是不得不实现一个 IClientChannelSink - 即使实际上该连接器是 IMessageSink。当查看随后部分的 IClientChannelSinkProvider 接口的时候,就会发现这个问题。
IClientChannelSink CreateSink(IChannelSender channel,
string url,
object remoteChannelData);
这个接口说明,在任何场景下,CreateSink() 都只会返回 IClientChannelSink,即使你的连接器只需要实现 IMessageSink。所以现在你不得不扩展你的 IMessageSink 同时去实现 IClientChannelSink。你还不得不使用条件判断,因为 IClientChannelSink 也定义来更多的方法需要实现。这些方法将在这个连接器作为通道连接器 ( channel sink ) 的时候而不是作为消息连接器 ( 也就是说,在格式化器之后 ) 被调用。 你可能不希望你的用户将该连接器放置在格式化器 之后 ( 它在这里并不有效,因为它修改了 IMessage 对象的内容 ),所以你会希望对于这些方法抛出异常。
完整的客户端 PriorityEmitterSink 代码,会在用于错误的顺序时抛出这些异常,如列表 13-12 所示:
using System;
using System.Collections;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Threading;
namespace PrioritySinks
{
public class PriorityEmitterSink : BaseChannelObjectWithProperties,
IClientChannelSink, IMessageSink
{
private IMessageSink _nextMsgSink;
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
// only for method calls
if (msg as IMethodCallMessage != null)
{
LogicalCallContext lcc =
(LogicalCallContext) msg.Properties["__CallContext"];
lcc.SetData("priority",Thread.CurrentThread.Priority);
return _nextMsgSink.AsyncProcessMessage(msg,replySink);
}
else
{
return _nextMsgSink.AsyncProcessMessage(msg,replySink);
}
}
public IMessage SyncProcessMessage(IMessage msg)
{
// only for method calls
if (msg as IMethodCallMessage != null)
{
LogicalCallContext lcc =
(LogicalCallContext) msg.Properties["__CallContext"];
lcc.SetData("priority",Thread.CurrentThread.Priority);
return _nextMsgSink.SyncProcessMessage(msg);
}
else
{
return _nextMsgSink.SyncProcessMessage(msg);
}
}
public PriorityEmitterSink (object next)
{
if (next as IMessageSink != null)
{
_nextMsgSink = (IMessageSink) next;
}
}
public IMessageSink NextSink
{
get
{
return _nextMsgSink;
}
}
public IClientChannelSink NextChannelSink
{
get
{
throw new RemotingException("Wrong sequence.");
}
}
public void AsyncProcessRequest(IClientChannelSinkStack sinkStack,
IMessage msg,
ITransportHeaders headers,
Stream stream)
{
throw new RemotingException("Wrong sequence.");
}
public void AsyncProcessRequest(IClientChannelSinkStack sinkStack,
IMessage msg,
ITransportHeaders headers,
Stream stream)
{
throw new RemotingException("Wrong sequence.");
}
public void AsyncProcessResponse(
IClientResponseChannelSinkStack sinkStack,
object state,
ITransportHeaders headers,
Stream stream)
{
throw new RemotingException("Wrong sequence.");
}
public System.IO.Stream GetRequestStream(IMessage msg,
ITransportHeaders headers)
{
throw new RemotingException("Wrong sequence.");
}
public void ProcessMessage(IMessage msg,
ITransportHeaders requestHeaders,
Stream requestStream,
out ITransportHeaders responseHeaders,
out Stream responseStream)
{
throw new RemotingException("Wrong sequence.");
}
}
}
客户端的 PriorityEmitterSinkProvider 如列表 13-13 所示,实现很直接。只有方法 CreateSink() 比较值得关注。
列表 13-13 客户端的 PriorityEmitterSinkProvider
using System;
using System.Collections;
using System.Runtime.Remoting.Channels;
namespace PrioritySinks
{
public class PriorityEmitterSinkProvider: IClientChannelSinkProvider
{
private IClientChannelSinkProvider next = null;
public PriorityEmitterSinkProvider(IDictionary properties,
ICollection providerData)
{
// not needed
}
public IClientChannelSink CreateSink(IChannelSender channel,
string url, object remoteChannelData)
{
IClientChannelSink nextsink =
next.CreateSink(channel,url,remoteChannelData);
return new PriorityEmitterSink(nextsink);
}
public IClientChannelSinkProvider Next
{
get { return next; }
set { next = value; }
}
}
}
列表 13-14 是服务器端的 IServerChannelSink 实现,与客户端不同,它不是 IMessageSink,所以该实现更为一致。你不需要在这里实现任何其它接口。
列表 13-14 服务器端的 PriorityChangerSink
using System;
using System.Collections;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging ;
using System.Runtime.Remoting.Channels;
using System.Threading;
namespace PrioritySinks
{
public class PriorityChangerSink : BaseChannelObjectWithProperties,
IServerChannelSink, IChannelSinkBase
{
private IServerChannelSink _next;
public PriorityChangerSink (IServerChannelSink next)
{
_next = next;
}
public void AsyncProcessResponse (
IServerResponseChannelSinkStack sinkStack,
Object state,
IMessage msg,
ITransportHeaders headers,
Stream stream)
{
// restore the priority
ThreadPriority priority = (ThreadPriority) state;
Console.WriteLine(" -> Post-execution change back to {0}",priority);
Thread.CurrentThread.Priority = priority;
}
public Stream GetResponseStream (IServerResponseChannelSinkStack sinkStack,
Object state,
IMessage msg,
ITransportHeaders headers )
{
return null;
}
public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,
IMessage requestMsg,
ITransportHeaders requestHeaders,
Stream requestStream,
out IMessage responseMsg,
out ITransportHeaders responseHeaders,
out Stream responseStream)
{
LogicalCallContext lcc =
(LogicalCallContext) requestMsg.Properties["__CallContext"];
// storing the current priority
ThreadPriority oldprio = Thread.CurrentThread.Priority;
// check if the logical call context contains "priority"
if (lcc != null && lcc.GetData("priority") != null)
{
// fetch the priority from the call context
ThreadPriority priority =
(ThreadPriority) lcc.GetData("priority");
Console.WriteLine("-> Pre-execution priority change {0} to {1}",
oldprio.ToString(),priority.ToString());
// set the priority
Thread.CurrentThread.Priority = priority;
}
// push on the stack and pass the call to the next sink
// the old priority will be used as "state" for the response
sinkStack.Push(this,oldprio);
ServerProcessing spres = _next.ProcessMessage (sinkStack,
requestMsg, requestHeaders, requestStream,
out responseMsg,out responseHeaders,out responseStream);
// restore priority if call is not asynchronous
if (spres != ServerProcessing.Async)
{
if (lcc != null && lcc.GetData("priority") != null)
{
Console.WriteLine("-> Post-execution change back to {0}",oldprio);
Thread.CurrentThread.Priority = oldprio;
}
}
return spres;
}
public IServerChannelSink NextChannelSink
{
get {return _next;}
set {_next = value;}
}
}
}
列表 13-15 是相关的服务器端连接器提供器,它实现了接口 IServerChannelSinkProvider。
列表 13-15 服务器端的 PriorityChangerSinkProvider
using System;
using System.Collections;
using System.Runtime.Remoting.Channels;
namespace PrioritySinks
{
public class PriorityChangerSinkProvider: IServerChannelSinkProvider
{
private IServerChannelSinkProvider next = null;
public PriorityChangerSinkProvider(IDictionary properties,
ICollection providerData)
{
// not needed
}
public void GetChannelData (IChannelDataStore channelData)
{
// not needed
}
public IServerChannelSink CreateSink (IChannelReceiver channel)
{
IServerChannelSink nextSink = next.CreateSink(channel);
return new PriorityChangerSink(nextSink);
}
public IServerChannelSinkProvider Next
{
get { return next; }
set { next = value; }
}
}
}
为了测试该连接器的组合,使用如下的 SAO (服务器端激活对象) ,它会返回服务器端的当前线程的优先级:
public class TestSAO: MarshalByRefObject
{
public String getPriority()
{
return System.Threading.Thread.CurrentThread.Priority.ToString();
}
}
这个 SAO 将被客户端使用不同的线程优先级调用多次。服务器端使用的配置文件如下所示:
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http" port="5555">
<serverProviders>
<formatter ref="soap" />
<provider
type="PrioritySinks.PriorityChangerSinkProvider, PrioritySinks" />
</serverProviders>
</channel>
</channels>
<service>
<wellknown mode="Singleton"
type="Server.TestSAO, Server" objectUri="TestSAO.soap" />
</service>
</application>
</system.runtime.remoting>
</configuration>
客户端的配置文件如下所示:
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http">
<clientProviders>
<provider
type="PrioritySinks.PriorityEmitterSinkProvider, PrioritySinks" />
<formatter ref="soap" />
</clientProviders>
</channel>
</channels>
<client>
<wellknown type="Server.TestSAO, generated_meta"
url="http://localhost:5555/TestSAO.soap" />
</client>
</application>
</system.runtime.remoting>
</configuration>
在用于测试的客户端,你可以使用 SoapSuds 来抽取元数据。当你运行如列表 13-16 所示的应用程序的时候,将会看到如图 13-8 所示的输出。
using System;
using System.Runtime.Remoting;
using Server; // from generated_meta.dll
using System.Threading;
namespace Client
{
delegate String getPrioAsync();
class Client
{
static void Main(string[] args)
{
String filename = "client.exe.config";
RemotingConfiguration.Configure(filename);
TestSAO obj = new TestSAO();
test(obj);
Thread.CurrentThread.Priority = ThreadPriority.Highest;
test(obj);
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
test(obj);
Thread.CurrentThread.Priority = ThreadPriority.Normal;
test(obj);
Console.ReadLine();
}
static void test(TestSAO obj)
{
Console.WriteLine("----------------- START TEST CASE ---------------");
Console.WriteLine(" Local Priority: {0}",
Thread.CurrentThread.Priority.ToString());
String priority1 = obj.getPriority();
Console.WriteLine(" Remote priority: {0}",priority1.ToString());
Console.WriteLine("----------------- END TEST CASE ---------------");
}
}
}
图 13-8 通过客户端测试的输出,展示了连接器如愿工作
参考资料
- Advanced .NET Remoting
- PDF 样张 - 第 13 章 扩展 .NET Remoting
- Source Code of Advanced .NET Remoting
- 在线阅读:第 13 章 扩展 .NET Remoting
- 示例代码下载链接
- 全书示例代码下载
Advanced .NET Remoting: 第 9 章 3.在 Remoting 中传递额外的运行时信息的更多相关文章
- 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章 ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...
- 第十一章:WEB浏览器中的javascript
客户端javascript涵盖在本系列的第二部分第10章,主要讲解javascript是如何在web浏览器中实现的,这些章节介绍了大量的脚本宿主对象,这些对象可以表示浏览器窗口.文档树的内容.这些章节 ...
- 编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则)
编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则) 目录 建议1: 不要在常量和变量中出现易混淆的字母 建议2: 莫让常量蜕变成变量 建议3: 三元操作符的类型务 ...
- NodeJs>------->>第二章:Node.js中交互式运行环境--------REL
第二章:Node.js中交互式运行环境--------REL 一:REPL运行环境概述 C:\Users\junliu>node > foo = 'bar' ; 'bar' > 二: ...
- ANTLR4权威指南 - 第6章 尝试一些实际中的语法
第6章 尝试一些实际中的语法 在前一章,我们学习了通用词法结构和语法结构,并学习了如何用ANTLR的语法来表述这些结构.现在,是时候把我们学到的这些用来构建一些现实世界中的语法了.我们的主要目标是,怎 ...
- “全栈2019”Java第九十七章:在方法中访问局部内部类成员详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第八十五章:实现接口中的嵌套接口
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第二十七章:流程控制语句中循环语句for
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第二十六章:流程控制语句中循环语句do-while
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- “全栈2019”Java第二十五章:流程控制语句中循环语句while
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
随机推荐
- 《Vue.js 设计与实现》读书笔记 - 第8章、挂载与更新
第8章.挂载与更新 8.1 挂载子节点和元素的属性 扩展子元素的类型可以为数组,并判断如果是数组的话,就先依次挂载所有的子元素. 同时新增节点属性.属性可以通过 el.setAttribute 添加到 ...
- mysql用户相关操作(转载)
mysql用户相关操作 一. 创建用户 命令: CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 说明: username:你将创建的用户 ...
- USB协议详解第2讲(协议核心学习要点)
USB协议详解第2讲(协议核心学习要点) 看了这么多概念,想必大家会问"我要学会USB协议,并且会编程,我具体要学习那些有关的内容?",这一篇我们将会讲解在学习USB协议中务必要掌 ...
- vue的计算属性computed和监视属性waatch的区别
共同的:都是用于监听数据变化的属性: 计算属性:必须有返回值return ,依赖其它属性值,其它属性值发生变化的时候就会重新计算 : 监视属性:每当数据变化的时候就会触发执行,watch有两个新值和旧 ...
- 《Programming from the Ground Up》读后感
之所以看这本书,是想了解一些跟汇编相关的知识,打开这本书后就被作者的观点--"If you don't understand something the first time, reread ...
- kotlin类与对象——>可见性修饰符
1.在 Kotlin 中有这四个可⻅性修饰符:private .protected .internal 和public .如果没有显式指定修饰符的话,默认可⻅性是 public . 2.包,函数.属性 ...
- Android复习(五)设备兼容—>屏幕适配
1. 适配使用的布局 目前版本Google还是希望开发者通过 ConstraintLayout 布局完成适配 2. 对于特定屏幕 创建备用布局,即在res/layout/目录下创建对应尺寸的布局文件 ...
- .NET使用Graphql的演示
Graphql是什么?先来一段AI给的回答: GraphQL是一种为API设计的查询语言,与REST相比,它提供了更高效.强大和灵活的方法来与数据交互.GraphQL由Facebook于2012年开发 ...
- 基于 KubeKey 扩容 Kubernetes v1.24 Worker 节点实战
前言 知识点 定级:入门级 KubeKey 扩容 Worker 节点 openEuler 操作系统的基本配置 Kubernets 基本命令 实战服务器配置(架构 1:1 复刻小规模生产环境,配置略有不 ...
- KubeSphere 集群配置 NFS 存储解决方案
作者:申红磊,QingCloud 容器解决方案架构师,开源项目爱好者,KubeSphere Member 在正式阅读本文之前,先友情提醒一下:不建议您在生产环境中使用 NFS 存储(特别是 Kuber ...