色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Golang事件總線機制的實現

Linux愛好者 ? 來源:wangbjun.site ? 作者:wangbjun.site ? 2022-07-01 16:02 ? 次閱讀

【導讀】本文介紹了事件總線實現。

最近在學習開源項目Grafana的代碼,發現作者實現了一個事件總線的機制,在項目里面大量應用,效果也非常好,代碼也比較簡單,介紹給大家看看。

源碼文件地址:grafana/bus.go at main · grafana/grafana · GitHub

1.注冊和調用

在這個項目里面隨處可見這種寫法:

funcValidateOrgAlert(c*models.ReqContext){
id:=c.ParamsInt64(":alertId")

query:=models.GetAlertByIdQuery{Id:id}

iferr:=bus.Dispatch(&query);err!=nil{
c.JsonApiErr(404,"Alertnotfound",nil)
return
}

ifc.OrgId!=query.Result.OrgId{
c.JsonApiErr(403,"Youarenotallowedtoedit/viewalert",nil)
return
}
}

關鍵是bus.Dispatch(&query)這段代碼,它的參數是一個結構體GetAlertByIdQuery,內容如下:

typeGetAlertByIdQuerystruct{
Idint64
Result*Alert
}

根據名字可以看出這個方法就是通過Id去查詢Alert,其中Alert結構體就是結果對象,這里就不貼出來了。

通過查看源碼可以得知,Dispatch背后是調用了GetAlertById這個方法,然后把結果賦值到query參數的Result中返回。

funcGetAlertById(query*models.GetAlertByIdQuery)error{
alert:=models.Alert{}
has,err:=x.ID(query.Id).Get(&alert)
if!has{
returnfmt.Errorf("couldnotfindalert")
}
iferr!=nil{
returnerr
}
query.Result=&alert
returnnil
}

問題來了,這是怎么實現的呢?Dispatch到底做了哪些操作?這樣做有什么好處?

下面我來一一解答:

首先,在Dispatch之前,你需要先注冊這個方法,也就是調用AddHandler,在這個項目里面可以看到init函數里面有大量這樣的代碼:

funcinit(){
bus.AddHandler("sql",SaveAlerts)
bus.AddHandler("sql",HandleAlertsQuery)
bus.AddHandler("sql",GetAlertById)
...
}

其實這個方法的邏輯也很簡單,所謂注冊也就是把通過一個map把函數名和對應的函數做一個映射關系保存起來,當我們Dispatch的時候其實就是通過參數名查找之前注冊過的函數,然后通過反射調用該函數。

Bus結構體里面有幾個map成員,在這個項目里面作者定義了3種不同類型的handler,一種是普通的handler,也就是剛才展示的那種,第二種是帶上下文的handler,還有一種則是事件訂閱用到的handler,我們給一個事件注冊多個監聽者,當事件觸發的時候會依次調用多個監聽函數,其實就是一個觀察者模式。

//InProcBusdefinesthebusstructure
typeInProcBusstruct{
handlersmap[string]HandlerFunc
handlersWithCtxmap[string]HandlerFunc
listenersmap[string][]HandlerFunc
txMngTransactionManager
}

下面就看看具體的源碼,AddHandler方法內容如下:

func(b*InProcBus)AddHandler(handlerHandlerFunc){
handlerType:=reflect.TypeOf(handler)
queryTypeName:=handlerType.In(0).Elem().Name()//獲取函數第一個參數的名稱,在上面例子里面就是GetAlertByIdQuery
b.handlers[queryTypeName]=handler
}

Dispatch方法的源碼如下:

func(b*InProcBus)Dispatch(msgMsg)error{
varmsgName=reflect.TypeOf(msg).Elem().Name()

withCtx:=true
handler:=b.handlersWithCtx[msgName]//根據參數名查找注冊過的函數,先查找帶Ctx的handler
ifhandler==nil{
withCtx=false
handler=b.handlers[msgName]
ifhandler==nil{
returnErrHandlerNotFound
}
}
varparams=[]reflect.Value{}
ifwithCtx{
//如果查找到的handler是帶Ctx的就給個默認的Background的Ctx
params=append(params,reflect.ValueOf(context.Background()))
}
params=append(params,reflect.ValueOf(msg))

ret:=reflect.ValueOf(handler).Call(params)//通過反射機制調用函數
err:=ret[0].Interface()
iferr==nil{
returnnil
}
returnerr.(error)
}

對于AddHandlerCtxDispatchCtx這個2個方法基本上是一樣的,只不過多了一個上下文參數,可以拿來做超時控制或者其它用途。

