RPC(Remote Procedure Call)是远程过程调用,比如说现在有两台服务器A, B,一个在A服务器上的应用想要调用B服务器上的应用提供的某个,由于不在两个方法不在一个内存空间,不能直接调用,需要通过网络表达调用的语义和传达调用的数据。常存在于分布式系统中。
探讨tcp/http和RPC的区别的话,这里就不重复探讨了,戳:https://www.jianshu.com/p/959030de7f1c
gRPC是什么
gRPC是谷歌开源的一个 RPC 框架,面向移动和 HTTP/2 设计。
服务端负责实现定义好的接口并处理客户端的请求,客户端根据接口描述直接调用需要的服务。客户端和服务端可以分别使用gPRC支持的不同语言实现。
ProtoBuf 具有强大的IDL(interface description language,接口描述语言)和相关工具集(主要是protoc)。用户写好.proto描述文件后,protoc可以将其编译成众多语言的接口代码。
有了 gRPC, 我们可以一次性的在一个 .proto 文件中定义服务并使用任何支持它的语言去实现客户端和服务器,反过来,它们可以在各种环境中,从Google的服务器到你自己的平板电脑—— gRPC 帮你解决了不同语言及环境间通信的复杂性.使用 protocol buffers 还能获得其他好处,包括高效的序列号,简单的 IDL 以及容易进行接口更新。
Protobuf是什么
Protobuf是一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于网络通信和数据存储。
使用和使用前
https://github.com/protocolbuffers/protobuf/releases
将protoc下载下来,设置环境变量,后面将只会展示编译生成的golang代码,protobuf支持其他多种语言
golang 工具
拉取 golang的 grpc包
1 2
| # 在大陆的话,可以从https://github.com/grpc/grpc-go,将代码克隆下来放到你的gopath目录下src创建对应的文件夹存入 go get google.golang.org/grpc
|
代码实现
学习推荐: https://blog.csdn.net/lihao19910921/article/details/80166399
初始代码结构
编写proto协议文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| // demo/proto/hello.proto // 指定protobuf的版本 syntax = "proto3"
// 指定包名 package hello;
// 定义服务 service HelloService { // 定义rpc方法 request response rpc SayHello(HelloRequest) returns (HelloResponse) {} }
// 请求结构 message HelloRequest { string name = 1; }
// 响应结构 message HelloResponse { string message = 1; }
|
使用protoc将.proto文件编译成golang代码,包含服务端接口HelloServer描述,客户端接口及实现HelloClient,及HelloRequest、HelloResponse结构体
1 2 3
| # demo/proto/hello.proto # 使用grpc框架 protoc -I . --go_out=plugins=grpc:. ./hello.proto
|
新的目录结构如下:
服务端和客户端实现
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
package main
import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" pb "microservice-demo/gRPC/demo/proto" "net" )
const ( addr = "127.0.0.1:3000" )
type helloService struct { }
var HelloService = helloService{}
func (h helloService) SayHello(_ context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) { res := new(pb.HelloResponse) res.Message = "Hello " + req.Name + "."
return res, nil }
func main() { listen, err := net.Listen("tcp", addr) if err != nil { grpclog.Fatalf("failed to listen: %v", err) }
s := grpc.NewServer()
pb.RegisterHelloServer(s, HelloService)
fmt.Println("Listen on " + addr)
s.Serve(listen) }
|
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package main
import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" pb "microservice-demo/gRPC/demo/proto" )
const ( addr = "127.0.0.1:3000" )
func main() { conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil { grpclog.Fatalln(err) }
defer conn.Close()
c := pb.NewHelloClient(conn)
reqBody := new(pb.HelloRequest) reqBody.Name = "tomonori" r, err := c.SayHello(context.Background(), reqBody) if err != nil { grpclog.Fatalln(err) }
fmt.Println(r.Message) }
|
最终结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ...
func (h helloService) SayHello(_ context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) { res := new(pb.HelloResponse) res.Message = "Hello " + req.Name + "."
return res, nil } ...
... reqBody := new(pb.HelloRequest) reqBody.Name = "tomonori"
r, err := c.SayHello(context.Background(), reqBody) if err != nil { grpclog.Fatalln(err) }
fmt.Println(r.Message)
...
|
阅读资料
https://blog.csdn.net/lihao19910921/article/details/80166399
http://doc.oschina.net/grpc?t=60133