# RPC

RPC(Remote Procedure Call)是一种编程技术,允许程序员在编写程序时调用其他系统上的过程或函数,就像这些过程和函数是在本地执行一样。这种技术使得分布式应用程序的开发更加简单和直观。

在 RPC 中,客户端通过网络向远程服务器发送一个请求,请求包含要调用的函数名称、参数等信息。服务器接收到请求后,执行相应的函数,并将结果返回给客户端。这个过程对开发者来说是透明的,他们只需要关心函数调用的结果,而不必处理底层的网络通信细节。

RPC 的优点包括:

  1. 简化分布式系统的开发:它使开发者可以像调用本地函数一样调用远程函数。
  2. 提高代码复用性:服务可以被多个客户端使用,而不需要复制代码。
  3. 提高效率:某些情况下,RPC 可以比传统 HTTP 请求更快,因为它通常使用二进制编码而不是文本编码,并且可能具有更高效的序列化和反序列化机制。

然而,RPC 也存在一些挑战,例如需要处理网络错误、不同操作系统之间的兼容性问题以及安全问题。为了克服这些问题,现代的 RPC 框架通常会提供诸如负载均衡、容错、安全传输等功能。

# RPC 与 HTTP

RPC(Remote Procedure Call)和 HTTP 请求有以下几点主要区别:

  1. 目的

    • RPC 的目标是让远程调用看起来像本地函数调用,尽可能地隐藏网络细节。
    • HTTP 请求通常用于客户端与服务器之间的数据交换,它是一种应用层协议,广泛应用于 Web 开发。
  2. 协议

    • RPC 可以基于多种传输协议实现,包括 TCP、UDP 或自定义二进制协议等。例如,gRPC 使用 HTTP/2 作为传输协议。
    • HTTP 是一种应用层协议,基于 TCP/IP 协议栈,使用请求 - 响应模型进行通信。
  3. 数据格式

    • RPC 通常使用二进制编码来传递数据,这样可以减少传输的字节数量,提高效率。
    • HTTP 主要使用文本格式(如 JSON 或 XML)来传输数据,尽管也可以使用二进制格式(如 protobuf)。
  4. 语义

    • RPC 的语义更接近于直接执行程序逻辑,通常具有更好的性能表现。
    • HTTP 更关注资源访问和状态转移,它的设计是为了在分布式系统中提供文档共享和协作。
  5. 复杂性

    • RPC 实现起来可能比简单的 HTTP 调用更为复杂,因为它需要处理更多底层的网络通信细节。
    • HTTP 请求相对简单,很多编程语言都有内置的库或框架来支持 HTTP 操作。
  6. 安全性

    • RPC 的安全机制取决于具体的实现,可以通过加密和其他技术来保证数据的安全性。
    • HTTP 提供了一些标准的安全机制,比如 HTTPS(HTTP over SSL/TLS),能够提供端到端的数据加密。
  7. 跨平台和语言支持

    • RPC 的跨平台和跨语言支持依赖于所使用的特定实现。
    • HTTP 具有很好的跨平台和跨语言支持,因为它是互联网上的标准协议之一。
  8. 中间件和缓存

    • RPC 对中间件的支持程度取决于具体实现,但通常不如 HTTP 那么广泛。
    • HTTP 支持各种中间件,如代理、缓存服务器、负载均衡器等。

总之,RPC 和 HTTP 请求各有优缺点,适用于不同的场景。RPC 更适合那些对性能要求高、强调低延迟、需要隐藏网络细节的场景;而 HTTP 则更适合构建 RESTful API 和 Web 应用,因为它更加灵活,易于集成,并且有大量的现有工具和库支持。

# 分布式

分布式(Distributed)是一个广泛的计算机科学概念,指的是将一个整体的系统或任务分解成多个部分,并分布在不同的物理位置、计算机节点或者网络中的多台机器上执行。这些分散的部分通过网络进行通信和协作,以完成共同的任务目标。

