什么是RPC?
RPC(Remote Procedure Call)
远程过程调用,
是一种进程间通信方式。
简单地说就是能使应用像调用本地方法一样的调用远程的过程或服务,可以应用在分布式服务、分布式计算、远程服务调用等许多场景。说起 RPC 大家并不陌生,业界有很多开源的优秀 RPC 框架,例如 Dubbo、Thrift、gRPC、Hprose 等等。
下面先简单介绍一下 RPC 与常用远程调用方式的特点
架构图如下:
![](https://i-blog.csdnimg.cn/blog_migrate/6a0175bb77eedbd8e5acf22a2f588da2.png)
角色解释:
客户端(Client):
服务调用发起方,也称为服务消费者。
客户端存根(Client Stub):存放在客户端
- 主要用来存储要调用的服务器的地址
- 将客户端请求远端服务器程序的数据信息打包成数据包,通过网络发送给服务端Stub程序
- 接收服务端Stub程序发送的调用结果数据包,并解析返回给客户端
服务端(Server):
远端的计算机机器上运行的程序,其中有客户端要调用的方法。
服务端存根(Server Stub):存放在服务端
1.
接收客户Stub程序通过网络发送的请求消息数据包
2.
调用服务端中真正的程序功能方法,完成功能调用
3.
服务端执行调用的结果进行数据处理打包发送给客户端Stub程序
注册中心:
分布式服务应用,服务端注册服务信息和客户端拉取服务信息
其调用时序图如下:
![](https://i-blog.csdnimg.cn/blog_migrate/19b8f2ced529bc739bb22998c642764c.png)
什么是GRPC?
GRPC
是一个高性能、开源和通用的 RPC 框架
。目前提供 C、Java 和 Go 语言版本,
分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。
这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
在 gRPC 里
客户端
应用可以像调用本地对象一样直接调用另一台不同的机器上
服务端
应用的方法,使得您能够更容易地创建分布式应用和服务。
与许多 RPC 系统类似: gRPC 也是基于以下理念:定义一个
服务:
指定其能够被远程调用的方法(包含参数和返回类型)。
在服务端实现这个接口
,并运行一个 gRPC 服务器来处理客户端调用。
在客户端拥有一个
存根
能够像服务端一样的方法。
![](https://i-blog.csdnimg.cn/blog_migrate/5543ba2ebbc0b3e2b59d1fda0ca2dbb9.png)
使用 protocol buffers-协议缓冲区
gRPC 默认使用
protocol buffers - 协议缓冲区
,这是 Google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON,但是其效率和性能会高很多
)。
用
proto files
创建 gRPC 服务,用 protocol buffers 消息类型来定义方法参数和返回类型
Java 使用GRPC步骤:
-
在一个 .proto 文件内定义服务。
-
用 protocol buffer 编译器生成服务器和客户端代码。
-
使用 gRPC 的 Java API 为你的服务实现一个简单的客户端和服务器。
使用springBoot 整合GRPC
1 引入GRPC依赖和插件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-models</artifactId>
<groupId>com.hu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springboot-rpc</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<grpc-spring-boot-starter.version>2.3.2</grpc-spring-boot-starter.version>
<os-maven-plugin.version>1.6.0</os-maven-plugin.version>
<protobuf-maven-plugin.version>0.5.1</protobuf-maven-plugin.version>
</properties>
<repositories>
<repository>
<id>jcenter</id>
<url>https://jcenter.bintray.com/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.lognet</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<version>${grpc-spring-boot-starter.version}</version>
</dependency>
</dependencies>
<build>
<extensions>
<!-- os-maven-plugin -->
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>${os-maven-plugin.version}</version>
</extension>
</extensions>
<plugins>
<!-- spring-boot-maven-plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- protobuf-maven-plugin -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>${protobuf-maven-plugin.version}</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.11.0:exe:${os.detected.classifier}</pluginArtifact>
<outputDirectory>${project.build.sourceDirectory}</outputDirectory>
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2.编写缓冲区协议,定义服务
syntax = "proto3";
option java_multiple_files = true;
// 编译生成的文件路径
package com.hu.grpc.compileFile;
// 定义入参类型
message Person {
// first_name 作为第一个参数
string first_name = 1;
// last_name 作为第二个参数
string last_name = 2;
}
// 定义返回值类型
message Greeting {
// 返回结果
string message = 1;
}
// 服务名字
service HelloWorldService {
// 方法 sayHello
// 入参 Person
// 返回值 Greeting
rpc sayHello (Person) returns (Greeting);
}
3.编译,生成对应的java 文件 通过定义好的.proto文件生成Java代码,需要安装编译器protoc插件
4.根据生成的文件,编写自己的service方法:根据定义的请求参数, 实现客户端的方法,并将返回值序列化给到客户端
@Slf4j
@GRpcService
public class HelloWorldServiceImpl extends HelloWorldServiceGrpc.HelloWorldServiceImplBase {
/**
* @param request
* @param responseObserver
*/
@Override
public void sayHello(Person request, StreamObserver<Greeting> responseObserver) {
// super.sayHello(request, responseObserver);
log.info("服务端收到信息: server received {}", request);
String message = "Hello " + request.getFirstName() + " "
+ request.getLastName() + "!";
Greeting greeting =
Greeting.newBuilder().setMessage(message).build();
log.info("服务端返回信息: server responded {}", greeting);
// 返回数据 onNext 方法一个一个的处理客户端连续发送的消息,对应着客户端的一次onNext 调用
responseObserver.onNext(greeting);
// 结束方法 onCompleted方法表示 客户端发送消息结束,对应着客户端的一次onCompleted 调用
responseObserver.onCompleted();
}
}
5.编写对应的客户端代码,用于调用对应的service:初始化信道和存根: 信道,用于存放RPC通信的服务端ip和端口号; 存根,调用服务端提供的对应方法,并将返回值反序列化给到客户端
@Component
@Slf4j
public class HelloWorldClient {
private HelloWorldServiceGrpc.HelloWorldServiceBlockingStub helloWorldServiceBlockingStub;
@PostConstruct
// 初始化信道和存根: 信道,用于RPC通信的ip和端口号; 存根,存放的客户端提供的对应方法
private void init() {
// 对应的服务器地址
ManagedChannel managedChannel = ManagedChannelBuilder
.forAddress("localhost", 6565).usePlaintext().build();
// 客户端存根,调用方法就是对应的服务器提供的相应方法
helloWorldServiceBlockingStub =
HelloWorldServiceGrpc.newBlockingStub(managedChannel);
}
public String sayHello(String firstName, String lastName) {
// 根据参数创建入参
Person person = Person.newBuilder().setFirstName(firstName)
.setLastName(lastName).build();
log.info("客户端发送信息: client sending {}", person);
// 客户端调用对应的方法,并获得对应的返回值
Greeting greeting =
helloWorldServiceBlockingStub.sayHello(person);
log.info("客户端收到信息: client received {}", greeting);
return greeting.getMessage();
}
}
6.测试,注入客户端,利用GRPC实现服务的调用
@SpringBootTest
public class TestDemo {
@Autowired
private HelloWorldClient helloWorldClient;
@Test
public void testSayHello() {
helloWorldClient.sayHello("John", "Doe");
}
}
整个项目代码结构如下:
好了,以上就是关于GRPC的简单讲解,码字不易,一键三连!