一、gRPC介绍
gRPC是Google公司发布的一个开源的、高性能的、跨语言的RPC框架,它是基于HTTP2、Protobuf3.x、Netty4.x实现的。
与其他RPC的实现原理相似,gPRC的实现也是按照以下几个步骤:
- 定义服务接口,指定远程调用方法;
- 在服务端实现服务接口,并运行一个gRPC服务器来处理客户端的调用;
- 在客户端调用服务端的方法;
gRPC支持在不同语言环境下的运行和交互。例如你可以使用Java创建一个gRPC的服务端,使用Go创建它的客户端。
此外,在Google最新API中将有gRPC版本的接口,使得开发人员很容易地将Google的功能集成到你的应用中。
二、gRPC环境搭建
第一步:执行下面命令克隆grpc-go代码库;
go get google.golang.org/grpc
第二步:启动服务端;
$ cd $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_server
$ go run main.go
第三步:在另外一个终端启动客户端;
$ cd $GOPATH/src/google.golang.org/grpc/examples/helloworld/greeter_client
$ go run main.go
运行效果:
三、源码剖析
(1)服务端源码阅读
// 定义服务端监听的端口
const (
port = ":50051"
)
// 定义服务对象变量,该对象实现了helloworld.GreeterServer接口
type server struct{}
// 实现helloworld.GreeterServer接口的SayHello方法,该方法会与server服务对象一起发布出去
// 参数一:当前的上下文环境
// 参数二:客户端发送过来的消息
// 返回值一:服务端返回的消息
// 返回值二:异常对象
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
// 定义监听器,用于监听客户端的连接
lis, _:= net.Listen("tcp", port)
// 注册服务对象
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
// 在gRPC服务端注册反射服务(为什么要注册反射服务,后面做深入了解的时候再补充)
reflection.Register(s)
....
}
(2)客户端源码阅读
func main() {
// 创建服务器连接
conn, _ := grpc.Dial(address, grpc.WithInsecure())
defer conn.Close()
// 创建gRPC客户端
c := pb.NewGreeterClient(conn)
// 如果运行客户端时候没有传入参数,那么就使用默认值作为调用服务方法的实参
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
// 调用服务方法,并且把当前上下文环境对象,以及HelloRequest传入方法中
r, _ := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
// 打印服务端返回的数据
log.Printf("Greeting: %s", r.Message)
}
四、Go实现gRPC远程调用
我们模拟上面的示例在Windows环境下实现gPRC远程调用服务的功能。
第一步:在$GOPATH/src目录下新建/Project016/myproto文件夹。
cd $GOPATH/src
md \Project016\myproto
第二步:进入到myproto文件夹下,然后新建test.proto文件。文件内容如下:
syntax = "proto3";
package grpc_test;
service HelloServer {
rpc SayHello(HelloRequest) returns (HelloReply) {}
rpc GetHelloMsg(HelloRequest) returns (HelloMessage) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
message HelloMessage {
string msg = 1;
}
注意:包名不能够以数字开头,否则会提示错误。
第三步:进入proto文件所在目录,执行以下命令编译文件。
protoc --go_out=plugins=grpc:./ test.proto
编译后在当前文件夹下会生成一个test.pb.go的文件。
第四步:在项目下新建一个go文件,实现grpc服务端功能。
package main
import (
pt "Project016/myproto"
"context"
"google.golang.org/grpc"
"net"
"fmt"
)
const (
port = "127.0.0.1:10086"
)
// 定义服务对象的变量
type server struct {}
// 实现HelloServer.SayHello方法
func (this *server) SayHello(ctx context.Context, in *pt.HelloRequest) (*pt.HelloReply, error) {
msg := "hello" + in.Name
return &pt.HelloReply{Message: msg}, nil
}
// 实现HelloServer.GetHelloMsg方法
func (this *server) GetHelloMsg(ctx context.Context, in *pt.HelloRequest) (*pt.HelloMessage, error) {
return &pt.HelloMessage{Msg: "this is from server HAHA!"}, nil
}
func main() {
// 定义监听器
ln ,err := net.Listen("tcp", port)
if err!=nil {
fmt.Println("网络异常",err)
}
// 注册服务对象
srv:= grpc.NewServer()
pt.RegisterHelloServerServer(srv,&server{})
// 监听grpc服务
err= srv.Serve(ln)
}
第五步:新建另外一个go文件,实现grpc客户端功能。
package main
import (
"google.golang.org/grpc"
pt "Project016/myproto"
"fmt"
"context"
)
// 定义服务端的地址
const (
server_port = "127.0.0.1:10086"
)
func main() {
// 连接服务器
conn, err := grpc.Dial(server_port, grpc.WithInsecure())
if err != nil {
fmt.Println("连接服务器失败", err)
}
defer conn.Close()
// 创建gRPC客户端
cli := pt.NewHelloServerClient(conn)
// 远程调用HelloServer服务的SayHello方法
r1, err := cli.SayHello(context.Background(), &pt.HelloRequest{Name: "panda"})
if err != nil {
fmt.Println("Sorry, can not get SayHello server!")
return
}
// 打印服务器返回的消息
fmt.Println("r1 = ", r1.Message)
// 远程调用HelloServer服务的GetHelloMsg方法
r2, err := cli.GetHelloMsg(context.Background(), &pt.HelloRequest{Name: "panda"})
if err != nil {
fmt.Println("Sorry, can not get GetHelloMsg server!")
return
}
// 打印服务器返回的消息
fmt.Println("r2 = ", r2.Msg)
}
第六步:启动服务端和客户端。运行效果如下图所示: