首页 > 其他分享 >RPC 服务与 gRPC 的入门案例

RPC 服务与 gRPC 的入门案例

时间:2024-12-16 17:02:38浏览次数:6  
标签:调用 入门 gRPC grpc RPC 存根 服务端 客户端

RPC 协议

RPC(Remote Procedure Call Protocol)即远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务的协议,允许一个计算机程序可以像调用本地服务一样调用远程服务

RPC的主要作用是不同的服务间方法调用就像本地调用一样便捷,它隐藏了网络通信的细节,使得开发者可以像调用本地函数一样调用远程函数,而无需关注底层网络通信的复杂性。

在这里插入图片描述

由于客户端和服务端部署在不同的机器上,服务间的调用就会涉及到网络通信,就需要写一堆网络通信相关的代码。
例如在调用 RESTful 服务时,调用端需要使用 HttpClient 设置很多参数,再去解析状态和返回值,非常复杂且易出错。
而 RPC 的方式可以让我们像调用本地服务一样调用远程服务,而且不必关心网络通信的细节。

RPC原理

Socket套接字

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端被称为 Socket。

Socket 用于描述 IP 地址和端口,是一个通信连接的句柄,可以用来实现不同的计算机之间的通信,是网络编程接口的具体实现。

Socket 套接字是客户端/服务端网络机构程序的基本组成部分,为程序提供了一种相对简单的机制,可以与另一个本地或远程机器上的程序建立连接,并可以来回发送消息。

我们可以使用它的收发消息功能来设计我们的分布式应用程序,也可以使用这些收发功能把 RPC 调用包装成透明的远程服务调用。

调用过程

实现透明的远程调用的重点是创建客户存根(类似于代理模式中的代理),在生成代理代码后,代理的代码就能与远程服务端通信了,通信的过程是由RPC 框架实现,而调用者就像调用本地代码一样。

对于客户端而言,存根函数就像普通的本地函数,但实际上包含了通过网络发送和接收消息的代码。

在这里插入图片描述

  • 第1 步,客户端调用本地的客户端存根方法,客户端存根方法会将参数打包并封装成一个或多个网络消息体并发送到服务端。
  • 第 2 步,客户端存根通过系统调用,使用操作系统内核提供的 Socket 套接字接口来向远程服务发送我们编码的网络消息。
  • 第 3 步,网络消息由内核通过协议(TCP/UDP)传输到远程服务端。
  • 第 4 步,服务端存根接收客户端发送的消息,并对参数消息解码,将参数从标准的网络格式转换为特定的语言格式。
  • 第 5 步,服务端存根调用服务端的方法,并且将从客户端接收的参数传递给该方法,它来运行具体的功能并返回,这部分代码的执行对客户端来说就是远程过程调用。
  • 第 6 步,服务端的方法在执行完成后,会把结果返回到服务端存根代码中。
  • 第 7 步,服务端存根在将该返回值编码且序列化后,通过一个或多个网络消息发送给客户端。
  • 第 8 步,消息通过网络发送到客户端存根中。
  • 第 9 步,客户端存根从本地 Socket 接口中读取结果消息。
  • 第 10 步,客户端存根再将结果返回给客户端函数,并且将消息从网络二进制形式转换为本地语言格式,这样就完成了远程服务调用,客户端代码继续执行后续的操作。

IDL接口

为了生成客户端和服务器的存根函数,我们需要定义一个远程调用接口的定义文件,该文件是使用一种叫做 IDL 的接口定义语言编写的。

接口定义类似于函数原型声明:函数名称、输入参数和返回参数。

在 RPC 编译器运行后,客户端和服务端的程序可以编译并链接到各自的存根函数。客户端存根必须实现初始化 RPC 通信的机制,找到服务器并进行连接,并能与远程服务器通信,并对远程过程调用失败的情况进行处理。

RPC框架-gRPC

gRPC 是 Google 主导开发的一个高性能开源的 RPC 框架,基于 HTTP/2 协议标准设计而成,并用 ProtoBuf 作为序列化工具和接口定义语言(IDL),支持多语言开发。

在这里插入图片描述

定义序列化数据结构

在一个.proto文件中定义数据的格式,这个格式由一系列messages组成,每个message包含多个字段,字段就是数据的名称和值的配对。

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
}

接着通过.proto文件来定义gRPC服务,其中RPC方法的参数和返回类型都是上述定义的message

