欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > RPC 详解

RPC 详解

2024/12/22 1:43:10 来源:https://blog.csdn.net/qq_33807380/article/details/144139364  浏览:    关键词:RPC 详解

一、简介

RPC(Remote Procedure Call,远程过程调用)是一种计算机通信协议,允许程序在不同的计算机上执行过程或服务。RPC 使得开发者能够像调用本地函数一样调用远程服务,简化了网络编程的复杂性。使得开发者能够专注于业务逻辑,而不必过多关注底层的网络通信细节。

二、工作原理

  1. 客户端调用:客户端程序调用一个本地的代理(Stub),这个代理的功能是模拟远程过程的调用。
  2. 参数打包:代理将调用的参数打包(序列化),将其转换为可以通过网络传输的格式。这一过程通常称为“编码”或“序列化”。
  3. 发送请求:代理通过网络将请求发送到远程服务器。这个请求包含了要调用的函数名和参数。
  4. 服务器接收请求:远程服务器上的代理(Stub)接收到请求后,进行解码(反序列化),将参数转换回原始格式。
  5. 执行过程:服务器的代理调用实际的服务端过程,并将结果返回给代理。
  6. 结果返回:服务器的代理将结果打包并发送回客户端。
  7. 客户端接收结果:客户端的代理接收到结果后进行解码,并将结果返回给原始调用的客户端程序。

三、优缺点

优点:

  • 透明性:开发者可以像调用本地函数一样调用远程服务,简化了分布式系统的开发。
  • 语言无关性:RPC 可以在不同的编程语言之间进行通信,只要双方遵循相同的协议。
  • 高效性:通过使用序列化和网络传输,RPC 可以高效地进行远程调用。

缺点:

  • 网络延迟:由于涉及网络通信,RPC 调用的延迟通常高于本地调用。
  • 错误处理:网络问题可能导致调用失败,开发者需要处理这些异常情况。
  • 安全性:远程调用可能面临安全风险,需要采取适当的安全措施。

四、常见RPC框架

一、gRPC

