主頁 >  其他 > Dubbo-go 原始碼筆記(一)Server 端開啟服務程序

Dubbo-go 原始碼筆記(一)Server 端開啟服務程序

2020-11-06 05:25:31 其他

1.png

作者 | 李志信

dubbo-go 原始碼:https://github.com/apache/dubbo-go

導讀:隨著微服務架構的流行,許多高性能 rpc 框架應運而生,由阿里開源的 dubbo 框架 go 語言版本的 dubbo-go 也成為了眾多開發者不錯的選擇,本文將介紹 dubbo-go 框架的基本使用方法,以及從 export 呼叫鏈的角度進行 server 端原始碼導讀,希望能引導讀者進一步認識這款框架,下周將發表本文的姊妹篇:《從 client 端原始碼導讀 dubbo-go 框架》,

當拿到一款框架之后,一種不錯的原始碼閱讀方式大致如下:從運行最基礎的 helloworld demo 原始碼開始 —> 再查看組態檔 —> 開啟各種依賴服務(比如zk、consul) —> 開啟服務端 —> 再到通過 client 呼叫服務端 —> 列印完整請求日志和回包,呼叫成功之后,再根據框架的設計模型,從組態檔決議開始,自頂向下遞閱讀整個框架的呼叫堆疊,

對于 C/S 模式的 rpc 請求來說,整個呼叫堆疊被拆成了 client 和 server 兩部分,所以可以分別從 server 端的組態檔決議閱讀到 server 端的監聽啟動,從 client 端的組態檔決議閱讀到一次 invoker Call 呼叫,這樣一次完整請求就明晰了起來,

運行官網提供的 helloworld-demo

官方 demo 相關鏈接:https://github.com/dubbogo/dubbo-samples/tree/master/golang/helloworld/dubbo

1. dubbo-go 2.7 版本 QuickStart

1)開啟一個 go-server 服務

  • 將倉庫 clone 到本地

$ git clone https://github.com/dubbogo/dubbo-samples.git

  • 進入 dubbo 目錄

$ cd dubbo-samples/golang/helloworld/dubbo

進入目錄后可看到四個檔案夾,分別支持 go 和 java 的 client 以及 server,我們嘗試運行一個 go 的 server,進入 app 子檔案夾內,可以看到里面保存了 go 檔案,

$ cd go-server/app

  • sample 檔案結構

可以在 go-server 里面看到三個檔案夾:app、assembly、profiles,

其中 app 檔案夾下保存 go 原始碼,assembly 檔案夾下保存可選的針對特定環境的 build 腳本,profiles 下保存組態檔,對于 dubbo-go 框架,組態檔非常重要,沒有檔案將導致服務無法啟動,

  • 設定指向組態檔的環境變數

由于 dubbo-go 框架依賴組態檔啟動,讓框架定位到組態檔的方式就是通過環境變數來找,對于 server 端需要兩個必須配置的環境變數:CONF_PROVIDER_FILE_PATH、APP_LOG_CONF_FILE,分別應該指向服務端組態檔、日志組態檔,

在 sample 里面,我們可以使用 dev 環境,即 profiles/dev/log.yml 和 profiles/dev/server.yml 兩個檔案,在 app/ 下,通過命令列中指定好這兩個檔案:

$ export CONF_PROVIDER_FILE_PATH="…/profiles/dev/server.yml"

$ export APP_LOG_CONF_FILE="…/profiles/dev/log.yml"

  • 設定 go 代理并運行服務

$ go run .

如果提示 timeout,則需要設定 goproxy 代理,

$ export GOPROXY=“http://goproxy.io”

再運行 go run 即可開啟服務,

2)運行 zookeeper

安裝 zookeeper,并運行 zkServer, 默認為 2181 埠,

3)運行 go-client 呼叫 server 服務

  • 進入 go-client 的原始碼目錄

$ cd go-client/app

  • 同理,在 /app 下配置環境變數

