原文鏈接: 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 名门烂。
那么乳愉,配置信息又是從哪里來的呢?
- 配置屯远。用戶可以通過定義 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) ...
-
https://github.com/mosn/mosn/blob/0.9.0/pkg/mosn/starter.go#L173
- 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) ...
-
https://github.com/mosn/mosn/blob/0.9.0/pkg/xds/conv/update.go#L71
所以房揭,filters 的配置主要是來源于配置文件备闲。
運(yùn)行
filters 的配置是在 listener 之下的,配置解析會將每個(gè) listener 配置聲明要用到的 filters 初始化到 listener 實(shí)例里捅暴,在連接 accept 以及處理過程中恬砂,調(diào)用上文的函數(shù)初始化 filters 并調(diào)用。
MOSN 會將連接的上下文信息放在 context 內(nèi)傳給 filter蓬痒,并將 stream 傳給 filter觉既。下面是代碼流程:
-
連接 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)
-
將連接上下文放入 context,并用以創(chuàng)建 filter chain乳幸,并初始化 filter manager瞪讼,執(zhí)行 filters 的
OnNewConnection
函數(shù)。
filter manager 是 filters 的代理粹断,外部會在不同階段調(diào)用 filter manager 的不同函數(shù)符欠,filter manager 管理 filters 的執(zhí)行邏輯-
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) } ...
-
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() ...
-
-
執(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#L484func (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)容:
- init 函數(shù),register 創(chuàng)建 filter manager 的回調(diào)函數(shù)
- 回調(diào)函數(shù)需要返回一個(gè)實(shí)現(xiàn)了
types.NetworkFilterChainFactory
接口的 struct - 該 struct 的
CreateFilterChain
方法創(chuàng)建具體邏輯的實(shí)例阅嘶,并調(diào)用callback
參數(shù)的addReadFilter
|addWriteFilter
函數(shù)属瓣,進(jìn)行 filter 注入到 filter manager - 具體的業(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 自己具體的使用場景选泻。
參考資料: