gRPC 是一个现代的远程过程调用(RPC)框架,由 Google 开发。它使用 HTTP/2 作为传输协议,并采用 Protocol Buffers(protobuf)作为接口描述语言(IDL)。gRPC 提供高效的通信、语言无关性和跨平台支持,非常适合构建分布式系统。
准备接口描述文件即 IDL
// /home/zhangshixing/protoc/temp/hello.proto
// 指定proto版本
syntax = "proto3";
// 指定包名
package mypackage;
// 指定文件生成的路径和包名
// ./hello为路径
// mytest为生成的包名
// 如果指定的go_package="./hello";则包名和路径同名(如果路径有多层,则包名和路径的最后一层相同)
//option go_package="./hello;mytest";
option go_package="./hello";
// 定义Hello服务
service Hello {
// 定义SayHello方法
rpc SayHello(HelloRequest) returns (HelloReply) {}
}
// HelloRequest 请求结构
message HelloRequest {
string name = 1;
string age = 2;
}
// HelloReply 响应结构
message HelloReply {
string message = 1;
}
生成代码
利用上文定义的接口文件,生成 golang 代码:
protoc -I . --go_out=plugins=grpc:. hello.proto 生成 hello.pb.go 文件
生成的代码主要是:结构体的编解码序列化代码 和 用于创建和发起 RPC 调用的桩代码
服务端代码
本质就是启动一个 rpcServer 监听指定端口,其中 pb.RegisterHelloServer(rpcServer, &helloSever) 的分析如下:
- RegisterHelloServer 是告诉 rpcServer 什么请求由谁处理;作用和普通 web server 的路由一样。
- HelloServer 的 sayHello 的具体逻辑是开发人员自定义实现的,不过其入参和返回值已经由IDL决定了
package main
import (
pb "awesomeProject/libadv/grpcdbg/hello"
"context"
"flag"
"fmt"
"google.golang.org/grpc"
"log"
"net"
)
type HelloServer struct {
}
func (*HelloServer) SayHello(ctx context.Context, request *pb.HelloRequest) (*pb.HelloReply, error) {
fmt.Printf("In the sayHello, param = %s, age = %s\n", request.GetName(), request.GetAge())
var helloReply pb.HelloReply
helloReply.Message = fmt.Sprintf("Hello %s", request.GetName())
return &helloReply, nil
}
var (
port = flag.Int("port", 50052, "The server port")
)
func main() {
/*
* 修改 hello.proto 后,执行 protoc -I . --go_out=plugins=grpc:. hello.proto
* from: https://blog.csdn.net/qq_30614345/article/details/131860694
*/
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
rpcServer := grpc.NewServer()
var helloSever HelloServer
pb.RegisterHelloServer(rpcServer, &helloSever)
log.Printf("server listening at %v", lis.Addr())
if err := rpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
pb.RegisterHelloServer(rpcServer, &helloSever) 的源码:
// 下面的代码来自第2步生产的 hello.pb.go
func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
s.RegisterService(&_Hello_serviceDesc, srv) ---------告诉 rpcServer 什么请求由谁处理;作用和普通 web server 的路由一样
}
func _Hello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
/* 第一个参数 srv 猜测会在 rpcServer 收到请求后,把 s.RegisterService(&_Hello_serviceDesc, srv) 的第二个参数传过来 */
in := new(HelloRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HelloServer).SayHello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/mypackage.Hello/SayHello",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HelloServer).SayHello(ctx, req.(*HelloRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Hello_serviceDesc = grpc.ServiceDesc{
ServiceName: "mypackage.Hello",
HandlerType: (*HelloServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SayHello",
Handler: _Hello_SayHello_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "hello.proto",
}
客户端代码
package main
import (
pb "awesomeProject/libadv/grpcdbg/hello"
"context"
"flag"
"google.golang.org/grpc/credentials/insecure"
"log"
"time"
)
import "google.golang.org/grpc"
var defaultName = "world"
var (
//addr = flag.String("addr", "localhost:50052", "the address to connect to")
addr = flag.String("addr", "127.0.0.1:50052", "the address to connect to")
name = flag.String("name", defaultName, "Name to greet")
)
func main() {
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
helloClient := pb.NewHelloClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
request := pb.HelloRequest{Name: *name, Age: "22"}
r, err := helloClient.SayHello(ctx, &request)
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}