分布式系统的例子包括:

  • 分布式计算:比如 MapReduce 模型,它将大数据处理任务分解成多个子任务,分布到多台服务器上并行处理。
  • 分布式数据库:数据在多台服务器上分片存储,使得读写操作可以在多台服务器上同时进行,提高性能和可用性。
  • 分布式文件系统:如 Hadoop HDFS,将大文件分割成多个块存储在多台服务器上,提供高容错性和高吞吐量的数据访问。
  • 分布式服务架构:例如微服务架构,将大型应用程序拆分成一组小型的服务,每个服务运行在独立的进程中,可以独立部署和扩展。

分布式系统的主要优势包括可伸缩性、容错性、资源共享以及更高的并发处理能力。然而,分布式系统的设计和实现也面临一些挑战,如数据一致性、网络延迟、故障恢复、安全性等问题。

# gRPC 与 RPC

gRPC 和传统 RPC(Remote Procedure Call)的核心目标都是实现远程服务调用,但它们在实现方式、性能、功能特性等方面存在显著差异。以下是两者的关键区别:

# 1. 协议与传输层

  • RPC:
    • 通用概念:RPC 是一种通信模式的抽象,本身不绑定具体协议。传统 RPC 实现(如早期的 XML-RPC、JSON-RPC)可能基于 HTTP/1.x、TCP 或其他协议。
    • 效率较低:许多传统 RPC 框架使用文本格式(如 XML、JSON),序列化 / 反序列化开销较大。
  • gRPC:
    • 基于 HTTP/2:默认使用 HTTP/2 协议,支持多路复用(Multiplexing)、头部压缩(HPACK)、服务端推送等特性,显著提升传输效率。
    • 二进制协议:使用 Protocol Buffers(Protobuf)作为默认的序列化格式,体积更小、解析更快。

# 2. 接口定义与代码生成

  • RPC:
    • 松散定义:传统 RPC 可能依赖文档或人工约定接口,容易导致前后端不一致。
    • 手动处理:需要开发者手动编写客户端和服务端代码,易出错。
  • gRPC:
    • 强类型接口:通过 .proto 文件明确定义服务接口和数据结构(使用 IDL,接口定义语言)。
    • 自动代码生成:Protobuf 编译器自动生成客户端和服务端代码(支持多种语言),确保类型安全,减少手写代码的错误。

# 3. 通信模式

  • RPC:

    • 简单请求 - 响应:大多数传统 RPC 仅支持单向的同步调用(客户端发送请求,等待服务端响应)。
  • gRPC:

    多种通信模式

    • Unary RPC:传统请求 - 响应模式。
    • Server Streaming RPC:服务端推送多个响应(如实时日志)。
    • Client Streaming RPC:客户端发送多个请求后,服务端返回一个响应(如文件上传)。
    • Bidirectional Streaming RPC:双向实时流式通信(如聊天室)。

# 4. 跨语言支持

  • RPC:
    • 依赖具体实现:不同 RPC 框架的跨语言支持差异较大,例如 Java RMI 仅支持 Java。
  • gRPC:
    • 多语言原生支持:官方支持 10+ 种语言(如 Go、Java、Python、C++、Node.js 等),通过 Protobuf 实现无缝跨语言通信。

# 5. 生态与工具链

  • RPC:
    • 碎片化:传统 RPC 框架(如 Dubbo、Thrift、XML-RPC)各自为战,生态工具分散。
  • gRPC:
    • 丰富生态:集成 Kubernetes、Envoy 等云原生工具链,支持负载均衡、重试、超时等特性。
    • 与云原生融合:天然适配微服务架构,常用于服务网格(如 Istio)中的服务间通信。

# 6. 典型应用场景

  • RPC:
    • 简单的内部服务调用。
    • 对性能要求不高的场景(如小型单体应用)。
  • gRPC:
    • 微服务架构:需要高效、类型安全的服务间通信。
    • 移动端与后端通信:利用 HTTP/2 和 Protobuf 减少带宽消耗。
    • 实时流式系统:如 IoT 设备数据推送、在线游戏、金融行情等。

