共计 4664 个字符,预计需要花费 12 分钟才能阅读完成。
gRPC 的原理是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面丸趣 TV 小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
什么是 gRPC
gRPC 是什么?可以用官网的一句话来概括:A high-performance, open-source universal RPC framework。
所谓 RPC(remote procedure call 远程过程调用) 框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从 server/client 模型。使用的时候客户端调用 server 端提供的接口就像是调用本地的函数一样。如下图所示就是一个典型的 RPC 结构图。
gRPC vs Restful API
gRPC 和 restful API 都提供了一套通信机制,用于 server/client 模型通信,而且它们都使用 http 作为底层的传输协议 (严格地说,gRPC 使用的 http2.0,而 restful api 则不一定)。不过 gRPC 还是有些特有的优势,如下:
gRPC 可以通过 protobuf 来定义接口,从而可以有更加严格的接口约束条件。关于 protobuf 可以参见下期 Protobuf 简明教程,另外,通过 protobuf 可以将数据序列化为二进制编码,这会大幅减少需要传输的数据量,从而大幅提高性能。
gRPC 可以方便地支持流式通信 (理论上通过 http2.0 就可以使用 streaming 模式, 但是通常 web 服务的 restful api 似乎很少这么用,通常的流式数据应用如视频流,一般都会使用专门的协议如 HLS,RTMP 等,这些就不是我们通常 web 服务了,而是有专门的服务器应用。)
使用场景
需要对接口进行严格约束的情况,比如我们提供了一个公共的服务,很多人,甚至公司外部的人也可以访问这个服务,这时对于接口我们希望有更加严格的约束,我们不希望客户端给我们传递任意的数据,尤其是考虑到安全性的因素,我们通常需要对接口进行更加严格的约束。这时 gRPC 就可以通过 protobuf 来提供严格的接口约束。
对于性能有更高的要求时。有时我们的服务需要传递大量的数据,而又希望不影响我们的性能,这个时候也可以考虑 gRPC 服务,因为通过 protobuf 我们可以将数据压缩编码转化为二进制格式,通常传递的数据量要小得多,而且通过 http2 我们可以实现异步的请求,从而大大提高了通信效率。
但是,通常我们不会去单独使用 gRPC,而是将 gRPC 作为一个部件进行使用,这是因为在生产环境,我们面对大并发的情况下,需要使用分布式系统来去处理,而 gRPC 并没有提供分布式系统相关的一些必要组件。而且,真正的线上服务还需要提供包括负载均衡,限流熔断,监控报警,服务注册和发现等等必要的组件。不过,这就不属于本篇文章讨论的主题了,我们还是先继续看下如何使用 gRPC。
gRPC DEMO 实例详解
通过 protobuf 来定义接口和数据类型
编写 gRPC server 端代码
编写 gRPC client 端代码
本文使用 golang 去实现 demo,其中 protobuf 和 grpc 扩展的安装就跳过了。
新建 userrpc.proto
syntax = proto3
package user;
option go_package = ./grpc/user
// The User service definition.
service User {
// Get all Users with id - A server-to-client streaming RPC.
rpc GetUsers(UserFilter) returns (stream UserRequest) {}
// Create a new User - A simple RPC
rpc CreateUser (UserRequest) returns (UserResponse) {}
// Request message for creating a new user
message UserRequest {
int32 id = 1; // Unique ID number for a User.
string name = 2;
string email = 3;
string phone= 4;
message Address {
string province = 1;
string city = 2;
}
repeated Address addresses = 5;
message UserResponse {
int32 id = 1;
bool success = 2;
message UserFilter { int32 id = 1;}
编译 .proto 文件
protoc --go_out=plugins=grpc:. userrpc.proto
新建服务端 server.go
package main
import (
log
net
golang.org/x/net/context
google.golang.org/grpc
pb userrpc/grpc/user
const (
port = :50051
// server is used to implement user.UserServer.
type server struct { savedUsers []*pb.UserRequest
// CreateUser creates a new User
func (s *server) CreateUser(ctx context.Context, in *pb.UserRequest) (*pb.UserResponse, error) { s.savedUsers = append(s.savedUsers, in)
return pb.UserResponse{Id: in.Id, Success: true}, nil
// GetUsers returns all users by given id
func (s *server) GetUsers(filter *pb.UserFilter, stream pb.User_GetUsersServer) error {
for _, user := range s.savedUsers {
if filter.Id == 0 {
continue
}
if err := stream.Send(user); err != nil {
return err
}
}
return nil
func main() { lis, err := net.Listen( tcp , port)
if err != nil { log.Fatalf( failed to listen: %v , err)
}
// Creates a new gRPC server
s := grpc.NewServer()
pb.RegisterUserServer(s, server{})
s.Serve(lis)
}
客户端 client.go
package main
import (
io
log
golang.org/x/net/context
google.golang.org/grpc
pb userrpc/grpc/user
const (
address = localhost:50051
// createUser calls the RPC method CreateUser of UserServer
func createUser(client pb.UserClient, user *pb.UserRequest) { resp, err := client.CreateUser(context.Background(), user)
if err != nil { log.Fatalf( Could not create User: %v , err)
}
if resp.Success { log.Printf( A new User has been added with id: %d , resp.Id)
}
// getUsers calls the RPC method GetUsers of UserServer
func getUsers(client pb.UserClient, id *pb.UserFilter) {
// calling the streaming API
stream, err := client.GetUsers(context.Background(), id)
if err != nil { log.Fatalf( Error on get users: %v , err)
}
for { user, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil { log.Fatalf( %v.GetUsers(_) = _, %v , client, err)
}
log.Printf(User: %v , user)
}
func main() {
// Set up a connection to the gRPC server.
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil { log.Fatalf( did not connect: %v , err)
}
defer conn.Close()
// Creates a new UserClient
client := pb.NewUserClient(conn)
user := pb.UserRequest{
Id: 1,
Name: test ,
Email: fasd@163.com ,
Phone: 132222222 ,
Addresses: []*pb.UserRequest_Address{
pb.UserRequest_Address{
Province: hebei ,
City: shijiazhuang ,
},
},
}
// Create a new user
createUser(client, user)
// Filter with an id
filter := pb.UserFilter{Id: 1}
getUsers(client, filter)
}
启动 server.go
go run server.go
新打开一个窗口,启动 client.go
go run client.go
结果为
2019/07/04 17:01:16 A new User has been added with id: 1
2019/07/04 17:01:16 User: id:1 name: test
Api 实现起来比较繁琐,给开发带来难度。总的来说 gRPC 是一个不错的跨语言 rpc 解决方案,当然每个人都自己的看法或见解。针对不同的业务场景采用不同的解决方案,最终都是运行效率和开发效率的相互妥协的结果。
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注丸趣 TV 行业资讯频道,感谢您对丸趣 TV 的支持。