.Net Core 基于CAP框架的事件总线
.Net Core 基于CAP框架的事件总线
CAP 是一个在分布式系统中(SOA,MicroService)实现事件总线及最终一致性(分布式事务)的一个开源的 C# 库,她具有轻量级,高性能,易使用等特点。
github:https://github.com/dotnetcore/CAP
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框架的事件总线的更多相关文章
- 【DDD-Apwork框架】事件总线和事件聚合器
第一步:事件总线和事件聚合器 [1]事件总线 IEventBus IUnitOfWork.cs using System; using System.Collections.Generic; usin ...
- Asp.net Core基于MVC框架实现PostgreSQL操作
简单介绍 Asp.net Core最大的价值在于跨平台.跨平台.跨平台.重要的事情说三遍.但是目前毕竟是在开发初期,虽然推出了1.0.0 正式版,但是其实好多功能还没有完善.比方说编译时的一些文件编码 ...
- [Abp vNext 源码分析] - 13. 本地事件总线与分布式事件总线 (Rabbit MQ)
一.简要介绍 ABP vNext 封装了两种事件总线结构,第一种是 ABP vNext 自己实现的本地事件总线,这种事件总线无法跨项目发布和订阅.第二种则是分布式事件总线,ABP vNext 自己封装 ...
- 【Shashlik.EventBus】.NET 事件总线,分布式事务最终一致性
[Shashlik.EventBus].NET 事件总线,分布式事务最终一致性 简介 github https://github.com/dotnet-shashlik/shashlik.eventb ...
- C#事件总线
目录 简介 实现事件总线 定义事件基类 定义事件参数基类 定义EventBus 使用事件总线 事件及事件参数 定义发布者 定义订阅者 实际使用 总结 参考资料 简介 事件总线是对发布-订阅模式的一种实 ...
- .NET Core 事件总线,分布式事务解决方案:CAP 基于Kafka
背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用的过程中也会遇到分布式事务的问题,那么 CAP 就是在这样的背景 ...
- .NET Core 事件总线,分布式事务解决方案:CAP
背景 相信前面几篇关于微服务的文章也介绍了那么多了,在构建微服务的过程中确实需要这么一个东西,即便不是在构建微服务,那么在构建分布式应用的过程中也会遇到分布式事务的问题,那么 CAP 就是在这样的背景 ...
- ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线
在上文中,我们讨论了事件处理器中对象生命周期的问题,在进入新的讨论之前,首先让我们总结一下,我们已经实现了哪些内容.下面的类图描述了我们已经实现的组件及其之间的关系,貌似系统已经变得越来越复杂了. 其 ...
- 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线
重温.NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...
随机推荐
- 【前端知识体系-JS相关】深入理解MVVM和VUE
1. v-bind和v-model的区别? v-bind用来绑定数据和属性以及表达式,缩写为':' v-model使用在表单中,实现双向数据绑定的,在表单元素外使用不起作用 2. Vue 中三要素的是 ...
- HDU-1719 Friend 数学推导
Friend HDU - 1719 Friend number are defined recursively as follows. (1) numbers 1 and 2 are friend n ...
- javascript中的发布订阅模式与观察者模式
这里了解一下JavaScript中的发布订阅模式和观察者模式,观察者模式是24种基础设计模式之一. 设计模式的背景 设计模式并非是软件开发的专业术语,实际上设计模式最早诞生于建筑学. 设计模式的定义是 ...
- AngleSharp 实战(01)之最简单的示例
文档地址:https://anglesharp.github.io/docs/Examples.html 直接贴代码了: using System; using System.Linq; using ...
- 分布式Redis深度历险-Cluster
本文为分布式Redis深度历险系列的第三篇,主要内容为Redis的Cluster,也就是Redis集群功能. Redis集群是Redis官方提供的分布式方案,整个集群通过将所有数据分成16384个槽来 ...
- 机器学习实战:基于Scikit-Learn和TensorFlow 第5章 支持向量机 学习笔记(硬间隔)
数据挖掘作业,需要实现支持向量机进行分类,记录学习记录 环境:win10,Python 3.7.0 SVM的基本思想:在类别之间拟合可能的最宽的间距,也叫作最大间隔分类 书上提供的源代码绘制了两个图, ...
- 初学dubbo遇到的那些坑
昨天刚接触dubbo,遇到了一些坑,当然,这也与刚从eclipse换到了idea有一定的关系. 首先是maven仓库的问题,c盘下面的.m2文件夹默认的会被开发工具访问,所以要访问自己的本地仓库,.m ...
- 高强度学习训练第一天总结:Java内存区域
---恢复内容开始--- 程序计数器: 程序计数器(Program Counter Register) 是一块较小的空间,他可以看作是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里(仅是概念 ...
- swift(一)基础变量类型
import Foundation println("Hello, World!") /* int a; */ var a = //隐式类型转换 a = println(a) le ...
- java中方法的重载和覆盖分别要满足的条件
1.重载:遵循“两同三不同” 两同:同一个类中的同名方法 三不同:形参的类型,个数,顺序不同 特别提醒: 返回值不同构不能方法重载 形参名称不同构不成方法重载 2.覆盖(重写)的要求 子类方法的名称, ...