今天粗粗看了老赵的文章适合C# Actor的消息执行方式 -中看不(3):中用的解决方案,我在想如果用我以前写的消息总线来实现那不是中看也中用了,于是顺手写了一个测试代码(具体内容参见适合C# Actor的消息执行方式 -中看不(3):中用的解决方案回复),说来很惭愧我的消息总线系列已经一年多没有更新了,我这人太懒散惯了,没办法。废话不多说了,下面我就具体讲解一下设计思路。
在Actor模式中,最重要的就是Actor间的消息发送以及消息路由了。
消息发送:举一个例子实现一个ActorA向ActorB发消息,最简单的方法就是:ActorA中需要内聚或依赖一个ActorB对象,然后就可以直接发消息通讯了,这种方法优点不言而喻-简单,但是当一个系统中存在无数个Actor,一个Actor有可能向N多的其它Actor发消息,这种方法的弊端就暴露无疑了:强依赖,强耦合。 怎么结局这种问题呢,这时候设计模式中的中介者模式就可以派上用场了。所有的Actor都向中介者发消息即可,由中介者把消息通过一定的策略路由到特定的Actor,由这个特定的Actor进行处理即可。
消息路由:消息路由的关键就是路由表,路由表可以用字典来实现,Key-可以用消息类型+Topic来标记即可,Value :就用委托函数即可。
具体简略设计图如下:
类图详解:
ISubject<TMessage>代表Actor,该接口继承了IPublisher接口,同时定义了Observers事件,典型的观察者模式,所以该接口具有发布和订阅消息的功能。
//发布器接口
public interface IPublisher
{
void Publish(object sender, object msg);
}
public interface IPublisher<TMessage> : IPublisher
{
void Publish(object sender, TMessage msg);
}
//Actor 接口
public interface ISubject:IPublisher
{
//发布消息的Topic名称
string Name { get; set; }
void Close();//退出,从消息总线中移除该Topic的Actor
}
public interface ISubject<TMessage> : ISubject,IPublisher<TMessage>
{
//观察者集合
event ObserverHandler<TMessage> Observers;
//消息订阅者
ISubscriber<TMessage> Subscriber { get; set; }
//消息队列
IQueue<KeyValuePair<object,TMessage>> Queue { get; set; }
//消息执行器
IExecutor<TMessage> Executor { get; set; }
bool Closed { get; }
}
IMessageRouter 内聚了ISubject(Actor)的字典,Actor的消息发送以及消息接收的重担全部都交给了IMessageRouter,使Actor和Actor之间不需要知道对方的存在,Actor只需要发布消息(或基于Topic的消息)即可,或者只需要接受消息(或基于Topic的消息)即可。
public interface IMessageRouter:IEnumerable<KeyValuePair<Key,ISubject>>
{
string Name { get; set; }
ISubjectBuilder Builder { get;}
IHookManager HookManager { get;}
IDelegateInvoker DelegateInvoker { get;}
bool ContainSubject(Type type);
bool ContainSubject(string topic, Type type);
bool ContainSubject<T>();
bool ContainSubject<T>(string topic);
ISubject[] Items { get; }
ISubject<TMessage> Subject<TMessage>();
ISubject<TMessage> Subject<TMessage>(string topic);
//Delete Subject by Message Type or Topic
void Remove<TMessage>();
void Remove<TMessage>(string topic);
void Remove(Type type);
void Remove(string topic, Type type);
//订阅消息-注册观察者
void Subscribe<TMessage>(ObserverHandler<TMessage> handler);
void Subscribe<TMessage>(string topic, ObserverHandler<TMessage> handler);
void Subscribe<TMessage>(object observer, ObserverHandler<TMessage> handler);
void Subscribe<TMessage>(string topic,object observer, ObserverHandler<TMessage> handler);
//移除订阅者
void Unsubscribe<TMessage>(ObserverHandler<TMessage> handler);
void Unsubscribe<TMessage>(string topic, ObserverHandler<TMessage> handler);
//订阅前置过滤器消息 - 注册前置过滤器观察者
void SubscribePreFilter<TMessage>(PreFilterHandler<TMessage> handler);
void SubscribePreFilter<TMessage>(string topic, PreFilterHandler<TMessage> handler);
void SubscribePreFilter<TMessage>(object sender, PreFilterHandler<TMessage> handler);
void SubscribePreFilter<TMessage>(string topic, object sender, PreFilterHandler<TMessage> handler);
void UnsubscribePreFilter<TMessage>(PreFilterHandler<TMessage> handler);
void UnsubscribePreFilter<TMessage>(string topic,PreFilterHandler<TMessage> handler);
//注册过滤器消息 - 注册过滤器观察者
void SubscribeFilter<TMessage>(FilterHandler<TMessage> handler);
void SubscribeFilter<TMessage>(string topic, FilterHandler<TMessage> handler);
void SubscribeFilter<TMessage>(object observer, FilterHandler<TMessage> handler);
void SubscribeFilter<TMessage>(string topic, object observer, FilterHandler<TMessage> handler);
void UnsubscribeFilter<TMessage>(FilterHandler<TMessage> handler);
void UnsubscribeFilter<TMessage>(string topic, FilterHandler<TMessage> handler);
int Count { get; }
//注册钩子(钩子的部分函数也可充当消息过滤器使用)
void RegisterHook(params IHook[] hooks);
void UnRegisterHook(params IHook[] hooks);
//发布消息
void Publish<TMessage>(TMessage msg);
void Publish<TMessage>(string topic, TMessage msg);//发布基于主题的消息
void Publish<TMessage>(object sender, TMessage msg);
void Publish<TMessage>(string topic, object sender, TMessage msg);//发布基于主题的消息
//推荐方式:注册卸载发布基于主题的消息
void SubscribeTopic<TMessage>(string topic, ObserverHandler<Topic<TMessage>> handler);
void UnsubscribeTopic<TMessage>(string topic, ObserverHandler<Topic<TMessage>> handler);
void PublishTopic<TMessage>(Topic<TMessage> topic);
void Clear();
}
改进版的Ping/Pong, Ping和Pong互不依赖,它们之间仅仅通过Topic和消息类型作为标签进行通讯
Topics 定义:
class Topics
{
public const string Ping = "Topic://PingPongTest/Ping";
public const string Pong = "Topic://PingPongTest/Pong";
}
消息类:就用Int类型
Ping 类的实现代码:
class Ping
{
int m_count = 5;
public Ping()
{
Actor.Receive<int>(Topics.Pong, (s, e) =>
{
Console.WriteLine("Ping received pong :" + e.Message);
if (--this.m_count > 0)
{
Console.WriteLine();
Console.WriteLine("Ping sent :" + m_count.ToString());
Actor.Post<int>(Topics.Ping, m_count);
}
else
{
Console.WriteLine("Finished");
Actor.Exit<Ping>(Topics.Ping);
}
});
}
public void Start()
{
Console.WriteLine("Ping sent :" + m_count.ToString());
Actor.Post<int>(Topics.Ping, m_count);
}
}
Pong 类的实现非常简单:
class Pong
{
public Pong()
{
Actor.Receive<int>(Topics.Ping, (s, e) =>
{
Console.WriteLine("Pong received ping :" + e.Message.ToString());
Console.WriteLine("Pong sent :" + e.Message.ToString());
Actor.Post<int>(Topics.Pong, e.Message);
});
}
}
测试代码:
[Test]
public void Test()
{
var ping = new Ping();
var pong = new Pong();
ping.Start();
}
程序输出:
Ping sent :5
Pong received ping :5
Pong sent :5
Ping received pong :5
Ping sent :4
Pong received ping :4
Pong sent :4
Ping received pong :4
Ping sent :3
Pong received ping :3
Pong sent :3
Ping received pong :3
Ping sent :2
Pong received ping :2
Pong sent :2
Ping received pong :2
Ping sent :1
Pong received ping :1
Pong sent :1
Ping received pong :1
Finished
OK,暂时先写到这里,欢迎大家一起进行交流!