網站首頁 編程語言 正文
事件總線
關于事件總線
ABP 中,為了方便進程間通訊,給開發者提供了一個叫?事件總線
?的功能,事件總線分為?本地事件總線
、分布式事件總線
,本篇文章講的是?本地事件總線
,系列教程中暫時不考慮講解?分布式事件總線
。
事件總線
?需要使用?Volo.Abp.EventBus
?庫,ABP 包中自帶,不需要額外引入。
事件總線是通過 訂閱-發布 形式使用的,某一方只需要按照格式推送事件,而不需要關注是誰接收了事件和如何處理事件。
你可以參考官方文檔:https://docs.abp.io/zh-Hans/abp/latest/Local-Event-Bus
為什么需要這個東西
首先列舉一下,你工作開發的項目中,編寫 控制器時,是不是有這幾種代碼。
// 記錄日志 1 Task.Run(()=> { _apiLog.Info($"xxxxxxxx"); });
// 記錄日志 2 catch(Exception ex) { _apiLog.Error(ex); }
// 記錄日志 3 _apiLog.Info($"登陸信息:用戶 [{userName}({clientAdrress})]\);
筆者認為,改善的上述問的方法之一是將函數的功能跟記錄日志分開,函數執行任務時,只需要把狀態和信息通過事件總線推送,而不需要了關注應該如何處理這些內容。
另外,還有當函數執行某些步驟時,產生了事件,開發者喜歡?new Thread
?一個新的線程去執行別的任務,或者?Task.Run
。
其實,通過事件總線,我們更加好地隔離代碼,遵從?單一職責原則
?。當然還有很多方面值得使用事件總線,這里我們就不再扯淡了。
前面,我們編寫了全局異常攔截器,還有日志組件,這一篇我們將通過事件總線,將 Web 程序的一些部件組合起來。
事件總線創建過程
訂閱事件
創建一個服務來訂閱事件,當程序中發生某種事件時,此服務將被調用。
事件服務必須繼承?ILocalEventHandler
?接口,并實現以下函數:
Task HandleEventAsync(TEvent eventData);
一個系統中,事件服務可以有多個,每個服務的?TEvent
?類型不能相同,因為?TEvent
?的類型是調用服務的標識。當發生?TEvent
?事件后,系統通過?TEvent
?去找到這個服務。
事件服務創建完畢后,需要加入到依賴注入中,你可以多繼承一個?ITransientDependency
?接口,然后統一掃描程序集加入到 依賴注入容器中(第三篇提到過)。
事件
即上面提到的?TEvent
。
假設有一個系統中所有的事件服務都放到一個容器中,發布者只能傳遞一個事件,而不能指定誰來提供響應服務。
容器是通過?TEvent
?來查找服務的。
事件就是一個模型類,也可以使用?int
或者?string
?等簡單類型(請不要用簡單類型做事件),用于傳遞信息。
一般使用?Event
?做后綴。
發布事件
如果需要發布一個事件,只需要注入?ILocalEventBus
?即可。
private readonly ILocalEventBus _localEventBus; public MyService(ILocalEventBus localEventBus) { _localEventBus = localEventBus; }
然后發布事件:
await _localEventBus.PublishAsync( new TEvent { ... ... } );
全局異常加入事件總線功能
創建事件
在?AbpBase.Web
?中,創建一個?Handlers
?目錄,再在?Handlers
?目錄下,創建?HandlerEvents
?目錄。
然后在?HandlerEvents
?目錄,創建一個?CustomerExceptionEvent.cs
?文件。
CustomerExceptionEvent
?作為一個異常事件,用于傳遞異常的信息,而不僅僅是將?Exception ex
?記錄就了事。
其文件內容如下:
using System; using System.Collections.Generic; using System.Reflection; using System.Text; namespace AbpBase.Application.Handlers.HandlerEvents { ////// 全局異常推送事件 /// public class CustomerExceptionEvent { ////// 只記錄異常 /// /// public CustomerExceptionEvent(Exception ex) { Exception = ex; } ////// 此異常發生時,用戶請求的路由地址 /// /// /// public CustomerExceptionEvent(Exception ex, string actionRoute) { Exception = ex; Action = actionRoute; } ////// 此異常發生在哪個類型的方法中 /// /// /// public CustomerExceptionEvent(Exception ex, MethodBase method) { Exception = ex; MethodInfo = (MethodInfo)method; } ////// 記錄異常信息 /// /// /// /// public CustomerExceptionEvent(Exception ex, string actionRoute, MethodBase method) { Exception = ex; Action = actionRoute; MethodInfo = (MethodInfo)method; } ////// 當前出現位置 /// public MethodInfo MethodInfo { get; private set; } ////// ////// MethodInfo = (MethodInfo)MethodBase.GetCurrentMethod(); ///
////// 發生異常的 Action /// public string Action { get; private set; } ////// 具體異常 /// public Exception Exception { get; private set; } } }
訂閱事件
訂閱事件,即將其定義為事件的響應者、服務提供者。
當異常發生后,異常的位置,推送異常信息,那么誰來處理這些信息呢?是訂閱者。
這里我們定義一個異常日志處理類,來處理程序推送的異常信息。
在?AbpBase.Web
?項目的?Handlers
?目錄中,添加一個?CustomerExceptionHandler
?類,繼承:
public class CustomerExceptionHandler : ILocalEventHandler, ITransientDependency
服務要處理事件,必須繼承?ILocalEventHandler
,而?ITransientDependency
?是為了此服務可以可以自動注入到容器中。
其文件內容如下:
using AbpBase.Application.Handlers.HandlerEvents; using Serilog; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; namespace AbpBase.Application.Handlers { ////// 全局異常記錄日志 /// public class CustomerExceptionHandler : ILocalEventHandler, ITransientDependency { private readonly ILogger _ILogger; public CustomerExceptionHandler(ILogger logger) { _ILogger = logger; } public async Task HandleEventAsync(CustomerExceptionEvent eventData) { StringBuilder stringBuilder = new StringBuilder(256); stringBuilder.AppendLine(); stringBuilder.Append("Action: "); stringBuilder.AppendLine(eventData.Action); if (eventData.MethodInfo != null) { stringBuilder.Append("Class-Method: "); stringBuilder.Append(eventData.MethodInfo?.DeclaringType.FullName); stringBuilder.AppendLine(eventData.MethodInfo?.Name); } stringBuilder.Append("Source: "); stringBuilder.AppendLine(eventData.Exception.Source); stringBuilder.Append("TargetSite: "); stringBuilder.AppendLine(eventData.Exception.TargetSite?.ToString()); stringBuilder.Append("InnerException: "); stringBuilder.AppendLine(eventData.Exception.InnerException?.ToString()); stringBuilder.Append("Message: "); stringBuilder.AppendLine(eventData.Exception.Message); stringBuilder.Append("HelpLink: "); stringBuilder.AppendLine(eventData.Exception.HelpLink); _ILogger.Fatal(stringBuilder.ToString()); await Task.CompletedTask; } } }
這樣寫,記錄的日志可以有很好的層次結構。
發布事件
定義了事件的格式和定義服務來訂閱事件后,我們來創建一個發布者。
我們修改一下?WebGlobalExceptionFilter
。
增加依賴注入:
private readonly ILocalEventBus _localEventBus; public WebGlobalExceptionFilter(ILocalEventBus localEventBus) { _localEventBus = localEventBus; }
發布事件:
public async Task OnExceptionAsync(ExceptionContext context) { if (!context.ExceptionHandled) { await _localEventBus.PublishAsync(new CustomerExceptionEvent(context.Exception, context.ActionDescriptor?.DisplayName)); ... ...
測試
創建一個 Action :
[HttpGet("/T4")] public string MyWebApi4() { int a = 1; int b = 0; int c = a / b; return c.ToString(); }
然后訪問?https://localhost:5001/T4?,會發現請求后報錯
在?AbpBase.Web
?的?Logs
?目錄中,打開?-Fatal.txt
?文件。
可以看到:
2020-09-16 18:49:27.750 +08:00 [FTL] Action: ApbBase.HttpApi.Controllers.TestController.MyWebApi4 (ApbBase.HttpApi) Source: ApbBase.HttpApi TargetSite: System.String MyWebApi4() InnerException: Message: Attempted to divide by zero. HelpLink:
除了異常信息外,我們還可以很方便的知道異常發生在?TestController.MyWebApi4
?這個位置。
記錄事件
如果在普通方法里面出現異常,我們這樣這樣記錄:
catch (Exception ex) { ... new CustomerExceptionEvent(ex, MethodBase.GetCurrentMethod()); ... }
MethodBase.GetCurrentMethod()
?可以獲取當前正在運行的方法,獲得信息后將此參數傳遞給異常記錄服務,會自動解析出具體是哪個地方發生異常。
由于目前 Web 程序中還沒有編寫什么服務,因此我們先結合到異常日志功能中,后面編寫服務時,會再次用到事件總線。
完整代碼參考:https://github.com/whuanle/AbpBaseStruct/tree/master/src/4/AbpBase
源碼地址:https://github.com/whuanle/AbpBaseStruct
原文鏈接:https://www.cnblogs.com/whuanle/p/13679991.html
相關推薦
- 2022-12-12 python?使用?with?open()?as?讀寫文件的操作方法_python
- 2022-04-09 Maven 編譯提示:spring-boot-maven-plugin:2.1.9.RELEASE
- 2022-12-15 如何使用Python最小二乘法擬合曲線代碼詳解_python
- 2022-03-20 C語言輸出任意邊長的菱形(用c語言輸出一個菱形)
- 2022-03-17 詳解Docker在哪里保存日志文件_docker
- 2022-11-06 Git?Commitizen提交規范化自動生成changelog文件_相關技巧
- 2022-09-09 Python?cv.Canny()方法參數與使用方法_python
- 2022-09-17 python?Pandas之DataFrame索引及選取數據_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支