诺禾

Wesley13
• 阅读 602

最近把 Event 相关的逻辑做了一个重构,修正 EventStore,引入了 IEventHandlerFactory,重新设计了 Event 相关的组件

重构后的 Event
Event: 事情的笼统定义
EventHandler:事情处置器笼统定义
EventHandlerFactory:事情处置器工厂,用来依据事情类型获取事情处置器(新增)
EventPublisher:事情发布器,用于事情发布
EventSubscriber:事情订阅器,用于管理事情的订阅
EventSubscriptionManager:事情订阅管理器,在 EventSubscriber 的根底上增加了一个依据事情类型获取事情订阅器类型的办法
EventBus:事情总线,由 EventPubliser 和 EventSubscriber 组合而成,用来比拟便当的做事情发布和订阅
EventQueue:事情队列,希望某些音讯次第处置的时分能够思索用 EventQueue 的形式
EventStore:事情存储,事情的耐久化存储(在之前的版本里,EventStore 实践作用是一个 EventSubscriptionManager,在最近的版本更新中已修正)
以上 EventSubscriber 和 EventSubscriptionManager 普通不直接用,普通用 EventBus 来处置即可

EventHandlerFactory
这次引入了 EventHandlerFactory 用来笼统获取 EventHandler 的逻辑,原来的设计里是在处置 Event 的时分获取 EventHandler 的类型,然后从依赖注入框架中获取或创立新的 event handler 实例之后再调用 EventHandler 的 Handle 办法处置事情,有一些冗余

运用 EventHandlerFactory 之后就能够直接获取一个 EventHandler 实例汇合,详细是实例化还是从依赖注入中获取就由 EventHandlerFactory 来决议了,这样就能够对依赖注入很友好,关于基于内存的简单 EventBus 来说,在效劳注册之后就不需求再调用 Subscribe 去显式订阅了,由于再注册效劳的时分就曾经隐式完成了订阅的逻辑,这样实践就不需求 EventSubscriptionManager 来管理订阅了,订阅信息都在依赖注入框架内部,比方说 CounterEvent,要获取它的订阅信息,我只需求从依赖注入框架中获取 IEventHandler 的实例即可,实践就替代了原先 “EventStoreInMemory”,如今的 EventSubscriptionManagerInMemory

基于依赖注入的 EventHandlerFactory 定义:

public sealed class DependencyInjectionEventHandlerFactory : IEventHandlerFactory
{
private readonly IServiceProvider _serviceProvider;
public DependencyInjectionEventHandlerFactory(IServiceProvider serviceProvider = null)
{
_serviceProvider = serviceProvider ?? DependencyResolver.Current;
}
public ICollection GetHandlers(Type eventType)
{
var eventHandlerType = typeof(IEventHandler<>).MakeGenericType(eventType);
return _serviceProvider.GetServices(eventHandlerType).Cast().ToArray();
}
}
假如不运用依赖注入,也能够依据 IEventSubscriptionManager 订阅信息来完成:

public sealed class DefaultEventHandlerFactory : IEventHandlerFactory
{
private readonly IEventSubscriptionManager _subscriptionManager;
private readonly ConcurrentDictionary<Type, ICollection> _eventHandlers = new ConcurrentDictionary<Type, ICollection>();
private readonly IServiceProvider _serviceProvider;
public DefaultEventHandlerFactory(IEventSubscriptionManager subscriptionManager, IServiceProvider serviceProvider = null)
{
_subscriptionManager = subscriptionManager;
_serviceProvider = serviceProvider ?? DependencyResolver.Current;
}
public ICollection GetHandlers(Type eventType)
{
var eventHandlers = _eventHandlers.GetOrAdd(eventType, type =>
{
var handlerTypes = _subscriptionManager.GetEventHandlerTypes(type);
var handlers = handlerTypes
.Select(t => (IEventHandler)_serviceProvider.GetServiceOrCreateInstance(t))
.ToArray();
return handlers;
});
return eventHandlers;
}
}
EventQueue Demo
来看一下 EventQueue 的示例,示例基于 asp.net core 的,定义了一个 HostedService 来完成一个 EventConsumer 来消费 EventQueue 中的事情信息

EventConsumer 定义如下:

public class EventConsumer : BackgroundService
{
private readonly IEventQueue _eventQueue;
private readonly IEventHandlerFactory _eventHandlerFactory;
public EventConsumer(IEventQueue eventQueue, IEventHandlerFactory eventHandlerFactory)
{
_eventQueue = eventQueue;
_eventHandlerFactory = eventHandlerFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var queues = await _eventQueue.GetQueuesAsync();
if (queues.Count > 0)
{
await queues.Select(async q =>
{
var @event = await _eventQueue.DequeueAsync(q);
if (null != @event)
{
var handlers = _eventHandlerFactory.GetHandlers(@event.GetType());
if (handlers.Count > 0)
{
await handlers
.Select(h => h.Handle(@event))
.WhenAll()
;
}
}
})
.WhenAll()
;
}
await Task.Delay(1000, stoppingToken);
}
}
}
定义 PageViewEvent 和 PageViewEventHandler,用来记载和处置恳求访问记载

public class PageViewEvent : EventBase
{
}
public class PageViewEventHandler : EventHandlerBase
{
public static int Count;
public override Task Handle(PageViewEvent @event)
{
Interlocked.Increment(ref Count);
return Task.CompletedTask;
}
}
事情很简单,事情处置也只是增加了 PageViewEventHandler 内定义的 Count。

效劳注册:

