Golang | 插件化方案

一、什麼是插件(也叫動態庫)

在寫C++程序時,時常須要將一個class寫成DLL(動態連接庫),供客戶端程序調用。這樣的DLL能夠導出整個class,也能夠導出這個class的某個方法。golang

經過DLL調用和把代碼寫在程序裏調用的區別:看這個函數是否提供給別的程序調用;算法

別的程序確定無法調用這個程序的某個函數,總不能把代碼拷給他把,且不說可不能夠拷,就算能夠也麻煩阿,直接寫成DLL讓他本身調用去;安全

插件就相似dll;模塊化

經過使用插件在運行時擴展程序的功能, 而無需從新編譯程序;函數

啓動啓程以後不用中止就能添加新的功能(函數);測試

優勢:ui

  • 動態加載, 也稱熱加載, 每次升級時不用從新編譯整個工程,從新部署服務, 而是添加插件時進行動態更新。這對於不少比較重型的服務來講很是重要。

缺點:編碼

  • 帶來必定的安全風險, 若是一些非法模塊被注入如何防範;
  • 給系統帶來必定的不穩定的因素, 若是模塊有問題, 沒有通過良好的測試, 容易致使服務崩潰

二、Go的插件系統:Plugin

Go插件是使用-buildmode = plugin標記編譯的一個包, 用於生成一個共享對象(.so)庫文件。spa

Go包中的導出的函數和變量被公開爲ELF符號,可使用plugin包在運行時查找並綁定ELF符號。這樣就能夠查詢並調用插件裏的函數;插件

三、插件設計原則

  • 插件獨立
  • 使用接口類型做爲邊界
  • Unix模塊化原則
  • 版本控制

四、go使用插件

  • 用plugin.Open()打開插件文件

  • 用plguin.Lookup(「Export-Variable-Name」)查找導出的符號」Car」或者」Phone」(就是函數名)

    請注意,符號名稱與插件模塊中定義的變量名稱相匹配

  • 類型斷言,而後就能夠進行調用了

go build -buildmode=plugin file.go,編譯生成插件

==go中的插件不是進程隔離的,是單進程的;==

golang的插件很不完善,只能加載不能卸載。如何須要卸載須要本身實現一套重載機制。

五、使用plugin注意

Go plugin判斷兩個插件是否相同是經過比較pluginpath實現的,若是沒有指定pluginpath,則由內部的算法生成, 生成的格式爲plugin/unnamed-「 + root.Package.Internal.BuildID 。

在開始編碼以前,要先解決幾個問題:
1、同一個so文件只會被打開一次
2、每一個so有一個pluginpath用來標識是否重複,若是兩個so文件不同,但pluginpath同樣仍是會報錯。會被識別成是同一個文件,同時加載這兩個插件時,會報錯;
3、不一樣so文件定義的結構體不能使用類型斷言進行轉換

對於上面的問題,有以下解決方案:
編譯的時候新增--ldflags="-pluginpath=xxx"參數,本身指定pluginpath參數,避免重複。
複製代碼

六、關於插件內存

加載.so文件的時候,就會爲全局變量申請內存空間,同時執行init()初始化函數;

同一進程空間內的線程:
堆:是你們共有的空間
棧:是個線程獨有的

同一進程空間,函數能夠直接相互調用;
複製代碼

思考:我在插件中調用項目的其餘部分,如models,編譯插件的時候不會報錯嗎?我插件都還沒加載?

首先插件又叫動態庫,和靜態庫沒什麼區別。靜態庫就是程序編譯完了以後,再來修改靜態庫就程序不起做用,要從新編譯才行。

而動態庫就不一樣了,在程序編譯運行以後,動態庫能夠照樣更改,改完以後程序從新加載一遍就完事了;

至於插件中調用了models,毛問題都沒有,插件編譯的時候都沒調用到models,並且插件只是編譯而已,就算調用有問題,那也是等插件被加載以後的事情了;

相關文章
相關標籤/搜索