gRPC 基本原理

基础知识

RPC的语义是远程过程调用(Remote Procedure Call,RPC)就是将一个服务调用封装在一个本地方法中,让调用者像使用本地方法一样调用服务,对其屏蔽实现细节。

rpc 架构图

  1. RPC 会给对应的服务接口名生成一个代理类,即客户端 Stub。使用代理类可以屏蔽掉 RPC 调用的具体底层细节,使得用户无感知的调用远程服务。
  2. 客户端 Stub 会将当前调用的方法的方法名、参数类型、实参数等根据协议组装成网络传输的消息体,将其序列化成二进制流后,通过 Sockect 发送给 RPC 服务端。
  3. 服务端收到二进制数据流后,根据约定的协议解析出请求数据,然后反序列化得到参数,通过内部路由找到具体调用的方法,调用该方法拿到执行结果,将其序列化二进制流后,通过 Socket 返回给 RPC 客户端。

GRPC

调用
  1. Unary 模式:即请求响应模式
  2. Client Streaming 模式:Client 发送 多次,Server 回复一次
  3. Server Streaming 模式:Client 发送一次,Server 发送多次
  4. 双向 Streaming 模式:Client/Server 都发送多次
核心类

grpc 核心类层级结构图
● ServerBuilder:这是gRPC暴露给业务层的启动入口,通过设置端口号和对外提供服务的实现类,可以构造一个gRPC Server实例并启动。
● Server:gRPC 服务端中最顶层的服务抽象,有start启动和shutdown关闭两个核心动作。实现类为ServerImpl,实现了服务、方法与方法处理器的绑定、端口监听、不同类型的 Server 实现的调用、Server 生命周期管理等。
● ServerCall:服务调用抽象,在收到Body请求以后真正被触发,发起本地服务调用。
● InternalServer:gRPC真正完成通信动作的内部服务抽象,实现类为NettyServer。
● ServerTransport:InternalServer 内部依赖的通信窗口。
● NettyServerHandler:向Netty注册的处理器,是真正的核心消息接收逻辑的处理者。
● TransportState:通信状态标识,用来标识信道流的处理情况,承担实际的请求接收,解码分发工作。

服务端创建

服务端创建过程
gRPC 服务端创建采用 Build 模式,对底层服务绑定、NettyServer和gRPC Server 的创建和实例化做了封装和屏蔽,让服务调用者不用关心 RPC 调用细节。

int port = 50052;
Server server = ServerBuilder.forPort(port) // 1.绑定端口.addService(new MyGrpcServiceImpl())  // 2.添加服务实现类.build() // 3.创建NettyServer、Sever实例.start(); // 4.启动NettyServer实例,开始监听

添加服务
将需要调用的服务端接口实现类注册到内部的注册表中,RPC 调用时可以根据 RPC 请求消息中的服务定义信息(ServerServiceDefinition)查询到服务接口实现类。

gRPC与其它一些RPC框架的差异点是服务接口实现类的调用并不是通过动态代理和反射机制,而是通过proto工具生成代码,在服务端启动时,将服务接口实现类实例注册到gRPC内部的服务注册中心上。请求消息接入之后,可以根据服务名和方法名,直接调用启动时注册的服务实例,而不需要通过反射的方式进行调用,性能更优。

示例

定义 PB 文件

hellworld.proto

//第一行指定了正在使用 proto3 语法:若未指定编译器会使用 proto2。这个指定语法行必须是文件的非空非注释的第一个行。
syntax = "proto3";// 为每个 message 单独生成一个类文件
option java_multiple_files = true;
option java_package = "com.kuaishou.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";//指定 .proto 文件包名,防止不同项目之间的命名冲突。在java中,包名称用作Java包。
package helloworld;// The greeting service definition.
service Greeter {// Sends a greetingrpc SayHello (HelloRequest) returns (HelloReply) {}
}// The request message containing the user's name.
message HelloRequest {string name = 1;
}// The response message containing the greetings
message HelloReply {string message = 1;
}

