前言
hello,我是asong,這是我的第七篇原創文章。上一篇我們使用go的標準庫rpc進行實踐,使用起來還是很方便的,這一篇我們來學習一grpc。
1. gRPC
gRPC是Google開發的高性能、通用的開源RPC框架,其由Google主要面向移動應用開發并基于HTTP/2協議標準而設計,基于ProtoBuf(Protocol Buffers)序列化協議開發,且支持眾多開發語言。他的目標是跨語言開發,支持多種語言。在gRPC里客戶端應用可以像調用本地對象一樣直接調用另一臺不同的機器上服務端應用的方法,使得您能夠更容易地創建分布式應用和服務。
gRPC基于以下理念:定義一個服務,指定其能夠被遠程調用的方法(包含參數和返回類型)。在服務端實現這個接口,并運行一個gRPC服務器來處理客戶端調用。在客戶端擁有一個存根能夠像服務端一樣的方法。gRPC支持多種語言,所以我們可以用自己的喜歡的語言來進行開發,官網都有相關介紹。
2. protocol buffers
gRPC默認使用protocol buffers,這是Google開源的一套成熟的結構數據序列化機制,也可以使用其他數據格式,比如json。現在protocol buffers版本已經到proto3了,他擁有輕量簡化的語法,一些有用的新功能,并支持多種語言。這里就不介紹protocol buffers的具體使用了,詳細學習點擊這里:https://developers.google.com/protocol-buffers。下面講一個簡單的例子來看看一gRPC的具體使用。
3. gRPC實踐
語言:golang
功能:一個用戶登陸接口,兩個server,一個webserver用來和前端進行交互、一個tcpserver用來處理登陸邏輯,兩個server通過rpc進行請求調用。因為這個主要是演示gRPC的使用,登陸邏輯就不操作數據庫了,簡單寫個返回示例。
首先我們來編寫proto文件,因為是登陸接口,所以我們可以設計登陸請求參數、返回參數如下所示:
syntax = "proto3";
package proto;
option go_package = "proto/user.proto;user_proto";
//the request message containing the user's name password
message LoginRequest {
//user name
string username = 1;
//password
string password = 2;
}
//the response message containing the user's information
message LoginResponse {
//user name
string username = 1;
string nickname = 2;
//token
string token = 3;
//result code
uint32 code = 4;
//result msg
string msg = 5;
}
service UserService {
rpc login (LoginRequest) returns (LoginResponse){
}
}
gRPC提供代碼生成工具,使用protoc-gen-go-grpc即可生成代碼,具體操作如下:
$ export GO111MODULE=on # Enable module mode
$ go get github.com/golang/protobuf/protoc-gen-go
執行以上操作后,會在你的GOPATH/bin目錄下生成可執行文件,把這個路徑加入到環境變量即可。
現在就可以使用protoc命令進行代碼生成,進入項目根目錄,創建proto文件,將.proto文件放到這個文件下,執行以下命令:
$ protoc \ --go_out=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config:. \ --go-grpc_out=Mgrpc/service_config/service_config.proto=/internal/proto/grpc_service_config:. \ --go_opt=paths=source_relative \ --go-grpc_opt=paths=source_relative \ proto/user.proto
此時會在proto目錄下生成user_pb.go、user_grpc.pb.go兩個。現在協議部分已經完事了,開始編寫server、client部分代碼如下:
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "asong.cloud/Golang_Dream/code_demo/grpc_demo/proto"
)
type Server struct {
pb.UnimplementedUserServiceServer
}
func (s *Server)Login(ctx context.Context,in *pb.LoginRequest) (*pb.LoginResponse,error) {
log.Printf("Received username: %s", in.Username)
log.Printf("Received password: %s",in.Password)
return &pb.LoginResponse{Username: in.Username,Nickname: "Golang夢工廠",Token: "asong_grpc",Code: 0,Msg: "登陸成功"},nil
}
func main() {
lis, err := net.Listen("tcp",":8080")
if err != nil{
log.Fatalf("failed to listen :%v",err)
}
grpc := grpc.NewServer()
pb.RegisterUserServiceServer(grpc, &Server{})
if err := grpc.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
pb "asong.cloud/Golang_Dream/code_demo/grpc_demo/proto"
)
const (
address = "localhost:8080"
)
func main() {
conn,err := grpc.Dial(address,grpc.WithInsecure(),grpc.WithBlock())
if err != nil{
log.Fatalf("did not connect: %v",err)
}
defer conn.Close()
c := pb.NewUserServiceClient(conn)
ctx,cannel := context.WithTimeout(context.Background(),time.Second * 10)
defer cannel()
rsp ,err := c.Login(ctx,&pb.LoginRequest{Username: "asong",Password: "123"})
if err != nil{
log.Fatalf("could not login request: %v",err)
}
log.Printf("Username: %s Code: %d Msg: %s", rsp.GetUsername(),rsp.Code,rsp.Msg)
}
代碼部分寫完了,執行go run *.go,控制臺將會有如下輸出:
tcpserver控制臺輸出:
2020/07/30 22:12:37 Received username: asong
2020/07/30 22:12:37 Received password: 123
webserver控制臺輸出:
2020/07/30 22:12:37 Username: asong Code: 0 Msg: 登陸成功
4 總結
好啦,這一篇介紹到這里就結束了。這只是一個簡單的應用,可以作為入門觀看,具體深入,還是要看官方文檔,根據項目進行深入思考。
我是asong,一名普普通通的努力奮斗的程序員。歡迎關注個人公眾號:Golang夢工廠,定期發布go方面知識,你想學的這里都有!!!
獲取2020最新GIN官方中文文檔,公眾號后臺回復:GIN,即可獲取。