// 注册事情中心组件
// 会注册 EventBus、EventHandlerFactory、EventQueue 等
services.AddEvents()
// 注册 EventHanlder
.AddEventHandler<PageViewEvent, PageViewEventHandler>()
;
// 注册 EventQueuePubliser,默许注册的 IEventPublisher 是 EventBus
services.AddSingleton<IEventPublisher, EventQueuePublisher>();
// 注册 EventConsumer
services.AddHostedService();
事情发布,定义了一个中间件来发布 PageViewEvent,定义如下:

// pageView middleware
app.Use((context, next) =>
{
var eventPublisher = context.RequestServices.GetRequiredService();
eventPublisher.Publish(new PageViewEvent());
return next();
});
然后定义一个接口来获取上面定义的 PageViewEventHandler 中的 Count

[Route("api/[controller]")]
public class EventsController : ControllerBase
{
[HttpGet("pageViewCount")]
public IActionResult Count()
{
return Ok(new { Count = PageViewEventHandler.Count });
}
}
运转起来之后,访问几次接口,看上面的接口返回 Count 能否会增加,正常的话每访问一次接口就会增加 1,并发访问问题也不大,由于每个事情都是次第处置的,即便并发访问也没有关系,事情发布之后,在队列里都是次第处置的,这也就是引入事情队列的目的(仿佛上面的原子递增没什么用了...) 假如没看到了增加,稍等一会儿再访问试试,事情处置会迟到,但总会处置,毕竟是异步处置的,有些延迟很正常,而且上面我们还有一个 1s 的延迟

点赞
收藏
评论区
推荐文章
Promise入门
ES6为什么推出PromisePromise是ES6新增的引用类型,让我们的异步逻辑代码更加优雅。异步就相当于你会了影分身,本来你只能同一时间做一个事情,但是当你有了分身之后,你可以和他同一时间做不同的事情。所以,异步增加了我们事情完成的效率,这也就是我们常说的避免进程等待一个长时间的线程操作,同时执行,减少耗时,增加性能。异步是JavaScript的基
李志宽 李志宽
2年前
只有网安人才知道的事情!
大家好,我是周杰伦。知乎上有一个问题:这里有一个回答,我觉得写得挺不错,分享给大家。1、有一种看起来像天方夜谭一般的攻击方式,叫做旁路攻击,可以不接触指定设备而入侵设备,包括但不限于:获取电子设备运算时辐射的电磁波、看电子设备闪烁的led、记录设备运算的时间等等。2、听起来越天方夜谭的旁路攻击,实现起来越困难,所以这些方法一直不主流。3、你总觉得,泄漏
Wesley13 Wesley13
3年前
Go 中的并发和并行
并行是指同一时间做多件事情,并发是指同一时间具有做多件事情的能力。在很多情况下,并发的效果比并行好,因为操作系统的硬件资源和总资源是很少而且固定的,不能无限去扩张。Go语言设计的时候应该也是推崇这种使用较少资源做更多事情的哲学。并发执行Go语言中可以使用系统功能设置可以使用的物理处理器(核),如果设置为1的话,所有的协程会在一个核上
Stella981 Stella981
3年前
Jetty源码导读一:启动过程
启动过程总体流程ServerThreadPool、HandlerConnectorServer启动过程,主要做了以下的事情:检查,如果ErrorHandler没有,则创建一个注册和启动关闭钩子启动除了Connector的其他所有组件ThreadPool
Stella981 Stella981
3年前
OSChina 周一乱弹 ——人生有那些难忘的事情
Osc乱弹歌单(2017)请戳(这里(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fmusic.163.com%2F%23%2Fplaylist%3Fid%3D626314321))【今日歌曲】@莱布妮子(https://my.oschina.net/u/36
Wesley13 Wesley13
3年前
DOIS大会参会总结和思考
上周去参加DOIS(DevOpsInternationalSummit,缩写:DOIS)会议。除了自己的分享外,也看了一些其他公司当前在做的事情,谈谈个人的看法:一、对于DevOps的理解目前来看大家都在推行DevOps相关的事情,不同的公司,不同的阶段,不同的行业,都会从不同的维度去入手。1、ThoughtWorks林冰玉微服务
Wesley13 Wesley13
3年前
2017——关于坚持了一年的事情
!(https://static.oschina.net/uploads/space/2017/1228/115741_PoOw_1859679.png)其实一直想写一篇,关于坚持一件事的心得,但看惯了网上大v的方法论,以及遇到各种方法的局限性之后,我便不敢妄下结论进行自行分析,以免使用不当,带偏了一批听信我的人,而这份信任异常难得,被辜负十分可惜。
Wesley13 Wesley13
3年前
Java日志正确使用姿势
前言关于日志,在大家的印象中都是比较简单的,只须引入了相关依赖包,剩下的事情就是在项目中“尽情”的打印我们需要的信息了。但是往往越简单的东西越容易让我们忽视,从而导致一些不该有的bug发生,作为一名严谨的程序员,怎么能让这种事情发生呢?所以下面我们就来了解一下关于日志的那些正确使用姿势。正文日志规范
Wesley13 Wesley13
3年前
IOS关于SELF点的一些事情
主要参考文章:http://blog.sina.com.cn/s/blog\_a263f0c601010qj9.html橙色字体为自己标注的的内容,方便加深印象IPHONE开发SELF的用法(https://www.oschina.net/action/GoToLink?ur
从iOS App启动速度看如何为基础性能保驾护航 | 京东物流技术团队
启动是App给用户的第一印象,一款App的启动速度,不单单是用户体验的事情,往往还决定了它能否获取更多的用户。所以到了一定阶段App的启动优化是必须要做的事情。