$ export CONF_CONSUMER_FILE_PATH="…/profiles/dev/client.yml"

$ export APP_LOG_CONF_FILE="…/profiles/dev/log.yml"

配置 go 代理:

$ export GOPROXY=“http://goproxy.io”

  • 運行程式

$ go run .

即可在日志中找到列印出的請求結果:

response result: &{A001 Alex Stocks 18 2020-10-28 14:52:49.131 +0800 CST}

同樣,在運行的 server 中,也可以在日志中找到列印出的請求:

req:[]interface {}{“A001”}

rsp:main.User{Id:“A001”, Name:“Alex Stocks”, Age:18, Time:time.Time{…}

恭喜!一次基于 dubbo-go 的 rpc 呼叫成功,

4)常見問題

  • 當日志開始部分出現 profiderInit 和 ConsumerInit 均失敗的日志,檢查環境變數中配置路徑是否正確,組態檔是否正確,

  • 當日志中出現 register 失敗的情況,一般為向注冊中心注冊失敗,檢查注冊中心是否開啟,檢查組態檔中關于 register 的埠是否正確,

  • sample 的默認開啟埠為 20000,確保啟動前無占用,

2. 配置環境變數

export APP_LOG_CONF_FILE="../profiles/dev/log.yml"
export CONF_CONSUMER_FILE_PATH="../profiles/dev/client.yml"

3. 服務端原始碼

1)目錄結構

dubbo-go 框架的 example 提供的目錄如下:

2.png

  • app/ 檔案夾下存放原始碼,可以自己撰寫環境變數配置腳本 buliddev.sh

  • assembly/ 檔案夾下存放不同平臺的構建腳本

  • profiles/ 檔案夾下存放不同環境的組態檔

  • target/ 檔案夾下存放可執行檔案

2)關鍵原始碼

原始碼放置在 app/ 檔案夾下,主要包含 server.go 和 user.go 兩個檔案,顧名思義,server.go 用于使用框架開啟服務以及注冊傳輸協議;user.go 則定義了 rpc-service 結構體,以及傳輸協議的結構,

  • user.go
func init() {
	config.SetProviderService(new(UserProvider))
	// ------for hessian2------
	hessian.RegisterPOJO(&User{})
}
type User struct {
	Id   string
	Name string
	Age  int32
	Time time.Time
}
type UserProvider struct {
}
func (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User, error) {

可以看到,user.go 中存在 init 函式,是服務端代碼中最先被執行的部分,User 為用戶自定義的傳輸結構體,UserProvider 為用戶自定義的 rpc_service;包含一個 rpc 函式,GetUser,當然,用戶可以自定義其他的 rpc 功能函式,

在 init 函式中,呼叫 config 的 SetProviderService 函式,將當前 rpc_service 注冊在框架 config 上,

可以查看 dubbo 官方檔案提供的設計圖:

3.png

service 層下面就是 config 層,用戶服務會逐層向下注冊,最終實作服務端的暴露,

rpc-service 注冊完畢之后,呼叫 hessian 介面注冊傳輸結構體 User,

至此,init 函式執行完畢,

  • server.go
// they are necessary:
//  	export CONF_PROVIDER_FILE_PATH="xxx"
//  	export APP_LOG_CONF_FILE="xxx"
func main() {
	hessian.RegisterPOJO(&User{})
	config.Load()
	initSignal()
}
func initSignal() {
	signals := make(chan os.Signal, 1)
	...

之后執行 main 函式,

main 函式中只進行了兩個操作,首先使用 hessian 注冊組件將 User 結構體注冊(與之前略有重復),從而可以在接下來使用 getty 打解包,

之后呼叫 config.Load 函式,該函式位于框架 config/config_loader.go 內,這個函式是整個框架服務的啟動點,下面會詳細講這個函式內重要的配置處理程序,執行完 Load() 函式之后,組態檔會讀入框架,之后根據組態檔的內容,將注冊的 service 實作到配置結構里,再呼叫 Export 暴露給特定的 registry,進而開啟特定的 service 進行對應埠的 tcp 監聽,成功啟動并且暴露服務,

最終開啟信號監聽 initSignal() 優雅地結束一個服務的啟動程序,

4. 客戶端原始碼

客戶端包含 client.go 和 user.go 兩個檔案,其中 user.go 與服務端完全一致,不再贅述,

  • client.go
// they are necessary:
//  	export CONF_CONSUMER_FILE_PATH="xxx"
//  	export APP_LOG_CONF_FILE="xxx"
func main() {
	hessian.RegisterPOJO(&User{})
	config.Load()
	time.Sleep(3e9)
	println("\n\n\nstart to test dubbo")
	user := &User{}
	err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user)
	if err != nil {
  panic(err)
	}
	println("response result: %v\n", user)
	initSignal()
}

main 函式和服務端也類似,首先將傳輸結構注冊到 hessian 上,再呼叫 config.Load() 函式,在下文會介紹,客戶端和服務端會根據配置型別執行 config.Load() 中特定的函式 loadConsumerConfig() 和 loadProviderConfig(),從而達到“開啟服務”、“呼叫服務”的目的,

加載完配置之后,還是通過實作服務、增加函式 proxy、申請 registry 和 reloadInvoker 指向服務端 ip 等操作,重寫了客戶端實體 userProvider 的對應函式,這時再通過呼叫 GetUser 函式,可以直接通過 invoker,呼叫到已經開啟的服務端,實作 rpc 程序,

下面會從 server 端和 client 端兩個角度,詳細講解服務啟動、registry 注冊和呼叫程序,

5. 自定義組態檔(非環境變數)方法

1)服務端自定義組態檔

  • var providerConfigStr = xxxxx// 組態檔內容,可以參考 log 和 client,在這里你可以定義組態檔的獲取方式,比如配置中心,本地檔案讀取,

