闲来无聊在我的Biwen.QuickApi中实现一下极简的事件总线,其实代码还是蛮简单的,对于初学者可能有些帮助 就贴出来,有什么不足的地方也欢迎板砖交流~

首先定义一个事件约定的空接口

    public interface IEvent{}

然后定义事件订阅者接口

public interface IEventSubscriber<T> where T : IEvent
{
Task HandleAsync(T @event, CancellationToken ct);
/// <summary>
/// 执行排序
/// </summary>
int Order { get; } /// <summary>
/// 如果发生错误是否抛出异常,将阻塞后续Handler
/// </summary>
bool ThrowIfError { get; }
}
public abstract class EventSubscriber<T> : IEventSubscriber<T> where T : IEvent
{
public abstract Task HandleAsync(T @event, CancellationToken ct);
public virtual int Order => 0;
/// <summary>
/// 默认不抛出异常
/// </summary>
public virtual bool ThrowIfError => false;
}

接着就是发布者


internal class Publisher(IServiceProvider serviceProvider)
{
public async Task PublishAsync<T>(T @event, CancellationToken ct) where T : IEvent
{
var handlers = serviceProvider.GetServices<IEventSubscriber<T>>();
if (handlers is null) return;
foreach (var handler in handlers.OrderBy(x => x.Order))
{
try
{
await handler.HandleAsync(@event, ct);
}
catch
{
if (handler.ThrowIfError)
{
throw;
}
//todo:
}
}
}
}

到此发布订阅的基本代码也就写完了.接下来就是注册发布者和所有的订阅者了

核心代码如下:

        static readonly Type InterfaceEventSubscriber = typeof(IEventSubscriber<>);
static readonly object _lock = new();//锁
static bool IsToGenericInterface(this Type type, Type baseInterface)
{
if (type == null) return false;
if (baseInterface == null) return false;
return type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == baseInterface);
}
static IEnumerable<Type> _eventHanlers = null!;
static IEnumerable<Type> EventHandlers
{
get
{
lock (_lock)
return _eventHanlers ??= ASS.InAllRequiredAssemblies.Where(x =>
!x.IsAbstract && x.IsPublic && x.IsClass && x.IsToGenericInterface(InterfaceEventSubscriber));
}
}
//注册EventSubscribers
foreach (var handlerType in EventHandlers)
{
var baseType = handlerType.GetInterfaces().First(x => x.IsGenericType && x.GetGenericTypeDefinition() == InterfaceEventSubscriber);
services.AddScoped(baseType, handlerType);
}
//注册Publisher
services.AddScoped<Publisher>();

至此发布订阅的代码也就完成了!

现在我们将发布订阅封装到QuickApi中使用:


internal interface IPublisher
{
/// <summary>
/// Event Publish
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="event">Event</param>
/// <returns></returns>
Task PublishAsync<T>(T @event, CancellationToken cancellationToken) where T : IEvent;
}

然后BaseQuickApi实现IPublisher接口


internal interface IQuickApi<Req, Rsp> : IHandlerBuilder, IQuickApiMiddlewareHandler, IAntiforgeryApi, IPublisher
{
ValueTask<Rsp> ExecuteAsync(Req request);
} // BaseQuickApi.PublishAsync
public virtual async Task PublishAsync<T>(T @event, CancellationToken cancellationToken = default) where T : IEvent
{
using var scope = ServiceRegistration.ServiceProvider.CreateScope();
var publisher = scope.ServiceProvider.GetRequiredService<Publisher>();
await publisher.PublishAsync(@event, cancellationToken);
}

至此功能完成,接下来我们测试一下:


using Biwen.QuickApi.Events;
using Microsoft.AspNetCore.Mvc; namespace Biwen.QuickApi.DemoWeb.Apis
{
public class MyEvent : BaseRequest<MyEvent>,IEvent
{
[FromQuery]
public string? Message { get; set; }
} public class MyEventHandler : EventSubscriber<MyEvent>
{
private readonly ILogger<MyEventHandler> _logger;
public MyEventHandler(ILogger<MyEventHandler> logger)
{
_logger = logger;
} public override Task HandleAsync(MyEvent @event, CancellationToken ct)
{
_logger.LogInformation($"msg 2 : {@event.Message}");
return Task.CompletedTask;
}
} /// <summary>
/// 更早执行的Handler
/// </summary>
public class MyEventHandler2 : EventSubscriber<MyEvent>
{
private readonly ILogger<MyEventHandler2> _logger;
public MyEventHandler2(ILogger<MyEventHandler2> logger)
{
_logger = logger;
} public override Task HandleAsync(MyEvent @event, CancellationToken ct)
{
_logger.LogInformation($"msg 1 : {@event.Message}");
return Task.CompletedTask;
} public override int Order => -1; } /// <summary>
/// 抛出异常的Handler
/// </summary>
public class MyEventHandler3 : EventSubscriber<MyEvent>
{
private readonly ILogger<MyEventHandler3> _logger;
public MyEventHandler3(ILogger<MyEventHandler3> logger)
{
_logger = logger;
} public override Task HandleAsync(MyEvent @event, CancellationToken ct)
{
throw new Exception("error");
} public override int Order => -2; public override bool ThrowIfError => false; } [QuickApi("event")]
public class EventApi : BaseQuickApi<MyEvent>
{
public override async ValueTask<IResultResponse> ExecuteAsync(MyEvent request)
{
//publish
await PublishAsync(request);
return IResultResponse.Content("send event");
}
}
}

最后我们运行项目测试一下功能:

curl -X 'GET' \
'http://localhost:5101/quick/event?Message=hello%20world' \
-H 'accept: */*'

源代码我发布到了GitHub,欢迎star! https://github.com/vipwan/Biwen.QuickApi