gRPC(Google Remote Procedure Call)是一个高性能、开源和通用的远程过程调用(RPC)框架,由Google开发。它基于HTTP/2协议,支持多种编程语言,旨在简化微服务之间的通信。

  • 语言支持:多种编程语言,包括(C++, Java, Python, Go, C#, Node.js)等。
  • 主要特性:
    • 高性能:使用HTTP/2,支持多路复用、流控和头压缩,能够提高网络效率和降低延迟。
    • IDL(接口定义语言):使用Protocol Buffers(protobuf)作为接口定义语言,允许开发者定义服务和消息格式,具有良好的跨语言兼容性。
    • 流式传输:支持四种类型的服务方法:单向 RPC、服务器流式 RPC、客户端流式 RPC 和双向流式 RPC,适合不同的应用场景。
    • 负载均衡和故障恢复:内置了负载均衡和故障恢复机制,能够提高系统的可用性和可靠性。
    • 安全性:支持TLS加密,确保数据在传输过程中的安全性。
示例代码:

下面使用gRPC实现一个Java客户端调用Python服务端的示例,包括服务定义、服务实现和客户端调用。主要有以下几个步骤:

  1. 定义gRPC服务:使用Protocol Buffers(.proto文件)定义服务和消息。
  2. 生成代码:使用protoc编译器生成Java和Python代码。
  3. 实现Python服务端:编写Python代码来实现gRPC服务。
  4. 实现Java客户端:编写Java代码来调用Python服务。
(1)定义 gRPC 服务

首先,需要定义一个 gRPC 服务。创建一个 .proto 文件,例如:example.proto:

syntax = "proto3";package example;// 定义请求消息
message HelloRequest {string name = 1;
}// 定义响应消息
message HelloResponse {string message = 1;
}// 定义服务
service Greeter {rpc SayHello(HelloRequest) returns (HelloResponse);
}
(2)生成代码

使用protoc编译器生成Java和Python代码。确保已经安装了protoc和相应的gRPC插件。

  • 生成Python代码
    python -m grpc_tools.protoc -I. --python_out=./python_out --grpc_python_out=./python_out helloworld.proto-I.:指定 proto 文件的搜索路径。
    --python_out=./python_out:指定生成的 Python 代码输出目录。
    --grpc_python_out=./python_out:指定生成的 gRPC Python 代码输出目录。
    
  • 生成Java代码
    protoc --java_out=./java_out --grpc_out=./java_out --plugin=protoc-gen-grpc-java=path/to/protoc-gen-grpc-java  helloworld.proto--java_out=./java_out:指定生成的 Java 代码输出目录。
    --grpc_out=./java_out:指定生成的 gRPC 代码输出目录。
    --plugin=protoc-gen-grpc-java=path/to/protoc-gen-grpc-java:指定 gRPC Java 插件的路径。
    

生成的代码将会在指定的输出目录中。对于 Java,通常会生成以下文件:

  • GreeterGrpc.java
  • HelloRequest.java
  • HelloResponse.java

对于 Python,通常会生成以下文件:

  • example_pb2.py
  • example_pb2_grpc.py
(3)实现Python服务端

在 Python 中,使用生成的代码来实现 gRPC 服务,创建一个名为server.py的文件,内容如下:

import grpc
from concurrent import futures
import time# 导入生成的python代码
import example_pb2
import example_pb2_grpc// 定义一个ExampleService类,继承自 example_pb2_grpc.ExampleServiceServicer,这意味着它实现了 gRPC 服务的基本功能
class ExampleService(example_pb2_grpc.ExampleServiceServicer):/*** SayHello 是一个 RPC 方法,接收两个参数:* request: 包含客户端发送的数据,通常是一个包含请求字段的对象。* context: 提供与请求相关的上下文信息,例如元数据、取消请求等。* 方法返回一个 HelloResponse 对象,该对象是通过 example_pb2 模块定义的。*/def SayHello(self, request, context):return example_pb2.HelloResponse(message=f"Hello, {request.name}!")def serve():// 创建一个 gRPC 服务器,使用线程池执行器,最大工作线程数为 10server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))// 将 ExampleService 的实现添加到服务器example_pb2_grpc.add_ExampleServiceServicer_to_server(ExampleService(), server)// 服务器监听所有可用的网络接口,端口为 50051server.add_insecure_port('[::]:50051')// 启动服务器server.start()print("Server is running on port 50051...")try:while True:# 运行一天time.sleep(86400)  except KeyboardInterrupt:# 停止服务器server.stop(0)if __name__ == '__main__':serve()
(4)实现Java客户端

在Java中,使用生成的代码来实现客户端,创建一个名为Client.java的文件,内容如下:

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;# 导入生成的Java代码
import example.ExampleServiceGrpc;
import example.HelloRequest;
import example.HelloResponse;public class Client {public static void main(String[] args) {// 创建一个 gRPC 管道,连接到本地的 50051 端口ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051).usePlaintext()  // 使用明文传输,不加密.build();  // 构建管道// 创建一个阻塞式的存根,用于调用 ExampleService 服务ExampleServiceGrpc.ExampleServiceBlockingStub stub = ExampleServiceGrpc.newBlockingStub(channel);// 构建请求HelloRequest request = HelloRequest.newBuilder().setName("World").build();// 通过存根发送请求并接收响应HelloResponse response = stub.sayHello(request);// 打印从服务器接收到的响应消息System.out.println("Response from server: " + response.getMessage());// 关闭管道channel.shutdown();}
}
二、Thrift

Thrift 是一个开源的跨语言服务开发框架,最初由 Facebook 开发,旨在简化不同编程语言之间的服务调用。

  • 语言支持:多种编程语言,包括(Java、C++、Python、PHP、Ruby、Go)等。
  • 主要特性:
    • 高效的序列化:Thrift 提供了高效的二进制序列化机制,能够快速地将数据结构转换为字节流,适合高性能的网络通信。
    • 灵活的接口定义:Thrift 使用一种简单的接口定义语言(IDL)来定义服务和数据结构,开发者可以通过 Thrift 编译器生成相应语言的代码。
    • 多种传输和协议:Thrift 支持多种传输方式(如 TCP、HTTP)和协议(如二进制协议、JSON 协议),可以根据需求选择合适的组合。
    • 服务治理:Thrift 提供了服务注册和发现的功能,方便管理和调用分布式服务。
示例代码:

下面使用Thrift实现一个Java客户端调用Python gRPC服务端的示例。主要涉及以下几个步骤,包括定义服务、生成代码、实现服务端和客户端。

(1)定义服务

首先,创建一个IDL(Interface Definition Language)文件,主要用于定义服务接口和数据结构,以便在不同编程语言之间进行高效的远程过程调用(RPC)。例如下面创建的example.thrift文件中主要包含以下几部分:

  • 命名空间:使用 namespace 关键字定义不同编程语言的命名空间。
  • 结构体:使用 struct 定义数据结构,字段使用 类型 名称 的格式,并且每个字段都有一个唯一的 ID(数字)。
  • 服务:使用 service 定义服务接口,服务中的每个方法可以定义输入参数和返回值。
namespace py example  // Python 命名空间
namespace java example  // Java 命名空间// 定义一个简单的结构
struct User {1: i32 id,          // 用户 ID2: string name,     // 用户名3: string email     // 用户邮箱
}// 定义一个服务
service ExampleService {// 创建用户void createUser(1: User user),// 获取用户信息User getUser(1: i32 id),// 更新用户信息void updateUser(1: User user),// 删除用户void deleteUser(1: i32 id)
}
(2)生成代码

使用Thrift编译器生成Java和Python代码,生成Python和Java的代码,分别在 gen-py 和 gen-java 目录下。在已经安装了Thrift编译器的情况下,可以使用以下命令:

thrift --gen py example.thrift
thrift --gen java example.thrift
(3)实现Python服务端

在Python中实现服务端。创建一个文件 server.py:

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from example import ExampleService
from example.ttypes import Userclass ExampleServiceHandler:def __init__(self):self.users = {}def createUser(self, user):self.users[user.id] = userdef getUser(self, id):return self.users.get(id)def updateUser(self, user):if user.id in self.users:self.users[user.id] = userdef deleteUser(self, id):if id in self.users:del self.users[id]if __name__ == '__main__':handler = ExampleServiceHandler()processor = ExampleService.Processor(handler)transport = TSocket.TServerSocket(host='127.0.0.1', port=9090)tfactory = TTransport.TBufferedTransportFactory()pfactory = TBinaryProtocol.TBinaryProtocolFactory()server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)print("Starting the server...")server.serve()
(4)实现Java客户端

在Java中实现客户端。创建一个文件 Client.java:

import org.apache.thrift.TException;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.protocol.TBinaryProtocol;
import example.ExampleService;
import example.User;public class ExampleClient {public static void main(String[] args) {TTransport transport = new TSocket("127.0.0.1", 9090);try {transport.open();TBinaryProtocol protocol = new TBinaryProtocol(transport);ExampleService.Client client = new ExampleService.Client(protocol);// 创建用户User user = new User();user.setId(1);user.setName("Alice");user.setEmail("alice@example.com");client.createUser(user);// 获取用户信息User retrievedUser = client.getUser(1);System.out.println("Retrieved User: " + retrievedUser.getName());// 更新用户信息user.setEmail("alice_new@example.com");client.updateUser(user);// 删除用户client.deleteUser(1);} catch (TException x) {x.printStackTrace();} finally {transport.close();}}
}
三、Dubbo

Dubbo 是一个开源的高性能 Java RPC 框架,最初由阿里巴巴开发。它主要用于构建分布式服务,支持服务的注册、发现、调用和负载均衡等功能。Dubbo 以其高效、灵活和可扩展性而受到广泛欢迎,尤其是在微服务架构中。

  • 语言支持:Java
  • 主要特性:
    • 高性能:Dubbo 采用了高效的网络通信协议,能够处理大量的并发请求。
    • 服务治理:提供服务注册与发现、负载均衡、容错、限流等功能,帮助开发者管理微服务。
    • 多协议支持:支持多种协议(如 Dubbo、HTTP、REST、gRPC 等),可以根据需求选择合适的协议。
    • 扩展性:支持 SPI(Service Provider Interface)机制,允许用户自定义扩展。
    • 监控与管理:提供监控和管理工具,帮助开发者实时监控服务的状态和性能。
  • 组件
    • Provider:提供服务的应用。
    • Consumer:调用服务的应用。
    • Registry:服务注册中心,负责服务的注册与发现。
    • Monitor:监控中心,收集服务调用的统计信息。
示例代码:

通过将服务提供者和消费者同时注册到nacos注册中心后,直接在消费者服务中掉用提供者服务的接口。主要包含以下几步:

(1)环境准备
  • 一个能正常启动的提供者服务。
  • 一个能正常启动的消费者服务。
  • 一个能正常启动的Nacos 服务器。
(2)配置提供者服务
  1. 首先在提供者服务的 pom.xml 中添加以下依赖:

    <dependencies><!-- Dubbo 依赖 --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.0.0</version> <!-- 请根据需要选择版本 --></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>3.0.0</version></dependency><!-- Nacos 依赖 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2.2.0</version> <!-- 请根据需要选择版本 --></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2.2.0</version></dependency>
    </dependencies>
    
  2. 然后在提供者服务的 src/main/resources/application.properties 中添加 Nacos 的配置:

    spring.application.name=dubbo-demo
    spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
    spring.cloud.nacos.config.server-addr=127.0.0.1:8848
    
  3. 在提供者服务中创建一个服务接口,例如 HelloService:

    package com.example.service;public interface HelloService {String sayHello(String name);
    }
    
  4. 实现服务接口,创建接口的实现类,使用@DubboService注解来定义这个服务是dubbo服务:

    package com.example.service.impl;import com.example.service.HelloService;
    import org.apache.dubbo.config.annotation.DubboService;@DubboService
    public class HelloServiceImpl implements HelloService {@Overridepublic String sayHello(String name) {return "Hello, " + name;}
    }
    
(3)配置消费者服务
  1. 首先在消费者服务的 pom.xml 中添加以下依赖:

    <dependencies><!-- Dubbo 依赖 --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.0.0</version> <!-- 请根据需要选择版本 --></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>3.0.0</version></dependency><!-- Nacos 依赖 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2.2.0</version> <!-- 请根据需要选择版本 --></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2.2.0</version></dependency>
    </dependencies>
    
  2. 然后在消费者服务的 src/main/resources/application.properties 中添加 Nacos 的配置:

    spring.application.name=dubbo-consumer
    spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
    
  3. 使用 Dubbo 进行 RPC 调用,通过创建一个消费者类,并使用@DubboReference注解自动注入远程服务的代理对象来调用提供者服务的service:

    import org.apache.dubbo.config.annotation.DubboReference;
    import com.example.service.HelloService;
    import org.springframework.stereotype.Component;@Component
    public class HelloConsumer {@DubboReference // Dubbo 的注解,标识这是一个服务消费者private HelloService helloService;public void sayHello(String name) {String result = helloService.sayHello(name);System.out.println(result);}
    }
    
四、Spring Cloud

Spring Cloud 是一组工具和框架,旨在帮助开发者构建分布式系统,特别是微服务架构。在 Spring Cloud 中实现 RPC 调用通常可以通过使用 Spring Cloud OpenFeign 或 Spring Cloud Ribbon + Spring Cloud Eureka 来完成。

  • 语言支持:Java
  • 主要特性:
    • 服务注册与发现:Spring Cloud 提供了 Eureka 作为服务注册与发现的解决方案,并结合 Ribbon 或 Spring Cloud LoadBalancer,客户端可以在多个服务实例之间进行负载均衡。
    • 服务调用:通过Spring Cloud Feign 是一个声明式的 Web 服务客户端,可以简化 HTTP 请求的编写。传统的 RESTful 服务调用方式,可以使用RestTemplate 来进行 HTTP 请求。
    • 容错处理:Hystrix提供了熔断器模式的实现,能够在服务调用失败时快速返回默认值,防止服务雪崩。而Resilience4j作为 Hystrix 的替代品,提供了更轻量级的熔断、限流和重试机制。
    • API 网关:Spring Cloud Gateway提供了一个简单的 API 网关解决方案,可以路由请求到不同的微服务,并支持过滤器、负载均衡等功能。
    • 配置管理:Spring Cloud Config提供了集中式的配置管理,支持动态刷新配置,方便微服务的配置管理。
    • 链路追踪:Sleuth 和 Zipkin提供了分布式追踪的能力,可以追踪请求在微服务之间的流转,帮助开发者分析性能瓶颈和故障。
    • 安全性:Spring Security: 可以与 Spring Cloud 结合,提供安全认证和授权机制,保护微服务的访问。
    • 消息驱动:Spring Cloud Stream提供了基于消息中间件的微服务通信方式,支持多种消息中间件(如 RabbitMQ、Kafka),实现异步消息传递。
    • 监控与管理:Spring Boot Actuator: 提供了监控和管理微服务的功能,可以查看服务的健康状态、指标等。
示例代码:

下面使用 Spring Cloud OpenFeign 实现rpc跨服务调用:

(1)添加依赖

在 pom.xml 中添加 OpenFeign 的依赖。

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
(2)启用 Feign 客户端

在主应用类上添加 @EnableFeignClients 注解。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication
@EnableFeignClients
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
(3)定义 Feign 客户端

创建一个接口并使用 @FeignClient 注解。

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(name = "service-name") // service-name 是要调用的服务名称
public interface MyFeignClient {@GetMapping("/api/resource/{id}") // 指定调用服务的接口地址Resource getResourceById(@PathVariable("id") Long id);
}
(4)使用 Feign 客户端

在服务中注入并使用 Feign 客户端。通过调用客户端接口中的方法,就会调用到该方法@GetMapping注解中对应的远程服务的接口。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class MyService {@Autowiredprivate MyFeignClient myFeignClient;public Resource fetchResource(Long id) {return myFeignClient.getResourceById(id);}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com