从不同的 AppDomain 调用 SignalR 方法
Posted
技术标签:
【中文标题】从不同的 AppDomain 调用 SignalR 方法【英文标题】:Call a SignalR method from a different AppDomain 【发布时间】:2018-02-03 01:19:37 【问题描述】:我有一个使用 SignalR 用 C# 编写的工作 Web 服务器。这是一个自托管的 Owin 应用程序。一切正常。
现在我必须在不同的 AppDomain 中重新定位我的控制器。这破坏了 SignalR 部分,因为 GlobalHost
仅在一个 AppDomain 中保持不变,并且不可序列化(因此我无法将其传递给其他 AppDomain)。
我找到了很多关于从控制器/其他类/其他类中调用 SignalR 集线器方法的示例/问题/教程,但在默认 AppDomain(初始化 Owin 应用程序的地方)之外没有任何内容。
如何从与 Hub 不同的 AppDomain 中设置的控制器向客户端发送消息?
【问题讨论】:
【参考方案1】:我找到的解决方案非常简单:对于任何 AppDomain 间的通信,我们需要能够跨越 AppDomain 边界的东西,即数据或类的代理。
因此,以下工作:
创建一个扩展 MarshalByRefObject 的类:当我们将它传递给不同 AppDomain 中的另一个类时,这将自动创建该类的代理
public class InterAppDomainForSignalR : MarshalByRefObject
public void Publish(PublishParameter param)
var clients = GlobalHost.ConnectionManager.GetHubContext<TradeHub>().Clients;
dynamic chan;
if (param.group != null && param.group.Length > 0)
chan = clients.Group(param.group, param.ids);
else
if(param.ids == null || param.ids.length = 0)
return; //not supposed to happen
chan = clients.Client(param.ids[0]);
chan.OnEvent(param.channelEvent.ChannelName, param.channelEvent);
[Serializable]
public class PublishParameter
public string group get; set;
public string[] ids get; set;
public ChannelEvent channelEvent get; set;
确保你的参数是Serializable
:这里,PublishParameter
显然是正确的,但是ChannelEvent
也必须是可序列化的,并且只包含Serializable
成员等...
创建该类的实例,并将其传递给不同AppDomains中的对象(communicationChannel
是InterAppDomainForSignalR
的实例):
AppDomain domain = AppDomain.CreateDomain(myDomainName);
Type type = typeof(ClassInOtherAppDomain);
ClassInOtherAppDomain startpoint = (ClassInOtherAppDomain)domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName) as ClassInOtherAppDomain;
var session = startpoint.initialize(communicationChannel);
将communicationChannel
存储在ClassInOtherAppDomain
实例中,并随意使用它;):
public class ClassInOtherAppDomain
private InterAppDomainForSignalR communicationChannel get; set;
public void initialize(InterAppDomainForSignalR communicationChannel)
this.communicationChannel = communicationChannel;
public void Publish(PublishParameter param)
this.communicationChannel.Publish(param);
就是这样=)
更多关于如何实现跨AppDomain通信的文档可以在here和here找到。
【讨论】:
【参考方案2】:从外面:
var context = GlobalHost.ConnectionManager.GetHubContext<YOURHUBCLASS>();
context.Clients.All.yourHubTask();
【讨论】:
我假设YOURCLASSNAME
指的是集线器类。这是行不通的:集线器已经在与 Owin 应用程序相同的 AppDomain 中启动。我遇到的问题是:如何从运行在不同 AppDomain 中的服务调用 Owin 应用程序的 AppDomain 中存在的内容。
GlobalHost
是一个静态变量,因此它仅在 AppDomain 内具有相同的“值”。因此,如果我的 Owin 应用程序在 AppDomain 1 中启动,并且我在 AppDomain 2 中调用 GlobalHost.ConnectionManager.GetHubContext<YOURHUBCLASS>().Clients.All
,我将看不到任何客户端:它们都在 AppDomain 1 中注册...... Owin 应用程序,但这不是我的用例。
为什么不能在AppDomain 2中调用AppDomain.CurrentDomain.Load(typeof(YOURHUBCLASS).Assembly.FullName)
?
我无法访问客户端:它们连接到 AppDomain 1 中存在的类。以上是关于从不同的 AppDomain 调用 SignalR 方法的主要内容,如果未能解决你的问题,请参考以下文章
jquery ajax响应+不同AppDomain中的函数调用流程