網站首頁 編程語言 正文
go-micro是一個知名的golang微服務框架,最新版本是v4,這篇文章將介紹go-micro v4開發RPC服務的方法及其運作原理。
基本概念
go-micro有幾個重要的概念,后邊開發RPC服務和介紹其運行原理的時候會用到,這里先熟悉下:
- Service:代表一個go-micro應用程序,Service中包括:Server、Client、Broker、Transport、Registry、Config、Store、Cache等程序運行所需的各個模塊。
- Server:代表一個go-micro服務器,主要函數包括:Start、Stop、Handle、Subscribe。默認創建的Server是 rpcServer。
- Broker:用于處理異步消息,主要的函數包括:Connect、Publish、Subscribe。默認的Broker是httpBroker。
- Router:用于消息處理的路由,內部包括兩種路由方式:RPC服務映射serviceMap和消息訂閱器subscribers。
- Codec:用于消息的編解碼,主要函數包括:Marshal、Unmarshal默認的Codec是json.Marshaler,是基于jsonpb的。RPC服務是根據請求頭中的Content-Type自動創建的。
- Registry:用于服務發現,主要函數包括:Register、Deregister、GetService、ListServices、Watch。默認的Registry是mdns。
- Selector:?用于從同一個服務的多個實例之中選擇一個,支持緩存,有隨機和輪詢兩種策略。
- Transport:用于同步通信,主要函數包括:Dial、Listen。它的底層基于Socket的send、recv語義,有多種實現,包括http、grpc、quic等。默認的Transport是httpTransport。
開發RPC服務
RPC全稱是Remote Procedure Call,翻譯過來是就是:遠程過程調用,中心思想是:像調用本地函數一樣調用遠程函數。常見的Dubbo、Spring Cloud都可以稱為RPC框架,還有最近很流行的gRPC。
使用go-micro創建一個RPC服務很簡單,共分三步走:
1、編寫proto協議文件
這個服務提供的功能很簡單,名字為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服務端代理
需要首先安裝protoc和兩個代碼生成插件。
protoc下載地址:https://github.com/protocolbuffers/protobuf/releases,保存到GO?PATH/bin目錄中。同時建議將?GOPATH/bin?添加到環境變量?PATH?中,方便直接執行相關命令。
兩個插件直接通過命令即可安裝:
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install go-micro.dev/v4/cmd/protoc-gen-micro@v4
然后在項目的目錄下執行命令:
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 。
下個步驟中就要使用它們來創建RPC服務。
3、編寫go-micro服務
這里先把代碼貼出來,然后再做一個簡要說明:
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) } }
上邊我們創建了一個 Hello 類型,然后給它綁定了一個名為Say的函數。這個是和proto協議對應的,其實是實現了生成代碼 hello.pb.micro.go 中的HelloHandler接口:
type HelloHandler interface { Say(context.Context, *SayRequest, *SayResponse) error }
然后main函數中是我們的重頭戲:先創建一個Server,默認情況下就是rpc Server,設置它的名字、監聽地址等參數;然后創建一個Service,并綁定剛剛創建的Server;然后使用生成的服務端代理函數將我們編寫的Hello服務注冊到Server中;最后開啟運行Service。
當然只有一個服務端沒有什么意義,還得有客戶端來訪問它。這里也給一個例子:
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() }
這里調用服務的時候沒有指定服務的地址和端口,因為內部走了服務發現,服務端會自動注冊服務,客戶端會根據服務名稱查找到對應的地址和端口。默認的服務發現機制使用的是mdns。
RPC服務的運行原理
這里從服務端的角度進行介紹,先來看一張圖:
請大家參考代碼從上往下看。
NewServer 時創建一個rpcServer,這個rpcServer還會創建一個httpTransport用于程序間網絡通信,并綁定到當前rpcServer。
RegisterXXXHandler 時使用我們編寫的Handler創建一個內部的service實例,然后注冊這個service實例到rpcServer內部的router中,客戶端請求時會用到它。這里其實可以注冊任意一個帶方法的類型,并不一定要定義proto協議,定義它只是為了協作更方便。
Service.Run 時會調用rpcServer的Start方法,這個方法內部會調用其綁定的httpTransport的Listen方法,然后在其創建的Listener上接收客戶端連接,接收方法Accept傳入了當前rpcServer的連接處理方法:rpcServer.ServeConn,有連接到來時會調用它。
當客戶端請求來臨時,客戶端連接被交給rpcServer的ServeConn方法,然后又調用到HandleEvent方法。
然后進入rpcServer內部的router的函數ServeRequest中,通過分析請求消息,找到請求的服務名字和方法名字,在router中找到前面注冊過的service,通過servcie.call,再進入function.call,最終通過反射調用到我們編寫的Handler的業務方法。
有的同學可能會想,反射不是性能很低嗎?!反射性能低主要是查找方法和字段的時候,調用方法的性能并不低,而查找方法和字段等的操作已經在RegisterXXXHandler的步驟中做了,并且緩存到了router中,所以性能并不受影響。
原文鏈接:https://www.cnblogs.com/bossma/p/16184306.html
相關推薦
- 2022-05-18 python基礎教程之csv格式文件的寫入與讀取_python
- 2022-10-28 Swift類和對象的底層探索分析_Swift
- 2022-10-31 Golang中map數據類型的使用方法_Golang
- 2023-12-09 如何使用Python核對文件夾內的文件
- 2022-10-04 go?mode?tidy出現報錯go:?warning:?“all“?matched?no?pack
- 2022-07-24 Goland中Protobuf的安裝、配置和使用_Golang
- 2022-12-15 裝Oracle用PLSQL連接登錄時不顯示數據庫的解決_oracle
- 2023-01-12 C語言技巧提升之回調函數的掌握_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支