.Net Core 基于CAP框架的事件总线


CAP 是一个在分布式系统中(SOA,MicroService)实现事件总线及最终一致性(分布式事务)的一个开源的 C# 库,她具有轻量级,高性能,易使用等特点。

github:https://github.com/dotnetcore/CAP

doc:http://cap.dotnetcore.xyz

CAP是一款优秀的框架,但是CAP在消息订阅的处理类必须使用[CapSubscribe]特性来绑定,本人觉得不是很科学.

好在2.5.1版本开放了接口IConsumerServiceSelector为public,提供了扩展的基础,以下将基于CAP,扩展成简洁的事件总线组件.

使用:

   /// <summary>
/// 修改用户名事件
/// </summary>
public class UserNameUpdateEvent : IEvent
{
public int Id { get; set; } public string OldName { get; set; } public string NewName { get; set; }
} /// <summary>
/// 发布事件,你可以在任意地方注入IEventPublisher,进行事件发布
/// </summary>
public class PublishEvent : Guc.Kernel.Dependency.ITransient
{
public PublishEvent(IEventPublisher eventPublisher)
{
EventPublisher = eventPublisher;
} IEventPublisher EventPublisher { get; } public void Publish()
{
EventPublisher.Publish(new UserNameUpdateEvent
{
Id = ,
OldName = "老王1",
NewName = "老王2"
});
}
} /// <summary>
/// 事件处理,你可以注入任何需要的类型
/// </summary>
public class UserNameUpdateEventHandler : IEventHandler<UserNameUpdateEvent>
{
public UserNameUpdateEventHandler(IUserStore userStore, ILogger<UserNameUpdateEventHandler> logger)
{
UserStore = userStore;
Logger = logger;
} IUserStore UserStore { get; }
ILogger<UserNameUpdateEventHandler> Logger { get; } /// <summary>
/// 执行事件处理
/// </summary>
/// <param name="event">事件对象</param>
/// <returns></returns>
public async Task Execute(UserNameUpdateEvent @event)
{
Logger.LogInformation($"修改用户名:{@event.OldName}->{@event.NewName}");
await Task.CompletedTask;
UserStore.Update(new UserModel { Id = @event.Id, Name = @event.NewName });
}
}

约定事件类型的FullName为事件名称,如例中的:UserNameUpdateEvent的类型FullName:Guc.Sample.UserNameUpdateEvent;

约定事件处理类型的FullName为GroupName,如例中的:UserNameUpdateEventHandler 的类型FullName:Guc.Sample.UserNameUpdateEventHandler

如果使用的RabbitMQ作为消息的传输,则Guc.Sample.UserNameUpdateEvent为路由的Key,Guc.Sample.UserNameUpdateEventHandler 为队列名称.

使用:

   services.AddGucKernel()
