Dubbo-go Server端開啓服務過程

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"導讀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"導讀:隨着微服務架構的流行,許多高性能 rpc 框架應運而生,由阿里開源的 dubbo 框架 go 語言版本的 dubbo-go 也成爲了衆多開發者不錯的選擇。本文將介紹 dubbo-go 框架的基本使用方法,以及從 export 調用鏈的角度進行 server 端源碼導讀,希望能引導讀者進一步認識這款框架。下週將發表本文的姊妹篇:《從 client 端源碼導讀 dubbo-go 框架》。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"序言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"近日閱讀了部分dubbo-go源碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/dubbogo/dubbo-go","title":""},"content":[{"type":"text","text":"https://github.com/dubbogo/dubbo-go"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當拿到一款框架之後,一種不錯的源碼閱讀方式大致如下:從運行最基礎的helloworld demo 源碼開始,再查看配置文件,開啓各種依賴服務(比如zk、consul),開啓服務端,再到通過client調用服務端,打印完整請求日誌和回包。調用成功之後,再根據框架的設計模型,從配置文件解析開始,自頂向下遞閱讀整個框架的調用棧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於C/S模式的rpc請求來說,整個調用棧被拆成了client和server兩部分,所以可以分別從server端的配置文件解析閱讀到server端的監聽啓動,從client端的配置文件解析閱讀到一次invoker Call 調用。這樣一次完整請求就明晰了起來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"1. 運行官網提供的helloworld-demo"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/dubbogo/dubbo-samples/tree/master/golang/helloworld/dubbo","title":""},"content":[{"type":"text","text":"官方demo"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.1 dubbo-go 2.7版本 QuickStart"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1. 開啓一個go-server服務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將倉庫clone 到本地"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ git clone "},{"type":"link","attrs":{"href":"https://github.com/dubbogo/dubbo-samples.git","title":""},"content":[{"type":"text","text":"https://github.com/dubbogo/dubbo-samples.git"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進入dubbo目錄"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ cd dubbo-samples/golang/helloworld/dubbo"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進入目錄後可看到四個文件夾,分別支持go和java的client以及server,我們嘗試運行一個go的server"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進入app子文件夾內,可以看到裏面保存了go文件。"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ cd go-server/app"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"sample文件結構:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可在go-server裏面看到三個文件夾:app、assembly、profiles"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中app文件夾下保存go源碼,assembly文件夾下保存可選的針對特定環境的build腳本,profiles下保存配置文件。對於dubbo-go框架,配置文件非常重要,沒有文件將導致服務無法啓動。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"設置指向配置文件的環境變量"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於dubbo-go框架依賴配置文件啓動,讓框架定位到配置文件的方式就是通過環境變量來找。對於server端需要兩個必須配置的環境變量:CONF"},{"type":"text","marks":[{"type":"italic"}],"text":"PROVIDER"},{"type":"text","text":"FILE_PATH、APP_LOG"},{"type":"text","marks":[{"type":"italic"}],"text":"CONF"},{"type":"text","text":"FILE,分別應該指向服務端配置文件、日誌配置文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在sample裏面,我們可以使用dev環境,即profiles/dev/log.yml profiles/dev/server.yml 兩個文件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在app/下,通過命令行中指定好這兩個文件:"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ export CONF"},{"type":"text","marks":[{"type":"italic"}],"text":"PROVIDER"},{"type":"text","text":"FILE_PATH=\"../profiles/dev/server.yml\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ export APP"},{"type":"text","marks":[{"type":"italic"}],"text":"LOG"},{"type":"text","text":"CONF_FILE=\"../profiles/dev/log.yml\""}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"設置go代理並運行服務"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ go run ."}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果提示timeout,則需要設置goproxy代理"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ export GOPROXY=\"http://goproxy.io\""}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再運行go run 即可開啓服務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2. 運行zookeeper"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安裝zookeeper,並運行zkServer, 默認爲2181端口"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3. 運行go-client調用server服務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進入go-client的源碼目錄"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ cd go-client/app"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同理,在/app下配置環境變量"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ export CONF"},{"type":"text","marks":[{"type":"italic"}],"text":"CONSUMER"},{"type":"text","text":"FILE_PATH=\"../profiles/dev/client.yml\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ export APP"},{"type":"text","marks":[{"type":"italic"}],"text":"LOG"},{"type":"text","text":"CONF_FILE=\"../profiles/dev/log.yml\""}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置go代理"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ export GOPROXY=\"http://goproxy.io\""}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行程序"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"$ go run ."}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即可在日誌中找到打印出的請求結果"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"response result: &{A001 Alex Stocks 18 2020-10-28 14:52:49.131 +0800 CST}"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣,在運行的server中,也可以在日誌中找到打印出的請求:"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"req:[]interface {}{\"A001\"}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rsp:main.User{Id:\"A001\", Name:\"Alex Stocks\", Age:18, Time:time.Time{...}"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"恭喜!一次基於dubbo-go的rpc調用成功。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"4. 常見問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1. 當日志開始部分出現profiderInit和ConsumerInit均失敗的日誌。檢查環境變量中配置路徑是否正確,配置文件是否正確。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2. 當日志中出現register失敗的情況,一般爲向註冊中心註冊失敗,檢查註冊中心是否開啓,檢查配置文件中關於register的端口是否正確。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3. sample的默認開啓端口爲20000,確保啓動前無佔用"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.2 配置環境變量"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"export APP_LOG_CONF_FILE=\"../profiles/dev/log.yml\"\nexport CONF_CONSUMER_FILE_PATH=\"../profiles/dev/client.yml\""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.3 服務端源碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1. 目錄結構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"dubbo-go框架的example提供的目錄如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/78/7823224a8a2d5277c3dca579c6ab5bc8.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"app/ 文件夾下存放源碼,可以自己編寫環境變量配置腳本buliddev.sh"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"assembly/ 文件夾下存放不同平臺的構建腳本"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"profiles/ 文件夾下存放不同環境的配置文件"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"target/ 文件夾下存放可執行文件"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2. 關鍵源碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"源碼放置在app/文件夾下,主要包含server.go 和user.go 兩個文件,顧名思義,server.go用於使用框架開啓服務以及註冊傳輸協議,user.go則定義了rpc-service結構體,以及傳輸協議的結構。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"user.go"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func init() {\n\tconfig.SetProviderService(new(UserProvider))\n\t// ------for hessian2------\n\thessian.RegisterPOJO(&User{})\n}\ntype User struct {\n\tId string\n\tName string\n\tAge int32\n\tTime time.Time\n}\ntype UserProvider struct {\n}\nfunc (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User, error) {"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到,user.go中存在init函數,是服務端代碼中最先被執行的部分。User爲用戶自定義的傳輸結構體,UserProvider爲用戶自定義的rpc_service。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"包含一個rpc函數,GetUser。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然,用戶可以自定義其他的rpc功能函數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在init函數中,調用config的SetProviderService函數,將當前rpc_service註冊在框架config上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**可以查看dubbo官方文檔提供的"},{"type":"link","attrs":{"href":"http://dubbo.apache.org/zh-cn/docs/dev/design.html","title":""},"content":[{"type":"text","text":"設計圖"}]},{"type":"text","text":"**"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5f/5f5bd4d9e989d2ce49854a7e7a7a66a1.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"service層下面就是config層,用戶服務會逐層向下註冊,最終實現服務端的暴露。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rpc-service註冊完畢之後,調用hessian接口註冊傳輸結構體User。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此init函數執行完畢"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"server.go"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"// they are necessary:\n// \texport CONF_PROVIDER_FILE_PATH=\"xxx\"\n// \texport APP_LOG_CONF_FILE=\"xxx\"\nfunc main() {\n\thessian.RegisterPOJO(&User{})\n\tconfig.Load()\n\tinitSignal()\n}\nfunc initSignal() {\n\tsignals := make(chan os.Signal, 1)\n\t..."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之後執行main函數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"main函數中只進行了兩個操作,首先使用hessian註冊組件將User結構體註冊(與之前略有重複),從而可以在接下來使用getty打解包。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之後調用config.Load函數,該函數位於框架config/config_loader.go內,這個函數是整個框架服務的啓動點,"},{"type":"text","marks":[{"type":"strong"}],"text":"下面會詳細講這個函數內重要的配置處理過程"},{"type":"text","text":"。執行完Load()函數之後,配置文件會讀入框架,之後根據配置文件的內容,將註冊的service實現到配置結構裏,再調用Export暴露給特定的registry,進而開啓特定的service進行對應端口的tcp監聽,成功啓動並且暴露服務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終開啓信號監聽initSignal()優雅地結束一個服務的啓動過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.4 客戶端源碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端包含client.go和user.go兩個文件,其中user.go與服務端完全一致,不再贅述。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"client.go"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"// they are necessary:\n// \texport CONF_CONSUMER_FILE_PATH=\"xxx\"\n// \texport APP_LOG_CONF_FILE=\"xxx\"\nfunc main() {\n\thessian.RegisterPOJO(&User{})\n\tconfig.Load()\n\ttime.Sleep(3e9)\n\tprintln(\"\\n\\n\\nstart to test dubbo\")\n\tuser := &User{}\n\terr := userProvider.GetUser(context.TODO(), []interface{}{\"A001\"}, user)\n\tif err != nil {\n panic(err)\n\t}\n\tprintln(\"response result: %v\\n\", user)\n\tinitSignal()\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"main函數和服務端也類似,首先將傳輸結構註冊到hessian上,再調用config.Load()函數。在下文會介紹,客戶端和服務端會根據配置類型執行config.Load()中特定的函數loadConsumerConfig()和loadProviderConfig(),從而達到“開啓服務”、“調用服務”的目的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"加載完配置之後,還是通過實現服務,增加函數proxy,申請registry,reloadInvoker指向服務端ip等操作,重寫了客戶端實例userProvider的對應函數,這時再通過調用GetUser函數,可以直接通過invoker,調用到已經開啓的服務端,實現rpc過程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面會從server端和client端兩個角度,詳細講解服務啓動、registry註冊、調用過程:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.5 自定義配置文件(非環境變量)方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1.5.1 服務端自定義配置文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"var providerConfigStr = "},{"type":"codeinline","content":[{"type":"text","text":"xxxxx"}]},{"type":"text","text":"// 配置文件內容,可以參考"},{"type":"link","attrs":{"href":"https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/log.yml","title":""},"content":[{"type":"text","text":"log"}]},{"type":"text","text":" 和 [client](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/client.yml)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - 在這裏你可以定義配置文件的獲取方式,比如配置中心,本地文件讀取"}]},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"config.Load()"}]},{"type":"text","text":"之前設置配置,例如:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func main() {\n\thessian.RegisterPOJO(&User{})\n\tproviderConfig := config.ProviderConfig{}\n\tyaml.Unmarshal([]byte(providerConfigStr), &providerConfig)\n\tconfig.SetProviderConfig(providerConfig)\n\tdefaultServerConfig := dubbo.GetDefaultServerConfig()\n\tdubbo.SetServerConfig(defaultServerConfig)\n\tlogger.SetLoggerLevel(\"warn\") // info,warn\n\tconfig.Load()\n\tselect {\n\t}\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1.5.2 客戶端自定義配置文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"var consumerConfigStr = "},{"type":"codeinline","content":[{"type":"text","text":"xxxxx"}]},{"type":"text","text":"// 配置文件內容,可以參考"},{"type":"link","attrs":{"href":"https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/log.yml","title":""},"content":[{"type":"text","text":"log"}]},{"type":"text","text":" 和 [client](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/client.yml)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - 在這裏你可以定義配置文件的獲取方式,比如配置中心,本地文件讀取"}]},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"codeinline","content":[{"type":"text","text":"config.Load()"}]},{"type":"text","text":"之前設置配置,例如:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func main() {\n p := config.ConsumerConfig{}\n yaml.Unmarshal([]byte(consumerConfigStr), &p)\n config.SetConsumerConfig(p)\n defaultClientConfig := dubbo.GetDefaultClientConfig()\n dubbo.SetClientConf(defaultClientConfig)\n logger.SetLoggerLevel(\"warn\") // info,warn\n config.Load()\n\n user := &User{}\n err := userProvider.GetUser(context.TODO(), []interface{}{\"A001\"}, user)\n if err != nil {\n log.Print(err)\n return\n }\n log.Print(user)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"2. server端:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務暴露過程涉及到多次原始rpcService的封裝、暴露,網上其他文章的圖感覺太過籠統,我簡要的繪製了一個用戶定義服務的數據流圖"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a8/a80738bfc1ea9a6e55292f823b03bb32.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1 加載配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1.1 框架初始化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在加載配置之前,框架提供了很多已定義好的協議、工廠等組件,都會在對應模塊init函數內註冊到extension模塊上,以供接下來配置文件中進行選用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中重要的有:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"默認函數代理工廠"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" common/proxy/proxy_factory/default.go"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func init() {\n\textension.SetProxyFactory(\"default\", NewDefaultProxyFactory)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"他的作用是將原始rpc-service進行封裝,形成proxy_invoker,更易於實現遠程call調用,詳情可見其invoke函數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"註冊中心註冊協議"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" registry/protocol/protocol.go"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func init() {\n\textension.SetProtocol(\"registry\", GetProtocol)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"他負責在將invoker暴露給對應註冊中心,比如zk註冊中心。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"3","normalizeStart":"3"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"zookeeper註冊協議"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" registry/zookeeper/zookeeper.go"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func init() {\n\textension.SetRegistry(\"zookeeper\", newZkRegistry)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"他合併了base_resiger,負責在服務暴露過程中,將服務註冊在zookeeper註冊器上,從而爲調用者提供調用方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"4","normalizeStart":"4"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"dubbo傳輸協議"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" protocol/dubbo/dubbo.go"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func init() {\n\textension.SetProtocol(DUBBO, GetProtocol)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"他負責監聽對應端口,將具體的服務暴露,並啓動對應的事件handler,將遠程調用的event事件傳遞到invoker內部,調用本地invoker並獲得執行結果返回。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"5","normalizeStart":"5"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"filter包裝調用鏈協議"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" protocol/protocolwrapper/protocol"},{"type":"text","marks":[{"type":"italic"}],"text":"filter"},{"type":"text","text":"wrapper.go"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func init() {\n\textension.SetProtocol(FILTER, GetProtocol)\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"他負責在服務暴露過程中,將代理invoker打包,通過配置好的filter形成調用鏈,並交付給dubbo協議進行暴露。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述提前註冊好的框架已實現的組件,在整個服務暴露調用鏈中都會用到,會根據配置取其所需。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1.2 配置文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務端需要的重要配置有三個字段:services、protocols、registries"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"profiles/dev/server.yaml:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"yaml"},"content":[{"type":"text","text":"registries :\n \"demoZk\":\n protocol: \"zookeeper\"\n timeout\t: \"3s\"\n address: \"127.0.0.1:2181\"\nservices:\n \"UserProvider\":\n # 可以指定多個registry,使用逗號隔開;不指定默認向所有註冊中心註冊\n registry: \"demoZk\"\n protocol : \"dubbo\"\n # 相當於dubbo.xml中的interface\n interface : \"com.ikurento.user.UserProvider\"\n loadbalance: \"random\"\n warmup: \"100\"\n cluster: \"failover\"\n methods:\n - name: \"GetUser\"\n retries: 1\n loadbalance: \"random\"\nprotocols:\n \"dubbo\":\n name: \"dubbo\"\n port: 20000"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中service指定了要暴露的rpc-service名(\"UserProvider),暴露的協議名(\"dubbo\"),註冊的協議名(\"demoZk\"),暴露的服務所處的interface,負載均衡策略,集羣失敗策略,調用的方法等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中中間服務的協議名需要和registries下的mapkey對應,暴露的協議名需要和protocols下的mapkey對應"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看到上述例子中,使用了dubbo作爲暴露協議,使用了zookeeper作爲中間註冊協議,並且給定了端口。如果zk需要設置用戶名和密碼,也可以在配置中寫好。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1.3 配置文件的讀入和檢查"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"config/config_loader.go:: Load()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上述example的main函數中,有config.Load()函數的直接調用,該函數執行細節如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"// Load Dubbo Init\nfunc Load() {\n\t// init router\n\tinitRouter()\n\t// init the global event dispatcher\n\textension.SetAndInitGlobalDispatcher(GetBaseConfig().EventDispatcherType)\n\t// start the metadata report if config set\n\tif err := startMetadataReport(GetApplicationConfig().MetadataType, GetBaseConfig().MetadataReportConfig); err != nil {\n logger.Errorf(\"Provider starts metadata report error, and the error is {%#v}\", err)\n return\n\t}\n\t// reference config\n\tloadConsumerConfig()\n\t// service config\n\tloadProviderConfig()\n\t// init the shutdown callback\n\tGracefulShutdownInit()\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在本文中,我們重點關心loadConsumerConfig()和loadProviderConfig()兩個函數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於provider端,可以看到loadProviderConfig()函數代碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a8/a8a9d5ab5174783ef11cc2baa49c560b.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前半部分是配置的讀入和檢查,進入for循環後,是單個service的暴露起始點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面提到,在配置文件中已經寫好了要暴露的service的種種信息,比如服務名、interface名、method名等等。在圖中for循環內,會將所有service的服務依次實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"for循環的第一行,根據key調用GetProviderService函數,拿到註冊的rpcService實例,這裏對應上述提到的init函數中用戶手動註冊的自己實現的rpc-service實例:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8b/8bb9a2e1836795b7146d5c0acc88c7a9.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個對象也就成爲了for循環中的rpcService變量,將這個對象註冊通過Implement函數寫到sys(ServiceConfig類型)上,設置好sys的key和協議組,最終調用了sys的Export方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此處對應流程圖的部分:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6d/6d4f49ed967e0df0e0a2d1eaf1db128b.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,框架配置結構體已經拿到了所有service有關的配置,以及用戶定義好的rpc-service實例,他觸發了Export方法,旨在將自己的實例暴露出去。這是Export調用鏈的起始點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2 原始service封裝入proxy_invoker"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"config/service_config.go :: Export()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來進入ServiceConfig.Export()函數:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個函數進行了一些細碎的操作,比如爲不同的協議分配隨機端口,如果指定了多箇中心註冊協議,則會將服務通過多箇中心註冊協議的registryProtocol暴露出去,我們只關心對於一個註冊協議是如何操作的。還有一些操作比如生成調用url和註冊url,用於爲暴露做準備。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.1 首先通過配置生成對應registryUrl和serviceUrl"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d2/d27db4b336ea63941233caed29fd8601.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"registryUrl是用來向中心註冊組件發起註冊請求的,對於zookeeper的話,會傳入其ip和端口號,以及附加的用戶名密碼等信息"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個regUrl目前只存有註冊(zk)相關信息,後續會補寫入ServiceIvk,即服務調用相關信息,裏面包含了方法名,參數等..."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.2 對於一個註冊協議,將傳入的rpc-service實例註冊在common.ServiceMap"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b4/b4edc3f7f21f8b42a6effaed1d402cde.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個Register函數將服務實例註冊了兩次,一次是以Interface爲key寫入接口服務組內,一次是以interface和proto爲key寫入特定的一個唯一的服務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後續會從common.Map裏面取出來這個實例。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.3 獲取默認代理工廠,將實例封裝入代理invoker"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"// 拿到一個proxyInvoker,這個invoker的url是傳入的regUrl,這個地方將上面註冊的service實例封裝成了invoker\n// 這個GetProxyFactory返回的默認是common/proxy/proxy_factory/default.go\n// 這個默認工廠調用GetInvoker獲得默認的proxyInvoker,保存了當前註冊url\ninvoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl)\n// 暴露出來 生成exporter,開啓tcp監聽\n// 這裏就該跳到registry/protocol/protocol.go registryProtocol 調用的Export,將當前proxyInvoker導出\nexporter = c.cacheProtocol.Export(invoker)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這一步的GetProxyFactory(\"default\")方法獲取默認代理工廠,通過傳入上述構造的regUrl,將url封裝入代理invoker。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以進入common/proxy/proxy_factory/default.go::ProxyInvoker.Invoke()函數裏,看到對於common.Map取用爲svc的部分,以及關於svc對應Method的實際調用Call的函數如下:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/75/7551e09bcf873a8b2171b4e82a8f13f9.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到這裏,上面GetInvoker(*regUrl)返回的invoker即爲proxy"},{"type":"text","marks":[{"type":"italic"}],"text":"invoker,他封裝好了用戶定義的rpc"},{"type":"text","text":"service,並將具體的調用邏輯封裝入了Invoke函數內。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼使用Proxy_invoker來調用?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我認爲,通過這個proxy_invoke調用用戶的功能函數,調用方式將更加抽象化,可以在代碼中看到,通過ins和outs來定義入參和出參,將整個調用邏輯抽象化爲invocation結構體,而將具體的函數名的選擇,參數向下傳遞,reflect反射過程封裝在invoke函數內,這樣的設計更有利於之後遠程調用。我認爲這是dubbo Invoke調用鏈的設計思想。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,實現了圖中對應的部分:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.3 registry協議在zkRegistry上暴露上面的proxy_invoker"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面,我們執行到了exporter = c.cacheProtocol.Export(invoker)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的cacheProtocol爲一層緩存設計,對應到原始的demo上,這裏是默認實現好的registryProtocol"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"registry/protocol/protocol.go:: Export()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個函數內構造了多個EventListener,非常有java的設計感。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們只關心服務暴露的過程,先忽略這些監聽器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.3.1 獲取註冊url和服務url"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/08/083ed9be0fd435e414d48b6d0623a0a9.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.3.2 獲取註冊中心實例zkRegistry"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8d/8df22189b6679eae919f7139af4e3690.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一層緩存操作,如果cache沒有需要從common裏面重新拿zkRegistry"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.3.3 zkRegistry調用Registry方法,在zookeeper上註冊dubboPath"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述拿到了具體的zkRegistry實例,該實例的定義在"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"registry/zookeeper/registry.go"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f2/f2ed0f8a04a434074cdfb825123662cf.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該結構體組合了registry.BaseRegistry結構,base結構定義了註冊器基礎的功能函數,比如Registry、Subscribe等,但在這些默認定義的函數內部,還是會調用facade層(zkRegistry層)的具體實現函數,這一設計模型能在保證已有功能函數不需要重複定義的同時,引入外層函數的實現,類似於結構體繼承卻又複用了代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這一設計模式我認爲值得學習。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們查看上述registry/protocol/protocol.go:: Export()函數,直接調用了:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"// 1. 通過zk註冊器,調用Register()函數,將已有@root@rawurl註冊到zk上\n\terr := reg.Register(*registeredProviderUrl)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將已有RegistryUrl註冊到了zkRegistry上。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這一步調用了baseRegistry的Register函數,進而調用zkRegister的DoRegister函數,進而調用:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/67/67b793a6cfae4f082c4fb39734cdd5fc.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個函數裏,將對應root創造一個新的節點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/29/2919ac7fc349afb08d6f2fb3aeef24df.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"並且寫入具體node信息,node爲url經過encode的結果,"},{"type":"text","marks":[{"type":"strong"}],"text":"包含了服務端的調用方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這部分的代碼較爲複雜,具體可以看baseRegistry的 "},{"type":"link","attrs":{"href":"https://github.com/apache/dubbo-go/blob/811f4f1a2dcc75b4294f62cb795da0f3f399be93/registry/base_registry.go#L243","title":""},"content":[{"type":"text","text":"processURL()函數"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,將服務端調用url註冊到了zookeeper上,而客戶端如果想獲取到這個url,只需要傳入特定的dubboPath,向zk請求即可。目前client是可以獲取到訪問方式了,但服務端的特定服務還沒有啓動,還沒有開啓特定協議端口的監聽,這也是registry/protocol/protocol.go:: Export()函數接下來要做的事情:"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.3.4 proxy"},{"type":"text","marks":[{"type":"italic"}],"text":"invoker 封裝入wrapped"},{"type":"text","text":"invoker,得到filter調用鏈"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"// invoker封裝入warppedInvoker\nwrappedInvoker := newWrappedInvoker(invoker, providerUrl)\n// 經過爲invoker增加filter調用鏈,再使用dubbo協議Export,開啓service並且返回了Exporter 。\n// export_1\ncachedExporter = extension.GetProtocol(protocolwrapper.FILTER).Export(wrappedInvoker)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新建一個WrappedInvoker,用於之後鏈式調用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"拿到提前實現並註冊好的ProtocolFilterWrapper,調用Export方法,進一步暴露。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"protocol/protocolwrapped/protocol"},{"type":"text","marks":[{"type":"italic"}],"text":"filter"},{"type":"text","text":"wrapper.go:Export()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b4/b4b1d50cb0e9475d16073263a4e09fd5.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"protocol/protocolwrapped/protocol"},{"type":"text","marks":[{"type":"italic"}],"text":"filter"},{"type":"text","text":"wrapper.go:buildInvokerChain"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/dd/ddbe7ca0ec13c69c31e09ceb07ad38d6.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可見,根據配置的內容,通過鏈式調用的構造,層層將proxy_invoker包裹在調用鏈的最底部,最終返回一個調用鏈invoker。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對應圖中部分:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3d/3ddce878a3e356b288234c9024a58a9e.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"至此,我們已經拿到filter調用鏈,期待將這個chain暴露到特定端口,用於相應請求事件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.3.5 通過dubbo協議暴露wrapped_invoker"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"protocol/protocolwrapped/protocol"},{"type":"text","marks":[{"type":"italic"}],"text":"filter"},{"type":"text","text":"wrapper.go:Export()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"// 通過dubbo協議Export dubbo_protocol調用的 export_2\n\treturn pfw.protocol.Export(invoker)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回到上述Export函數的最後一行,調用了dubboProtocol的Export方法,將上述chain真正暴露。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該Export方法的具體實現在"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"protocol/dubbo/dubbo_protocol.go: Export()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/03/0350eb38d0974b72cc4d797b2cee69d8.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這一函數做了兩個事情:構造觸發器、啓動服務"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"將傳入的Invoker調用chain進一步封裝,封裝成一個exporter,再將這個export放入map保存。"},{"type":"text","marks":[{"type":"strong"}],"text":"注意!這裏昂exporter放入了SetExporterMap中,在下面服務啓動的時候,會以註冊事件監聽器的形式將這個exporter取出!"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"調用dubboProtocol的openServer方法,開啓一個針對特定端口的監聽"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3b/3b6dbaf7096a1de497fe3cc02ba5c582.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 如上圖所示,一個Session被傳入,開啓對應端口的事件監聽。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 至此構造出了exporter,完成圖中部分:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/99/995a81a977b34be52c56df8fdb75565c.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.4 註冊觸發動作"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述只是啓動了服務,但還沒有看到觸發事件的細節,點進上面的s.newSession可以看到,dubbo協議爲一個getty的session默認使用瞭如下配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/85/85c3397843eaee263685d4aca92a0981.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中很重要的一個配置是EventListener,傳入的是dubboServer的默認rpcHandler"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"protocol/dubbo/listener.go:OnMessage()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"rpcHandler有一個實現好的OnMessage函數,根據getty的API,當client調用該端口時,會觸發OnMessage"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"// OnMessage notified when RPC server session got any message in connection\nfunc (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這一函數實現了在getty session接收到rpc 調用後的一系列處理:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"傳入包的解析"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6a/6ab007df392c1034307736a7d6cb0674.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據請求包構造請求url"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3b/3b972f413c1fb3139d63e588d1cdba2b.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"拿到對應請求key,找到要被調用的exporter"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/36/367bbe3a9918de44af29b11ca859c5e3.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"拿到對應的Invoker"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bc/bcfec0d805233925717bb2d45c8f4b9f.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"構造invocation"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/41/416d0d5563c46e3594d7fa08905215b6.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"調用"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3e/3e2fc769e381120a82991a168e0ddba8.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"返回"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/54/54b36c11e1ea34fdc29d008ba8301f4d.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 整個被調過程一氣呵成。實現了從getty.Session的調用事件,到經過層層封裝的invoker的調用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 至此,一次rpc調用得以正確返回。3. 小結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於Invoker的層層封裝"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 能把一次調用抽象成一次invoke,能把一個協議抽象成針對invoke的封裝,能把針對一次invoke所做出的特定改變封裝到invoke函數內部,可以降低模塊之間的耦合性。層層封裝邏輯更加清晰"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於URL的抽象"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 關於dubbo的統一化請求對象URL的極度抽象是我之前沒有見過的... 我認爲這樣封裝能保證請求參數列表的簡化和一致。但我認爲在開發的過程中,濫用極度抽象的接口可能造成...debug的困難?以及不知道哪些字段是當前已經封裝好的,哪些字段是無用的。"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於協議的理解"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 之前理解的協議還是太過具體化了,而關於dubbo-go對於dubboProtocol的協議,我認爲是基於getty的進一步封裝,他定義了客戶端和服務端,對於getty的session應該有哪些特定的操作,從而保證主調和被調的協議一致性,而這種保證也是一種協議的體現,是由dubbo協議來規範的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你有任何疑問,歡迎釘釘掃碼加入交流羣【釘釘羣號 23331795】:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b8/b80717516f8abccb3f949c88f271a816.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者簡介"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"李志信 (GitHubID LaurenceLiZhixin),中山大學軟件工程專業在校學生,擅長使用 Java/Go 語言,專注於雲原生和微服務等技術方向。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章