MOSN 源碼分析 - 插件機(jī)制

原文鏈接: https://trainyao.github.io/post/mosn/source_filter/

本文記錄了對 MOSN 的源碼研究 - MOSN 的插件機(jī)制, 以及如何創(chuàng)建自己的插件來擴(kuò)展 MOSN嚷缭。

本文的內(nèi)容基于 MOSN v0.9.0。

機(jī)制

使用過濾器模式來實(shí)現(xiàn)擴(kuò)展是常見的設(shè)計(jì)模式涎拉,MOSN 也是使用了這種方式來構(gòu)建可擴(kuò)展性。

MOSN 把過濾器相關(guān)的代碼放在了 pkg/filter 目錄下:

?  mosn git:(2c6f58c5) ? ll pkg/filter
total 24
drwxr-xr-x   8 mac  staff   256 Feb  5 08:52 .
drwxr-xr-x  30 mac  staff   960 Feb  5 08:52 ..
drwxr-xr-x   3 mac  staff    96 Aug 28 22:37 accept
-rw-r--r--   1 mac  staff  2556 Feb  5 08:52 factory.go
-rw-r--r--   1 mac  staff  2813 Feb  5 08:52 factory_test.go
drwxr-xr-x   6 mac  staff   192 Aug 28 22:37 network
drwxr-xr-x   7 mac  staff   224 Aug 28 22:37 stream
-rw-r--r--   1 mac  staff  1248 Feb  5 08:52 types.go
?  mosn git:(2c6f58c5) ?

包括 accept 過程的 filter怀喉,network 處理過程的 filter联予,以及 stream 處理的 filter辑甜。其中 accept filters 目前暫不提供擴(kuò)展(加載、運(yùn)行寫死在代碼里面凝赛,如要擴(kuò)展需要修改源碼)注暗,
steram、network filters 是可以通過定義新包在 pkg/filter 目錄下實(shí)現(xiàn)擴(kuò)展哄酝。

每一個(gè) filter 包都會有一個(gè) init 函數(shù)友存,拿 pkg/filter/network/proxy 包為例:

...

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package proxy

import (
    ...
)

func init() {
    filter.RegisterNetwork(v2.DEFAULT_NETWORK_FILTER, CreateProxyFactory)
}

...

包被運(yùn)行時(shí)會將 filter 注冊到 filter factory 里,在必要時(shí)候,注冊的回調(diào)函數(shù)(如例子里的 CreateProxyFactory)就會被調(diào)用。

加載 & 運(yùn)行 filters

下面來詳細(xì)看看 filters 被加載和運(yùn)行的過程黔攒。

加載

我們可以看看税产,init 函數(shù)注冊的回調(diào)函數(shù)是在什么時(shí)機(jī)被調(diào)用的:

mosn.io/mosn/pkg/filter/factory.go L57:

...
// CreateNetworkFilterChainFactory creates a StreamFilterChainFactory according to filterType
func CreateNetworkFilterChainFactory(filterType string, config map[string]interface{}) (types.NetworkFilterChainFactory, error) {
    if cf, ok := creatorNetworkFactory[filterType]; ok {
...

mosn.io/mosn/pkg/config/parser.go L372:

...
// GetNetworkFilters returns a network filter factory by filter.Type
func GetNetworkFilters(c *v2.FilterChain) []types.NetworkFilterChainFactory {
    var factories []types.NetworkFilterChainFactory
    for _, f := range c.Filters {
        factory, err := filter.CreateNetworkFilterChainFactory(f.Type, f.Config)
...

可見,MOSN 在調(diào)用 config.GetNetworkFilters 會根據(jù)配置信息filter.Type 作為名字,在已注冊的 filters 中找到需要加載的 filter,并返回 networkFilterChainFactory。
可以看到焚刺, filter.Type 其實(shí)就是 init 函數(shù)調(diào)用時(shí),包調(diào)用 filter.RegisterNetwork 函數(shù)的第一個(gè)參數(shù):filter 名门烂。

那么乳愉,配置信息又是從哪里來的呢?

  1. 配置屯远。用戶可以通過定義 MOSN 的 config.json蔓姚,MOSN 啟動(dòng)時(shí)會根據(jù) listener 指定的 filter 數(shù)組進(jìn)行 filters 的初始化。
    • https://github.com/mosn/mosn/blob/0.9.0/pkg/mosn/starter.go#L173
        func NewMosn(c *config.MOSNConfig) *Mosn {
        ...
        
            var nfcf []types.NetworkFilterChainFactory
            var sfcf []types.StreamFilterChainFactory
        
            // Note: as we use fasthttp and net/http2.0, the IO we created in mosn should be disabled
            // network filters
            if !lc.UseOriginalDst {
                // network and stream filters
                nfcf = config.GetNetworkFilters(&lc.FilterChains[0])
                sfcf = config.GetStreamFilters(lc.StreamFilters)
      
        ...
      
  2. XDS 信息解析慨丐。MOSN 里的 XDS client 會將 XDS resources 解析成 MOSN 的 config坡脐,當(dāng) downstream client 連接進(jìn)來的時(shí)候根據(jù) config 進(jìn)行組裝需要用到的 filters。
    • https://github.com/mosn/mosn/blob/0.9.0/pkg/xds/conv/update.go#L71
      // ConvertAddOrUpdateListeners converts listener configuration, used to  add or update listeners
      func ConvertAddOrUpdateListeners(listeners []*envoy_api_v2.Listener) {
      ...
            var streamFilters []types.StreamFilterChainFactory
            var networkFilters []types.NetworkFilterChainFactory
      
            if !mosnListener.UseOriginalDst {
                for _, filterChain := range mosnListener.FilterChains {
                    nf := config.GetNetworkFilters(&filterChain)
                    networkFilters = append(networkFilters, nf...)
                }
                streamFilters = config.GetStreamFilters(mosnListener.StreamFilters)
      ...
      

所以房揭,filters 的配置主要是來源于配置文件备闲。

運(yùn)行

filters 的配置是在 listener 之下的,配置解析會將每個(gè) listener 配置聲明要用到的 filters 初始化到 listener 實(shí)例里捅暴,在連接 accept 以及處理過程中恬砂,調(diào)用上文的函數(shù)初始化 filters 并調(diào)用。

MOSN 會將連接的上下文信息放在 context 內(nèi)傳給 filter蓬痒,并將 stream 傳給 filter觉既。下面是代碼流程:

  1. 連接 accept

    https://github.com/mosn/mosn/blob/0.9.0/pkg/server/handler.go#L394

    func (al *activeListener) OnAccept(rawc net.Conn, useOriginalDst bool, oriRemoteAddr net.Addr, ch chan types.Connection, buf []byte) {
    ...
    arc.ContinueFilterChain(ctx, true)
    
  2. 將連接上下文放入 context,并用以創(chuàng)建 filter chain乳幸,并初始化 filter manager瞪讼,執(zhí)行 filters 的 OnNewConnection 函數(shù)。
    filter manager 是 filters 的代理粹断,外部會在不同階段調(diào)用 filter manager 的不同函數(shù)符欠,filter manager 管理 filters 的執(zhí)行邏輯

    1. https://github.com/mosn/mosn/blob/0.9.0/pkg/server/handler.go#L394

      func (al *activeListener) OnAccept(rawc net.Conn, useOriginalDst bool, oriRemoteAddr net.Addr, ch chan types.Connection, buf []byte) {
      ...
      
      ctx := mosnctx.WithValue(context.Background(), types.ContextKeyListenerPort, al.listenPort)
      ctx = mosnctx.WithValue(ctx, types.ContextKeyListenerType, al.listener.Config().Type)
      ctx = mosnctx.WithValue(ctx, types.ContextKeyListenerName, al.listener.Name())
      ctx = mosnctx.WithValue(ctx, types.ContextKeyNetworkFilterChainFactories, al.networkFiltersFactories)
      ctx = mosnctx.WithValue(ctx, types.ContextKeyStreamFilterChainFactories, &al.streamFiltersFactoriesStore)
      ctx = mosnctx.WithValue(ctx, types.ContextKeyAccessLogs, al.accessLogs)
      if rawf != nil {
          ctx = mosnctx.WithValue(ctx, types.ContextKeyConnectionFd, rawf)
      }
      if ch != nil {
          ctx = mosnctx.WithValue(ctx, types.ContextKeyAcceptChan, ch)
          ctx = mosnctx.WithValue(ctx, types.ContextKeyAcceptBuffer, buf)
      }
      if oriRemoteAddr != nil {
          ctx = mosnctx.WithValue(ctx, types.ContextOriRemoteAddr, oriRemoteAddr)
      }
      ...
      
      
    2. https://github.com/mosn/mosn/blob/0.9.0/pkg/server/handler.go#L454

      func (al *activeListener) OnNewConnection(ctx context.Context, conn types.Connection) {
       //Register Proxy's Filter
       filterManager := conn.FilterManager()
       for _, nfcf := range al.networkFiltersFactories {
           nfcf.CreateFilterChain(ctx, al.handler.clusterManager, filterManager)
       }
       filterManager.InitializeReadFilters()
      ...
      
  3. 執(zhí)行 filter 的 OnData 方法

    https://github.com/mosn/mosn/blob/0.9.0/pkg/network/connection.go#L428 ->
    https://github.com/mosn/mosn/blob/0.9.0/pkg/network/connection.go#L484

    func (c *connection) doRead() (err error) {
    ...
        c.onRead()
    ...
    
    func (c *connection) onRead() {
    ...
    c.filterManager.OnRead()
    

至此,filter 就實(shí)現(xiàn)了對連接的干預(yù)瓶埋,filter 就像中間件希柿,可以返回 type.Continue 控制連接繼續(xù)進(jìn)行,
也可以返回 type.Stop 停止連接繼續(xù)處理养筒。即可以對連接內(nèi)容進(jìn)行干預(yù)曾撤,比如在 static response 的場景,
返回既定的 response 內(nèi)容晕粪;也可以對連接處理流程進(jìn)行干預(yù)挤悉,比如在 fault injection 的場景,增加連接延時(shí)巫湘,等等装悲。

MOSN 實(shí)現(xiàn)了的 filters

根據(jù)文件目錄我們可以看出 MOSN 目前實(shí)現(xiàn)了哪些 filter:


?  mosn git:(2c6f58c5) ? ll pkg/filter/network
total 0
drwxr-xr-x   7 mac  staff  224 Aug 28 22:37 .
drwxr-xr-x   8 mac  staff  256 Feb  9 15:49 ..
drwxr-xr-x  3 mac  staff   96 Feb  8 10:35 connectionmanager // 連接管理
drwxr-xr-x  4 mac  staff  128 Feb  5 08:52 faultinject // 錯(cuò)誤注入相關(guān)
drwxr-xr-x  3 mac  staff   96 Feb  9 12:37 proxy // 代理邏輯, 這個(gè) filter 目前是 MONS 里的邏輯一部分, 負(fù)責(zé)將 stream 發(fā)送到 serverStream, 開啟流量的代理
drwxr-xr-x  6 mac  staff  192 Feb  5 08:52 tcpproxy // tcp 代理邏輯

?  mosn git:(2c6f58c5) ? ll pkg/filter/stream
total 0
drwxr-xr-x   7 mac  staff  224 Aug 28 22:37 .
drwxr-xr-x   8 mac  staff  256 Feb  9 15:49 ..
drwxr-xr-x  11 mac  staff  352 Feb  5 08:52 commonrule // stream filter 邏輯公共部分, rule engine 等等
drwxr-xr-x   6 mac  staff  192 Feb  5 08:52 faultinject // 錯(cuò)誤注入相關(guān)
drwxr-xr-x   3 mac  staff   96 Aug 28 22:37 healthcheck // upstream 健康檢查相關(guān)
drwxr-xr-x   3 mac  staff   96 Feb  5 08:52 mixer // mixer 邏輯相關(guān)
drwxr-xr-x   4 mac  staff  128 Feb  5 08:52 payloadlimit // 限流相關(guān)

具體每個(gè) filter 的邏輯都可以根據(jù)下述的 filter 結(jié)構(gòu),進(jìn)行代碼跟讀和調(diào)試尚氛,分別理解每個(gè) filter 的作用诀诊。

filter 包含的內(nèi)容 & 如何擴(kuò)展 MOSN filters

一個(gè) filter 包含以下內(nèi)容:

  1. init 函數(shù),register 創(chuàng)建 filter manager 的回調(diào)函數(shù)
  2. 回調(diào)函數(shù)需要返回一個(gè)實(shí)現(xiàn)了 types.NetworkFilterChainFactory 接口的 struct
  3. 該 struct 的 CreateFilterChain 方法創(chuàng)建具體邏輯的實(shí)例阅嘶,并調(diào)用 callback 參數(shù)的 addReadFilter | addWriteFilter 函數(shù)属瓣,進(jìn)行 filter 注入到 filter manager
  4. 具體的業(yè)務(wù)邏輯

由此可見,通過 filter 機(jī)制擴(kuò)展 MOSN讯柔,我們只需實(shí)現(xiàn)上述 4 樣?xùn)|西抡蛙,與 MOSN 一同編譯。再通過適當(dāng)?shù)?config 內(nèi)容配置磷杏,或者與 XDS server 協(xié)作并生成含有你擴(kuò)展的 filter 名及配置
(這部分需要修改 MOSN 源碼溜畅,MOSN 與 XDS server 交互并生成 config,選用哪些 filter 目前是寫死在代碼里的)极祸,MOSN 就會在適當(dāng)時(shí)候加載并運(yùn)行你的 filter慈格,對代理流量進(jìn)行干預(yù)。

總結(jié)

本文通過分析 MOSN 源碼遥金,簡述了 MOSN 的插件擴(kuò)展機(jī)制浴捆,并簡述了實(shí)現(xiàn)自己的 filter 需要做的東西。大家可以通過該機(jī)制稿械,使用 MONS 輕松 cover 自己具體的使用場景选泻。


參考資料:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子页眯,更是在濱河造成了極大的恐慌梯捕,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窝撵,死亡現(xiàn)場離奇詭異傀顾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)碌奉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門短曾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赐劣,你說我怎么就攤上這事嫉拐。” “怎么了魁兼?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵婉徘,是天一觀的道長。 經(jīng)常有香客問我璃赡,道長判哥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任碉考,我火速辦了婚禮塌计,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘侯谁。我一直安慰自己锌仅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布墙贱。 她就那樣靜靜地躺著热芹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惨撇。 梳的紋絲不亂的頭發(fā)上伊脓,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音魁衙,去河邊找鬼报腔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛剖淀,可吹牛的內(nèi)容都是我干的纯蛾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼纵隔,長吁一口氣:“原來是場噩夢啊……” “哼翻诉!你這毒婦竟也來了炮姨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤碰煌,失蹤者是張志新(化名)和其女友劉穎舒岸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拄查,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吁津,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了堕扶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梭依,死狀恐怖稍算,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情役拴,我是刑警寧澤糊探,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站河闰,受9級特大地震影響科平,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜姜性,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一瞪慧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧部念,春花似錦弃酌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至乌询,卻和暖如春榜贴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妹田。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工唬党, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秆麸。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓初嘹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沮趣。 傳聞我的和親對象是個(gè)殘疾皇子屯烦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容