2.訂閱和發布

除此之外,還有2個方法AddEventListenerPublish,即事件的訂閱和發布。

func(b*InProcBus)AddEventListener(handlerHandlerFunc){
handlerType:=reflect.TypeOf(handler)
eventName:=handlerType.In(0).Elem().Name()
_,exists:=b.listeners[eventName]
if!exists{
b.listeners[eventName]=make([]HandlerFunc,0)
}
b.listeners[eventName]=append(b.listeners[eventName],handler)
}

查看源碼可以得知,可以給一個事件注冊多個handler函數,而Publish的時候則是依次調用注冊的函數,邏輯也不復雜。

func(b*InProcBus)Publish(msgMsg)error{
varmsgName=reflect.TypeOf(msg).Elem().Name()
varlisteners=b.listeners[msgName]

varparams=make([]reflect.Value,1)
params[0]=reflect.ValueOf(msg)

for_,listenerHandler:=rangelisteners{
ret:=reflect.ValueOf(listenerHandler).Call(params)
e:=ret[0].Interface()
ife!=nil{
err,ok:=e.(error)
ifok{
returnerr
}
returnfmt.Errorf("expectedlistenertoreturnanerror,got'%T'",e)
}
}
returnnil
}

這里面有一點不好,所有訂閱函數的調用是順序的,并沒有使用協程,所以如果注冊了很多個函數,這樣效率也不高啊。

3.好處

可能有人會好奇,為什么明明可以直接調用函數就行,為啥非得繞個彎子,整這么復雜?

況且,每次調用都得使用反射機制,性能也不行。

我覺得主要有以下幾點:

1.這種寫法邏輯清晰,解耦

2.方便單元測試

3.性能不是最大考量,雖然說反射會降低性能

原文標題:Golang 事件系統 Event Bus

文章出處:【微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 總線
    +關注

    關注

    10

    文章

    2878

    瀏覽量

    88052
  • 開源
    +關注

    關注

    3

    文章

    3309

    瀏覽量

    42471
  • 代碼
    +關注

    關注

    30

    文章

    4779

    瀏覽量

    68525

原文標題:Golang 事件系統 Event Bus

