RPC 服务与 gRPC 的入门案例

news2025/2/26 23:25:13

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());
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261415.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Spring Boot的体育商品推荐系统

一、系统背景与目的 随着电子商务的快速发展和人们健康意识的提高&#xff0c;体育商品市场呈现出蓬勃发展的态势。然而&#xff0c;传统的体育商品销售方式存在商品种类繁多、用户选择困难、个性化需求无法满足等问题。为了解决这些问题&#xff0c;基于Spring Boot的体育商品…

在 DDD 中优雅的发送 Kafka 消息

前言 1:host 映射 下载 SwitchHost 配置一个映射地址。点击 添加一个本地环境&#xff0c;之后配置你的 IP kafka 这样就能找这个地址了。IP 为你本地的IP&#xff0c;如果是云服务器就是公网IP地址 使用docker-compose.yml进行一键部署安装 version: 3.0 # docker-compose …

数字电视标准与分类

数字电视相关内容是一个极其成熟且久远的领域&#xff0c;并不像其它的技术方面那么前沿。但是学习技术的另外一个方面也不就是可以维持咱们的好奇心以及认识生活中多个事务后面的技术本质。 近年来&#xff0c;电视领域发生了一系列的变化&#xff0c;电视数字化的进程明显加快…

AI视频配音技术创新应用与商业机遇

随着人工智能技术的飞速发展&#xff0c;AI视频配音技术已经成为内容创作者和营销人员的新宠。这项技术不仅能够提升视频内容的吸引力&#xff0c;还能为特定行业带来创新的解决方案。本文将探讨AI视频配音技术的应用场景&#xff0c;并讨论如何合法合规地利用这一技术。 AI视频…

Airbus结构数字样机理念及实践(转)

关注作者 1、数字样机的背景 早期的设计文档通过二维工程图来描述&#xff0c;对工程师来说&#xff0c;绘制工程图足够表达设计思想&#xff0c;工程图成为了标准的“工程师语言”。但是外围的用户通常通过透视图来表达设计意图&#xff0c;不得不产生了大量针对不同教育背景…

UNIAPP框架uView初步集成与开发设计

uView UI&#xff0c;是uni-app生态最优秀的UI框架&#xff0c;全面的组件和便捷的工具会让您信手拈来&#xff0c;如鱼得水。本文章分享UNIAPP集成使用uView页面动态开发设计。 一、使用HBuilder X 直接导入插件&#xff0c;下载后重启 uView - DCloud 插件市场 二、配置样…

【图像处理lec7】图像恢复、去噪

目录 一、图像退化与恢复概述 二、图像退化中的噪声模型 1、使用 imnoise 函数添加噪声 &#xff08;1&#xff09;imnoise 函数的概述 &#xff08;2&#xff09;函数语法 &#xff08;3&#xff09;支持的噪声类型与具体语法 &#xff08;4&#xff09;噪声类型的详细…

dpdk中udp包的接受与发送

预备知识 dpdk中一些关键宏定义与结构体定义 以太网帧相关 宏RTE_ETHER_ADDR_LEN mac地址长度&#xff0c;6字节48位 宏RTE_ETHER_TYPE_IPV4 代表ipv4 struct rte_ether_hdr 以太网帧头结构体&#xff0c;包含了三个成员变量&#xff0c;目的地址&#xff0c;源地址&#…

C语言进阶(2) ---- 指针的进阶

前言&#xff1a;指针的主题&#xff0c;我们在初阶的《指针》章节已经接触过了&#xff0c;我们知道了指针的概念&#xff1a; 1.指针就是个变量&#xff0c;用来存放地址&#xff0c;地址唯一标识一块内存空间。 2.指针的大小是固定的4/8个字节(32位平台/64位平台)。 3.指针是…

项目整体结构优化

文章目录 1.依赖配置方式1.作为专门管理依赖的模块2.作为父模块3.作为子模块4.注意事项1.关于relativePath的配置2.关于打包的配置3.遇到maven报错的解决方案1.首先刷新maven2.从子模块开始clean-install3.最终:在最顶级的模块clean-package 2.概览3.新建一个子模块sun-depende…

