title: gRPC初步认识
date: 2020/4/30
comments: true
categories:
- 微服务
tags: - 微服务
permalink: “8.11”
gRPC简介
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
gRPC 客户端和服务端可以在多种环境中运行和交互 - 从 google 内部的服务器到你自己的笔记本,并且可以用任何 gRPC 支持的语言来编写。所以,你可以很容易地用 Java 创建一个 gRPC 服务端,用 Go、Python、Ruby 来创建客户端。此外,Google 最新 API 将有 gRPC 版本的接口,使你很容易地将 Google 的功能集成到你的应用里。
gRPC相对于RESTful api的优势
gRPC和restful API都提供了一套通信机制,用于server/client模型通信,而且它们都使用http作为底层的传输协议(严格地说, gRPC使用的http2.0,而restful api则不一定)。不过gRPC还是有些特有的优势,如下:
gRPC可以通过protobuf来定义接口,从而可以有更加严格的接口约束条件。关于protobuf可以参见笔者之前的小文Google Protobuf简明教程
另外,通过protobuf可以将数据序列化为二进制编码,这会大幅减少需要传输的数据量,从而大幅提高性能。
gRPC可以方便地支持流式通信(理论上通过http2.0就可以使用streaming模式, 但是通常web服务的restful api似乎很少这么用,通常的流式数据应用如视频流,一般都会使用专门的协议如HLS,RTMP等,这些就不是我们通常web服务了,而是有专门的服务器应用。)
使用 protocol buffers
gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)。通常情况下我们用 proto files 创建 gRPC 服务,用 protocol buffers 消息类型来定义方法参数和返回类型。
关于protocol buffers的版本问题,虽然你可以使用 proto2 (当前默认的 protocol buffers 版本), 我们通常建议你在 gRPC 里使用 proto3,因为这样你可以使用 gRPC 支持全部范围的的语言,并且能避免 proto2 客户端与 proto3 服务端交互时出现的兼容性问题,反之亦然。
hello gRPC
安装
环境配置
1、安装protoc
到这里下载最新版的 protoc
并配置环境变量到path下
2、安装golang protobuf
go get -u github.com/golang/protobuf/proto // golang protobuf 库
go get -u github.com/golang/protobuf/protoc-gen-go //protoc --go_out 工具
3、安装gRPC-go
go get google.golang.org/grpc
编写protobuf接口
syntax = "proto3";
package mygrpc;
// define a service
service HelloWorldService {
// define the interface and data type
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// define the data type of request
message HelloRequest {
string name = 1;
}
// define the data type of response
message HelloReply {
string message = 1;
}
编译protobuf,命令
protoc --go_out=plugins=grpc:./ *.proto
创建服务端程序
package main
import (
"context"
"fmt"
"net"
pt "server/mygrpc"
"google.golang.org/grpc"
)
const (
post = "127.0.0.1:8000"
)
type server struct{}
//实现SayHello接口
func (s *server) SayHello(ctx context.Context, in *pt.HelloRequest) (*pt.HelloReply, error) {
return &pt.HelloReply{Message: "hello" + in.Name}, nil
}
func (s *server) GetMessage(ctx context.Context, in *pt.HelloRequest) (*pt.HelloReply, error) {
return &pt.HelloReply{Message: "This is from the server "}, nil
}
func main() {
ln, err := net.Listen("tcp", post)
if err != nil {
fmt.Println("网络异常", err)
}
//创建一个grpc句柄
srv := grpc.NewServer()
pt.RegisterHelloWorldServiceServer(srv, &server{})
err = srv.Serve(ln)
if err != nil {
fmt.Println("网络启动异常", err)
}
}
编写客户端程序
package main
import (
pt "client/mygrpc"
"context"
"fmt"
"google.golang.org/grpc"
)
const (
post = "127.0.0.1:8000"
)
func main() {
conn, err := grpc.Dial(post, grpc.WithInsecure())
if err != nil {
fmt.Println("连接服务器失败", err)
}
defer conn.Close()
c := pt.NewHelloWorldServiceClient(conn)
//远程调用
r1, err := c.SayHello(context.Background(), &pt.HelloRequest{Name: "熊猫阿宝"})
if err != nil {
fmt.Println("can't get server", err)
}
fmt.Println(r1.Message)
}
首先运行server,然后运行client,就能看到 “hello熊猫阿宝”的输出了