go-micro開(kāi)發(fā)RPC服務(wù)以及運(yùn)行原理介紹
go-micro是一個(gè)知名的golang微服務(wù)框架,最新版本是v4,這篇文章將介紹go-micro v4開(kāi)發(fā)RPC服務(wù)的方法及其運(yùn)作原理。
基本概念
go-micro有幾個(gè)重要的概念,后邊開(kāi)發(fā)RPC服務(wù)和介紹其運(yùn)行原理的時(shí)候會(huì)用到,這里先熟悉下:
- Service:代表一個(gè)go-micro應(yīng)用程序,Service中包括:Server、Client、Broker、Transport、Registry、Config、Store、Cache等程序運(yùn)行所需的各個(gè)模塊。
- Server:代表一個(gè)go-micro服務(wù)器,主要函數(shù)包括:Start、Stop、Handle、Subscribe。默認(rèn)創(chuàng)建的Server是 rpcServer。
- Broker:用于處理異步消息,主要的函數(shù)包括:Connect、Publish、Subscribe。默認(rèn)的Broker是httpBroker。
- Router:用于消息處理的路由,內(nèi)部包括兩種路由方式:RPC服務(wù)映射serviceMap和消息訂閱器subscribers。
- Codec:用于消息的編解碼,主要函數(shù)包括:Marshal、Unmarshal默認(rèn)的Codec是json.Marshaler,是基于jsonpb的。RPC服務(wù)是根據(jù)請(qǐng)求頭中的Content-Type自動(dòng)創(chuàng)建的。
- Registry:用于服務(wù)發(fā)現(xiàn),主要函數(shù)包括:Register、Deregister、GetService、ListServices、Watch。默認(rèn)的Registry是mdns。
- Selector: 用于從同一個(gè)服務(wù)的多個(gè)實(shí)例之中選擇一個(gè),支持緩存,有隨機(jī)和輪詢兩種策略。
- Transport:用于同步通信,主要函數(shù)包括:Dial、Listen。它的底層基于Socket的send、recv語(yǔ)義,有多種實(shí)現(xiàn),包括http、grpc、quic等。默認(rèn)的Transport是httpTransport。
開(kāi)發(fā)RPC服務(wù)
RPC全稱是Remote Procedure Call,翻譯過(guò)來(lái)是就是:遠(yuǎn)程過(guò)程調(diào)用,中心思想是:像調(diào)用本地函數(shù)一樣調(diào)用遠(yuǎn)程函數(shù)。常見(jiàn)的Dubbo、Spring Cloud都可以稱為RPC框架,還有最近很流行的gRPC。
使用go-micro創(chuàng)建一個(gè)RPC服務(wù)很簡(jiǎn)單,共分三步走:
1、編寫proto協(xié)議文件
這個(gè)服務(wù)提供的功能很簡(jiǎn)單,名字為Hello,提供一個(gè)方法名字為Say,需要傳入一個(gè)字符串Name,然后返回一個(gè)字符串Message。這個(gè)文件我命名為 hello.proto,放到了項(xiàng)目中的 proto 文件夾中。
syntax = "proto3"; option go_package="/proto"; package Business; service Hello { rpc Say (SayRequest) returns (SayResponse); } message SayResponse { string Message = 1; } message SayRequest { string Name = 1; }
2、生成go-micro服務(wù)端代理
需要首先安裝protoc和兩個(gè)代碼生成插件。
protoc下載地址:https://github.com/protocolbuffers/protobuf/releases,保存到GO PATH/bin目錄中。同時(shí)建議將 GOPATH/bin 添加到環(huán)境變量 PATH 中,方便直接執(zhí)行相關(guān)命令。
兩個(gè)插件直接通過(guò)命令即可安裝:
go install google.golang.org/protobuf/cmd/protoc-gen-go go install go-micro.dev/v4/cmd/protoc-gen-micro@v4
然后在項(xiàng)目的目錄下執(zhí)行命令:
protoc --go_out=. --go_opt=paths=source_relative --micro_out=. --micro_opt=paths=source_relative proto/hello.proto
然后會(huì)在proto文件夾中生成兩個(gè)文件:hello.pb.go 和 hello.pb.micro.go 。
下個(gè)步驟中就要使用它們來(lái)創(chuàng)建RPC服務(wù)。
3、編寫go-micro服務(wù)
這里先把代碼貼出來(lái),然后再做一個(gè)簡(jiǎn)要說(shuō)明:
package main import ( "context" "fmt" "log" "rpchello/proto" "go-micro.dev/v4" "go-micro.dev/v4/server" ) type Hello struct{} func (s *Hello) Say(ctx context.Context, req *proto.SayRequest, rsp *proto.SayResponse) error { fmt.Println("request:", req.Name) rsp.Message = "Hello " + req.Name return nil } func main() { rpcServer := server.NewServer( server.Name("rpchello.service"), server.Address("0.0.0.0:8001"), ) proto.RegisterHelloHandler(rpcServer, &Hello{}) service := micro.NewService( micro.Server(rpcServer), ) if err := service.Run(); err != nil { log.Fatal(err) } }
上邊我們創(chuàng)建了一個(gè) Hello 類型,然后給它綁定了一個(gè)名為Say的函數(shù)。這個(gè)是和proto協(xié)議對(duì)應(yīng)的,其實(shí)是實(shí)現(xiàn)了生成代碼 hello.pb.micro.go 中的HelloHandler接口:
type HelloHandler interface { Say(context.Context, *SayRequest, *SayResponse) error }
然后main函數(shù)中是我們的重頭戲:先創(chuàng)建一個(gè)Server,默認(rèn)情況下就是rpc Server,設(shè)置它的名字、監(jiān)聽(tīng)地址等參數(shù);然后創(chuàng)建一個(gè)Service,并綁定剛剛創(chuàng)建的Server;然后使用生成的服務(wù)端代理函數(shù)將我們編寫的Hello服務(wù)注冊(cè)到Server中;最后開(kāi)啟運(yùn)行Service。
當(dāng)然只有一個(gè)服務(wù)端沒(méi)有什么意義,還得有客戶端來(lái)訪問(wèn)它。這里也給一個(gè)例子:
package main import ( "bufio" "context" "fmt" "os" "rpchello/proto" "go-micro.dev/v4" "go-micro.dev/v4/client" ) func main() { service := micro.NewService( micro.Client(client.NewClient()), ) service.Init() client := proto.NewHelloService("rpchello.service", service.Client()) rsp, err := client.Say(context.TODO(), &proto.SayRequest{Name: "BOSSMA"}) if err != nil { fmt.Println(err) } fmt.Println(rsp) fmt.Println("Press Enter key to exit the program...") in := bufio.NewReader(os.Stdin) _, _, _ = in.ReadLine() }
這里調(diào)用服務(wù)的時(shí)候沒(méi)有指定服務(wù)的地址和端口,因?yàn)閮?nèi)部走了服務(wù)發(fā)現(xiàn),服務(wù)端會(huì)自動(dòng)注冊(cè)服務(wù),客戶端會(huì)根據(jù)服務(wù)名稱查找到對(duì)應(yīng)的地址和端口。默認(rèn)的服務(wù)發(fā)現(xiàn)機(jī)制使用的是mdns。
RPC服務(wù)的運(yùn)行原理
這里從服務(wù)端的角度進(jìn)行介紹,先來(lái)看一張圖:
請(qǐng)大家參考代碼從上往下看。
NewServer 時(shí)創(chuàng)建一個(gè)rpcServer,這個(gè)rpcServer還會(huì)創(chuàng)建一個(gè)httpTransport用于程序間網(wǎng)絡(luò)通信,并綁定到當(dāng)前rpcServer。
RegisterXXXHandler 時(shí)使用我們編寫的Handler創(chuàng)建一個(gè)內(nèi)部的service實(shí)例,然后注冊(cè)這個(gè)service實(shí)例到rpcServer內(nèi)部的router中,客戶端請(qǐng)求時(shí)會(huì)用到它。這里其實(shí)可以注冊(cè)任意一個(gè)帶方法的類型,并不一定要定義proto協(xié)議,定義它只是為了協(xié)作更方便。
Service.Run 時(shí)會(huì)調(diào)用rpcServer的Start方法,這個(gè)方法內(nèi)部會(huì)調(diào)用其綁定的httpTransport的Listen方法,然后在其創(chuàng)建的Listener上接收客戶端連接,接收方法Accept傳入了當(dāng)前rpcServer的連接處理方法:rpcServer.ServeConn,有連接到來(lái)時(shí)會(huì)調(diào)用它。
當(dāng)客戶端請(qǐng)求來(lái)臨時(shí),客戶端連接被交給rpcServer的ServeConn方法,然后又調(diào)用到HandleEvent方法。
然后進(jìn)入rpcServer內(nèi)部的router的函數(shù)ServeRequest中,通過(guò)分析請(qǐng)求消息,找到請(qǐng)求的服務(wù)名字和方法名字,在router中找到前面注冊(cè)過(guò)的service,通過(guò)servcie.call,再進(jìn)入function.call,最終通過(guò)反射調(diào)用到我們編寫的Handler的業(yè)務(wù)方法。
有的同學(xué)可能會(huì)想,反射不是性能很低嗎?!反射性能低主要是查找方法和字段的時(shí)候,調(diào)用方法的性能并不低,而查找方法和字段等的操作已經(jīng)在RegisterXXXHandler的步驟中做了,并且緩存到了router中,所以性能并不受影響。
到此這篇關(guān)于go-micro開(kāi)發(fā)RPC服務(wù)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
golang string、int、int64 float 互相轉(zhuǎn)換方式
這篇文章主要介紹了golang string、int、int64 float 互相轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07golang使用正則表達(dá)式解析網(wǎng)頁(yè)
這篇文章主要介紹了golang使用正則表達(dá)式解析網(wǎng)頁(yè),需要的朋友可以參考下2015-03-03安裝GoLang環(huán)境和開(kāi)發(fā)工具的圖文教程
Go是一門由Google開(kāi)發(fā)的編程語(yǔ)言,GoLand的安裝非常簡(jiǎn)單,本文主要介紹了安裝GoLang環(huán)境和開(kāi)發(fā)工具的圖文教程,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09golang環(huán)形隊(duì)列實(shí)現(xiàn)代碼示例
這篇文章主要介紹了golang環(huán)形隊(duì)列實(shí)現(xiàn)代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11詳解Go如何實(shí)現(xiàn)協(xié)程并發(fā)執(zhí)行
線程是通過(guò)本地隊(duì)列,全局隊(duì)列或者偷其它線程的方式來(lái)獲取協(xié)程的,目前看來(lái),線程運(yùn)行完一個(gè)協(xié)程后再?gòu)年?duì)列中獲取下一個(gè)協(xié)程執(zhí)行,還只是順序執(zhí)行協(xié)程的,而多個(gè)線程一起這么運(yùn)行也能達(dá)到并發(fā)的效果,接下來(lái)就給給大家詳細(xì)介紹一下Go如何實(shí)現(xiàn)協(xié)程并發(fā)執(zhí)行2023-08-08Golang當(dāng)中的定時(shí)器實(shí)例詳解
這篇文章主要給大家介紹了關(guān)于Golang當(dāng)中定時(shí)器的相關(guān)資料,定時(shí)器的實(shí)現(xiàn)大家應(yīng)該都遇到過(guò),最近在學(xué)習(xí)golang,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07重學(xué)Go語(yǔ)言之基礎(chǔ)數(shù)據(jù)類型詳解
Go語(yǔ)言有非常強(qiáng)大的數(shù)據(jù)類型系統(tǒng),其支持的數(shù)據(jù)類型大體上可分為四類:基礎(chǔ)數(shù)據(jù)類型、引用數(shù)據(jù)類型、接口類型、復(fù)合類型。本文就來(lái)講講它們各自的用法吧2023-02-02