log 地址:https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/log.yml

client 地址:https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/client.yml

  • config.Load() 之前設定配置,例如:
func main() {
	hessian.RegisterPOJO(&User{})
	providerConfig := config.ProviderConfig{}
	yaml.Unmarshal([]byte(providerConfigStr), &providerConfig)
	config.SetProviderConfig(providerConfig)
	defaultServerConfig := dubbo.GetDefaultServerConfig()
	dubbo.SetServerConfig(defaultServerConfig)
	logger.SetLoggerLevel("warn") // info,warn
	config.Load()
	select {
	}
}

2)客戶端自定義組態檔

  • var consumerConfigStr = xxxxx// 組態檔內容,可以參考 log 和 clien,在這里你可以定義組態檔的獲取方式,比如配置中心,本地檔案讀取,

  • config.Load() 之前設定配置,例如:

func main() {
     p := config.ConsumerConfig{}
     yaml.Unmarshal([]byte(consumerConfigStr), &p)
     config.SetConsumerConfig(p)
     defaultClientConfig := dubbo.GetDefaultClientConfig()
     dubbo.SetClientConf(defaultClientConfig)
     logger.SetLoggerLevel("warn") // info,warn
     config.Load()

     user := &User{}
     err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user)
     if err != nil {
         log.Print(err)
         return
     }
  log.Print(user)
}

Server 端

服務暴露程序涉及到多次原始 rpcService 的封裝、暴露,網上其他文章的圖感覺太過籠統,在此,簡要地繪制了一個用戶定義服務的資料流圖:

4.png

1. 加載配置

1)框架初始化

在加載配置之前,框架提供了很多已定義好的協議、工廠等組件,都會在對應模塊 init 函式內注冊到 extension 模塊上,以供接下來組態檔中進行選用,

其中重要的有:

  • 默認函式代理工廠:common/proxy/proxy_factory/default.go
func init() {
	extension.SetProxyFactory("default", NewDefaultProxyFactory)
}

它的作用是將原始 rpc-service 進行封裝,形成 proxy_invoker,更易于實作遠程 call 呼叫,詳情可見其 invoke 函式,

  • 注冊中心注冊協議
    registry/protocol/protocol.go