计算机视觉-边缘检测

图片分类 一张图片中可能有多个需要识别的物体&#xff0c;会用方框标注他们的位置和类别 例&#xff1a; 给出一张照片&#xff0c;计算机需要从中识别出这是一只猫 一张图片的计算量是较大的&#xff0c;这张图片的尺寸虽然是6464&#xff0c;因为每张图片有3个颜色通道&am…

多模块应用、发布使用第三方库(持续更新中)

目录: 1、多模块概述&#xff08;HAP、HSP、HAR&#xff09; HAR与HSP两种共享包的主要区别体现在&#xff1a; 2、三类模块&#xff1a; 3、创建项目&#xff1a;项目名&#xff1a;meituan &#xff08;1&#xff09;创建Ability类型的Module&#xff0c;编译后为HAP文件…

安卓 文件管理相关功能记录

文件管理细分为图片、视频、音乐、文件四类 目录 权限 静态声明权限 动态检查和声明权限方法 如何开始上述动态申请的流程 提示 图片 获取图片文件的对象列表 展示 删除 视频 获取视频文件的对象列表 获取视频file列表 按日期装载视频文件列表 展示 播放 删除…

CHIMA网络安全攻防大赛经验分享

比赛模式 第一轮&#xff1a;20分钟基础知识赛&#xff08;50道题&#xff09; 安全运维&#xff0c;法律法规&#xff0c;linux操作系统等 第二轮&#xff1a;50分钟CTF夺旗&#xff08;5道题&#xff09; 题目涵盖 密码学 运用多种工具&#xff0c;如ASCII对照&#xff0c…

QT 国际化(翻译)

QT国际化&#xff08;Internationalization&#xff0c;简称I18N&#xff09;是指将一个软件应用程序的界面、文本、日期、数字等元素转化为不同的语言和文化习惯的过程。这使得软件能够在不同的国家和地区使用&#xff0c;并且可以根据用户的语言和地区提供本地化的使用体验。…

[Java] 使用 VSCode 来开发 Java

目录 前言Java 环境怎么看自己是否已经配置完成&#xff1f;安装 JDK安装 Maven 环境修改 Maven 依赖源 完善 VS Code配置插件配置 Maven配置 Maven Settings配置 Maven 可执行文件地址 前言 由于使用 VSCode 编码已经成为习惯&#xff0c;并且它确实相对其他的 IDE 较为轻量化…

如何高效获取Twitter数据:Apify平台上的推特数据采集解决方案

引言 在数据分析和市场研究领域&#xff0c;Twitter&#xff08;现在的X&#xff09;数据一直是重要的信息来源。但是&#xff0c;自从Twitter更改API定价策略后&#xff0c;获取数据的成本大幅提升。本文将介绍一个经济实惠的替代方案。 为什么需要Twitter数据&#xff1f; …

vue3+ant design vue实现日期选择器不展示清除按钮

1、代码&#xff1a;只需设置:allowClear"false"即可 <a-date-pickerv-model:value"value1":disabledDate"disabledDate"change"queryRate":allowClear"false" />const disabledDate (current: Dayjs) > {// 获取…

S2CRNet 图像测评笔记 图像融合

空间分离曲线渲染网络用于高效高分辨率图像协调 开源地址&#xff1a; https://github.com/stefanLeong/S2CRNet 效果图&#xff1a; 左边是输入&#xff0c;最右边是效果&#xff1a;效果不是很理想&#xff0c;色差问题还在 本地代码&#xff1a; S2CRNet-demos-main

【计算机网络】Layer4-Transport layer

目录 传输层协议How demultiplexing works in transport layer&#xff08;传输层如何进行分用&#xff09;分用&#xff08;Demultiplexing&#xff09;的定义&#xff1a;TCP/UDP段格式&#xff1a; UDPUDP的特点&#xff1a;UDP Format端口号Trivial File Transfer Protocol…