理论闲话
之前在.netFramework平台用的好好的,可升级到.net core平台之后,由于不再需要二进制序列化,导致咱们的事件机制遇到了问题,之前大叔的事件一直是将处理程序序列化后进行存储的,处理存储的参数为事件源,一个事件源可以由多个处理程序订阅,当事件源被发布时,这些被序列化的代码段会被回调执行,这是大叔之前的思路,在RedisBus和MemoryBus里已经得到了实现,读过大叔源代码的同学应该有所了解了。
事件源和处理程序
/// <summary> /// 事件源 /// </summary> public class CreateUserCommand : BusData { public string UserName { get; set; } } /// <summary> /// 事件处理程序 /// </summary> public class CreateUserCommandHandler : IBusHandler<CreateUserCommand> { public void Handle(CreateUserCommand evt) { LoggerFactory.CreateLog().Logger_Debug(evt.UserName); Console.WriteLine("CreateUserCommandHandler"); } }
关于服务总线的实现方式
- RedisBus基于redis进行存储,事件发布后,所有相关处理程序被回调,要求事件和处理程序是可序列化的
- MemoryBus基于应用服务器缓存进行存储,所有相关处理程序被回调,集群环境不是很适合
- IoCBus基于redis作为事件字典,处理程序由IoC容器进行注入,使用场合更广
IoCBus实现思想与组成
- 应该有一个存储事件与处理程序对应关系的字典
- 字典应该被持久化到中间件里
- 应该有个容器,去管理字典值与处理程序的关系
代码实现
数据变更的定义
/// <summary> /// redis key /// </summary> const string ESBKEY = "IoCESBBus"; /// <summary> /// redis事件字典 /// </summary> IDatabase redis = RedisManager.Instance.GetDatabase(); /// <summary> /// 模式锁 /// </summary> private static object _objLock = new object(); /// <summary> /// 对于事件数据的存储,目前采用内存字典 /// </summary> private readonly IContainer container = new AutofacContainer();
事件的统一订阅
public void SubscribeAll() { var types = AssemblyHelper.GetTypesByInterfaces(typeof(IBusHandler<>)); Dictionary<string, List<string>> keyDic = new Dictionary<string, List<string>>(); foreach (var item in types) { if (!item.IsGenericParameter) { TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(item); foreach (var t in typeInfo.GetMethods().Where(i => i.Name == "Handle")) { //ioc name key var eventKey = t.GetParameters().First().ParameterType.Name; var key = t.GetParameters().First().ParameterType.Name + "_" + item.Name; //eventhandler var inter = typeof(IBusHandler<>).MakeGenericType(t.GetParameters().First().ParameterType); container.Register(inter, item, key); if (keyDic.ContainsKey(eventKey)) { var oldEvent = keyDic[eventKey]; oldEvent.Add(key); } else { var newEvent = new List<string>(); newEvent.Add(key); keyDic.Add(eventKey, newEvent); } } } //redis存储事件与处理程序的映射关系 foreach (var hash in keyDic) redis.HashSet( ESBKEY, hash.Key.ToString(), JsonConvert.SerializeObject(hash.Value)); } }
事件的发布,相关处理程序会从容器中取出,并执行它们的Handler方法
public void Publish<TEvent>(TEvent @event) where TEvent : class, IBusData { var keyArr = JsonConvert.DeserializeObject<List<string>>(redis.HashGet(ESBKEY, typeof(TEvent).Name)); foreach (var key in keyArr) { var item = container.ResolveNamed<IBusHandler<TEvent>>(key); item.Handle(@event); } }
说到这里,大叔的服务总线的IoC实现方式就算是完成了,经过测试后,在.net core上表现也很不错!
自己也骄傲一次,呵呵!