構建自己的C/C++插件開發框架

構建自己的C/C++插件開發框架zz

——初步設想

 最近一直在學習OSGI方面的知識。買了一本《OSGI原理和最佳實踐》,可是還沒有到。遺憾的是,OSGI目前的幾個開源框架只支持Java,對C和C++都不支持的。可惜我們公司目前主要的開發語言還是c和c++,即便是引進OSGI,所得的好處範圍有限。而我對鬆散耦合的模塊化開發嚮往已久。查了一下OSGI對C++支持的好像是有一個開源項目,不過好像應用範圍很小。而SCA標準中是有對C++實現模型的支持的,但是幾個開源的框架目前還只支持JAVA。

  昨天看了丁亮的轉載的一篇博客《C/C++:構建你自己的插件框架 》,原文的鏈接:http://blog.chinaunix.net/u/12783/showart_662937.html 。看了一下里面講的方法,自己倒是可以實現。所以有了構建自己的c/c++插件開發框架的想法。今天先寫一下初步的設想。

  C/C++插件開發框架的要素

  BlueDavy有一篇介紹服務框架要素的文章(鏈接:http://www.blogjava.net/BlueDavy/archive/2009/08/28/172259.html )。我的插件框架也要考慮、解決以下的幾個問題:

  1、如何註冊插件;

  2、如何調用插件;

  3、如何測試插件;

  4、插件的生命週期管理;

  5、插件的管理和維護;

  6、插件的組裝;

  7、插件的出錯處理;

  8、服務事件的廣播和訂閱(這個目前還沒有考慮要支持);

  其中有幾個點很重要:1)插件框架要能夠使模塊鬆散耦合,做到真正的面向接口編程;2)框架要支持自動化測試:包括單元測試,集成測試;3)簡化部署;4)支持分佈式,模塊可以調用框架外的插件。

  採用的技術
  插件框架要解決的一個問題就是插件的動態加載能力。這裏可以使用共享庫的動態加載技術。當然,爲了簡單,第一步只考慮做一個linux下的插件框架。

  總體結構

  框架的總體結構上,參考OSGI的“微內核+系統插件+應用插件”結構。這裏要好好考慮一下把什麼做在內核中。關於微內核結構,以前我做個一個微內核流程引擎,會在後面有時間和大家分享。

  框架中模塊間的數據傳送,有兩種解決方法:一是普元採用的XML數據總線的做法。優點是擴展性好,可讀性好。但是速度有些慢。二是採用我熟悉的信元流。優點的效率高,訪問方便,但是可讀性差一點,另外跨框架的數據傳送,需要考慮網絡字節序的問題。

  對於框架間的通信,通過系統插件封裝,對應用插件隱藏通信細節。

      部署

      努力做到一鍵式部署。

——總體功能

在這一系列的上一個文章中,介紹了構建C/C++插件開發框架的初步設想,下面我會一步步的向下展開,來實現我的這個設想。

今天主要談一下我對這個框架的功能認識,或是期望。昨天看了一篇關於持續集成能力成熟度模型 的一篇文章,受此啓發,我對此框架的認識漸漸清晰。

這個框架可以當做我們公司底層產品(交換機,資源服務器等)的基礎設施。上層基於java開發的產品可以直接在OSGI上開發。

核心功能:

1、最重要的一個功能是,提供一個模塊化的編程模型,促進模塊化軟件開發,真正的實現針對接口編程。

2、提供一個有助於提高模塊可重用性的基礎設施。

3、提供一個C/C++插件的運行環境。

4、提供一個動態插件框架,插件可以動態更改,而無需重啓系統。這個功能雖然不難實現,但是用處好像不是很大。


--------------------------------------------------------------------------------

擴展部分功能:

1、支持分佈式系統結構,多個運行框架組合起來形成一個系統,對模塊內部隱藏遠程通訊細節。

2、支持系統的分層架構。

3、能夠和其他的開發框架進行集成,比如OSGI,SCA等。

4、多個運行框架中,能夠實現對運行框架的有效管理。

5、概念上要實現類似於SCA中component(構件),composite(組合構件),Domain(域)的概念。


--------------------------------------------------------------------------------

開發部分功能:

1、爲了簡化開發,開發一個Eclipse插件,用於開發框架中的C/C++插件。能夠根據插件開發嚮導,最終生成符合插件規範的公共代碼,配置文件,Makefile文件等。


--------------------------------------------------------------------------------

調試部分功能:

1、提供一個統一的日誌處理函數,可以集成Log4cpp。

2、提供模塊間的消息日誌,以及框架對外的接口日誌。

3、提供消息和日誌的追蹤功能,能將和某事件相關的消息和日誌單獨提取出來。

4、提供資源監測功能,監測對資源(內存,套接字,文件句柄等)的使用情況。


--------------------------------------------------------------------------------

測試部分功能:

1、集成一些單元測試框架,比如unitcpp,達到自動化單元測試的目標。

2、自己實現自動化集成測試框架,並且開發相應的Eclipse插件,簡化集成測試(利用腳本和信元流)。

3、集成原有的自動化功能測試框架flowtest,並且開發相應的Eclipse插件,簡化功能測試。

4、實現性能測試,監測框架。


--------------------------------------------------------------------------------

部署部分功能:

1、實現自動化部署。特別是在分佈式應用的情況下。

2、提供一個命令行程序,通過命令更改系統配置,管理插件。

——總體結構

這幾天爲了設計插件開發框架,嘗試用了一下發散思維來思考問題。中間看過依賴注入,AOP(面向方面編程),以及契約式設計等。雖然有些工具無法直接使用,但是這些思想還是可以借鑑的,比如依賴注入,契約式設計。至於AOP,和工具相關性較大,雖然思想不錯,但是無法直接在C++中使用。

