什么是gRPC?有什么优点?

RPC(远程过程调用)被称为“远程”,是因为它可以在微服务架构下将服务部署在不同的服务器上时,在远程服务之间进行通信。从用户的角度来看,它就像是一个本地函数调用。

下面的图表说明了gRPC的整体数据流程。

b98afdcd-b567-4c90-9f47-5358df0adda6_1280x1619.jpeg

第1步:客户端发出REST调用。请求正文通常以JSON格式呈现。

第2-4步:订单服务(gRPC客户端)接收REST调用,对其进行转换,并向付款服务发出RPC调用。gPRC将客户端存根编码为二进制格式并将其发送到底层传输层。

第5步:gRPC通过HTTP2将数据包通过网络发送。由于二进制编码和网络优化,gRPC的速度比JSON快5倍。

第6-8步:付款服务(gRPC服务器)接收来自网络的数据包,对其进行解码并调用服务器应用程序。

第9-11步:结果从服务器应用程序返回,并被编码并发送到传输层。

第12-14步:订单服务接收数据包,对其进行解码,并将结果发送到客户端应用程序。

在使用gRPC时,需要定义protobuf消息和服务定义。protobuf消息是通信中传输的数据类型,而服务定义则描述了如何使用这些消息来实现RPC方法。

代码示例

下面是一个使用gRPC实现简单加法功能的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
syntax = "proto3";
package calculator;

service Calculator {
rpc Add (AddRequest) returns (AddResponse) {}
}

message AddRequest {
int32 a = 1;
int32 b = 2;
}

message AddResponse {
int32 result = 1;
}

上面的代码定义了一个名为Calculator的服务,该服务具有一个名为Add的RPC方法。此方法将一个请求消息AddRequest作为输入,并返回一个响应消息AddResponse

AddRequestAddResponse是protobuf消息。它们都具有简单的整数字段。AddRequest包含两个整数字段a和b,表示要相加的两个数字。AddResponse包含一个整数字段result,表示加法的结果。

现在,我们需要在客户端和服务器上实现这个服务。在开始实现客户端代码之前,需要先引入gRPC依赖库。可以通过在Maven或Gradle中添加以下依赖项来实现:

1
2
3
4
5
6
7
8
9
// Maven
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.42.0</version>
</dependency>

// Gradle
implementation 'io.grpc:grpc-netty-shaded:1.42.0'

接下来,我们可以编写一个简单的Java客户端程序来调用我们之前定义的加法服务

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
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import com.example.grpc.AddRequest;
import com.example.grpc.AddResponse;
import com.example.grpc.MathServiceGrpc;

public class MathClient {
private final ManagedChannel channel;
private final MathServiceGrpc.MathServiceBlockingStub blockingStub;

public MathClient(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port)
.usePlaintext()
.build();
blockingStub = MathServiceGrpc.newBlockingStub(channel);
}

public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}

public void add(int a, int b) {
AddRequest request = AddRequest.newBuilder()
.setA(a)
.setB(b)
.build();
AddResponse response;
try {
response = blockingStub.add(request);
} catch (StatusRuntimeException e) {
System.err.println("RPC failed: " + e.getStatus());
return;
}
System.out.println("Result: " + response.getResult());
}

public static void main(String[] args) throws Exception {
MathClient client = new MathClient("localhost", 50051);
try {
client.add(1, 2);
} finally {
client.shutdown();
}
}
}

在此代码中,我们首先创建了一个ManagedChannel实例,该实例连接到我们的服务器。然后,我们使用这个ManagedChannel实例创建一个新的MathServiceGrpc.MathServiceBlockingStub实例,该实例是通过gRPC自动生成的,可以用来调用我们定义的加法方法。

add方法中,我们首先构建一个AddRequest实例,该实例包含了我们需要相加的两个整数。然后,我们使用我们之前创建的MathServiceGrpc.MathServiceBlockingStub实例调用add方法,并传递这个AddRequest实例作为参数。最后,我们将结果打印出来。

main方法中,我们创建一个新的MathClient实例,并使用它来调用add方法。最后,我们在finally块中关闭了MathClient实例。

这个Java客户端代码可以通过以下命令来编译:

1
$ javac MathClient.java

然后,我们可以使用以下命令来运行它:

1
$ java MathClient

运行这个程序后,将输出以下内容:

1
Result: 3

这表明我们的加法服务已经成功地在客户端和服务器之间通信了。

总结

  • gRPC可以实现简单的RPC方法,在客户端和服务器之间成功地进行通信。
  • protobuf消息和服务定义使得定义和使用RPC方法更容易。
  • gRPC提供高效的网络传输和编码,从而提高了通信速度和可靠性。
  • gRPC为开发人员提供了轻松构建分布式系统的强大工具。
  • 在微服务架构下,gRPC使服务之间的通信更加容易和高效。
  • 在实际开发中需要注意潜在的问题,例如网络延迟和数据大小限制,需要仔细设计服务接口和消息类型,以确保通信的稳定性和可靠性。
  • gRPC是值得尝试的分布式系统开发工具,提供高效的RPC通信和易于使用的接口定义。
请我吃🍗