在Biwen.QuickApi中整合一个极简的发布订阅(事件总线)的更多相关文章

  1. Android 使用RxJava实现一个发布/订阅事件总线

    1.简单介绍 1.1.发布/订阅事件主要用于网络请求的回调. 事件总线可以使Android各组件之间的通信变得简单,而且可以解耦. 其实RxJava实现事件总线和EventBus比较类似,他们都依据与 ...

  2. RELabel : 一个极简的正则表达式匹配和展示框架

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  3. Spring Boot(5)一个极简且完整的后台框架

    https://blog.csdn.net/daleiwang/article/details/75007588 Spring Boot(5)一个极简且完整的后台框架 2017年07月12日 11:3 ...

  4. 借助腾讯云的云函数实现一个极简的API网关

    借助腾讯云的云函数实现一个极简的API网关 Intro 微信小程序的域名需要备案,但是没有大陆的服务器,而且觉得备案有些繁琐,起初做的小程序都有点想要放弃了,后来了解到腾讯云的云函数,于是利用腾讯云的 ...

  5. Vue.js 入门:从零开始做一个极简 To-Do 应用

    Vue.js 入门:从零开始做一个极简 To-Do 应用 写作时间:2019-12-10版本信息:Vue.js 2.6.10官网文档:https://cn.vuejs.org/ 前言  学习 Vue ...

  6. 【转】手摸手,带你用vue撸后台 系列四(vueAdmin 一个极简的后台基础模板)

    前言 做这个 vueAdmin-template 的主要原因是: vue-element-admin 这个项目的初衷是一个vue的管理后台集成方案,把平时用到的一些组件或者经验分享给大家,同时它也在不 ...

  7. 我喜欢的两个js类实现方式 现在再加上一个 极简主义法

    闭包实现 变量是不会变的:) var myApplication = function(){ var name = 'Yuri'; var age = '34'; var status = 'sing ...

  8. 使用apache-cxf-2.2.10来制作一个极简版WebService程序

    原想拿最新版cxf来制作的,无奈Apache的zip包总下不下来,国内的apache-cxf-2.2.10却一蹴而就,也就用了这个版本.下载地址是:http://pan.baidu.com/s/1td ...

  9. 一个极简的守护进程Bash脚本

    由于最近写的Node.js程序因为一些Bug,会出现一些自动退出的问题,所以需要在它退出的时候及时发现,并重新启动 于是查阅了些资料,写了一个Bash的程序,功能十分简单,就是每隔3s判断一次处在60 ...

  10. 飘逸的python - 实现一个极简的优先队列

    一个队列至少满足2个方法,put和get. 借助最小堆来实现. 这里按"值越大优先级越高"的顺序. #coding=utf-8 from heapq import heappush ...

随机推荐

  1. 白话分解入门操作系统到 Java

    一.完成一个任务需要什么? 时间 + 资源 + 处理能力 时间就是时间. 资源就是资源. 处理能力就是能够利用时间和资源完成任务的主体. 二.关于操作系统 处理能力就是cpu. 资源就是存储. 时间就 ...

  2. 资讯速递 | ArkUI-X 预览版已正式开源!

     OpenHarmony项目群技术指导委员会(以下简称"TSC")-跨平台应用开发框架TSG所孵化项目 -- ArkUI-X,近期已正式开源 ,开发者基于一套主代码,就可以将在Op ...

  3. Qt调用系统DLL,判断网络连接状态

    *: Win32 网络连接 dll 文件名叫:wininet.dll,位置在 C:\WINDOWS\system32 目录下,将 其拷贝到项目工程下. #include <QLibrary> ...

  4. HarmonyOS实践之应用状态变量共享

      平时在开发的过程中,我们会在应用中共享数据,在不同的页面间共享信息.虽然常用的共享信息,也可以通过不同页面中组件间信息共享的方式,但有时使用应用级别的状态管理会让开发工作变得简单. 根据不同的使用 ...

  5. setTimeout(fn, 0) // it works - JavaScript 事件循环 动画演示

    在前端代码中很经常看到使用 setTimeout(fn, 0),如下面代码所示,乍一看很多余,但是移除了可能会出现一些奇奇怪怪的问题.要解释这个就需要理解 事件循环(Event Loop),下面会通过 ...

  6. 离线语音识别,vosk,离线流式实时静音噪声监测,支持多语言开发python c++ c# java等

    #!/usr/bin/env python3 from vosk import Model, KaldiRecognizer, SetLogLevel import sys import os imp ...

  7. 牛客网-SQL专项训练23

    ①假设创建新用户nkw,现在想对于任何IP的连接,仅拥有user数据库里面的select和insert权限,则列表语句中能够实现这一要求的语句是(B) 解析: 考察知识点-数据库授权命令: GRANT ...

  8. 亿图version 9.2安装教程

    记录一下自己安装亿图9.2版本的安装过程~ 先获取安装资料: 百度网盘链接: 链接:https://pan.baidu.com/s/1zJDcF9Y0Xy2CvD4mG_oOfQ?pwd=pqy9 提 ...

  9. 全面提升易用性:OpenClusterManagement 0.7 版本发布

    ​简介:千呼万唤始出来,三月末 OpenClusterManagement 社区正式发布了 v0.7 版本.在新的版本有一系列新的功能特性欢迎感兴趣的读者体验探索,同时在这个版本中社区维护者对目前已有 ...

  10. 喜马拉雅 Apache RocketMQ 消息治理实践

    ​简介:本文通过喜马拉雅的RocketMQ治理实践分享,让大家了解使用消息中间件过程中可能遇到的问题,避免实战中踩坑. 作者:曹融,来自喜马拉雅,从事微服务和消息相关中间件开发. ​ 本文通过喜马拉雅 ...