文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    CAN總線與LIN總線的區別

    不同的數據傳輸速率,從最低的10 kbps到最高的1 Mbps。 拓撲結構: 通常采用雙絞線結構,支持多點通信。 錯誤檢測: 具有強大的錯誤檢測機制,包括位錯誤、幀錯誤等。 仲裁機制: 使用基于優先級的非破壞性總線仲裁
    的頭像 發表于 11-12 10:13 ?1080次閱讀

    如何使用Arduino實現CAN總線通信

    開源硬件平臺,通過添加CAN總線模塊,也可以實現CAN通信。 硬件準備 Arduino開發板 :可以選擇Arduino Uno、Mega等型號。 CAN總線模塊 :如MCP2515或MCP2562,這些模塊
    的頭像 發表于 11-12 10:09 ?736次閱讀

    Golang配置代理方法

    由于一些客觀原因的存在,我們開發 Golang 項目的過程總會碰到無法下載某些依賴包的問題。這不是一個小問題,因為你的工作會被打斷,即便你使用各種神通解決了問題,很可能這時你的線程已經切換到其他的事情上了(痛恨思路被打斷!)。所以最好是一開始我們就重視這個問題,并一勞永逸的解決它。
    的頭像 發表于 11-11 11:17 ?209次閱讀
    <b class='flag-5'>Golang</b>配置代理方法

    前端總線是屬于什么總線

    總線的介紹: 一、前端總線的定義與功能 前端總線是計算機系統內部的一種接口標準,它負責連接CPU與主板上的內存、顯卡等關鍵組件,實現數據的高速傳輸。作為系統
    的頭像 發表于 10-10 17:11 ?502次閱讀

    CAN總線控制器的工作原理

    CAN(Controller Area Network,控制器局域網)總線控制器的工作原理涉及多個方面,包括消息傳輸、沖突檢測與解決、總線仲裁等關鍵機制。以下是對CAN總線控制器工作原
    的頭像 發表于 09-30 11:33 ?818次閱讀

    EN?Power?Bus二總線接口轉接485方案芯片-485接口芯片

    EN20F18 是采用低壓直流供電總線通訊技術設計的一款通訊接口芯片,是英銳恩EN Power Bus二總線接口轉接485方案芯片,用于兩總線終端產品,利用總線電壓識別和電流回饋
    發表于 09-29 16:04

    干貨分享 | TSMaster—LIN 喚醒與休眠機制

    在汽車總線中常見的喚醒方式有硬線喚醒、網絡喚醒和特定信號喚醒,而LIN總線則是通過休眠幀與喚醒電平來實現的,本文將介紹LIN的喚醒與休眠機制。本文關鍵詞:LIN網絡管理,休眠,喚醒
    的頭像 發表于 09-25 08:03 ?1569次閱讀
    干貨分享 | TSMaster—LIN 喚醒與休眠<b class='flag-5'>機制</b>

    【米爾NXP i.MX 93開發板試用評測】4、使用golang搭建Modbus 服務器

    負責處理來自客戶端(通常稱為Modbus客戶端或從站)的請求,并根據請求提供相應的數據或執行操作。 快速開發modbus服務器 可以使用golang快速部署一個modbus服務器。我們先在開發板上安裝
    發表于 09-21 22:51

    異步總線中傳送操作的控制機制

    據傳輸過程中存在一定的延遲,因此需要一種有效的控制機制來保證數據傳輸的準確性和可靠性。 異步總線概述 1.1 異步總線的定義 異步總線是一種在計算機系統中用于數據傳輸的通信方式,其特點
    的頭像 發表于 07-23 09:17 ?634次閱讀

    求助,是否有自帶timeout機制的EEPROM?

    請問下各位大佬們是否有自帶timeout機制的EEPROM? 如果由于主設備異常復位導致總線死鎖,是否有能檢測到SDA低于一段時間后,會將自己reset的EEPROM;(主設備沒有解決總線死鎖的手段) 我找了一圈沒有找到,請問下
    發表于 07-05 06:14

    如何快速實現CAN總線故障定位?

    快速實現CAN總線故障定位是汽車電子和工業自動化領域中的一個重要課題。CAN總線作為一種重要的通信網絡,其穩定性和可靠性對于整個系統的運行至關重要。
    的頭像 發表于 04-09 15:46 ?840次閱讀

    Golang為何舍棄三元運算符

    golang中不存在?:運算符的原因是因為語言設計者已經預見到三元運算符經常被用來構建一些極其復雜的表達式。雖然使用if進行替代會讓代碼顯得更長,但這毫無疑問可讀性更強。
    的頭像 發表于 04-03 15:13 ?693次閱讀

    CAN總線的可靠通信是依靠什么機制實現的?

    CAN總線采取多種技術措施來消除外界干擾,確??煽客ㄐ拧?/div>
    的頭像 發表于 01-30 09:50 ?1676次閱讀

    如何使用Golang連接MySQL

    首先我們來看如何使用Golang連接MySQL。
    的頭像 發表于 01-08 09:42 ?3347次閱讀
    如何使用<b class='flag-5'>Golang</b>連接MySQL

    IIC總線的FPGA實現說明

    DE2_TV中,有關于寄存器的配置的部分,采用的方法是通過IIC的功能,這里對IIC總線的FPGA實現做個說明。
    的頭像 發表于 01-05 10:16 ?1052次閱讀
    IIC<b class='flag-5'>總線</b>的FPGA<b class='flag-5'>實現</b>說明
    主站蜘蛛池模板: 久久精品视频16| 国产精品私人玩物在线观看| 亚洲一区高清| 日韩综合网| 美女扣逼软件| 含羞草传媒在线观看| 大中国免费视频大全在线观看| 在线看片福利无码网址| 少妇性饥渴BBBBBBBBB| 久久香蕉电影| 国产精品青青青高清在线密亚| 999精品在线| 夜色福利院在线观看免费| 无码国产色欲XXXX视频| 青青草原成人| GAY东北澡堂激情2022| 伊人影院综合| 久久4k岛国高清一区二区| 亚洲一区免费香蕉在线| 日本免费无码A专区在线观看| 麻豆国产精品AV色拍综合| 国产色青青视频在线观看 | 亚洲精品无码AV中文字幕蜜桃| 日本午夜看x费免| 亚洲野狼综合网站| 外女思春台湾三级| 欧美性猛交xxxxxxxx软件| 国产系列在线亚洲视频| 凹凸精品视频分类视频| 在线高清无码欧美久章草| 精品水蜜桃久久久久久久| 狠狠久久免费视频在线| FREE17一18外女破| 伊人久久伊人| 亚洲精品视频在线播放| 亚洲AV无码国产精品色午夜情| 午夜一区欧美二区高清三区| 天堂Av亚洲欧美日韩国产综合| 我的家庭女教师| 亚洲国产系列一区二区三区| 亚洲欧美一区二区三区导航|