protobuf
概念
protobuf是 Google 的语言中立、平台中立、可扩展的结构化数据序列化机制——想想 XML,但更小、更快、更简单。您可以定义一次数据的结构化方式,然后您可以使用特殊生成的源代码轻松地使用各种语言在各种数据流中写入和读取结构化数据。
protobuf目前支持在 Java、Python、Objective-C 和 C++ 中生成的代码。使用我们新的 proto3 语言版本,您还可以使用 Dart、Go、Ruby 和 C#,以及更多的语言。官网
跨语言的序列化方案
跨语言序列化方案有: protobuf, thrift, json,xml
Protobuf是一个纯粹的应用层协议,可以和各种传输层协议一起使用
定义协议格式
go get github.com/golang/protobuf/protoc-gen-go
go get github.com/golang/protobuf/proto
Address book.proto
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
option go_package = "../tutorial";
message Person {
string name = 1;
int32 id =2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
google.protobuf.Timestamp last_updated = 5;
}
message AddressBook {
repeated Person people = 1;
}
编译protocol buffers
protoc --go_out=. *proto
自动生成如下代码
使用示例
go get google.golang.org/protobuf/reflect/protoreflect
go get google.golang.org/protobuf/runtime/protoimpl
server.go
/**
* Createby GoLand
* User xzw jsjxzw@163.com
* Date 2021/8/1
* Time 下午4:38
*/
package main
import (
"github.com/gin-gonic/gin"
"imooc.com/ccmouse/learngo/grpc/tutorial"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/protobuf", func(c *gin.Context) {
p := &tutorial.Person{
Name: "xzw",
Id: 1,
Email: "jsjxzw@163.com",
Phones: []*tutorial.Person_PhoneNumber{
{
Number:"15012345678",
Type:tutorial.Person_MOBILE,
},
},
}
c.ProtoBuf(http.StatusOK, p)
})
r.Run(":8080")
}
client.go
/**
* Createby GoLand
* User xzw jsjxzw@163.com
* Date 2021/8/1
* Time 下午4:56
*/
package main
import (
"fmt"
"github.com/golang/protobuf/proto"
"imooc.com/ccmouse/learngo/grpc/tutorial"
"io/ioutil"
"net/http"
"reflect"
)
func main() {
resp,err := http.Get("http://localhost:8080/protobuf")
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
body,err := ioutil.ReadAll(resp.Body)
if err!= nil {
fmt.Println(err)
return
}
p := &tutorial.Person{}
proto.Unmarshal(body, p)
fmt.Println(*p)
fmt.Println(reflect.TypeOf(*p))
}
gRPC
概念
gRPC 是一种现代开源高性能远程过程调用 (RPC) 框架,可以在任何环境中运行。它可以通过对负载平衡、跟踪、健康检查和身份验证的可插拔支持,有效地连接数据中心内和数据中心之间的服务。它还适用于分布式计算的最后一英里,将设备、移动应用程序和浏览器连接到后端服务。官网
定义协议格式
go get google.golang.org/protobuf/cmd/protoc-gen-go@v1.27
go get google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
在原基础上添加
// gRPC
service Greeter {
rpc GetPerson (PersonId) returns (Person) {}
}
message PersonId {
int32 id = 1;
}
编译protocol buffers
protoc --go-grpc_out=. *.proto# 同时生成 http 和 grpc 序列化类protoc --go_out=. --go-grpc_out=. *.proto
使用示例
go get google.golang.org/grpc
server.go
/** * Createby GoLand * User xzw jsjxzw@163.com * Date 2021/8/1 * Time 下午5:36 */package mainimport ( "context" "google.golang.org/grpc" "imooc.com/ccmouse/learngo/grpc/tutorial" "log" "net")type server struct { tutorial.UnimplementedGreeterServer}func (s *server) GetPerson(ctx context.Context, in *tutorial.PersonId) (*tutorial.Person, error) { log.Printf("Received: %v", in.GetId()) return &tutorial.Person{ Name: "", Id: in.GetId(), Email: "", Phones: nil, LastUpdated: nil, }, nil}func main() { lis, err := net.Listen("tcp", ":8000") if err != nil { log.Fatalf("failed to listen:%v", err) } s := grpc.NewServer() tutorial.RegisterGreeterServer(s, &server{}) log.Printf("server listening as %v", lis.Addr()) go func() { if err := ser.Serve(lis); err != nil { log.Fatalf("failed to server:%v", err) } }() c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) for { s := <-c switch s { case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: // 自动优雅停止,tcp避免占用资源 ser.GracefulStop() return } }}
client.go
/** * Createby GoLand * User xzw jsjxzw@163.com * Date 2021/8/1 * Time 下午5:47 */package mainimport ( "context" "google.golang.org/grpc" "imooc.com/ccmouse/learngo/grpc/tutorial" "log" "os" "strconv" "time")const ( address = "localhost:8000" defaultId = 123)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 := tutorial.NewGreeterClient(conn) var id int32 = defaultId if len(os.Args) > 1 { x, err := strconv.ParseInt(os.Args[1], 10, 32) if err != nil { log.Fatalf("param illgal: %v", err) } id = int32(x) } ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.GetPerson(ctx, &tutorial.PersonId{Id:id}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %d", r.GetId())}
client_http.go
/**
* Createby GoLand
* User xzw jsjxzw@163.com
* Date 2021/8/1
* Time 下午7:11
*/
package main
import (
"context"
"github.com/gin-gonic/gin"
"google.golang.org/grpc"
"imooc.com/ccmouse/learngo/grpc/tutorial"
"log"
"net/http"
"time"
)
type Server struct {
engine *gin.Engine
webClient tutorial.GreeterClient
}
func InitWebServer (addr string) tutorial.GreeterClient {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
conn,err := grpc.DialContext(ctx, addr, []grpc.DialOption{grpc.WithInsecure(), grpc.WithBlock(), }...)
if err != nil {
log.Fatalf("did not connect: %v", err)
}
client := tutorial.NewGreeterClient(conn)
return client
}
func (s *Server) CallWeb(txt *gin.Context) {
var in *tutorial.PersonId
if err := txt.ShouldBind(&in); err != nil {
log.Fatalf(err.Error())
}
out, err := s.webClient.GetPerson(context.Background(), in)
if err != nil {
log.Fatalf("could not greet: %v", err)
}
txt.JSON(http.StatusOK, out)
}
func NewHttp(httpAddr string, rpcAddr string) {
r := gin.Default()
s := Server{
engine: r,
webClient: InitWebServer(rpcAddr),
}
s.engine.GET("/hello", s.CallWeb)
r.Run(httpAddr)
}
func main() {
NewHttp(":8080", ":8000")
}