func init() {
	extension.SetProtocol("registry", GetProtocol)
}

它負責將 invoker 暴露給對應注冊中心,比如 zk 注冊中心,

  • zookeeper 注冊協議:registry/zookeeper/zookeeper.go
func init() {
	extension.SetRegistry("zookeeper", newZkRegistry)
}

它合并了 base_resiger,負責在服務暴露程序中,將服務注冊在 zookeeper 注冊器上,從而為呼叫者提供呼叫方法,

  • dubbo 傳輸協議:protocol/dubbo/dubbo.go
func init() {
	extension.SetProtocol(DUBBO, GetProtocol)
}

它負責監聽對應埠,將具體的服務暴露,并啟動對應的事件 handler,將遠程呼叫的 event 事件傳遞到 invoker 內部,呼叫本地 invoker 并獲得執行結果回傳,

  • filter 包裝呼叫鏈協議:protocol/protocolwrapper/protocol_filter_wrapper.go
func init() {
	extension.SetProtocol(FILTER, GetProtocol)
}

它負責在服務暴露程序中,將代理 invoker 打包,通過配置好的 filter 形成呼叫鏈,并交付給 dubbo 協議進行暴露,

上述提前注冊好的框架已實作的組件,在整個服務暴露呼叫鏈中都會用到,會根據配置取其所需,

2)組態檔

服務端需要的重要配置有三個欄位:services、protocols、registries,

profiles/dev/server.yml:

registries :
  "demoZk":
    protocol: "zookeeper"
    timeout	: "3s"
    address: "127.0.0.1:2181"
services:
  "UserProvider":
    # 可以指定多個registry,使用逗號隔開;不指定默認向所有注冊中心注冊
    registry: "demoZk"
    protocol : "dubbo"
    # 相當于dubbo.xml中的interface
    interface : "com.ikurento.user.UserProvider"
    loadbalance: "random"
    warmup: "100"
    cluster: "failover"
    methods:
    - name: "GetUser"
      retries: 1
      loadbalance: "random"
protocols:
  "dubbo":
    name: "dubbo"
    port: 20000

其中 service 指定了要暴露的 rpc-service 名("UserProvider)、暴露的協議名(“dubbo”)、注冊的協議名(“demoZk”)、暴露的服務所處的 interface、負載均衡策略、集群失敗策略及呼叫的方法等等,

其中,中間服務的協議名需要和 registries 下的 mapkey 對應,暴露的協議名需要和 protocols 下的 mapkey 對應,

可以看到上述例子中,使用了 dubbo 作為暴露協議,使用了 zookeeper 作為中間注冊協議,并且給定了埠,如果 zk 需要設定用戶名和密碼,也可以在配置中寫好,

3)組態檔的讀入和檢查

config/config_loader.go:: Load()

在上述 example 的 main 函式中,有 config.Load() 函式的直接呼叫,該函式執行細節如下:

// Load Dubbo Init
func Load() {
	// init router
	initRouter()
	// init the global event dispatcher
	extension.SetAndInitGlobalDispatcher(GetBaseConfig().EventDispatcherType)
	// start the metadata report if config set
	if err := startMetadataReport(GetApplicationConfig().MetadataType, GetBaseConfig().MetadataReportConfig); err != nil {
  logger.Errorf("Provider starts metadata report error, and the error is {%#v}", err)
  return
	}
	// reference config
	loadConsumerConfig()
	// service config
	loadProviderConfig()
	// init the shutdown callback
	GracefulShutdownInit()
}

在本文中,我們重點關心 loadConsumerConfig() 和 loadProviderConfig() 兩個函式,

對于 provider 端,可以看到 loadProviderConfig() 函式代碼如下:

5.png

前半部分是配置的讀入和檢查,進入 for 回圈后,是單個 service 的暴露起始點,

