RPC

Remote Procedure Call的简称,中文叫远程过程调用。用于解决分布式系统中服务之间的调用问题。通俗地讲,就是开发者能够像调用本地方法一样调用远程的服务。所以,RPC的作用主要体现在这两个方面:

  • 屏蔽远程调用跟本地调用的区别,让我们感觉就是调用项目内的方法;
  • 隐藏底层网络通信的复杂性,让我们更专注于业务逻辑的开发。

长期以来,谷歌有一个名为 Stubby 的通用 RPC 框架,用来连接成千上万的微服务,这些微服务跨多个数据中心并且使用完全不同的技术来构建。
Stubby 的核心 RPC 层每秒能处理数百亿次的互联网请求。Stubby有许多很棒的特性,但无法标准化为业界通用的框架,这是因为它与谷歌内部的基础设施耦合得过于紧密。

GRPC

2015 年,谷歌发布了开源 RPC 框架 gRPC,由google内部的Stubby框架演化而来。这个 RPC 基础设施具有标准化、可通用和跨平台的特点,旨在提供类似 Stubby 的可扩展性、性能和功能,但它主要面向社区。用于减小网络传输数据大小的技术。它将发送的数据压缩为较小的尺寸,以减少带宽占用和提高网络性能。压缩算法可以在数据发送之前对其进行压缩,并在接收端解压缩,使数据能够原样恢复。gRPC+提供了多种压缩算法,以满足不同的需求和环境。这些算法使用不同的压缩技术和策略,例如字典编码、熵编码和预测编码等。根据数据类型、网络带宽和延迟要求等因素,选择合适的压缩算法非常重要。
作用:数据在网络上传输时,需要消耗带宽和时间。高带宽和低延迟的网络连接可以提供快速的数据传输,但是在某些情况下,网络条件可能较差或带宽受限。在这种情况下,压缩算法可以帮助减小数据量,减少传输的时间和网络占用。+压缩算法还可以降低网络传输的成本。使用较小的数据尺寸传输需要的带宽更少,这意味着可以减少使用的网络资源和相关费用。+另外,压缩算法还可以提高整体的网络性能。较小的数据传输量意味着可以更快地完成数据传输,减少了网络延迟和响应时间。这对于实时应用程序和对网络敏感的任务非常重要。

压缩算法

gRPC+提供了多种压缩算法供选择。以下是一些常见的选项:

  • gRPC内置的算法:gRPC提供了一些内置的压缩算法,包括Gzip、Deflate和Snappy等。这些算法都是通用的压缩算法,使用起来非常简单,并且可以满足大多数场景的需求。
  • brotli:Brotli是一种新兴的开源压缩算法,由Google开发。它能够以更高的压缩比和更低的延迟来减小数据尺寸。Brotli在某些情况下比传统的压缩算法效果更好,特别适用于大数据块的压缩。
  • zstd:Zstd是另一种高效的压缩算法,由Facebook开发。它具有可配置的压缩比和压缩速度,可以根据需求进行调整。Zstd在多种应用中都有良好的表现,特别适用于高带宽、高延迟的环境。
  • LZ4是一种极快的无损压缩算法,适用于对压缩速度要求较高的场景。它能够以接近硬盘传输速率的速度进行压缩和解压缩,具有出色的性能表现。
    以上仅是一些常见的选项,实际上还有其他许多压缩算法可供选择。选取适当的算法需要考虑压缩比、压缩速度、解压缩速度以及资源消耗等因素。
    如何选择:
    选择合适的压缩算法取决于多个因素。以下是一些参考的指导原则:
  • 数据类型:不同的数据类型可能对压缩算法有不同的需求。例如,文本数据和二进制数据通常需要不同的压缩策略。因此,需要根据数据类型来选择合适的算法。
  • 压缩比和速度:有些场景需要高压缩比,而有些场景则需要更快的速度。根据实际需求选择合适的算法。例如,如果对网络带宽要求不高,但是延迟要求较低,可以选择高压缩比的算法;如果对带宽要求较高,但是压缩速度要求也高,可以选择速度更快的算法。
  • 网络条件:考虑网络带宽和延迟对算法选择的影响。如果网络带宽受限,可以选择更高压缩比的算法;如果网络延迟较高,可以选择速度更快的算法。
  • 服务和客户端支持:确保选择的算法在服务和客户端上都有良好的支持。一些算法可能需要在服务器和客户端上都进行配置和启用。

官网地址

核心设计思路

  • 网络通信 ------> gRPC 自己封装了网络通信的部分,提供了多种语言的 网络通信的封装,解决异构服务的问题 (CJava[Netty] Go)
  • 协议 ------> HTTP2 传输数据的时候 使用二进制的数据内容。支持双向流(双工) 支持连接的多路复用
  • 序列化 ------> 数据传输的格式,主要有两种 基于文本(JSON) 基于二进制 java原生的序列化方式,谷歌开源的序列化方式:protubuf:Protocol Buffers 时间和空间效率是JSON的3-5倍​ IDL语言
  • 代理的创建 ----> 让调用者像调用本地方法那样,去调用远端的服务方法 称为 stub

