go语言 | grpc原理介绍(二)

news2024/11/28 8:28:54

gRPC

gRPC 是一个高性能、通用的开源 RPC 框架,其由 Google 2015 年主要面向移动应用开发并基于 HTTP/2 协议标准而设计,基于 ProtoBuf 序列化协议开发,且支持众多开发语言。

由于是开源框架,通信的双方可以进行二次开发,所以客户端和服务器端之间的通信会更加专注于业务层面的内容,减少了对由 gRPC 框架实现的底层通信的关注。

如下图,DATA 部分即业务层面内容,下面所有的信息都由 gRPC 进行封装。

在这里插入图片描述

gRPC 的特点

  • 跨语言使用,支持 C++、Java、Go、Python、Ruby、C#、Node.js、Android Java、Objective-C、PHP 等编程语言;
  • 基于 IDL 文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub;
  • 通信协议基于标准的 HTTP/2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量;
  • 序列化支持 PB(Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 调用的高性能;
  • 安装简单,扩展方便(用该框架每秒可达到百万个RPC)。

gRPC调用模型

在这里插入图片描述
1、客户端(gRPC Stub)调用 A 方法,发起 RPC 调用。

2、对请求信息使用 Protobuf 进行对象序列化压缩(IDL)。

3、服务端(gRPC Server)接收到请求后,解码请求体,进行业务逻辑处理并返回。

4、对响应结果使用 Protobuf 进行对象序列化压缩(IDL)。

5、客户端接受到服务端响应,解码请求体。回调被调用的 A 方法,唤醒正在等待响应(阻塞)的客户端调用并返回响应结果。


客户端与服务端是如何交互的

在这里插入图片描述

  1. 在建立连接之前,客户端/服务端都会发送连接前言(Magic+SETTINGS),确立协议和配置项。
  2. 在传输数据时,是会涉及滑动窗口(WINDOW_UPDATE)等流控策略的。
  3. 传播 gRPC 附加信息时,是基于 HEADERS 帧进行传播和设置;而具体的请求/响应数据是存储的 DATA 帧中的。
  4. 请求/响应结果会分为 HTTP 和 gRPC 状态响应两种类型。
  5. 客户端发起 PING,服务端就会回应 PONG,反之亦可。

浅谈使用理解

RPC 流

在 RPC 架构下,服务端(Server)定义了一组对外暴露的远程方法(Remote Procedures)。客户端(Client)通过生成对应的存根(Stub)来实现这些远程方法的本地代理。存根内包含与服务端远程方法相对应的函数签名,从而使客户端能够通过本地调用来触发远程操作。操作流程如下:

  1. 客户端通过存根(Stub)调用 getProduct 方法。
  2. 客户端存根对传入的消息(Message)执行序列化操作,并构建一个 HTTP POST 请求。在 gRPC 框架中,这一请求遵application/grpc 的 content-type规范,并将目标远程方法(/ProductInfo/getProduct)标记在 HTTP Header(Path)中。
  3. 经构建的 HTTP POST 请求随后通过网络传输到服务端。
  4. 服务端接收到消息后,从 HTTP Header 解析出待调用的远程方法,并将消息交由服务端存根(Server Stub)处理。
  5. 服务端存根对收到的消息执行反序列化,恢复为原始的数据结构。
  6. 服务端本地执行 getProduct 方法,其中反序列化后的消息作为输入参数。
  7. 方法执行后,服务端对返回结果进行序列化,并以 HTTP 响应的形式返回给客户端。该响应的构建过程与客户端的请求构建过程相对应。
  8. 客户端收到服务端的响应消息后,进行反序列化操作,并将解码后的数据提供给等待的客户端进程。

在这里插入图片描述

这些步骤与大多数 RPC 系统(如 CORBA、Java RMI 等)非常相似。gRPC 与它们之间的主要区别在于它对 message 进行编码的方式,它使用 protocol buffer 进行编码。

Protocol Buffers

Protocol buffers are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.

Protocol Buffers 是 Google 的一种语言中立、平台中立和可扩展的结构化数据序列化机制,可以视为是更小、更快、更简单的 XML。您只需一次性定义数据结构,然后可以使用特殊生成的源代码轻松地将结构化数据写入和读取出各种数据流,并且支持多种编程语言。

你可以理解 ProtoBuf 是一种更加灵活、高效的数据格式,与 XML、JSON 类似,在一些高性能且对响应速度有要求的数据传输场景非常适用。

ProtoBuf 在 gRPC 的框架中主要有三个作用:定义数据结构、定义服务接口,通过序列化和反序列化方式提升传输效率。

为什么 ProtoBuf 会提高传输效率呢?

我们知道使用 XML、JSON 进行数据编译时,数据文本格式更容易阅读,但进行数据交换时,设备就需要耗费大量的 CPU 在 I/O 动作上,自然会影响整个传输速率。Protocol Buffers 不像前者,它会将字符串进行序列化后再进行传输,即二进制数据。

在这里插入图片描述

接下来我们看下如何使用 protocol buffer 对消息进行编码。

使用 protocol buffer 定义服务包括定义服务中的远程方法和定义我们希望通过网络发送的消息。

仍以 ProductInfo 服务中的 getProduct 函数为例。该 getProduct 函数接受一个 ProductID 消息作为输入参数并返回一个 Product 消息。protocol buffer 定义如下:

syntax = "proto3";

package ecommerce;

service ProductInfo {
   rpc getProduct(ProductID) returns (Product);
}

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

message ProductID {
    string value = 1;
}

假设我们需要获取产品 ID 为 15 的产品详细信息,我们创建一个值为 15 的 ProductID 消息,并将其传递给 getProduct 函数。

product, err := c.GetProduct(ctx, &pb.ProductID{Value:15})

在下面的 ProductID 消息结构中,有一个 value 字段,其索引为 1。当我们创建一个 value 等于 15 的消息实例时,生成的字节内容为该字段的字段标识符(field identifier)+value 具体的编码值。字段标识符有时也称为标签(tag):

message ProductID {
 string value = 1;
}

字节内容结构如下图所示,其中,每个消息字段由一个标签值(Tag)和其编码值(Value)组成。
在这里插入图片描述

在 Protocol Buffers(通常简称为 Protobuf)中,标签值(Tag)具有非常重要的作用,它用于标识序列化数据流中的各个字段。

  1. 字段索引: 在 .proto 文件中,每个字段都会有一个唯一的数字标识,通常称为字段索引(Field Index)。这个数字是用于在序列化和反序列化过程中快速识别字段的。
  2. 类型编号: 类型编号(Type Identifier)表明了该字段的数据类型(例如:int32, string等)。这有助于解析器理解如何解码或解析这个字段。

标签值组合了这两部分信息(字段索引和类型编号)来生成一个唯一的标识符,用于在序列化数据流中定位和解析字段。

在给定的

 message ProductID { string value = 1; } 

示例中,字段 value 的字段索引是 1。字符串类型(string)在 Protocol Buffers 中对应的类型标识符(Wire Type)是 2。为了生成标签值,首先需要将字段索引左移 3 位,然后将类型标识符添加到其中。
下表显示了字段类型如何映射到类型编号的:
在这里插入图片描述
计算标签值:

标签值 = (字段索引 << 3) | 类型标识符
       = (1 << 3) | 2
       = 8 | 2
       = 10

为什么使用左移 3 位(<< 3):这个设计选择允许 3 位用于类型标识符(Wire Type)。左移 3 位是为了在标签值中留出空间来存储类型标识符。类型标识符是一个 0 到 7 的值,正好可以用 3 位二进制数来表示。这样,通过字段索引左移 3 位后,最低的 3 位就可以用来存储类型标识符。这种设计方式实现了一个紧凑、高效的编码方案,同时也提供了足够的信息来解析序列化后的数据。
在Protocol Buffers中,变长整数(Varints)采用一种特殊的编码方式,使得较小的数值可以用较少的字节表示。具体来说,每个字节的最低7位用于存储整数的实际数值,而最高位(第8位)用于标记是否还有更多字节。如果最高位是1,那么表示该整数还有后续字节;如果最高位是0,那么表示这是该整数的最后一个字节。

字符串通常使用一种称为"Length-delimited"的编码方式。这种方法也适用于字节串(byte arrays)和嵌套消息。

  1. 前缀长度:在实际的字符串内容之前,会有一个变长整数(Varint)来表示接下来的字符串(或字节串或嵌套消息)的长度。这个变长整数是使用Protocol Buffers的Varint编码方式进行编码的。
  2. 字符串内容:紧接着前缀长度的是字符串的实际内容,它是以原始字节形式存在的。这些字节的数量与前缀长度中指定的数值相匹配。
  3. 解码:在解码阶段,首先会读取前缀长度(它本身是一个Varint,可以用一个或多个字节表示)。解码器然后会知道接下来要读取多少字节来获取完整的字符串或字节串。
当然,让我给出一个简单的例子以说明如何使用Length-delimited编码方式来编码字符串。假设我们有一个字符串"hello"。

计算长度: 字符串"hello"包含5个字符,因此其长度是5。

编码长度: 使用Protocol Buffers的Varint编码方式,我们将这个长度数字(即5)编码为一个变长整数。在这个特定的情况下,5是一个小的数值,所以它可以用单个字节来表示:0b00000101。

字符串内容: "hello"的ASCII编码分别是:0x68 0x65 0x6C 0x6C 0x6F

组合: 最后,我们将编码后的长度和实际的字符串内容组合起来:0b00000101 0x68 0x65 0x6C 0x6C 0x6F

所以,按照Length-delimited编码方式,字符串"hello"将被编码为:05 68 65 6C 6C 6F(以十六进制表示)。

从上面的介绍,我们得出在编码方面 Protocol Buffers 对比 JSON、XML 的优点:

  • 标准的 IDL 和 IDL 编译器,这使得其对工程师非常友好;
  • 序列化数据非常简洁,紧凑,与 XML 相比,其序列化之后的数据量约为 1/3 到 1/10;
  • 解析速度非常快,比对应的 XML 快约 20-100 倍;
  • 提供了非常友好的动态库,使用非常简单,反序列化只需要一行代码。

Protobuf 也有其局限性:

  • 二进制格式导致可读性差
  • 不具备自描述能力

Protobuf 适用场景:

  • Protobuf 具有广泛的用户基础,空间开销小以及高解析性能是其亮点,非常适合于公司内部的对性能要求高的 RPC 调用;
  • 由于 Protobuf 提供了标准的 IDL 以及对应的编译器,其 IDL 文件是参与各方的非常强的业务约束;
  • Protobuf 与传输层无关,采用 HTTP 具有良好的跨***的访问属性,所以 Protobuf 也适用于公司间对性能要求比较高的场景;
  • 由于其解析性能高,序列化后数据量相对少,非常适合应用层对象的持久化场景;

长度前缀消息帧

在 gRPC 通信协议中,采用了长度前缀消息帧(Length-Prefixed Message Framing)机制来进行数据打包和传输。具体来说,每个编码后的消息体前会附加一个 4 字节的字段,用于标识接下来的消息体的字节长度。

这种机制提供了一种有效的方式来界定消息的边界,从而允许接收方进行准确和高效的消息解析。由于长度字段由 4 字节表示,因此理论上最大可支持的消息体尺寸为 2 32 − 1 2^{32} - 1 2321 字节,即近 4 GB。

这样的设计旨在实现高效的流解析和处理,同时也支持消息体的动态长度,进一步提升了通信协议的灵活性和可扩展性。

假设我们有一个简单的 gRPC 服务,其中有一个名为 GetUserInfo 的远程调用,用于获取用户信息。当客户端要获取用户 ID 为 123 的用户信息时,该调用的消息体(已序列化和编码)可能是一个二进制字符串,例如 0x12 0x07 0x55 0x73 0x65 0x72 0x31 0x32 0x33(这只是一个假设的二进制表示)。在这种情况下,消息体的长度为 9 字节。
长度前缀添加:在这个二进制消息体前,gRPC 会添加一个 4 字节的长度字段。假设长度用大端字节序表示,则该字段可能是 0x00 0x00 0x00 0x09。
完整的消息帧:添加了长度前缀后,完整的消息帧将变为 0x00 0x00 0x00 0x09 0x12 0x07 0x55 0x73 0x65 0x72 0x31 0x32 0x33。
发送到服务器:这个长度为 13 字节的消息帧将通过网络发送到 gRPC 服务器。
服务器解析:服务器首先读取前 4 字节(0x00 0x00 0x00 0x09),解析出消息体的长度为 9 字节。然后,服务器会读取接下来的 9 字节,作为完整的消息体进行解码和处理。

这样,通过使用长度前缀消息帧,gRPC 能够准确地界定每个消息体的开始和结束,从而实现高效和准确的消息解析。

在这里插入图片描述

除了消息大小之外,消息帧还会预留一个 1 字节的无符号整数来指示数据是否被压缩。Compressed-Flag 值为 1 表示二进制数据使用 Message-Encoding 标头中声明的压缩方式进行压缩,值 0 表示没有对消息字节进行压缩。

现在消息已装帧并准备好通过网络发送给对应的处理方。对于 client 端请求消息,接收者是 server。对于响应消息,接收者是 client 端。在接收端,一旦收到一条消息,首先需要读取第一个字节,检查消息是否被压缩。然后,读取接下来的四个字节以获取二进制消息的大小。一旦知道大小,就可以从消息流中读取具体的消息了。对于简单消息,我们只需处理一条带长度前缀的消息,而对于流式消息,我们需要处理多条带长度前缀的消息。

在gRPC和Protocol Buffers的上下文中,长度前缀消息帧(Length-Prefixed Message Framing)与字段的三部分(字段编号(Tag)、类型、编码值)是两个在不同层次上应用的编码机制,它们不会互相矛盾。

长度前缀消息帧:这是一种更为底层的打包方式,通常用在gRPC的传输层。这里的4字节长度前缀用于标识接下来整个消息体(即一个完整的Protocol Buffers消息)的长度。这不仅包括字段的编码值,还包括每个字段的标签和类型信息。这样,收到消息的一方知道要读取多少字节来获取完整的消息。

字段的三部分编码:这是Protocol Buffers内部的编码机制。在这里,每个字段都被编码为标签、类型和值。这三者都包含在上面提到的长度前缀消息帧内。

由于这两个机制作用在不同的层次,因此不会有矛盾。具体来说,长度前缀消息帧为整个Protocol Buffers消息提供一个“外壳”,而字段的三部分编码则是这个“外壳”内部的内容。

关于顺序性的问题,实际上,字段的三部分(标签、类型、编码值)总是作为一个整体连续存储和传输的。也就是说,一个字段的标签、类型和值会连续出现在数据流中,然后才是下一个字段的标签、类型和值。这种连续性由Protocol Buffers的编码和解码算法来保证。

综上所述,长度前缀消息帧和字段的三部分编码各自在不同的层面起作用,两者可以和谐共存,不会出现顺序或界定的问题。长度前缀是针对整个消息体,而字段的标签、类型和编码值是针对消息体内部的各个字段。这样设计确保了数据传输的高效和准确。

思考下长度前缀消息帧,是否会失序?

在gRPC和大多数基于TCP的通信协议中,数据的顺序性是由底层的TCP协议保证的。TCP是一个面向连接的、可靠的、字节流协议,它确保所有的字节都会按照发送的顺序到达接收端。

长度前缀消息帧在这种上下文中是作为一个整体发送的,即4字节的长度前缀和跟随的消息体(由长度前缀指定的长度)会作为一个整体在网络上传输。由于TCP保证了数据的顺序性和完整性,因此不会出现长度前缀和消息体之间、或不同消息之间的失序问题。

这样的设计确保了在从底层TCP流中读取数据并解析成gRPC消息时,能够正确地识别每个消息的边界,并按照正确的顺序处理它们。

简言之,长度前缀消息帧在gRPC通信中不会失序,这是由底层的TCP协议保证的。这也是为什么gRPC可以作为一个高性能、可靠的RPC框架被广泛使用的原因之一。


基于 HTTP/2 的 gRPC

除了 Protocol Buffers 之外,从交互图中和分层框架可以看到, gRPC 还有另外一个优势——它是基于 HTTP 2.0 协议的。由于 gRPC 基于 HTTP 2.0 标准设计,带来了更多强大功能,如多路复用、二进制帧、头部压缩、推送机制。
这些功能给设备带来重大益处,如节省带宽、降低 TCP 连接次数、节省 CPU 使用等,gRPC 既能够在客户端应用,也能够在服务器端应用,从而以透明的方式实现两端的通信和简化通信系统的构建。
HTTP 1.X 定义了四种与服务器交互的方式,分别为 GET、POST、PUT、DELETE,这些在 HTTP 2.0 中均保留,我们看看 HTTP 2.0 的新特性:双向流、多路复用、二进制帧、头部压缩

跟其他的相比:

  • XML序列化(Xstream)无论在性能和简洁性上比较差。
  • Thrift 与 Protobuf 相比在时空开销方面都有一定的劣势。
  • Protobuf 和 Avro 在两方面表现都非常优越。

如下图所示,gRPC Channel 表示 client 端与 server 端之间的连接,即 HTTP/2 连接。当 client 端创建 gRPC Channel 时,它会在后台创建与 server 端的 HTTP/2 连接。创建好 Channel 后,我们可以重用它来向 server 端发送多个远程调用,这些远程调用会映射到 HTTP/2 的流中。在远程调用中发送的消息以 HTTP/2 帧的形式发送。一个帧可能携带一个 gRPC 长度前缀消息,如果一个 gRPC 消息非常大,它可能跨越多个数据帧

在这里插入图片描述

在gRPC中,Channel是一个抽象概念,它代表了客户端(client)与服务端(server)之间的一个连接。具体来说,它封装了底层的HTTP/2连接以及其他与连接相关的状态信息。Channel负责管理底层连接的建立、维护以及关闭,并提供了一种简单的方式来进行远程过程调用(RPC)。

在上一节中,我们讨论了如何将我们的消息帧转为以长度为前缀的消息。当我们通过网络将它们作为请求或响应消息发送时,我们需要随消息一起发送额外的 HTTP 头。下面,我们看看如何构造请求 / 响应消息,以及需要为每条消息传递哪些标头。

请求消息

请求消息是发起远程调用的消息。在 gRPC 中,请求消息总是由 client 端应用程序触发,它由三个主要部分组成:请求头、长度前缀消息和流结束标志,如下图所示。client 端首先发送请求头,之后是长度前缀消息,最后是 EOS,标识消息发送完毕。
在这里插入图片描述

下面仍以 ProductInfo 服务中的 getProduct 函数为例,来解释请求消息是如何在 HTTP/2 帧中发送的。
当我们调用该 getProduct 函数时,client 端会发送以下请求头:

HEADERS (flags = END_HEADERS)

:method = POST 
:scheme = http 
:path = /ProductInfo/getProduct 
:authority = abc.com 
te = trailers 
grpc-timeout = 1S 
content-type = application/grpc 
grpc-encoding = gzip 
authorization = Bearer xxxxxx 

:method:设置 HTTP 方法。对于 gRPC,:method 标头始终为 POST.

:scheme:设置 HTTP 协议。如果启用了 TLS,则协议设置为 “https”,否则为 “http”。

:path:设置终端路径。对于 gRPC,此值构造为 “/{服务名称}/{方法名称}”。

:authority:设置目标 URI 的虚拟主机名。

te:设置不兼容代理的检测。对于 gRPC,该值必须是 “trailers”。

grpc-timeout:设置调用超时时常。如果未指定,server 端应假定无限超时。

content-type:设置内容类型。对于 gRPC,内容类型应以 application/grpc. 如果没有,gRPC server 会响应 HTTP 状态 415(不支持的媒体类型)。

grpc-encoding:设置消息压缩方式。可能的值为 identity、gzip、deflate、snappy 及自定义压缩方式。

authorization:这是可选的请求头,用于访问又安全限制的终端服务。

一旦 client 端发起与 server 端的调用,client 端就会以 HTTP/2 数据帧的形式发送带有长度前缀的消息。如果一个数据帧无法放下长度前缀消息,它可以跨越多个数据帧。通过在最后一个数据帧上添加一个 END_STREAM 标志来标识请求消息的结束。当没有数据要发送但我们需要关闭请求流时,我们需要发送一个带有 END_STREAM 标志的空数据帧:

DATA (flags = END_STREAM)

响应消息

响应消息由 server 端响应 client 端的请求而生成。与请求消息类似,在大多数情况下,响应消息也由三个主要部分组成:响应标头、带长度前缀的消息和尾部。当响应中没有以长度为前缀的消息需要发送给 client 端时,响应消息仅包含响应标头和尾部。

在这里插入图片描述

继续回到上一个例子。当 server 端向 client 端发送响应时,它首先发送响应头,如下所示:

HEADERS (flags = END_HEADERS)

:status = 200 
grpc-encoding = gzip 
content-type = application/grpc 

:status:标识 HTTP 请求的状态。

grpc-encoding:设置消息压缩类型。可能的值包括 identity、gzip、deflate、snappy 和自定义类型。

content-type:设置内容类型。对于 gRPC,content-type 应该设置为 application/grpc。

一旦 server 端发送完响应标头,就会以 HTTP/2 数据帧的形式发送带有长度前缀的消息。与请求消息类似,如果一个数据帧无法放下长度前缀消息,它可以跨越多个数据帧:

DATA

与请求消息不同的是,END_STREAM 标志不随数据帧一起发送,它作为一个单独的响应头发送(被称作 Trailers),通知 client 端我们完成了响应消息的发送。Trailers 还会携带请求的状态码和状态消息:

HEADERS (flags = END_STREAM, END_HEADERS)
grpc-status = 0 # OK 
grpc-message = xxxxxx 

grpc-status: gRPC 状态代码。可以参考 gRPC 官方文档查找状态码的定义。
grpc-message:错误描述。这是可选的,仅在处理请求出现错误时设置。

在某些情况下,请求调用可能会立即失败。在这些情况下,server 端需要在没有数据帧的情况下发回响应。此时,server 端只会发送 Trailers 作为响应。

关于gRPC的通信方式,在下一个章节中会介绍。

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

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

相关文章

Canoe UDS诊断技术

Canoe UDS诊断 汽车诊断技术概述诊断术语OBD诊断CAN诊断协议诊断周期UDS诊断服务Diagnostic Request和Response诊断服务介绍 诊断文件CDD介绍诊断安全访问服务(security Access)介绍 如何在Canoe UDS诊断实战CANoe 开启诊断功能Canoe 诊断实战 汽车诊断技术概述 汽车诊断技术是…

让SOME/IP运转起来——SOME/IP系统设计(上)

什么是SOME/IP&#xff1f; SOME/IP&#xff08;Scalable service-Oriented MiddlewarE over IP&#xff09;是AUTOSAR应用层的协议&#xff0c;是基于IP协议的面向服务的可拓展性的中间件。 SOME/IP中主要定义了&#xff1a; 数据的序列化&#xff1a;SOME/IP支持的数据类型…

DirectX3D 虚拟现实项目 三维物体的光照及着色(五个不同着色效果的旋转茶壶)

文章目录 任务要求原始代码CPP文件代码着色器文件代码 效果展示 任务要求 本篇文章是中国农业大学虚拟现实课程的一次作业内容&#xff0c;需要对五个茶壶模型使用不同的光照进行着色和渲染&#xff0c;然后旋转展示。 本人的代码也是在其他人的代码的基础上修改来的&#xf…

学习网络安全有哪些误区?学习之前要做哪些准备?如何系统的学习黑客技术/网络安全?

如果你想学习网络安全&#xff0c;首先你必须了解什么是网络安全&#xff01;什么是黑客&#xff01; 1.无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面性&#xff0c;例如 Web 安全技术&#xff0c;既有 Web 渗透2.也有 Web 防御技术&#xff08;WAF&am…

CVE-2023-34040 Kafka 反序列化RCE

漏洞描述 Spring Kafka 是 Spring Framework 生态系统中的一个模块&#xff0c;用于简化在 Spring 应用程序中集成 Apache Kafka 的过程&#xff0c;记录 (record) 指 Kafka 消息中的一条记录。 受影响版本中默认未对记录配置 ErrorHandlingDeserializer&#xff0c;当用户将容…

MATLAB 绘制 SISO 和 MIMO 线性系统的时间和频率响应图

系列文章目录 文章目录 系列文章目录前言一、时间响应二、频率响应三、极点/零点图和根节点四、响应特性五、分析 MIMO 系统六、系统比较七、修改时间轴或频率轴数值如果觉得内容不错&#xff0c;请点赞、收藏、关注 前言 本例演示如何绘制 SISO 和 MIMO 线性系统的时间和频率…

YOLOv8改进:检测头结构全新创新篇 | S_improve_Detect结构创新

🚀🚀🚀本文改进:S_improve_Detect 全新的结构头创新,适配各个YOLO 🚀🚀🚀S_improve_Detect 在各个场景都能够有效涨点 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; 1. S_improve_Detect介

Gradle笔记 一 Gradle的安装与入门

文章目录 Gradle 入门Gradle 简介学习Gradle 的原因&#xff1a; 常见的项目构建工具Gradle 安装Gradle 安装说明安装JDK 下载并解压到指定目录配置环境变量检测是否安装成功 Gradle 项目目录结构Gradle 创建第一个项目Gradle 中的常用指令修改maven 下载源Wrapper 包装器使用教…

CleanMyMac2024破解版如何下载?

CleanMyMac作为一款专业的苹果电脑清理软件&#xff0c;它不仅仅能单纯的卸载不用、少用的应用&#xff0c;同时还支持&#xff1a;1、清理应用程序的数据文件&#xff0c;将应用重置回初始状态&#xff0c;减少空间占用&#xff1b;2、自动检查应用更新&#xff0c;保持应用的…

Android系统Launcher启动流程学习(二)launcher启动

Zygote&#xff08;孵化器&#xff09;进程启动 在init进程中有解析.rc文件&#xff0c;在这个rc文件中配置了一个重要的服务service–zygote&#xff0c;这是app程序的鼻祖 zygote进程主要负责创建Java虚拟机&#xff0c;加载系统资源&#xff0c;启动SystemServer进程&#…

C语言switch语句

文章目录 前言一、switch语句二、switch语句中的break三、switch语句中的defaultswitch语句中的case和default的顺序问题: 总结 前言 本文将详细介绍switch语句&#xff0c;break和defualt的用法 一、switch语句 switch语句的表达式&#xff1a; switch (expression){case va…

由于flutter_app依赖于flutter_swiper>=0.0.2,不支持零安全,版本解决失败。

参考 dart3.0使用flutter_swiper报错记录 flutter_swiper package - All Versions从官网的信息可以看到 Dart3版本不兼容 最小兼容的Dart SDK版本需要2.0 Flutter SDK 版本列表Flutter SDK 版本列表 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 说明&#xff1a;因…

从物理磁盘到数据库 —— 存储IO链路访问图

原图来自&#xff1a;数据库IO链路访问图 – OracleBlog 由于很复杂&#xff0c;为了加深理解自己重新画了一次&#xff0c;另外参考其他文档补充了各部分的插图和介绍。 一、 存储服务器 1. 物理磁盘 外层的壳子称为硬盘笼 cage 2. chunklet Chunklet 是一个虚拟概念而不是实…

分布式锁在Redis集群中的实践与探讨

分布式锁的基本概念 分布式锁是在分布式计算环境下&#xff0c;用来确保多个进程或线程在访问某些共享资源时能够避免冲突的一种同步机制。其主要目的是为了保持数据的一致性和完整性。为了达到这个目的&#xff0c;分布式锁需要满足互斥性、无死锁和容错性三个基本条件。 互…

SystemC入门之测试平台编写完整示例:全加器

导读: 本文将完整演示基于systemC编写一个全加器的测试平台。具体内容包括&#xff1a;激励平台&#xff0c;监控平台&#xff0c;待测单元的编写&#xff0c;波形文件读取。 1&#xff0c;main函数模块 搭建一个测试平台主要由&#xff1a;Driver, Monitor, DUT(design under …

javaee实验:搭建maven+spring boot开发环境,开发“Hello,Spring Boot”应用

目录 mavenspringboot实验目的实验内容环境的搭建 在开发中&#xff0c;maven和spring都是非常常用、非常重要的管理工具和框架&#xff0c;今天就在这里使用idea进行环境的搭建和创建第一个spring程序 maven 1.1maven是一个跨平台的项目管理工具&#xff08;主要管理jar包&am…

Dubbo捕获自定义异常

一.问题描述 Dubbo远程服务提供者抛出的自定义异常无法被消费方正常捕获&#xff0c;消费方捕获的自定义异常全部变成RuntimeException&#xff0c;使用起来很不方便。 二.原因分析 相关源码 /** Licensed to the Apache Software Foundation (ASF) under one or more* con…

[自学记录08*]LDR、HDR与ToneMapping

一、Dynamic Range—动态范围 Dynamic Range表示动态范围&#xff0c;检测DR&#xff0c;DR又分为LDR&#xff08;Low Dynamic Range&#xff09;和HDR&#xff08;High Dynamic Range&#xff09;。它们都是表示亮度值范围的一种方式&#xff0c;那么有什么区别呢。 1.LDR …

系列六、过滤器(二)#案例演示

一、案例演示 说明&#xff1a;如下案例通过springboot的方式演示Filter是如何使用的&#xff0c;以获取Controller中的请求参数为切入点进行演示 1.1、前置准备工作 1.1.1、pom <dependencies><!-- spring-boot --><dependency><groupId>org.spring…

晨控CK-GW08系列网关控制器与CODESYS软件MODBUSTCP通讯手册

晨控CK-GW08系列是一款支持标准工业通讯协议ModbusTCP的网关控制器,方便用户集成到PLC等控制系统中。系统还集成了8路读写接口&#xff0c;用户可通过通信接口使用Modbus TCP协议对8路读写接口所连接的读卡器进行相对独立的读写操作。 晨控CK-GW08系列网关控制器适用于本公司多…