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