在 .pb文件 中定义好 service 后,通过借助 gRPC 的 protocol buffers 插件,生成 gRPC 服务端代码。在 Java 中可使用自动构建工具 maven 的 mvn compile 命令即可生成服务端基类代码以及客户端桩代码。
重点关注生成的类 xxxxGrpc(如:MyGrpcServiceGrpc),是 .proto 文件编译后生成的 service 类。xxxxGrpc 中有一个以 ImplBase 结尾的静态抽象内部类(如:MyGrpcServiceImplBase),服务端需要重写该类中的 rpc 方法,编写自己的业务逻辑。客户端通过 xxxxGrpc 中的静态内部类 xxxxStub 实例(如:MyGrpcServiceBlockingStub)可以调用相应的 rpc 方法。

服务端代码

MyServer.java

package com.kuaishou.helloworld;import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;/*** Server that manages startup/shutdown of a {@code Greeter} server.*/
public class MyServer {private static final Logger logger = Logger.getLogger(MyServer.class.getName());private Server server;private void start() throws IOException {/* The port on which the server should run */int port = 50051;server = ServerBuilder.forPort(port).addService(new GreeterImpl()).build().start();logger.info("Server started, listening on " + port);Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {// Use stderr here since the logger may have been reset by its JVM shutdown hook.System.err.println("*** shutting down gRPC server since JVM is shutting down");try {MyServer.this.stop();} catch (InterruptedException e) {e.printStackTrace(System.err);}System.err.println("*** server shut down");}});}private void stop() throws InterruptedException {if (server != null) {server.shutdown().awaitTermination(30, TimeUnit.SECONDS);}}/*** Await termination on the main thread since the grpc library uses daemon threads.*/private void blockUntilShutdown() throws InterruptedException {if (server != null) {server.awaitTermination();}}/*** Main launches the server from the command line.*/public static void main(String[] args) throws IOException, InterruptedException {final MyServer server = new MyServer();server.start();server.blockUntilShutdown();}/*** 该类实现proto文件中对应的service,类中的方法实现service中指定的rpc*/static class GreeterImpl extends GreeterGrpc.GreeterImplBase {@Overridepublic void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();responseObserver.onNext(reply);responseObserver.onCompleted();}}}

客户端代码

package com.kuaishou.helloworld;import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;/*** A simple client that requests a greeting from the {@link MyServer}.*/
public class MyClient {private static final Logger logger = Logger.getLogger(MyClient.class.getName());private final GreeterGrpc.GreeterBlockingStub blockingStub;private final GreeterGrpc.GreeterFutureStub futureStub;/** Construct client for accessing HelloWorld server using the existing channel. */public MyClient(Channel channel) {// 'channel' here is a Channel, not a ManagedChannel, so it is not this code's responsibility to// shut it down.// Passing Channels to code makes code easier to test and makes it easier to reuse Channels.blockingStub = GreeterGrpc.newBlockingStub(channel);futureStub = GreeterGrpc.newFutureStub(channel);}/** Say hello to server. */public void greet(String name) {logger.info("Will try to greet " + name + " ...");HelloRequest request = HelloRequest.newBuilder().setName(name).build();HelloReply response;ListenableFuture<HelloReply> futureResponse;try {response = blockingStub.sayHello(request);futureResponse = futureStub.sayHello(request);} catch (StatusRuntimeException e) {logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());return;}logger.info("Greeting: " + response.getMessage());try {logger.info("Future Greeting: " + futureResponse.get().getMessage());} catch (Exception e) {e.printStackTrace();}}/*** Greet server. If provided, the first element of {@code args} is the name to use in the* greeting. The second argument is the target server.*/public static void main(String[] args) throws Exception {String user = "Tom";// Access a service running on the local machine on port 50051String target = "localhost:50051";// Create a communication channel to the server, known as a Channel. Channels are thread-safe// and reusable. It is common to create channels at the beginning of your application and reuse// them until the application shuts down.ManagedChannel channel = ManagedChannelBuilder.forTarget(target)// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid// needing certificates..usePlaintext().build();try {MyClient client = new MyClient(channel);client.greet(user);} finally {// ManagedChannels use resources like threads and TCP connections. To prevent leaking these// resources the channel should be shut down when it will no longer be used. If it may be used// again leave it running.channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);}}
}

其它

并发相关知识