// The greeter service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

最后通过protoc编译器以及gRPC插件来从.proto文件中生成代码。

protoc是Protocol Buffers的编译器,它能够根据.proto文件中定义的数据结构生成各种语言的数据访问类。在gRPC中,protoc与gRPC插件一起使用,可以生成gRPC客户端和服务器的代码,以及用于填充、序列化和检索消息类型的常规协议缓冲区代码。

实战

项目结构

在这里插入图片描述

  • api:存放公共代码;
  • server:服务端;
  • client:客户端。

api

1)首先引入需要的依赖

<properties>
    <grpc.version>1.9.0</grpc.version>
    <protobuf-java.version>3.5.1</protobuf-java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>${protobuf-java.version}</version>
    </dependency>

    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-all</artifactId>
        <version>${grpc.version}</version>
        <exclusions>
            <exclusion>
                <artifactId>guava</artifactId>
                <groupId>com.google.guava</groupId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>24.1.1-jre</version>
    </dependency>

    <dependency> <!-- necessary for Java 9+ -->
        <groupId>org.apache.tomcat</groupId>
        <artifactId>annotations-api</artifactId>
        <version>6.0.53</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

2)引入插件

<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:${grpc.version}:exe:${os.detected.classifier}
                </pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

这个插件可以将我们编写的 .proto 文件自动转为对应的 Java 类。

3)创建 proto 目录,并定义.proto文件
在这里插入图片描述

syntax = "proto3";

option java_package = "org.young.grpc.demo";

package product;

service ProductInfo {
 rpc addProduct (Product) returns (ProductId);
 rpc getProduct(ProductId) returns(Product);
}

message Product {
 string id = 1;
 string name=2;
 string description=3;
 float price=4;
}

message ProductId {
 string value = 1;
}

4)使用插件生成 java 代码
在这里插入图片描述

compilecompile-custom 都要执行, compile 用来编译消息对象,compile-custom 则依赖消息对象,生成接口服务。

在这里插入图片描述

server

server 项目依赖 api,在 server 中,提供 ProductInfo 的具体实现。

@Slf4j
public class ProductInfoImpl extends ProductInfoGrpc.ProductInfoImplBase {

    /**
     * @param request
     * @param responseObserver
     */
    @Override
    public void addProduct(ProductOuterClass.Product request, StreamObserver<ProductOuterClass.ProductId> responseObserver) {
        log.info("request:{}", request.toString());

        responseObserver.onNext(ProductOuterClass.ProductId.newBuilder().setValue(request.getId()).build()); // 方法响应
        responseObserver.onCompleted(); // 标记 RPC 调用完成
    }

    /**
     * @param request
     * @param responseObserver
     */
    @Override
    public void getProduct(ProductOuterClass.ProductId request, StreamObserver<ProductOuterClass.Product> responseObserver) {
        responseObserver.onNext(ProductOuterClass.Product.newBuilder().setId(request.getValue()).setName("test_grpc").build());
        responseObserver.onCompleted();
    }
}

然后再启动该项目

public class ServerBootStrap {

    Server server;

    public static void main(String[] args) throws Exception {


        ServerBootStrap serverBootStrap = new ServerBootStrap();

        serverBootStrap.start();

        serverBootStrap.blockUntilShutdown();


    }

    void start() throws Exception{
        server = NettyServerBuilder.forPort(50091).addService(new ProductInfoImpl())
                .build()
                .start();
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                ServerBootStrap.this.stop();
            }
        });
    }

    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }
}

client

client 项目也需要依赖 api ,然后直接进行方法调用,如下:

@Slf4j
public class GrpcClient {
    public static void main(String[] args) {
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 50091).usePlaintext(true).build();

        ProductInfoGrpc.ProductInfoBlockingStub stub = ProductInfoGrpc.newBlockingStub(managedChannel);

        ProductOuterClass.Product req = ProductOuterClass.Product.newBuilder()
                .setId("1")
                .setPrice(100.0f)
                .setName("test-grpc")
                .setDescription("test-grpc")
                .build();

        ProductOuterClass.ProductId productId = stub.addProduct(req);

        log.info("productId.getValue:{}", productId.getValue());

        ProductOuterClass.Product product = stub.getProduct(ProductOuterClass.ProductId.newBuilder().setValue("99999").build());

