.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的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...
随机推荐
- Python 小案例实战 —— 简易银行存取款查询系统
Python 小案例实战 -- 简易银行存取款查询系统 涉及知识点 包的调用 字典.列表的混合运用 列表元素索引.追加 基本的循环与分支结构 源码 import sys import time ban ...
- ECMAScript 6.0 简要学习
由于在学习vue的时候有许多自己不懂的语法,于是简单的学习一下ES6. 1.ES简介 ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版. ...
- dedecms5.7文章页的标签随机插入到内容中并且标签的地址为其标签关联的其他文章地址
dedecms5.7文章页的标签随机插入到内容中并且标签的地址为其他标签关联的文章地址 1 添加2个自定义函数 在dede/include/extend.func.php底部 添加如下代码 //根据文 ...
- mysql error 1364 Field doesn't have a default values
https://stackoverflow.com/questions/15438840/mysql-error-1364-field-doesnt-have-a-default-values. us ...
- visa
- RabbitMQ系列(一)rabbitmq简介
------------恢复内容开始------------ 参考:https://www.cnblogs.com/vipstone/p/9275256.html RabbitMQ简介 在介绍Rabb ...
- C typedef、#define
参考链接:https://www.runoob.com/cprogramming/c-typedef.html 作用 typedef是用来为数据类型(可以是各种数据类型,包括自己定义的数据类型如结构体 ...
- C 储存类与运算符
储存类 参考链接:https://www.runoob.com/cprogramming/c-storage-classes.html 存储类定义 C 程序中变量/函数的范围(可见性)和生命周期 au ...
- Docker(二)-在Docker中部署Nginx实现负载均衡(视频教程)
本教程介绍利用Docker部署Nginx服务实现负载均衡. (双击全屏播放)
- 「白帽黑客成长记」Windows提权基本原理(上)
我们通常认为配置得当的Windows是安全的,事实真的是这样吗?今天让我们跟随本文作者一起深入了解Windows操作系统的黑暗角落,看看是否能得到SYSTEM权限. 作者将使用不同版本的Windows ...