我設計的插件間的依賴不是通過接口實現的,而是通過插件間的數據(信元流)。而信元流的檢測可以使用契約來檢查。

插件開發框架的總體結構

微內核 :

1、 負責插件的加載,檢測,初始化。

2、 負責服務的註冊。

3、 負責服務的調用。

4、 服務的管理。

擴展層:

1、 日誌的打印。

2、 消息(信元流)的解釋,將二進制格式解釋爲文本。便於定位。

3、 消息和日誌的追蹤。

分佈式處理層:

1、 用於和其他的框架通信。

2、 和其他的框架搭配,形成一個分佈式的系統。

自動化測試框架層:

1、 集成 cppunit 。

2、 自動化集成測試框架。

3、 自動化功能測試框架。

和第三方框架集成層:

1 、和 第三方框架 集成層。

——核心層設計和實現

上面一篇文章大致描述了一下插件開發框架整體結構。這篇描述一下核心層的設計和實現。

至於核心層的設計,我想借鑑 一下微內核的思想。核心層只負責實現下面幾個功能:

1、 插件的加載,檢測,初始化。

2、 服務的註冊。

3、 服務的調用。

4、 服務的管理。


插件的加載,檢測,初始化

插件的加載利用linux共享庫的動態加載技術。具體的方法可以看一下IBM網站的一篇資料《Linux 動態庫剖析》 。

服務的註冊

服務的註冊與調用採用表驅動的方法。核心層中維護一個服務註冊表。

//插件間交互消息類型
typedef enum __Service_Type
{
    Service_Max,

}Service_Type;


//插件用於和其他插件通信接口函數,由插件提供。
typedef PRsp_Ele_Stream (*PF_Invoke_Service_Func)(PReq_Ele_Stream pele_str);


//驅動表
typedef PF_Invoke_Service_Func Service_Drive_Table[Service_Max];


驅動表是一個數組,下標爲插件間交互消息類型,成員爲插件提供的接收的消息處理函數,由插件初始化的時候,調用插件框架的的註冊函數註冊到驅動表。

插件的初始化實現爲:
//插件用於註冊處理的消息類型的函數,由插件框架提供。
typedef RET_RESULT (*PF_Service_Register_Func)(Service_Type service_type);

//插件用於和其他插件通信接口函數,由插件框架提供。
typedef PRsp_Ele_Stream (*PF_Invoke_Service_Func)(PReq_Ele_Stream pele_str);

//插件回覆響應函數。插件收到異步請求後,處理完成後,發送響應消息給請求的插件。由插件框架提供
typedef void (*PF_Send_Response_Func)(PRsp_Ele_Stream pele_str);

//初始化插件信息
typedef struct Plugin_Init_St
{
    PF_Service_Register_Func register_func;//服務註冊函數,要註冊一系列的枚舉值。插件可以處理的服務枚舉值
    PF_Invoke_Service_Func invoke_serv_func;//和其他組件交互時,調用的用於和其他組件交互的函數。發送請求消息。
    PF_Send_Response_Func send_rsp_func;//再設計一個回覆響應消息的接口。收到異步請求後,處理完畢後通知請求模塊處理結果。
} Plugin_Init_St, *PPlugin_Init_St;

//初始化插件函數,類似於構造函數。由插件提供,供插件框架加載插件時初始化插件使用。
void PF_Init_Plugin(PPlugin_Init_St pinit_info);

      插件在函數PF_Init_Plugin中調用函數register_func來註冊插件要處理的消息類型。

服務的調用
//信元結構體
typedef struct Ele_St
{
    Ele_Tag tag;
    Ele_Length len;
    Ele_Value value;
    PEle_St next;
}Ele_St, *PEle_St;

//請求消息,信元流格式。
typedef struct Req_Ele_Stream
{
    Plugin_ID src_id;//源插件id
    Service_Type req_type;//請求類型
    PEle_St ele;
} Req_Ele_Stream, *PReq_Ele_Stream;

//響應消息,信元流格式。
typedef struct Rsp_Ele_Stream
{
    Plugin_ID dest_id;//目的插件id
    Service_Type req_type;//響應對應的請求的類型。
    Execute_Result result;//記錄執行結果
    Execute_Reason reason;//記錄執行結果的原因
    PEle_St ele;
} Rsp_Ele_Stream, *PRsp_Ele_Stream;

//接收插件調用服務請求函數,由插件提供,入參爲請求信元流。返回值爲響應信元流,用於同步請求處理。
PRsp_Ele_Stream PF_Receive_Invoke_Proc(PReq_Ele_Stream pele_str);

//插件收到響應消息的處理入口函數,由插件提供。如此爲響應信元流。
void PF_Receive_Rsponse_Porc(PRsp_Ele_Stream pele_str);

插件間的依賴關係是通過信元流來實現的。至於信元流的使用在我的另一篇博客《使用信元流(TLVStream)規範、簡化模塊(C/C++)間交互 》 中有描述。插件對外的接口都是統一的。

如果插件要和其他的插件通信,則調用PF_Init_Plugin函數的傳遞的服務調用接口: invoke_serv_func。插件框架根據信元流的類型,查找驅動表,找到對應的服務接收函數。插件用函數PF_Receive_Invoke_Proc接受其他插件的請求,此函數是插件想插件框架主動註冊到驅動表的。

如果服務時同步的,這直接通過此函數返回,返回的信息在響應信元流中。如果是異步的請求,這插件在處理完成後,通過 send_rsp_func函數來發送響應。

插件的卸載
//卸載插件時調用的函數,類似於析構函數。由插件提供,供插件框架卸載插件時調用。
void PF_Destroy_Func();

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章