首先從我們最最熟悉的 helloworld 例子在入手,對 go-micro 有一個初步的了解
源碼地址
系列文章
- 微服務實戰Go Micro v3 系列(一)- 基礎篇
- 微服務實戰Go Micro v3 系列(二)- HelloWorld
- 微服務實戰Go-Micro v3 系列(三)- 啟動HTTP服務
- 微服務實戰Go Micro v3 系列(四)- 事件驅動(Pub/Sub)
- 微服務實戰Go Micro v3 系列(五)- 注冊和配置中心
- 微服務實戰Go Micro v3 系列(六)- 綜合篇(愛租房項目)
ProtoBuf
簡介
protocol buffers (ProtoBuf)是一種語言無關、平台無關、可擴展的序列化結構數據的方法,它可用於(數據)通信協議、數據存儲等。
Protocol Buffers 是一種靈活,高效,自動化機制的結構數據序列化方法-可類比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更為簡單。
json\xml都是基於文本格式,protobuf是二進制格式。
你可以通過 ProtoBuf 定義數據結構,然后通過 ProtoBuf 工具生成各種語言版本的數據結構類庫,用於操作 ProtoBuf 協議數據
本教程介紹的是最新的protobuf proto3版本的語法。
使用ProtoBuf的例子
創建 .proto 文件,定義數據結構
使用 ProtoBuf ,首先需要通過 ProtoBuf 語法定義數據結構(消息),這些定義好的數據結構保存在.proto為后綴的文件中。
例子:
文件名: response.proto
// 指定protobuf的版本,proto3是最新的語法版本
syntax = "proto3";
// 定義數據結構,message 你可以想象成java的class,c語言中的struct
message Response {
string data = 1; // 定義一個string類型的字段,字段名字為data, 序號為1
int32 status = 2; // 定義一個int32類型的字段,字段名字為status, 序號為2
}
說明:proto文件中,字段后面的序號,不能重復,定義了就不能修改,可以理解成字段的唯一ID。
安裝ProtoBuf編譯器
protobuf的github發布地址: https://github.com/protocolbuffers/protobuf/releases
protobuf的編譯器叫protoc,在上面的網址中找到最新版本的安裝包,下載安裝。
這里下載的是:protoc-3.9.1-win64.zip , windows 64位系統版本的編譯器,下載后,解壓到你想要的安裝目錄即可。
提示:安裝完成后,將 [protoc安裝目錄]/bin 路徑添加到PATH環境變量中
打開cmd,命令窗口執行protoc命令,沒有報錯的話,就已經安裝成功。
更多protoc的教程請點擊這里
grpc
gRPC 是一個高性能、跨平台、開源和通用的 RPC 框架,面向移動和 HTTP/2 設計。目前提供 C/C++、Java、Python、Ruby、C#、PHP、Node.js、Go 語言等版本,幾乎你想到的語言都支持了.
gRPC 基於 HTTP/2 標准設計,帶來諸如雙向流、流控、頭部壓縮、單 TCP 連接上的多復用請求等特。這些特性使得其在移動設備上表現更好,更省電和節省空間占用。
下面先介紹grpc相關概念
grpc是什么?
在 gRPC 里客戶端應用可以像調用本地方法一樣直接調用另一台機器上服務端應用的方法,這樣我們就很容易創建分布式應用和服務。跟其他 RPC 系統類似,gRPC 也是基於以下理念:首先定義一個服務,定義能夠被遠程調用的方法(包含參數和返回類型)。在服務端實現這個方法,並運行一個 gRPC 服務器來處理客戶端調用。在客戶端擁有一個存根,這個存根就是長得像服務端一樣的方法(但是沒有具體實現),客戶端通過這個存根調用服務端的方法。
grpc工作原理,如下圖:
grpc使用的協議
gRPC 默認使用 protocol buffers,這是 Google 開源的一套成熟的結構數據的序列化機制,當然也可以使用其他數據格式如 JSON,不過通常都使用protocol buffers這種靈活、高效的數據格式,如果不了解protobuf語法,點擊這里學習 protocol buffers入門教程。
服務定義
使用gprc,首先需要定義服務, 指定其可以被遠程調用的方法及其參數和返回類型。
服務,你可以理解成服務端api接口的集合,對外提供一些功能。
通過protobuf定義服務的例子:
// 定義一個叫HelloService的服務
service HelloService {
// 定義一個叫SayHello的方法,這個方法接受HelloRequest消息作為參數,返回HelloResponse消息
rpc SayHello (HelloRequest) returns (HelloResponse);
}
// 定義HelloRequest消息
message HelloRequest {
string greeting = 1;
}
// 定義HelloResponse消息
message HelloResponse {
string reply = 1;
}
如果你把service和message關鍵詞當成class,是不是跟類定義很像!
gRPC 允許你定義四類服務方法,下面分別介紹如何定義,以及客戶端和服務端的交互方式。
單向RPC
即客戶端發送一個請求給服務端,從服務端獲取一個應答,就像一次普通的函數調用。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
服務端流式 RPC
即客戶端發送一個請求給服務端,可獲取一個數據流用來讀取一系列消息。客戶端從返回的數據流里一直讀取直到沒有更多消息為止。
通俗的講就是客戶端請求一次,服務端就可以源源不斷的給客戶端發送消息。
// 注意stream關鍵詞在什么地方
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
客戶端流式 RPC
即客戶端用提供的一個數據流寫入並發送一系列消息給服務端。一旦客戶端完成消息寫入,就等待服務端讀取這些消息並返回應答。
通俗的講就是請求一次,客戶端就可以源源不斷的往服務端發送消息。
// 注意stream關鍵詞在什么地方
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
雙向流式 RPC
即兩邊都可以分別通過一個讀寫數據流來發送一系列消息。這兩個數據流操作是相互獨立的,所以客戶端和服務端能按其希望的任意順序讀寫,例如:服務端可以在寫應答前等待所有的客戶端消息,或者它可以先讀一個消息再寫一個消息,或者是讀寫相結合的其他方式。每個數據流里消息的順序會被保持。
類似tcp通信,客戶端和服務端可以互相發消息。
// 注意stream關鍵詞在什么地方
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
更多關於grpc教程請點擊這里
編寫 go-micro HTTP服務
安裝 gofast插件
//gofast
go get -u -v github.com/gogo/protobuf/protoc-gen-gofast
開始編寫 go-micro HTTP服務
創建 go-micro-examples 目錄,然后在該目錄下創建 helloworld目錄
在 helloworld 目錄下,創建 proto 和 handler,並創建 main.go 和使用 go mod init 初始化項目,如下圖所示
其它文件暫時忽略,后面會解釋
在 proto 目錄創建 helloworld.proto,並寫入下面代碼
syntax = "proto3";
package helloworld;
option go_package = "proto;helloworld";
service Helloworld {
rpc Call(Request) returns (Response) {}
rpc Stream(StreamingRequest) returns (stream StreamingResponse) {}
rpc PingPong(stream Ping) returns (stream Pong) {}
}
message Message {
string say = 1;
}
message Request {
string name = 1;
}
message Response {
string msg = 1;
}
message StreamingRequest {
int64 count = 1;
}
message StreamingResponse {
int64 count = 1;
}
message Ping {
int64 stroke = 1;
}
message Pong {
int64 stroke = 1;
}
進入該目錄並輸入以下命令
並生成 helloworld.pb.go、helloworld.pb.micro.go
在 main.go,創建服務並注冊一下 handler,運行服務
package main
import (
"github.com/asim/go-micro/v3"
"github.com/asim/go-micro/v3/logger"
"go-micro-examples/helloworld/handler"
pb "go-micro-examples/helloworld/proto"
)
const (
ServerName = "go.micro.srv.HelloWorld" // server name
)
func main() {
// Create service
service := micro.NewService(
micro.Name(ServerName),
micro.Version("latest"),
)
// Register handler
if err := pb.RegisterHelloworldHandler(service.Server(), new(handler.Helloworld)); err != nil {
logger.Fatal(err)
}
// Run service
if err := service.Run(); err != nil {
logger.Fatal(err)
}
}
client 調用 service
創建 client 目錄並創建client.go,寫入一下代碼
package main
import (
"context"
"fmt"
"github.com/asim/go-micro/v3"
helloworld "go-micro-examples/helloworld/proto"
)
func main() {
// create a new service
service := micro.NewService()
// parse command line flags
service.Init()
// Use the generated client stub
cl := helloworld.NewHelloworldService("go.micro.srv.HelloWorld", service.Client())
// Make request
rsp, err := cl.Call(context.Background(), &helloworld.Request{
Name: "John",
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(rsp.Msg)
}
效果
先啟用 go.micro.srv.HelloWorld 服務,然后再啟動 client 調用,效果如下
啟動 client 之后,輸出 Hello World!
參考鏈接