.AddEventBus(capOptions =>
{
// CAP相关的配置 });

扩展代码,github:https://github.com/280780363/guc/blob/master/src/Guc.EventBus/GucConsumerServiceSelector.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Concurrent;
using DotNetCore.CAP;
using Guc.Kernel.Utils;
using Microsoft.Extensions.Options; namespace Guc.EventBus
{
class GucConsumerServiceSelector : IConsumerServiceSelector
{
private readonly CapOptions _capOptions;
private readonly IServiceProvider _serviceProvider; /// <summary>
/// since this class be designed as a Singleton service,the following two list must be thread safe!!!
/// </summary>
private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _asteriskList;
private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _poundList; /// <summary>
/// Creates a new <see cref="DefaultConsumerServiceSelector" />.
/// </summary>
public GucConsumerServiceSelector(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_capOptions = serviceProvider.GetService<IOptions<CapOptions>>().Value; _asteriskList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>();
_poundList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>();
} public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates()
{
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); executorDescriptorList.AddRange(FindConsumersFromInterfaceTypes(_serviceProvider));
return executorDescriptorList;
} public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{
var result = MatchUsingName(key, executeDescriptor);
if (result != null)
{
return result;
} //[*] match with regex, i.e. foo.*.abc
result = MatchAsteriskUsingRegex(key, executeDescriptor);
if (result != null)
{
return result;
} //[#] match regex, i.e. foo.#
result = MatchPoundUsingRegex(key, executeDescriptor);
return result;
} private IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(
IServiceProvider provider)
{
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); using (var scoped = provider.CreateScope())
{
var scopedProvider = scoped.ServiceProvider;
var consumerServices = scopedProvider.GetServices<ICapSubscribe>();
foreach (var service in consumerServices)
{
var typeInfo = service.GetType().GetTypeInfo(); // 必须是非抽象类
if (!typeInfo.IsClass || typeInfo.IsAbstract)
continue; // 继承自IEventHandler<>
if (!typeInfo.IsChildTypeOfGenericType(typeof(IEventHandler<>)))
continue; executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo));
} return executorDescriptorList;
}
} private List<string> GetEventNamesFromTypeInfo(TypeInfo typeInfo)
{
List<string> names = new List<string>();
foreach (var item in typeInfo.ImplementedInterfaces)
{
var @interface = item.GetTypeInfo();
if (!@interface.IsGenericType)
continue;
if (@interface.GenericTypeArguments.Length != )
continue; var eventType = @interface.GenericTypeArguments[].GetTypeInfo();
if (!eventType.IsChildTypeOf<IEvent>())
continue; names.Add(eventType.FullName);
}
return names;
} private IEnumerable<ConsumerExecutorDescriptor> GetTopicAttributesDescription(TypeInfo typeInfo)
{
var names = GetEventNamesFromTypeInfo(typeInfo);
if (names.IsNullOrEmpty())
return new ConsumerExecutorDescriptor[] { }; List<ConsumerExecutorDescriptor> results = new List<ConsumerExecutorDescriptor>();
var methods = typeInfo.GetMethods();
foreach (var eventName in names)
{
var method = methods.FirstOrDefault(r => r.Name == "Execute"
&& r.GetParameters().Length ==
&& r.GetParameters()[].ParameterType.FullName == eventName
&& r.GetParameters()[].ParameterType.IsChildTypeOf<IEvent>());
if (method == null)
continue; results.Add(new ConsumerExecutorDescriptor
{
Attribute = new CapSubscribeAttribute(eventName) { Group = typeInfo.FullName + "." + _capOptions.Version },
ImplTypeInfo = typeInfo,
MethodInfo = method
});
} return results;
} private ConsumerExecutorDescriptor MatchUsingName(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{
return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key);
} private ConsumerExecutorDescriptor MatchAsteriskUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{
var group = executeDescriptor.First().Attribute.Group;
if (!_asteriskList.TryGetValue(group, out var tmpList))
{
tmpList = executeDescriptor.Where(x => x.Attribute.Name.IndexOf('*') >= )
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor>
{
Name = ("^" + x.Attribute.Name + "$").Replace("*", "[0-9_a-zA-Z]+").Replace(".", "\\."),
Descriptor = x
}).ToList();
_asteriskList.TryAdd(group, tmpList);
} foreach (var red in tmpList)
{
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline))
{
return red.Descriptor;
}
} return null;
} private ConsumerExecutorDescriptor MatchPoundUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor)
{
var group = executeDescriptor.First().Attribute.Group;
if (!_poundList.TryGetValue(group, out var tmpList))
{
tmpList = executeDescriptor
.Where(x => x.Attribute.Name.IndexOf('#') >= )
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor>
{
Name = ("^" + x.Attribute.Name + "$").Replace("#", "[0-9_a-zA-Z\\.]+"),
Descriptor = x
}).ToList();
_poundList.TryAdd(group, tmpList);
} foreach (var red in tmpList)
{
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline))
{
return red.Descriptor;
}
} return null;
} private class RegexExecuteDescriptor<T>
{
public string Name { get; set; } public T Descriptor { get; set; }
}
}
}

