5

我从客户端应用程序向远程队列发送请求/事务(包含要执行的操作(和参数)+ transactionID)。远程服务器在某个时间点将请求出列并需要一些时间来处理它。

一旦完成处理,它会在客户端队列上发送一个响应(包含应用响应 + 事务 ID)......所以这是一种完全“断开连接”的通信模式,客户端可以将响应映射到请求是通过 transactionID。

消息响应在客户端出列,并与原始请求匹配(基于 transactionID)。

我现在正在做的是,当客户端将请求发布到服务器队列时,它会向保留 transactionId 和回调(委托)的字典添加回调。这是一个Dictionary<int, object>将 transactionId 映射回回调以调用操作结果。

回调/委托被存储为对象,因为根据请求,回调委托签名是不同的(例如,一个响应可能返回 aList<string>而另一个响应可能返回一个int)。

当客户端队列出列响应时,它知道响应的类型(以及相应的回调签名),因此它根据 transactionID 从字典中获取回调。然后它将对象转换回相应的委托类型并调用回调。

我发现这种方法不是很“性感”,但我并没有真正看到执行此类任务的另一种方法。

有没有更好的方法来执行此操作?

如果问题不够清楚,请告诉我,并通过一些编辑来澄清。

4

4 回答 4

3

您可能会发现 Reactive Extensions 是一种用于此类消息传递的便捷工具。

首先,让你的所有响应都实现一些接口或基类,比如 IMessage。每种响应都应该封装在一个单独的类中,就像这里

public interface IMessage
{
    int TransactionId { get; }
}

public class UserListMessage : IMessage
{
    int TransactionId { get; set; }
    public List<string> Users { get; set; }
}

然后让你的消息队列实现IObservable<IMessage>。反应式扩展提供了一个现成的实现,称为Subject<T>,您可能想要包装它或其他东西。

Observable 实现了Subscribe(IObserver<IMessage> observer)一种将观察者存储在内部列表中某处的方法。当一个新的响应到达时OnNext(IMessage message),每个订阅的观察者都会调用一个方法。

最后,您的响应处理注册代码可能如下所示:

var subscription = myQueue
    .OfType<UserListMessage>()
    .Where(msg => msg.TransactionId == id)
    .Subscribe(callback);

这将为具有给定事务 ID 的 UserListMessage 类型的消息注册回调。可能您还想在某个地方取消订阅。那将是:

subscription.Dispose();

这是一个简短的示例,使用 Rx 会是什么样子。现在去找一些更详细的教程。

于 2012-07-06T22:49:26.090 回答
1

您可以在客户端创建一个枚举,定义您可以获得的所有可能响应类型。然后,您可以编写一个包含大“选择案例”的函数,将枚举的每个值与其特定的函数调用相关联。然后,您可以通过字典将 transactionID 与枚举的值相关联,以识别您将从服务器获得的响应类型。

根据您需要在每种类型的响应上实现的功能类型,也许您可​​以找到一种更“面向对象”的做事方式......也许您可以创建一个基类 ResponseAction,使用通用的 Respond 方法,并且然后你可以从这个类继承你可以获得的每个可能的响应类型,然后当你调用服务器时,你实例化正确的 Response_Specific_Action 类,并将这个实例放在字典中......然后在每个响应上你将使用您的字典找到正确的实例,并调用相同的标准 Respond 方法,它将使用它的 Response_Specific_Action “特定”实现。

如果您选择 ResponseAction 类路由,您还可以考虑将 transactionID 作为基类的属性包含在内。

于 2012-07-06T22:28:23.983 回答
1

你的情况对我来说不是很清楚。首先,您没有提到您在客户端服务器事务期间使用哪种通信机制?你在使用 WCF 和它的回调通道吗?

为什么不将所有请求响应消息包装在具有所有事务的一些公共数据的基类下?

你很难过你正在为你的字典使用列表,因为响应因事务而异,但又为什么反对?为所有响应消息制定共同合同不是更好吗?请告诉我们更多有关您的客户端和服务器之间的通信流程的信息

于 2012-07-06T22:37:32.170 回答
1

这听起来完全像适配器模式问题。有关适配器的详细概述,请参阅此链接

您需要做的是创建一个抽象来封装整个客户端响应(包括您对它所做的任何事情List<string>int。您的抽象需要足够广泛的范围,以便您的不同行为都可以被相同的签名覆盖。像这样的东西:

public abstract class MessageCompleteHandler
{
    public abstract void Execute();
}

//name this one better, just an example :)
public class ListOfStringHandler : MessageCompleteHandler
{
    public override void Execute()
    {
        //get list of strings
        // do something with it
    }
}

public class MessageCompleteHandlerFactory
{
    public MessageCompleteHandler GetHandler(int transactionId)
    {
        //this replaces/uses your dictionary to match handlers with types.
    }
}

然后,当您获得响应时,您使用事务 ID 创建适当的处理程序对象并运行它。当然,如果您的处理程序变体的数量相当少,这种方法效果最好。

于 2012-07-06T23:23:22.550 回答