需要一个更好的“回调调度程序”组件设计

时间:2012-07-06 22:22:25

标签: c# algorithm

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

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

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

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

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

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

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

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

如果问题不够明确,请告诉我并通过一些修改澄清。

4 个答案:

答案 0 :(得分: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>。 Reactive扩展提供了一个名为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会是什么样子。现在去找一些更详细的教程。

答案 1 :(得分:1)

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

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

如果您使用ResponseAction类路由,您还可以考虑将transactionID包含为基类的属性。

答案 2 :(得分:1)

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

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

你难以为列表使用List,因为响应因事务而异,但为什么还要反对呢?所有回复信息的共同合同难道不是更难吗?请告诉我们更多关于客户端和服务器之间的通信流程

答案 3 :(得分:1)

这听起来就像是适配器模式问题。有关适配器的详细概述,请参阅this link

您需要做的是创建一个抽象来封装整个客户端响应(包括您对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创建适当的处理程序对象,然后运行它。如果你的处理程序变体数量相当小,那么这种方法效果最好。