前面提到,在組態檔中已經寫好了要暴露的 service 的種種資訊,比如服務名、interface 名、method 名等等,在圖中 for 回圈內,會將所有 service 的服務依次實作,

for 回圈的第一行,根據 key 呼叫 GetProviderService 函式,拿到注冊的 rpcService 實體,這里對應上述提到的 init 函式中,用戶手動注冊的自己實作的 rpc-service 實體:

6.png

這個物件也就成為了 for 回圈中的 rpcService 變數,將這個物件注冊通過 Implement 函式寫到 sys(ServiceConfig 型別)上,設定好 sys 的 key 和協議組,最終呼叫了 sys 的 Export 方法,

此處對應流程圖的部分:

7.png

至此,框架配置結構體已經拿到了所有 service 有關的配置,以及用戶定義好的 rpc-service 實體,它觸發了 Export 方法,旨在將自己的實體暴露出去,這是 Export 呼叫鏈的起始點,

2. 原始 service 封裝入 proxy_invoker

config/service_config.go :: Export()

接下來進入 ServiceConfig.Export() 函式.

這個函式進行了一些細碎的操作,比如為不同的協議分配隨機埠,如果指定了多個中心注冊協議,則會將服務通過多個中心注冊協議的 registryProtocol 暴露出去,我們只關心對于一個注冊協議是如何操作的,還有一些操作比如生成呼叫 url 和注冊 url,用于為暴露做準備,

1)首先通過配置生成對應 registryUrl 和 serviceUrl

8.png

registryUrl 是用來向中心注冊組件發起注冊請求的,對于 zookeeper 的話,會傳入其 ip 和埠號,以及附加的用戶名密碼等資訊,

這個 regUrl 目前只存有注冊(zk)相關資訊,后續會補寫入 ServiceIvk,即服務呼叫相關資訊,里面包含了方法名,引數等…

2)對于一個注冊協議,將傳入的 rpc-service 實體注冊在 common.ServiceMap

9.png

這個 Register 函式將服務實體注冊了兩次,一次是以 Interface 為 key 寫入介面服務組內,一次是以 interface 和 proto 為 key 寫入特定的一個唯一的服務,

后續會從 common.Map 里面取出來這個實體,

3)獲取默認代理工廠,將實體封裝入代理 invoker

// 拿到一個proxyInvoker,這個invoker的url是傳入的regUrl,這個地方將上面注冊的service實體封裝成了invoker
// 這個GetProxyFactory回傳的默認是common/proxy/proxy_factory/default.go
// 這個默認工廠呼叫GetInvoker獲得默認的proxyInvoker,保存了當前注冊url
invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl)
// 暴露出來 生成exporter,開啟tcp監聽
// 這里就該跳到registry/protocol/protocol.go registryProtocol 呼叫的Export,將當前proxyInvoker匯出
exporter = c.cacheProtocol.Export(invoker)

這一步的 GetProxyFactory(“default”) 方法獲取默認代理工廠,通過傳入上述構造的 regUrl,將 url 封裝入代理 invoker,

可以進入 common/proxy/proxy_factory/default.go::ProxyInvoker.Invoke() 函式里,看到對于 common.Map 取用為 svc 的部分,以及關于 svc 對應 Method 的實際呼叫 Call 的函式如下:

10.png

到這里,上面 GetInvoker(*regUrl) 回傳的 invoker 即為 proxy_invoker,它封裝好了用戶定義的 rpc_service,并將具體的呼叫邏輯封裝入了 Invoke 函式內,

為什么使用 Proxy_invoker 來呼叫?

通過這個 proxy_invoke 呼叫用戶的功能函式,呼叫方式將更加抽象化,可以在代碼中看到,通過 ins 和 outs 來定義入參和出參,將整個呼叫邏輯抽象化為 invocation 結構體,而將具體的函式名的選擇、引數向下傳遞和 reflect 反射程序封裝在 invoke 函式內,這樣的設計更有利于之后遠程呼叫,個人認為這是 dubbo Invoke 呼叫鏈的設計思想,