gRPC优点

  • 高效的进行进程间通信 序列化和协议
  • 支持多语言 原生支持 C Go Java (一等公民)实现,C语言版本上扩展
  • C++,C#,NodeJS,Python等(二等公民)
  • 支持多平台运行 GRPC采用protobuf
  • 使用HTTP2协议(只有GRPC使用)

在JAVA中使用gRPC

项目结构

1. xxx-api 模块 用于定义protobuf idl语言,并且通过命令创建具体的代码,后续client server引入使用
	1. message	
	2. service
2. xxx-server 模块
	1. 实现api模块中定义的服务接口
	2. 发布gRPC服务 (创建服务端程序)
3. xxx-client模块
	1. 创建服务端stub(代理)
	2. 基于代理(stub)进行RPC调用

一、引入依赖

<dependencies>
  <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty-shaded</artifactId>
    <version>1.21.0</version>
  </dependency>
  <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-protobuf</artifactId>
    <version>1.21.0</version>
  </dependency>
  <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>1.21.0</version>
  </dependency>
</dependencies>

二、引入插件

添加的插件作用是将.proto文件编译为java代码。
插件配置中goals中compile表示编译.proto文件为Message对象,compile-custom表示编译.proto文件为gRPC对象。此插件编译后输出的java代码不在源码目录下,默认位置在打包目录的generated-sources/protobuf/文件夹下,可参考插件官方文档

<build>
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.5.0.Final</version>
        </extension>
    </extensions>
    <plugins>
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.5.1</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.14.0:exe:${os.detected.classifier}</pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>8</source>
                <target>8</target>
            </configuration>
        </plugin>

    </plugins>
</build>

若需要将Message源文件和gRPC源文件输出到不同的目录,则可以在maven的配置文件下添加如下配置:

<properties>
  <!-- Message源文件输出目录 -->
  <javaOutputDirectory>${project.basedir}/src/main/java-proto</javaOutputDirectory>
  <!-- gRPC源文件输出目录 -->
  <protocPluginOutputDirectory>
    ${project.basedir}/src/main/java-grpc
  </protocPluginOutputDirectory>
</properties>

三、编写服务端代码

1.实现服务端的sayHello方法

package com.example.grpc;
 
import io.grpc.stub.StreamObserver;
 
public class HelloWorldRpcService extends HelloWorldGrpc.HelloWorldImplBase {
 
    @Override
    public void sayHello(Greeting request, StreamObserver<HelloResp> responseObserver) {
        String name = request.getName();
        HelloResp resp = HelloResp.newBuilder()
                .setReply("Hello " + name + "!")
                .build();
        responseObserver.onNext(resp);
        responseObserver.onCompleted();
    }
}

2. 创建gRPC的服务端

package com.example.grpc;
 
import io.grpc.Server;
import io.grpc.ServerBuilder;
 
import java.io.IOException;
 
public class GrpcServer {
 
    private Server server;
    /**
     * @param port 服务端占用的端口
     */
    public GrpcServer(int port) {
        server = ServerBuilder.forPort(port)
              // 将具体实现的服务添加到gRPC服务中
                .addService(new HelloWorldRpcService())
                .build();
    }
 
    public void start() throws IOException {
        server.start();
    }
 
    public void shutdown() {
        server.shutdown();
    }
}

四、创建客户端

创建客户端调用Server端的服务

package com.example.grpc;
 
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
 
public class HelloWorldClient {
 
    private final HelloWorldGrpc.HelloWorldBlockingStub blockingStub;
   /**
    * @param host gRPC服务的主机名
    * @param port gRPC服务的端口
    */
    public HelloWorldClient(String host, int port) {
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(host, port)
              // 使用非安全机制传输
                .usePlaintext()
                .build();
 
        blockingStub = HelloWorldGrpc.newBlockingStub(managedChannel);
    }
 
    public String sayHello(String name) {
        Greeting greeting = Greeting.newBuilder()
                .setName(name)
                .build();
        HelloResp resp = blockingStub.sayHello(greeting);
 
        return resp.getReply();
    }
}

五、启动测试

编写主类,测试服务端和客户端。

运行如下代码,若无异常,将会输出 Hello, HanMeiMei!。

package com.example.grpc;
 
public class HelloWorldApp {
 
    public static void main(String[] args) throws Exception {
        int port = 8000;
        GrpcServer server = new GrpcServer(port);
        server.start();
        HelloWorldClient client = new HelloWorldClient("localhost", port);
        String reply = client.sayHello("HanMeiMei");
        System.out.println(reply);
        server.shutdown();
    }
}
Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