  • ListenableFuture
  • Netty

参考

  • Protocol Buffers Document:PB 使用
  • Protocol Buffers 编码原理
  • grpc-java demo:grpc 代码示例
  • RPC框架原理简述:从实现一个简易RPCFramework说起:5 个 Java 类(1 个接口类 + 1 个实现类 + 1 个 RPC 框架类 + 1 个服务注册类 + 1 个服务应用类)讲清楚 RPC 本质
  • gRPC 官方文档中文版

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

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

相关文章

[docker] 关于docker的面试题

docker命名空间&#xff1f; docker与虚拟机的区别&#xff1f; 容器虚拟机所有容器共享宿主机的内核每个虚拟机都有独立的操作系统和内核通过namespace实现资源隔离&#xff0c;通过cgroup实现限制资源的最大使用量完全隔离。每个虚拟机都有独立的硬件资源秒级启动速度分钟级…

超越 Node.js:Bun 的创新与突破

1. Bun Bun 是一个全新的 JavaScript 运行时&#xff0c;类似于 Node.js 和 Deno&#xff0c;它专注于提供出色的性能和开发者体验。Bun 的一些特点包括&#xff1a; 快速的性能&#xff1a;Bun 旨在提供高性能&#xff0c;无论是启动时间、执行速度还是安装依赖包的速度。 兼…

使用小米手机

额&#xff0c;因为很久以前的华为荣耀手机&#xff08;虽然现在不是华为了&#xff09;退役了&#xff0c;所以使用了小米手机。 文章目录 小米手机和电脑互联 小米手机和电脑互联 这里我用的ubuntu 22.04 和Redmi note 13 pro。其实开始我也没想到&#xff0c;小米居然可以直…

【3.6数据库系统】数据库备份与恢复技术

目录 1.数据备份1.1备份方式1.2备份类型 2.数据库故障与恢复 1.数据备份 1.1备份方式 △冷备份也称为静态备份&#xff0c;别是将数据库正常关闭&#xff0c;在停止状态下&#xff0c;将数据库的文件全部备份(复制)下来。 △热备份也称为动态备份&#xff0c;是利用备份软件&a…

林浩然的哲学穿越之旅:从道家“道”到柏罗丁的“太一”

林浩然的哲学穿越之旅&#xff1a;从道家“道”到柏罗丁的“太一” Lin Haoran’s Philosophical Journey Across Time: From Dao in Daoism to Plotinus’s “The One” 在这个充满奇思妙想的故事里&#xff0c;我们的主角林浩然可不是个普通的家伙。他是当代的一位哲学侦探&a…

【Java面试】redis

目录 Redis 介绍Reids常用5种数据类型一个字符串类型的值能存储最大容量是多少&#xff1f;Redis 有哪些适合的场景&#xff1f;Redis的并发竞争问题如何解决?什么是缓存穿透&#xff1f;如何避免&#xff1f;什么是缓存雪崩&#xff1f;何如避免&#xff1f;Redis 中设置过期…

Flutter开发进阶之Canvas

Flutter开发进阶之Canvas 在Flutter开发中Canvas作为一个绘制2D图形的工具&#xff0c;提供了一系列绘图方法&#xff0c;可以用来绘制各种形状、线条、文本和图像等&#xff1b; Canvas对象是作为CustomPainter的子组件进行构建的&#xff1b; void paint(Canvas canvas, Si…

(M)unity受伤反弹以及死亡动画

受伤反弹 1.在人物控制脚本中添加受伤后速度将为0&#xff0c;并添加一个反弹的力 在刷新移动时&#xff0c;需要在没有受伤的状态 public bool isHurt; public float hurtForce; private void FixedUpdate() {if(!isHurt)Move(); }public void GetHurt(Transform attacker) …

策略者模式-C#实现

该实例基于WPF实现&#xff0c;直接上代码&#xff0c;下面为三层架构的代码。 目录 一 Model 二 View 三 ViewModel 一 Model using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace 设计模式练…

探索元宇宙的未来:数字人对话系统 - Linly-Talker —— “数字人交互,与虚拟的自己互动”

探索元宇宙的未来&#xff1a;数字人对话系统 - Linly-Talker —— “数字人交互&#xff0c;与虚拟的自己互动” 之前空闲的时候我似乎已经写过了有关于数字人的两篇文章&#xff0c;今天更多的写这篇文章就是想探索一下元宇宙的未来&#xff0c;这种数字人对话系统能做什么&…

Unity - gamma space下还原linear space效果

文章目录 环境目的环境问题实践结果处理要点处理细节【OnPostProcessTexture 实现 sRGB 2 Linear 编码】 - 预处理【封装个简单的 *.cginc】 - shader runtime【shader需要gamma space下还原记得 #define _RECOVERY_LINEAR_IN_GAMMA】【颜色参数应用前 和 颜色贴图采样后】【灯…

【UE5】如何给人物骨骼绑定Control Rig用来制作动画(控制)

本篇文章暂时只教绑定人物手部的Control Rig&#xff0c;脚的Control Rig举一反三即可 1&#xff0c;右键-创建-控制绑定 2在控制绑定中-右键创建基本IK 3&#xff0c;填入上臂-下臂-手 4【手和下臂】右键-新建-Add Controls For Selected&#xff0c;&#xff08;或者新建-…

第十四章 RabbitMQ应用

文章目录 前言1、RabbitMQ概念1.1、生产者和消费者1.2、队列1.3、交换机、路由键、绑定1.3.1、交换机类型 2、RabbitMQ运转流程2.1、生产者发送消息流程2.2、消费者接收消息的过程2.3、AMQP协议 3、RabbitMQ windows安装3.1、下载3.2、安装 4、Spring Boot 整合RabbitMQ4.1、在…

【语录】岁月

中年 写中年&#xff0c;应该是年少励志三千里 踌躇百步无寸功&#xff0c;转眼高堂已白发 儿女蹒跚学堂中&#xff0c;不如意事常八九&#xff0c;可与人言无二三 可是诸位&#xff0c;不用悲伤&#xff0c;稻盛和夫说&#xff0c; 人生并不是一场物质的盛宴&#xff0c;而是…

单片机学习笔记---LCD1602调试工具

LCD1602调试工具 这一节开始之前先说明一下&#xff0c;模块化编程相关的知识&#xff08;就是将代码分成多个文件来写&#xff0c;比如函数的定义放在.c文件中&#xff0c;函数的声明写在.h文件中&#xff09;属于是C语言的内容&#xff0c;学过C语言的伙伴应该都知道。由于这…

Vue2.0+Element实现日历组件

(壹)博主介绍 &#x1f320;个人博客&#xff1a; 尔滨三皮⌛程序寄语&#xff1a;木秀于林&#xff0c;风必摧之&#xff1b;行高于人&#xff0c;众必非之。 (贰)文章内容 1、安装依赖 npm install moment2.29.4 --savenpm install lunar0.0.3 --savenpm install lunar-java…

配置华为交换机生成树VBST案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom 思科认证CCNA\CCNP\CCIE 红帽认证Linux\RHCE\RHC…

代理IP使用指南:风险与注意事项

在当今的数字化时代&#xff0c;使用在线代理IP已经成为一种常见的网络行为。然而&#xff0c;在使用这些代理IP时&#xff0c;我们需要注意一些风险和问题&#xff0c;以确保我们的网络安全和隐本私文。将探讨使用代理IP时需要注意的几个关键问题。 1、代理IP的安全性 使用代理…

设计模式:工厂方法模式

工厂模式属于创建型模式&#xff0c;也被称为多态工厂模式&#xff0c;它在创建对象时提供了一种封装机制&#xff0c;将实际创建对象的代码与使用代码分离&#xff0c;有子类决定要实例化的产品是哪一个&#xff0c;把产品的实例化推迟到子类。 使用场景 重复代码 : 创建对象…

OpenAI ChatGPT-4开发笔记2024-07:Embedding之Text Similarity文本相似度

语义相似性semantic similarity 背景结果 背景 OpenAI has made waves online with its innovative embedding and transcription models, leading to breakthroughs in NLP and speech recognition. These models enhance accuracy, efficiency, and flexibility while speed…