至此,實作了圖中對應的部分:

11.png

3. registry 協議在 zkRegistry 上暴露上面的 proxy_invoker

上面,我們執行到了 exporter = c.cacheProtocol.Export(invoker),

這里的 cacheProtocol 為一層快取設計,對應到原始的 demo 上,這里是默認實作好的 registryProtocol,

registry/protocol/protocol.go:: Export()

這個函式內構造了多個 EventListener,非常有 java 的設計感,

我們只關心服務暴露的程序,先忽略這些監聽器,

1)獲取注冊 url 和服務 url

12.png

2)獲取注冊中心實體 zkRegistry

13.png

一層快取操作,如果 cache 沒有需要從 common 里面重新拿 zkRegistry,

3)zkRegistry 呼叫 Registry 方法,在 zookeeper 上注冊 dubboPath

上述拿到了具體的 zkRegistry 實體,該實體的定義在:registry/zookeeper/registry.go,

14.png

該結構體組合了 registry.BaseRegistry 結構,base 結構定義了注冊器基礎的功能函式,比如 Registry、Subscribe 等,但在這些默認定義的函式內部,還是會呼叫 facade 層(zkRegistry 層)的具體實作函式,這一設計模型能在保證已有功能函式不需要重復定義的同時,引入外層函式的實作,類似于結構體繼承卻又復用了代碼,這一設計模式值得學習,

我們查看上述 registry/protocol/protocol.go:: Export() 函式,直接呼叫了:

// 1. 通過zk注冊器,呼叫Register()函式,將已有@root@rawurl注冊到zk上
	err := reg.Register(*registeredProviderUrl)

將已有 RegistryUrl 注冊到了 zkRegistry 上,

這一步呼叫了 baseRegistry 的 Register 函式,進而呼叫 zkRegister 的 DoRegister 函式,進而呼叫:

15.png

在這個函式里,將對應 root 創造一個新的節點,

16.png

并且寫入具體 node 資訊,node 為 url 經過 encode 的結果,包含了服務端的呼叫方式,

這部分的代碼較為復雜,具體可以看 baseRegistry 的 processURL() 函式:http://t.tb.cn/6Xje4bijnsIDNaSmyPc4Ot,

至此,將服務端呼叫 url 注冊到了 zookeeper 上,而客戶端如果想獲取到這個 url,只需要傳入特定的 dubboPath,向 zk 請求即可,目前 client 是可以獲取到訪問方式了,但服務端的特定服務還沒有啟動,還沒有開啟特定協議埠的監聽,這也是 registry/protocol/protocol.go:: Export() 函式接下來要做的事情,

4)proxy_invoker 封裝入 wrapped_invoker,得到 filter 呼叫鏈

	// invoker封裝入warppedInvoker
	wrappedInvoker := newWrappedInvoker(invoker, providerUrl)
	// 經過為invoker增加filter呼叫鏈,再使用dubbo協議Export,開啟service并且回傳了Exporter ,
	// export_1
	cachedExporter = extension.GetProtocol(protocolwrapper.FILTER).Export(wrappedInvoker)

新建一個 WrappedInvoker,用于之后鏈式呼叫,

拿到提前實作并注冊好的 ProtocolFilterWrapper,呼叫 Export 方法,進一步暴露,

protocol/protocolwrapped/protocol_filter_wrapper.go:Export()

17.png

protocol/protocolwrapped/protocol_filter_wrapper.go:buildInvokerChain

18.png

可見,根據配置的內容,通過鏈式呼叫的構造,將 proxy_invoker 層層包裹在呼叫鏈的最底部,最侄訓傳一個呼叫鏈 invoker,

對應圖中部分:

19.png

至此,我們已經拿到 filter 呼叫鏈,期待將這個 chain 暴露到特定埠,用于相應請求事件,

5)通過 dubbo 協議暴露 wrapped_invoker

protocol/protocolwrapped/protocol_filter_wrapper.go:Export()