.Net Core 基于CAP框架的事件总线的更多相关文章

  1. 【DDD-Apwork框架】事件总线和事件聚合器

    第一步:事件总线和事件聚合器 [1]事件总线 IEventBus IUnitOfWork.cs using System; using System.Collections.Generic; usin ...

  2. Asp.net Core基于MVC框架实现PostgreSQL操作

    简单介绍 Asp.net Core最大的价值在于跨平台.跨平台.跨平台.重要的事情说三遍.但是目前毕竟是在开发初期,虽然推出了1.0.0 正式版,但是其实好多功能还没有完善.比方说编译时的一些文件编码 ...

  3. [Abp vNext 源码分析] - 13. 本地事件总线与分布式事件总线 (Rabbit MQ)

    一.简要介绍 ABP vNext 封装了两种事件总线结构,第一种是 ABP vNext 自己实现的本地事件总线,这种事件总线无法跨项目发布和订阅.第二种则是分布式事件总线,ABP vNext 自己封装 ...

  4. 【Shashlik.EventBus】.NET 事件总线,分布式事务最终一致性

    [Shashlik.EventBus].NET 事件总线,分布式事务最终一致性 简介 github https://github.com/dotnet-shashlik/shashlik.eventb ...

  5. C#事件总线

    目录 简介 实现事件总线 定义事件基类 定义事件参数基类 定义EventBus 使用事件总线 事件及事件参数 定义发布者 定义订阅者 实际使用 总结 参考资料 简介 事件总线是对发布-订阅模式的一种实 ...

  6. .NET Core 事件总线,分布式事务解决方案:CAP 基于Kafka

    背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用的过程中也会遇到分布式事务的问题,那么 CAP 就是在这样的背景 ...

  7. .NET Core 事件总线,分布式事务解决方案:CAP

    背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用的过程中也会遇到分布式事务的问题,那么 CAP 就是在这样的背景 ...

  8. ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    在上文中,我们讨论了事件处理器中对象生命周期的问题,在进入新的讨论之前,首先让我们总结一下,我们已经实现了哪些内容.下面的类图描述了我们已经实现的组件及其之间的关系,貌似系统已经变得越来越复杂了. 其 ...

  9. 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    重温.NET下Assembly的加载过程   最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...

随机推荐

  1. Vue.js 源码分析(二十六) 高级应用 作用域插槽 详解

    普通的插槽里面的数据是在父组件里定义的,而作用域插槽里的数据是在子组件定义的. 有时候作用域插槽很有用,比如使用Element-ui表格自定义模板时就用到了作用域插槽,Element-ui定义了每个单 ...

  2. Autoware 培训笔记 No. 1——构建点云地图

    1. 首记 相信许多刚开始玩无人驾驶的人都用过Autoware,对runtime manager都比较熟悉,虽然可以通过各种渠道了解到有些设置,甚至有些设置的app下参数的含义,但是,在真车的使用过程 ...

  3. 关于Idea突然无法输入的诡异问题解决

    问题描述 最近加班把自己的装有Debian的笔记本带到公司,使用Idea写代码的时候,突然间无法输入,ctrl与tab还可用,重启Idea能得到一阵的解决 解决参考 如果是Linux平台,请考虑是否是 ...

  4. WPF树形菜单--递归与非递归遍历生成树结构的集合

    一.新建了WPF项目作为测试,使用TreeView控件进行界面展示. 第一步创建实体类TreeEntity: public class TreeEntity { private int _mid; p ...

  5. ASP.NET MVC 中的过滤器

    这里用实例说明各种过滤器的用法,有不对的地方还请大神指出,共同探讨. 1. ActionFilter 方法过滤器: 接口名为 IActionFilter ,在控制器方法调用前/后执行. 在新建的MVC ...

  6. LinuxShell——正则表达式

    LinuxShell——正则表达式 摘要:本文主要学习了Shell中的正则表达式. 简介 含义 正则表达式,也称作正规表示法,是用于描述字符排列和匹配模式的一种语法规则,它主要用于字符串的模式分割.匹 ...

  7. Python【day 11】闭包

    闭包 1.闭包的概念: 嵌套函数中,父级函数的变量,在子集函数中用到了(访问.修改.返回),那么这个变量就被保护起来了 只有自己可以修改,父级函数()()就是闭包函数 2.闭包的特点: 1.常驻内存 ...

  8. codeforces #592(Div.2)

    codeforces #592(Div.2) A Pens and Pencils Tomorrow is a difficult day for Polycarp: he has to attend ...

  9. Java中级—转发和重定向的区别

    在设计Web应用程序的时候,经常需要把一个系统进行结构化设计,即按照模块进行划分,让不同的Servlet来实现不同的功能,例如可以让其中一个Servlet接收用户的请求,另外一个Servlet来处理用 ...

  10. 章节十四、2-自动完成功能-Autocomplete

    一.什么是自动匹配功能? 很多网站都有自动匹配功能,列如你在使用天猫搜索商品时,输入“鞋”,输入框的下面会出现很多与“鞋”有关的选项. 二.以https://www.expedia.com/网站的城市 ...