# 总结对比表

特性RPCgRPC
协议HTTP/1.x、TCP 等HTTP/2
序列化XML、JSON 等文本格式Protocol Buffers(二进制)
接口定义松散约定强类型 .proto 文件
代码生成手动编写自动生成多语言代码
通信模式单向请求 - 响应支持 4 种模式(含流式)
性能较低高(HTTP/2 + Protobuf)
适用场景简单内部调用微服务、移动端、实时流

# 何时选择?

  • 选 gRPC:需要高性能、强类型接口、流式通信,或跨语言微服务。
  • 选传统 RPC:遗留系统兼容、简单场景(如少量内部服务调用)。

gRPC 可以看作 RPC 的现代化演进版本,结合了高性能协议、强类型接口和丰富的生态,是云原生时代的首选 RPC 框架。

# proto

# 1. 基本结构

一个 .proto 文件通常包含:

  • 版本声明(可选,默认 proto3
  • 包名(用于防止命名冲突)
  • 消息类型(定义数据结构)
  • 服务(定义 RPC 接口,需配合 gRPC 使用)
# 示例模板:
syntax = "proto3";  // 指定版本(proto3 或 proto2)
package mypackage;  // 包名(可选)
// 定义消息类型(类似类或结构体)
message MyMessage {
  // 字段定义
  int32 id = 1;
  string name = 2;
}
// 定义服务(RPC 接口)
service MyService {
  rpc MyMethod(MyRequest) returns (MyResponse);
}

# 2. 字段定义规则

# (1) 字段格式
[字段规则] 数据类型 字段名 = 字段编号;
  • 字段规则(proto3 默认是 singular,即单值):
    • optional :可选字段(proto3 中需显式声明)。
    • repeated :数组 / 列表(如 repeated string tags = 3; )。
    • oneof :多选一(类似联合类型)。
# (2) 数据类型
类型说明对应编程语言示例
int3232 位整数Java int
string字符串Python str
bool布尔值Go bool
bytes二进制数据C++ std::string
double双精度浮点数Java double
message嵌套的自定义消息类型自定义类
# (3) 字段编号
  • 必须唯一:每个字段的编号在消息内不可重复。
  • 范围:1 到 536,870,911(但 1~15 占用 1 字节,适合高频字段)。

# 3. 枚举(Enum)

定义一组固定值:

message SearchRequest {
  string query = 1;
  enum SearchType {
    IMAGE = 0;  // 枚举值必须从 0 开始
    VIDEO = 1;
    TEXT = 2;
  }
  SearchType type = 2;
}

# 4. 服务(Service)

定义 RPC 接口(需配合 gRPC 使用):

service UserService {
  // 定义一个方法,接收 Request 消息,返回 Response 消息
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
}
message GetUserRequest {
  int32 user_id = 1;
}
message GetUserResponse {
  string name = 1;
  string email = 2;
}

# 5. 高级特性

# (1) 嵌套消息
message Outer {
  message Inner {
    int32 id = 1;
  }
  repeated Inner inner_list = 1;
}
# (2) 导入其他 proto 文件
import "other.proto";  // 导入其他 proto 定义
# (3) 选项(Options)

控制代码生成行为:

option java_package = "com.example.mypackage";  // 指定 Java 包名
option optimize_for = SPEED;  // 优化模式(SPEED, CODE_SIZE, LITE_RUNTIME)

# 6. 编译 proto 文件

使用 protoc 编译器生成代码:

# 生成 Go 代码
protoc --go_out=. myproto.proto
# 生成 Java 代码
protoc --java_out=. myproto.proto
# 生成 Python 代码
protoc --python_out=. myproto.proto
# 生成 C++ 代码
protoc --cpp_out=. myproto.proto

# 7. 注意事项

  1. 字段编号不可重复:同一消息内的字段编号必须唯一。

  2. 保留字段:防止旧版本字段被误用:

    message Foo {
      reserved 2, 15 to 20;  // 保留字段编号或名称
      reserved "email";
    }
  3. 包名冲突:不同 .proto 文件避免使用相同包名。

# 综合示例

syntax = "proto3";
package example;
message Person {
  string name = 1;
  int32 age = 2;
  repeated string hobbies = 3;  // 列表
  optional string email = 4;    // 可选字段
  Gender gender = 5;
  enum Gender {
    UNKNOWN = 0;
    MALE = 1;
    FEMALE = 2;
  }
}
service PersonService {
  rpc GetPerson(GetPersonRequest) returns (Person);
}
message GetPersonRequest {
  int32 person_id = 1;
}

# tRPC

trpc/README.zh_CN.md

# Protocol Buffers—— 跨语言

Protocol Buffers,通常简称为 protobuf,是 Google 开发的一种数据序列化协议和工具包。它是一种灵活、高效且独立于语言的结构化数据编码方式,可以用于通信协议、数据存储等各种场景。

Protocol Buffers 的主要优点包括:

  1. 效率:与 XML 或 JSON 等文本格式相比,protobuf 采用二进制编码,所以它的数据占用空间更小,传输更快。
  2. 灵活性:你可以在不修改已部署的应用程序的情况下更新数据结构。
  3. 跨平台:官方支持 C++、Java、Python、Objective C、C#、Ruby、PHP、JavaScript 等多种编程语言,并且有大量的第三方拓展包几乎覆盖了所有流行的语言。
  4. 可扩展性:通过简单的 “版本控制” 机制,可以在不影响旧代码的情况下添加新字段。

使用 Protocol Buffers 的过程主要包括以下几个步骤:

  1. 定义数据结构:首先,你需要用 Protocol Buffers 的语法( .proto 文件)来定义你的数据结构。
  2. 生成代码:接着,使用 Protocol Buffers 编译器(protoc)根据 .proto 文件生成对应编程语言的类库。
  3. 编写应用程序:在你的应用程序中,你可以直接使用这些生成的类库来进行数据的序列化(将数据对象转换为字节流)和反序列化(从字节流恢复数据对象)。

Protocol Buffers 被广泛应用于各种场景,如网络通信、配置文件、数据持久化等。由于其高效的性能和良好的跨平台特性,它已经成为一种非常流行的数据交换格式。

# AOP

AOP(Aspect-Oriented Programming)是面向切面编程的简称,它是一种编程思想和方法论,旨在解决横切关注点问题。在软件开发中,常常存在一些功能或需求,它们跨越多个对象或者类,例如日志记录、安全检查、事务管理等。这些被称为横切关注点,因为它们贯穿于系统的多个模块。

传统的面向对象编程(OOP)通过封装、继承和多态来组织代码,但对处理这类横切关注点并不方便,往往导致大量重复代码或者 “面条式” 代码(即逻辑被分散到各个地方)。AOP 正是为了解决这个问题而提出的。

AOP 通过引入 “切面”(Aspects)的概念,将这些横切关注点从主要业务逻辑中分离出来,然后以声明的方式将它们织入(Weave)到程序的不同位置。切面可以包含通知(Advice)、切入点(Pointcut)和连接点(Join Point)等元素。

  • 通知:指的是在某个特定的连接点上执行的操作,比如方法调用前后的增强。
  • 切入点:定义了通知应该在哪些连接点上执行,通常是匹配一组方法签名的表达式。
  • 连接点:程序执行过程中的一些具体时间点,如方法调用、异常抛出等。

使用 AOP 可以在不修改原有代码的基础上添加新的功能,提高代码的可维护性和复用性。常见的 AOP 实现有 Spring AOP(基于 Java)和 AspectJ(独立于语言的 AOP 框架)。

更新于 阅读次数