// 通過dubbo協議Export  dubbo_protocol呼叫的 export_2
	return pfw.protocol.Export(invoker)

回到上述 Export 函式的最后一行,呼叫了 dubboProtocol 的 Export 方法,將上述 chain 真正暴露,

該 Export 方法的具體實作在:protocol/dubbo/dubbo_protocol.go: Export(),

20.png

這一函式做了兩個事情:構造觸發器、啟動服務,

  • 將傳入的 Invoker 呼叫 chain 進一步封裝,封裝成一個 exporter,再將這個 export 放入 map 保存,注意!這里把 exporter 放入了 SetExporterMap中,在下面服務啟動的時候,會以注冊事件監聽器的形式將這個 exporter 取出!

  • 呼叫 dubboProtocol 的 openServer 方法,開啟一個針對特定埠的監聽,

21.png

如上圖所示,一個 Session 被傳入,開啟對應埠的事件監聽,

至此構造出了 exporter,完成圖中部分:

22.png

4. 注冊觸發動作

上述只是啟動了服務,但還沒有看到觸發事件的細節,點進上面的 s.newSession 可以看到,dubbo 協議為一個 getty 的 session 默認使用了如下配置:

23.png

其中很重要的一個配置是 EventListener,傳入的是 dubboServer 的默認 rpcHandler,

protocol/dubbo/listener.go:OnMessage()

rpcHandler 有一個實作好的 OnMessage 函式,根據 getty 的 API,當 client 呼叫該埠時,會觸發 OnMessage,

// OnMessage notified when RPC server session got any message in connection
func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {

這一函式實作了在 getty session 接收到 rpc 呼叫后的一系列處理:

  • 傳入包的決議

24.png

  • 根據請求包構造請求 url

25.png

  • 拿到對應請求 key,找到要被呼叫的 exporter

26.png

  • 拿到對應的 Invoker

27.png

  • 構造 invocation

28.png

  • 呼叫

29.png

  • 回傳

30.png

整個被調程序一氣呵成,實作了從 getty.Session 的呼叫事件,到經過層層封裝的 invoker 的呼叫,

至此,一次 rpc 呼叫得以正確回傳,

小結

  • 關于 Invoker 的層層封裝

能把一次呼叫抽象成一次 invoke;能把一個協議抽象成針對 invoke 的封裝;能把針對一次 invoke 所做出的特定改變封裝到 invoke 函式內部,可以降低模塊之間的耦合性,層層封裝邏輯更加清晰,

  • 關于 URL 的抽象

關于 dubbo 的統一化請求物件 URL 的極度抽象是之前沒有見過的… 個人認為這樣封裝能保證請求引數串列的簡化和一致,但在開發的程序中,濫用極度抽象的介面可能造成… debug 的困難?以及不知道哪些欄位是當前已經封裝好的,哪些欄位是無用的,

  • 關于協議的理解

之前理解的協議還是太過具體化了,而關于 dubbo-go 對于 dubboProtocol 的協議,我認為是基于 getty 的進一步封裝,它定義了客戶端和服務端,對于 getty 的 session 應該有哪些特定的操作,從而保證主調和被調的協議一致性,而這種保證也是一種協議的體現,是由 dubbo 協議來規范的,

如果你有任何疑問,歡迎釘釘掃碼加入交流群:釘釘群號 23331795!

作者簡介

李志信 (GitHubID LaurenceLiZhixin),中山大學軟體工程專業在校學生,擅長使用 Java/Go 語言,專注于云原生和微服務等技術方向,

“阿里巴巴云原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦云原生流行技術趨勢、云原生大規模的落地實踐,做最懂云原生開發者的公眾號,”

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/203978.html

標籤:AI

上一篇:天貓Java研發崗面經(技術三面):基礎+演算法+MySQL+Redis+秒殺架構

下一篇:復盤逆襲之路!三個月時間深造,怒斬拼多多、位元組、螞蟻金服破50W年薪Offer(含自學路線圖)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more