概述
- 本文主要描述一個超精簡的訂閱發布事件組件--SPEvent。
- 在實際開發過程中,一個事件的產生會產生很多業務的執行,或者多個事件都要執行同一個業務的執行。在這種場景下有兩種做法:
- 將同一個事件的業務放在一個函數中,然后事件產生的時候執行對應的函數。
- 某個業務需要哪個事件,它自己監聽對應事件并執行。
- 顯然,第一種策略會將業務與業務之間耦合在一起,對后期維護是非常痛苦的;第二種顯然會更加有優勢,不同業務完全解耦,獨立完成事件的業務。
- 第二種策略的方式,實際在軟件架構中經常看到,比如MQTT的通信(通過訂閱對應的topic去監聽對應內容)。
- 有了上述的需求,作者做了一個超精簡的訂閱發布事件組件。整個邏輯很簡單。
超精簡的SPEvent組件,實現方法
-
整個訂閱發布事件機制,引入兩個東西:EventHub和EventNode。
- EventHub:每一個事件類型都為一個EventHub,然后掛在HubList中。
- EventNode:每一個訂閱事件的業務為一個EventNode,然后掛在對應的EventHub中。
-
整個訂閱發布事件機制圍繞著EventHub和EventNode,特點:
- 資源占用極小,接口操作簡單
- 事件支持動態訂閱,動態注銷。
-
SPEvent采用雙向鏈表進行維護整個訂閱-發布邏輯
- SPEvent一定存在一個EventHubList鏈表來維護事件類型,它默認是沒有任何EventHub節點,
- 訂閱事件流程:當訂閱者訂閱事件之后,如果事件不存在,則申請一個EventHub,并將EventHub掛在到EventHubList鏈表中;然后申請一個EventNode,及將對應EventNode掛在EventNodeList鏈表。
- 發布事件流程:當發布者發布事件時,會從EventHubList中查詢有沒有對應的EventHub,如果EventHub存在,則將事件消息發布給對應EventHub下所有EventNode。
- 注銷事件訂閱流程:當訂閱者注銷已經訂閱的事件,會從EventHubList中查詢有沒有對應的EventHub,如果EventHub存在,則將對應EventNode從EventHub中刪除。
超精簡的SPEvent組件,接口說明:
函數 | 說明 |
---|---|
SPEventInit | 初始化函數 |
SPEventDeinit | 去初始化函數 |
SPEventSubscribe | 訂閱事件函數 |
SPEventUnsubscribe | 注銷訂閱事件函數 |
SPEventPublish | 發布事件消息 |
SPEventClear | 清除事件池 |
RecvtInfoDump | 導出事件池信息 |
超精簡的SPEvent組件,代碼實現
- 整個代碼接口存在3個文件:spevent.c、spevent.h、spevent_def.h。其中:
- spevent_def.h文件說明:定義了屏蔽平臺相關接口的宏和定義了雙向量表操作的宏定義。雙向量表在SPEvent中式至關重要數據結構:
#ifndef__SPEVENT_DEF_H__
#define__SPEVENT_DEF_H__
#include
#include
#include
#include
#include
#include
#include
#defineSPEVENT_INLINEstatic__inline
#defineSPEVENT_EVENT_NAME_LEN16
#ifndefSPEVENT_MALLOC
#defineSPEVENT_MALLOCmalloc
#endif
#ifndefSPEVENT_FREE
#defineSPEVENT_FREEfree
#endif
#ifndefSPEVENT_PRINT
#defineSPEVENT_PRINTprintf
#endif
structSPEventListNode
{
structSPEventListNode*next;
structSPEventListNode*prev;
};
typedefstructSPEventListNodeSPEventList;
SPEVENT_INLINEvoidSPEVENT_LIST_INIT(SPEventList*l)
{
l->next=l->prev=l;
}
SPEVENT_INLINEvoidSPEVENT_LIST_INSERT_AFTER(SPEventList*l,SPEventList*n)
{
l->next->prev=n;
n->next=l->next;
l->next=n;
n->prev=l;
}
SPEVENT_INLINEvoidSPEVENT_LIST_INSERT_BEFORE(SPEventList*l,SPEventList*n)
{
l->prev->next=n;
n->prev=l->prev;
l->prev=n;
n->next=l;
}
SPEVENT_INLINEvoidSPEVENT_LIST_REMOVE(SPEventList*n)
{
n->next->prev=n->prev;
n->prev->next=n->next;
n->next=n->prev=n;
}
SPEVENT_INLINEintSPEVENT_LIST_LEN(constSPEventList*l)
{
intlen=0;
constSPEventList*p=l;
while(p->next!=l){
p=p->next;
len++;
}
returnlen;
}
#defineSPEVENT_CONTAINER_OF(ptr,type,member)
((type*)((char*)(ptr)-(unsignedlong)(&((type*)0)->member)))
#defineSPEVENT_LIST_OBJ_INIT(obj){&(obj),&(obj)}
#defineSPEVENT_LIST_ENTRY(node,type,member)
SPEVENT_CONTAINER_OF(node,type,member)
#defineSPEVENT_LIST_FOR_EACH(pos,head)
for(pos=(head)->next;pos!=(head);pos=pos->next)
#defineSPEVENT_LIST_FOR_EACH_SAFE(pos,n,head)
for(pos=(head)->next,n=pos->next;pos!=(head);
pos=n,n=pos->next)
#defineSPEVENT_LIST_FOR_EACH_ENTRY(pos,head,member)
for(pos=SPEVENT_LIST_ENTRY((head)->next,typeof(*pos),member);
&pos->member!=(head);
pos=SPEVENT_LIST_ENTRY(pos->member.next,typeof(*pos),member))
#defineSPEVENT_LIST_FOR_EACH_ENTRY_SAFE(pos,n,head,member)
for(pos=SPEVENT_LIST_ENTRY((head)->next,typeof(*pos),member),
n=SPEVENT_LIST_ENTRY(pos->member.next,typeof(*pos),member);
&pos->member!=(head);
pos=n,n=SPEVENT_LIST_ENTRY(n->member.next,typeof(*n),member))
#defineSPEVENT_LIST_FIRST_ENTRY(ptr,type,member)
SPEVENT_LIST_ENTRY((ptr)->next,type,member)
#endif
- spevent.c文件說明:SPEvent的接口實現;整個邏輯通過鏈表的嵌套,實現了事件的管理,事件的訂閱,事件的發布。
#include"spevent.h"
staticSPEventListg_hublist={0};
staticSPEventHubNode*SPEventFindHubNode(SPEventList*list,constchar*event)
{
SPEventHubNode*hubNode=NULL;
SPEventList*node=NULL;
SPEVENT_LIST_FOR_EACH(node,list){
hubNode=SPEVENT_LIST_ENTRY(node,SPEventHubNode,hubList);
if(hubNode!=NULL&&strcmp(hubNode->event,event)==0){
returnhubNode;
}
}
returnNULL;
}
staticSPEventEventNode*SPEventFindEventNode(SPEventList*eventList,SPEventHandlehandle)
{
SPEventEventNode*eventNode=NULL;
SPEventList*node=NULL;
SPEVENT_LIST_FOR_EACH(node,eventList){
eventNode=SPEVENT_LIST_ENTRY(node,SPEventEventNode,list);
if(eventNode!=NULL&&handle==eventNode->handle){
returneventNode;
}
}
returnNULL;
}
SPEventEventNode*SPEventSubscribe(constchar*event,SPEventHandlehandle)
{
SPEventHubNode*hubNode=NULL;
SPEventEventNode*eventNode=NULL;
hubNode=SPEventFindHubNode(&g_hublist,event);
if(hubNode==NULL){
hubNode=(SPEventHubNode*)SPEVENT_MALLOC(sizeof(SPEventHubNode));
if(hubNode==NULL){
SPEVENT_PRINT("SPEVENThub(%s)mallocfailedrn",event);
returnNULL;
}
memset(hubNode->event,0,SPEVENT_EVENT_NAME_LEN);
memcpy(hubNode->event,event,strlen(event));
SPEVENT_LIST_INSERT_AFTER(&g_hublist,&(hubNode->hubList));
SPEVENT_LIST_INIT(&(hubNode->eventList));
}
eventNode=(SPEventEventNode*)SPEVENT_MALLOC(sizeof(SPEventEventNode));
if(eventNode==NULL){
SPEVENT_PRINT("SPEVENTevent(%s)mallocfailedrn",event);
returnNULL;
}
eventNode->handle=handle;
SPEVENT_LIST_INSERT_AFTER(&hubNode->eventList,&(eventNode->list));
SPEVENT_PRINT("SPEVENTevent(%s)Subscribesuccessrn",event);
returneventNode;
}
boolSPEventUnsubscribe(constchar*event,SPEventEventNode*node)
{
SPEventHubNode*hubNode=NULL;
SPEventEventNode*eventNode=NULL;
hubNode=SPEventFindHubNode(&g_hublist,event);
if(hubNode==NULL){
SPEVENT_PRINT("SPEVENThub(%s)findfailedrn",event);
returnfalse;
}
eventNode=SPEventFindEventNode(&(hubNode->eventList),node->handle);
if(eventNode==NULL){
SPEVENT_PRINT("SPEVENTevent(%s)findfailedrn",event);
returnfalse;
}
SPEVENT_LIST_REMOVE(&(eventNode->list));
SPEVENT_FREE(eventNode);
eventNode=NULL;
SPEVENT_PRINT("SPEVENTevent(%s)Unsubscribesuccessrn",event);
returntrue;
}
boolSPEventPublish(constchar*event,void*payload)
{
SPEventHubNode*hubNode=NULL;
SPEventEventNode*eventNode=NULL;
SPEventList*node=NULL;
hubNode=SPEventFindHubNode(&g_hublist,event);
if(hubNode==NULL){
SPEVENT_PRINT("SPEVENThub(%s)findfailedrn");
returnfalse;
}
SPEVENT_LIST_FOR_EACH(node,&(hubNode->eventList)){
eventNode=SPEVENT_LIST_ENTRY(node,SPEventEventNode,list);
if(eventNode->handle){
eventNode->handle(event,payload);
}
}
SPEVENT_PRINT("SPEVENTevent(%s)Publishsuccessrn",event);
returntrue;
}
voidSPEventClear(void)
{
SPEventHubNode*hubNode=NULL;
SPEventEventNode*eventNode=NULL;
SPEventList*hubList=NULL;
SPEventList*eventList=NULL;
SPEVENT_LIST_FOR_EACH(hubList,&g_hublist){
hubNode=SPEVENT_LIST_ENTRY(hubList,SPEventHubNode,hubList);
if(hubNode==NULL){
continue;
}
SPEVENT_LIST_FOR_EACH(eventList,&(hubNode->eventList)){
eventNode=SPEVENT_LIST_ENTRY(eventList,SPEventEventNode,list);
if(eventNode==NULL){
continue;
}
SPEVENT_LIST_REMOVE(&(eventNode->list));
SPEVENT_FREE(eventNode);
eventNode=NULL;
}
SPEVENT_LIST_REMOVE(&(hubNode->hubList));
SPEVENT_FREE(hubNode);
hubNode=NULL;
}
}
voidRecvtInfoDump(void)
{
SPEventHubNode*hubNode=NULL;
SPEventEventNode*eventNode=NULL;
SPEventList*hubList=NULL;
SPEventList*eventList=NULL;
inteventNodeCount=0;
SPEVENT_PRINT("SPEVENTlist:rn");
SPEVENT_LIST_FOR_EACH(hubList,&g_hublist){
hubNode=SPEVENT_LIST_ENTRY(hubList,SPEventHubNode,hubList);
if(hubNode==NULL){
continue;
}
SPEVENT_PRINT("SPEVENTevent(%s):",hubNode->event);
eventNodeCount=0;
SPEVENT_LIST_FOR_EACH(eventList,&(hubNode->eventList)){
eventNode=SPEVENT_LIST_ENTRY(eventList,SPEventEventNode,list);
if(eventNode==NULL){
continue;
}
eventNodeCount++;
}
SPEVENT_PRINT("%drn",eventNodeCount);
}
}
voidSPEventInit(void)
{
SPEVENT_LIST_INIT(&g_hublist);
}
voidSPEventDeinit(void)
{
SPEventClear();
}
- spevent.h文件說明:SPEvent的接口申明及SPEvent相關接口體的定義。
#ifndef__SPEVENT_H__
#define__SPEVENT_H__
#include"spevent_def.h"
typedefvoid(*SPEventHandle)(constchar*event,void*payload);
typedefstruct
{
SPEventHandlehandle;
SPEventListlist;
}SPEventEventNode;
typedefstruct
{
charevent[SPEVENT_EVENT_NAME_LEN];
SPEventListeventList;
SPEventListhubList;
}SPEventHubNode;
voidSPEventInit(void);
voidSPEventDeinit(void);
SPEventEventNode*SPEventSubscribe(constchar*event,SPEventHandlehandle);
boolSPEventUnsubscribe(constchar*event,SPEventEventNode*node);
boolSPEventPublish(constchar*event,void*payload);
voidSPEventClear(void);
voidRecvtInfoDump(void);
#endif
超精簡的SPEvent組件,實例:
#include
voidSPEventHandle1(constchar*event,void*payload)
{
SPEVENT_PRINT("Event1:%s,payload:%s",event,payload);
}
voidSPEventHandle2(constchar*event,void*payload)
{
SPEVENT_PRINT(ent2:%s,payload:%s",event,payload);
}
intmain()
{
SPEventInit();
SPEventEventNode*eventNode1=SPEventSubscribe("Rice",SPEventHandle1);
if(eventNode1!=NULL)
{
SPEventPublish("Rice","hello");
}else{
rlog_e("SPEVENTsubscribeeventfailed");
}
SPEventEventNode*eventNode2=SPEventSubscribe("Rice",SPEventHandle2);
if(eventNode2!=NULL)
{
SPEventPublish("Rice","world");
}else{
rlog_e("SPEVENTsubscribeeventfailed");
}
RecvtInfoDump();
SPEventUnsubscribe("Rice",eventNode1);
SPEventPublish("Rice","Hello world");
RecvtInfoDump();
return0;
}
- 結果:
SPEVENTevent(Rice)Subscribesuccess
Event1:Rice,payload:hello
SPEVENTevent(Rice)Publishsuccess
SPEVENTevent(Rice)Subscribesuccess
Event2:Rice,payload:world
Event1:Rice,payload:world
SPEVENTevent(Rice)Publishsuccess
SPEVENTlist:
SPEVENTevent(Rice):2
SPEVENTevent(Rice)Unsubscribesuccess
Event2:Rice,payload:Hello world
SPEVENTevent(Rice)Publishsuccess
SPEVENTlist:
SPEVENTevent(Rice):1
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
組件
+關注
關注
1文章
505瀏覽量
17805 -
MQTT
+關注
關注
5文章
649瀏覽量
22435
發布評論請先 登錄
相關推薦
RabbitMQ中的發布訂閱模型
多個消費者同時接受到,消費者接收到的信息一致。 發布訂閱模型適合于做模塊之間的異步通信。 img 適用場景 發送并記錄日志信息 springcloud的config組件里面通知配置自動更新 緩存同步
基于ArkTS語言的OpenHarmony APP應用開發:公共事件的訂閱和發布
應用程序提供訂閱、發布、退訂公共事件的能力。
公共事件從系統角度可分為:系統公共事件和自定義公共事件。
系統公共事件:CES內部定義的公共事件,只有系統應用和系統服務才能發布,例如HAP安裝,更新,卸載
發表于 09-18 13:16
MQTT協議介紹之一:發布/訂閱
,Pub / Sub將正在接收消息(稱為訂戶)的另一客戶端(或更多客戶端)發送特定消息(稱為發布者)的客戶端去耦,這意味著發布者和訂閱者不了解彼此的存在,有一個第三個組件,稱為代理,由
發表于 08-25 19:58
STM32F107是怎樣通過LWIP實現MQTT發布和訂閱框架的呢
怎樣通過STM32CubeMX配置STM32F107VCTx的demo呢?STM32F107是怎樣通過LWIP實現MQTT發布和訂閱框架的呢?
發表于 10-27 06:06
NodeMCU實現訂閱和發布主題
NodeMCU實現訂閱和發布主題。1、要點掃盲1.1 MQTT《MQTT協議--MQTT協議簡介及原理》《MQTT協議--MQTT協議解析》1.2 OneNET《NodeMCU學習(十)--發送數據
發表于 11-01 08:37
超精簡的按鍵組件MultiButton概括
Growing up’s a funny thing. Sneaks up on you.長大是件很有趣的事,不經意間就發生了。一、概括項目的倉庫大佬的超精簡的軟件定時器multi_timer已經讓人眼前一亮,如今這個按鍵組件M
發表于 02-28 11:19
YoC組件發布開源操作指南須知
過程中提交代碼到組件開發倉庫,直到組件功能完成。2.1.5 貢獻發布組件開發者將組件貢獻合入YoC,需要按照以下章節3進行操作。2.2 yo
發表于 03-09 07:37
請問esp32c3,ble mesh怎么向訂閱的分組發布消息?
發布消息,為什么vnd_models模型不可以.有沒有更加簡單的api,直接傳訂閱分組地址就可以發布消息的?
發表于 02-13 06:47
請問esp32c3 ble mesh怎么向訂閱的分組發布消息?
發布消息,為什么vnd_models模型不可以.有沒有更加簡單的api,直接傳訂閱分組地址就可以發布消息的?
發表于 03-06 08:36
基于SOA的發布/訂閱系統設計
企業電子商務的迅猛發展已經改變了分布式系統的規模,傳統的基于請求/應答的點對
點、同步通信已不能滿足大規模動態分布式應用環境。基于SOA 的發布/訂閱系統模型
發表于 07-08 08:42
?21次下載
發布/訂閱消息傳遞協議有哪些?為什么這類協議在物聯網應用廣泛
發布/訂閱消息傳遞協議是一種消息傳遞模式,其中消息的發布者和訂閱者是解耦的,消息的發布者和訂閱者
給我兩分鐘,搞懂發布-訂閱模式很輕松!
什么是發布/訂閱模式?舉一個生活中常見的例子說明:小李到某房產中介提出租房需求,根據需求,房產中介將之前房東發布的出租信息提供給小李選擇,小李確定租房后,中介會將信息同步給房東知曉。這是一個典型
評論