        log.info("product:{}", product.toString());
    }
}

标签:调用,入门,gRPC,grpc,RPC,存根,服务端,客户端
From: https://blog.csdn.net/yqq962464/article/details/144491719

相关文章

  • Flask入门:打造简易投票系统
    目录准备工作创建项目结构编写HTML模板编写Flask应用代码解读进一步优化结语Flask,这个轻量级的PythonWeb框架,因其简洁和易用性,成为很多开发者入门Web开发的首选。今天,我们就用Flask来做一个简单的投票系统,让你快速上手Web开发,同时理解Flask的核心概念。准备工作......
  • 嵌入式从入门到入土:Linux(5)
    嵌入式从入门到入土:Linux(5)主要内容网络相关指令进程相关指令磁盘相关指令挂载与卸载Linux共享环境搭建虚拟机网络模式桥接模式和windows共享一个网段,并且能够与windows系统一样可以连接到互联网说明:虚拟机类似于一台电脑连接到了路由器好处:网络稍微会快一点坏处:占......
  • Envoy 进阶指南(上):从入门到核心功能全掌握
    文章目录1.Envoy入门1.1什么是Envoy1.2Envoy的核心功能1.3Envoy术语1.4设计目标1.5Sidecar模式2.初识Envoy2.1安装Envoy2.2简单示例了解Envoy2.3管理视图1.Envoy入门1.1什么是EnvoyEnvoy是一款CNCF旗下的开源项目,由Lyft开源。Envoy采用C++实现,......
  • HarmonyOS Next 入门实战 - 文字转拼音,文字转语音
    文字转拼音安装pinyin4js三方库ohpminstall@ohos/pinyin4jspinyin4js提供了以下接口:●文字转拼音(带声调和不带声调)●文字转拼音首字母●简体繁体互转letrawText="风急天高猿萧哀,渚清沙白鸟飞回;"letpinyin1:string=pinyin4js.convertToPinyinString(rawT......
  • 2024年最新最全网络安全护网行动指南【附零基础入门教程】_网络安全靶场整个业务指南
    前言随着《网络安全法》和《等级保护制度条例2.0》的颁布,国内企业的网络安全建设需与时俱进,要更加注重业务场景的安全性并合理部署网络安全硬件产品,严防死守“网络安全”底线。“HW行动”大幕开启,国联易安誓为政府、企事业单位网络安全护航!,网络安全形势变得尤为复杂严峻。......
  • CTF入门教程(非常详细)从零基础入门到竞赛,看这一篇就够了!
    目录一、CTF简介二、CTF竞赛模式三、CTF各大题型简介四、CTF学习路线4.1、初期1、html+css+js(2-3天)2、apache+php(4-5天)3、mysql(2-3天)4、python(2-3天)5、burpsuite(1-2天)4.2、中期1、SQL注入(7-8天)2、文件上传(7-8天)3、其他漏洞(14-15天)4.3、后期五、CTF学......
  • MyBatis入门
    MyBatis框架-它是一个优秀的持久层框架,用于在Java应用程序中与数据库进行交互。MyBatis主要的优势在于它可以将SQL语句和Java代码分离,使得代码结构更加清晰。开发人员可以在XML配置文件或者通过注解的方式编写SQL语句,MyBatis会根据这些SQL语句来操作数据库。例如,要查询数据库中......
  • Python入门:开发环境搭建(小白教程)
    ......
  • 网络信息安全工程师证2024年详细报考流程分享,零基础入门到精通,收藏这一篇就够了_网络
    网络信息安全工程师的报考流程通常包括了解相关信息、准备相关材料、在线报考和参加考试这几个步骤,学习的朋友可以按照自己的时间安排、能力情况、个人需求等方面,整理报考用到的材料并上交。另外,网络信息安全工程师证书的报考是在相关报考单位进行,个人是无法单独报考的。......
  • 渗透测试工程师薪酬待遇(非常详细)零基础入门到精通,收藏这一篇就够了
    你们有没有看过一部电影,叫《我是谁:没有绝对的安全系统》。这部电影围绕男主角和他的几位伙伴组建的黑客组织CLAY展开,讲述了他们为了追求正义而入侵国际安全系统的故事。凭借过人的黑客技术,他们的行动不仅吸引了媒体的关注,更引起了德国秘密警察和欧洲刑警组织的高度警觉。......