本文相關代碼:gitee
文章目錄
- 前言
- 一、Go-Micro是什麼?
-
- 1.go-micro簡介
- 2.go-micro的主要功能
- 3.go-micro通信流程
- 二、第一個微服務 hello-service
-
- 1.安裝micro
- 2.自動生成代碼
- 3.項目結構
- 4.protobuf
- 5.啟動服務
- 三、服務調用 hello-cli
- 總結
- 支援一下
前言
由于go語言和go-micro架構疊代迅速,我在學習go-micro架構查詢資料時,經常因為過時的資料遇到莫名其妙的錯誤。是以我決心根據自己的實踐與摸索,編寫一個适合go-micro V2版本切實可用的go-micro開發指南,重點記錄開發的流程和各類問題的解決方案。希望這份筆記能夠幫助以後的自己,和所有看到它的朋友實作以下目标:
- 順利搭建一個目前版本下被反複驗證可行的go-micro系統
- 通過一系列插件引入對go-micro架構全貌有大緻了解
- 在筆記中快速檢索到go-micro架構的各種搭建問題解決方案
注意:本章主要是使用micro工具自動生成第一個微服務,并用用戶端調用它,不會詳細講解其中的代碼,具體代碼邏輯以及如何手寫項目,請看第二章。
一、Go-Micro是什麼?
以下内容摘抄自:http://www.topgoer.com
1.go-micro簡介
Go Micro是一個插件化的基礎架構,基于此可以建構微服務,Micro的設計哲學是可插拔的插件化架構
在架構之外,它預設實作了consul作為服務發現(2019年源碼修改了預設使用mdns),通過http進行通信,通過protobuf和json進行編解碼
2.go-micro的主要功能
服務發現: 自動服務注冊和名稱解析。服務發現是微服務開發的核心。當服務A需要與服務B通話時,它需要該服務的位置。預設發現機制是多點傳播DNS(mdns),一種零配置系統。您可以選擇使用SWIM協定為p2p網絡設定八卦,或者為彈性雲原生設定設定consul
負載均衡: 基于服務發現建構的用戶端負載均衡。一旦我們獲得了服務的任意數量執行個體的位址,我們現在需要一種方法來決定要路由到哪個節點。我們使用随機散列負載均衡來提供跨服務的均勻分布,并在出現問題時重試不同的節點
消息編碼: 基于内容類型的動态消息編碼。用戶端和伺服器将使用編解碼器和内容類型為您無縫編碼和解碼Go類型。可以編碼任何種類的消息并從不同的用戶端發送。用戶端和伺服器預設處理此問題。這包括預設的protobuf和json
請求/響應: 基于RPC的請求/響應,支援雙向流。我們提供了同步通信的抽象。對服務的請求将自動解決,負載平衡,撥号和流式傳輸。啟用tls時,預設傳輸為http / 1.1或http2 Async
Messaging: PubSub是異步通信和事件驅動架構的一流公民。事件通知是微服務開發的核心模式。啟用tls時,預設消息傳遞是點對點http / 1.1或http2
可插拔接口: Go Micro為每個分布式系統抽象使用Go接口,是以,這些接口是可插拔的,并允許Go Micro與運作時無關,可以插入任何基礎技術 插件位址:https://github.com/micro/go-plugins
3.go-micro通信流程
Server監聽用戶端的調用,和Brocker推送過來的資訊進行處理。并且Server端需要向Register注冊自己的存在或消亡,這樣Client才能知道自己的狀态
Register服務的注冊的發現,Client端從Register中得到Server的資訊,然後每次調用都根據算法選擇一個的Server進行通信,當然通信是要經過編碼/解碼,選擇傳輸協定等一系列過程的
如果有需要通知所有的Server端可以使用Brocker進行資訊的推送,Brocker 資訊隊列進行資訊的接收和釋出
二、第一個微服務 hello-service
開始第一個項目前,請先使用
go version
指令檢視您的語言版本,因為截至發文時(2020.9.10) go-micro 依賴的部分包尚未适配go1.15版本,是以本教程建議在go1.14+版本中運作。
同時考慮到國内的網絡環境,請確定
go env
中
GO111MODULE=on
且配置了正确的依賴代理服務(推薦
GOPROXY=https://goproxy.io
)。
1.安裝micro
這裡需要說明一下,我們微服務使用的架構叫
go-micro
,他将被內建到我們的項目中。
同時,官方還為我們提供了另一個叫
micro
的項目,他是一個官方工具包,主要是通過編譯後的可執行檔案為我們開發提供各種幫助,這裡我們安裝的就是
micro
工具包:
go get github.com/micro/micro/v2
安裝完成後可以執行
micro help
看到如下資訊表示已經安裝成功:
NAME:
micro - A microservice runtime
Use `micro [command] --help` to see command specific help.
USAGE:
micro [global options] command [command options] [arguments...]
VERSION:
latest
COMMANDS:
server Run the micro server
以下省略...
2.自動生成代碼
實際上,通過閱讀幫助文檔不難發現,
micro
本身也是一個微服務,後續我們會講到他的更多作用,這裡我們重點講如何使用micro自動生成我們自己的微服務代碼。
執行如下指令:
# 首先進入到項目目錄,這裡我以我自己的go項目目錄為例
cd go-workspace
# 調用micro生成代碼
# 預設情況下Micro生成的代碼會放到GOPATH/src中,通過配置--gopath=false可以選擇在目前目錄下
micro new --gopath=false hello
3.項目結構
代碼生成後,micro會輸出大量項目資訊,我們觀察這段資訊。
首先是項目結構,可以看到micro為我們建立了一個
hello
檔案夾,并在裡面寫入了一個完整的
go mod
項目:
Creating service go.micro.service.hello in hello
.
├── main.go #程式入口
├── generate.go
├── plugin.go
├── handler #服務的主要處理邏輯,(僅僅是)類似java spring項目的service
│ └── hello.go
├── subscriber #消息處理邏輯
│ └── hello.go
├── proto #存放proto檔案,并生成相關業務代碼
│ └── hello
│ └── hello.proto
├── Dockerfile #docker鏡像配置檔案
├── Makefile #若幹make指令幫助我們自動化管理項目
├── README.md
├── .gitignore
└── go.mod
這裡補充說明一下,為了照顧大多數讀者,本教程盡量不會使用make指令,但還是建議 win環境的朋友自行搜尋MinGW的安裝和配置,因為這東西用過你就知道真香。
如果你安裝完mingw-get-setup在執行install的時候發現總是下載下傳逾時,請用USB将手機4G網共享給電腦,有奇效,當然梯子效果更佳。
4.protobuf
Protobuf是一種平台無關、語言無關、可擴充且輕便高效的序列化資料結構的協定,可以用于網絡通信和資料存儲。在go-micro中,官方雖然也支援json格式資料傳輸,但預設是protobuf。
這種格式相對json有兩個弊端:
- 首先,protobuf有一定學習成本,好在日常使用隻需要參考自動代碼中
的格式學會定義接口和參數即可(熟練後強烈建議進一步深入學習)hello.proto
- 其次,需要安裝不少的工具,go-micro官方也是考慮到這一點,是以在代碼生成文檔的最後詳細列出了工具安裝步驟(考慮到不同版本問題,請務必使用你的控制台中看到的指令,一下内容僅供參考)
# 首先要下下載下傳protoc協定的編解碼器,下載下傳安裝後放在任意`path`路徑下一邊被指令行調用
# 我一般會把他放在 GOPATH/bin 目錄下
download protoc zip packages (protoc-$VERSION-$PLATFORM.zip) and install:
visit https://github.com/protocolbuffers/protobuf/releases
# 接下來是下載下傳三個go語言的proto包,用于生成對應go語言的micro代碼
download protobuf for micro:
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v2/cmd/protoc-gen-micro
# 最後是進入到你建立的項目中,通過proto檔案生成micro代碼
compile the proto file hello.proto:
cd hello
# 沒有make的win環境朋友可以直接複制執行makefile檔案裡面的指令,效果是一樣的:
# protoc --proto_path=. --micro_out=Mproto/imports/api.proto=github.com/micro/go-micro/v2/api/proto:. --go_out=Mproto/imports/api.proto=github.com/micro/go-micro/v2/api/proto:. proto/hello/hello.proto
#這一步先不要着急執行,往下看(重要的話說三遍)
#這一步先不要着急執行,往下看(重要的話說三遍)
#這一步先不要着急執行,往下看(重要的話說三遍)
make proto
如果你不聽話,直接執行了,會發現程式順利執行,隻是有一條警告資訊:
2020/09/10 11:01:43 WARNING: Missing 'go_package' option in "proto/hello/hello.p
roto",
please specify it with the full Go package path as
a future release of protoc-gen-go will require this be specified.
See https://developers.google.com/protocol-buffers/docs/reference/go-generated#p
ackage for more information.
這裡,我們需要預處理一下
hello.proto
檔案,在最上面加入一行資訊:
這行資訊用于指定生成代碼所屬的包路徑,因為我們将代碼生成在
hello.proto
同目錄下,是以包路徑使用
proto/hello
,如果你的項目想把生成的go檔案放在其他路徑,則根據實際情況填寫。
完成修改後,執行
make proto
或者完整的生成指令:
> protoc --proto_path=. --micro_out=Mproto/imports/api.proto=github.com/micro/go-m
icro/v2/api/proto:. --go_out=Mproto/imports/api.proto=github.com/micro/go-micro/
v2/api/proto:. proto/hello/hello.proto
這裡官方寫法比較冗長,如果是目前目錄下可以簡寫為
protoc --proto_path=. --micro_out=. --go_out=. proto/hello/hello.proto
注意,其中的
--go_out
和
--micro_out
參數指定了生成檔案的輸出路徑,應與上一步我們修改的
go_package
保持一緻。
5.啟動服務
至此,所有代碼都已經生成完畢,理論上
go mod tidy && go run main.go
就可以愉快的跑起來了。
> go run main.go
2020-09-10 11:30:29 file=[email protected]/service.go:200 level=info Starting [service] go.micro.service.hello
2020-09-10 11:30:29 file=grpc/grpc.go:864 level=info Server [grpc] Listening on [::]:60662
2020-09-10 11:30:29 file=grpc/grpc.go:881 level=info Broker [http] Connected to 127.0.0.1:60663
2020-09-10 11:30:29 file=grpc/grpc.go:697 level=info Registry [mdns] Registering node: go.micro.service.hello-3b9e045d-6db0-47a5-9504-743b4d9175ba
2020-09-10 11:30:29 file=grpc/grpc.go:730 level=info Subscribing to topic: go.micro.service.hello
根據日志資訊,會發現架構幫我們啟動了
- 服務:
go.micro.service.hello-3b9e045d-6db0-47a5-9504-743b4d9175ba
- 服務發現:
mdns
- 基于
的Server服務:grpc
60662
- 基于
的Broker服務:http
60663
- 訂閱消息服務,主題:
go.micro.service.hello
你可以在另一個指令行視窗執行
micro list services
指令,可以看到我們的服務
go.micro.service.hello
已經被注冊:
> micro list services
go.micro.service.hello
micro.http.broker
如果你看到如下報錯,是由于第三方庫相容性造成的。這個問題困擾了我很久,但是在我寫文時,已經被官方最新版解決:
# github.com/coreos/etcd/clientv3/balancer/picker
F:\go\pkg\mod\github.com\coreos\[email protected]+incompatible\clientv3\balancer\picker\err.go:37:44: undefined: balancer.PickOptions
F:\go\pkg\mod\github.com\coreos\[email protected]+incompatible\clientv3\balancer\picker\roundrobin_balanced.go:55:54: undefined: balancer.PickOptions
# github.com/coreos/etcd/clientv3/balancer/resolver/endpoint
F:\go\pkg\mod\github.com\coreos\[email protected]+incompatible\clientv3\balancer\resolver\endpoint\endpoint.go:114:78: undefined: resolver.BuildOption
F:\go\pkg\mod\github.com\coreos\[email protected]+incompatible\clientv3\balancer\resolver\endpoint\endpoint.go:182:31: undefined: resolver.ResolveNowOption
# github.com/micro/go-micro/transport/quic
F:\go\pkg\mod\github.com\micro\[email protected]\transport\quic\quic.go:54:12: q.s.Close undefined (type quic.Session has no field or method Close)
F:\go\pkg\mod\github.com\micro\[email protected]\transport\quic\quic.go:121:3: unknown field 'IdleTimeout' in struct literal of type quic.Config
在 go.mod 檔案中添加以下兩行 replace 解決編譯問題
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
replace github.com/lucas-clemente/quic-go => github.com/lucas-clemente/quic-go v0.14.1
三、服務調用 hello-cli
如上所述,我們已經啟動了第一個go-micro微服務hello,怎麼調用他呢?我們可以建立一個
hello-cli.go
檔案:
package main
import (
"context"
"github.com/micro/go-micro/v2"
"github.com/micro/go-micro/v2/client"
pb "hello/proto/hello"
"log"
)
func main() {
// 這裡以HelloService預設提供的Call接口調用為例示範服務的調用
// 可以看到他的調用就像調用本地方法一樣,go-micro為我們隐藏了背後的服務注冊、發現、負載均衡以及網絡操作
testCallFunc()
// 這裡示範消息的發送
testSendMessage()
}
func testCallFunc(){
// 擷取hello服務
// 這裡第一個參數"go.micro.service.hello"必須與hello-service注冊資訊一緻
// 一般由micro生成的項目預設服務名為:{namespace 預設[go.micro]}.{type 預設[service]}.{項目名}組成
// 如果要修改預設值,在生成項目時可以這樣: micro --namespace=XXX --type=YYYY ZZZZ
// 當然也可以直接修改main.go中micro.Name("go.micro.service.hello")的内容
helloService := pb.NewHelloService("go.micro.service.hello", client.DefaultClient)
// 預設生成的hello服務中自帶三個接口: Call() Stream() PingPong(),分别對應參數調用、流傳輸和心跳
resp, err := helloService.Call(context.Background(), &pb.Request{
Name: "xiao xie",
})
if err != nil {
log.Panic("call func", err)
}
log.Println("call func success!", resp.Msg)
}
func testSendMessage(){
// 消息主題,定義規則與服務一緻
// 同樣,也可以修改main.go的micro.RegisterSubscriber("go.micro.service.hello", service.Server(), new(subscriber.Hello))
const topic = "go.micro.service.hello"
// 擷取消息發送接口,這裡我一直使用的時micro.NewPublisher()
// 但在寫文時發現NewPublisher()已經被廢止,改為NewEvent(),二者參數和傳回值一緻
event := micro.NewEvent(topic, client.DefaultClient)
if err := event.Publish(context.Background(), &pb.Message{
Say: "hello server!",
}); err != nil {
log.Panic("send msg", err)
}
log.Println("send msg success!")
}
執行
go run hello-cli.go
,即可看到運作結果:
> go run hello-cli.go
2020-09-10 12:43:25.446682 I | call func success! Hello xiao xie
2020-09-10 12:43:25.547441 I | send msg success!
至此,一個基礎的微服務就部署完成并通過測試了。
總結
本章我們介紹了借助
micro
工具包快速編寫go-micro微服務的基本操作流程,和微服務的兩種基本調用方式
gRpc
和
消息
,由于篇幅關系我們并沒有詳解自動生成的代碼的含義。
下一章我們将參考示例代碼結構手動編寫一個微服務,并在後續章節中逐漸開發一個微服務化的
todolist
項目。
支援一下
原創不易,支援一下買杯咖啡,謝謝:p