Implemente o RPC você mesmo

Autor: Zen e a arte da programação de computadores

1. Introdução

Na computação distribuída, chamada de procedimento remoto (Remote Procedure Call) é uma forma de comunicação entre serviços. A ideia central é permitir que diferentes processos ou computadores se comuniquem pela rede sem conhecer o protocolo de rede subjacente, protegendo os complexos detalhes de transmissão subjacentes, permitindo que os desenvolvedores chamem serviços remotos com a mesma facilidade com que chamam funções locais. Comparado com o modelo cliente-servidor tradicional baseado em Socket, o RPC fornece uma interface de programação mais conveniente, fácil de usar e orientada a objetos. Atualmente, as principais estruturas RPC no mercado incluem Apache Thrift, Google gRPC, etc. Este artigo começará com o mecanismo RPC mais básico e apresentará passo a passo como implementar você mesmo uma estrutura RPC simples. Depois de ler este artigo, os leitores poderão dominar facilmente o método de design, desenvolvimento e aplicação de uma estrutura RPC simples, mas totalmente funcional.

2. Conceitos básicos

2.1 Serviço

Um conceito central da chamada de procedimento remoto é o serviço. Refere-se a uma entidade que fornece uma determinada função, como um mecanismo de algoritmo ou um serviço de banco de dados. Um serviço geralmente consiste em duas partes: um arquivo de definição de interface (.proto), que é usado para descrever a assinatura do método e os parâmetros de solicitação do serviço; um processo em execução no servidor, que é responsável por monitorar as solicitações do cliente e processar as solicitações. Um serviço geralmente consiste em vários métodos. Cada método define parâmetros de entrada e tipos de valores de retorno e pode ser chamado remotamente pelo cliente.

2.2 Buffers de Protocolo

Para resolver o problema de troca de dados, o Google lançou Protocol Buffers (doravante denominado Protobuf), que é um formato de mensagem independente de linguagem, independente de plataforma e extensível que pode ser usado para serializar dados estruturados. Ele usa arquivos .proto como descrições de configuração e usa o compilador para gerar bibliotecas de classes em vários idiomas.Os usuários podem usar esta biblioteca de classes para serializar as estruturas de dados de cada idioma em sequências de bytes e depois enviá-las para a outra parte através da rede. O Protobuf pode gerar código automaticamente, economizar tempo de desenvolvimento, melhorar a eficiência e oferecer suporte a funções como compactação e verificação de dados.

2.3 Transmissão em rede (Transporte)

Depois que o serviço for implantado no servidor, ele poderá começar a receber solicitações de clientes. Para a comunicação em rede, os serviços precisam fornecer capacidades subjacentes de transmissão de rede. Atualmente, os dois protocolos de transmissão de rede mais populares são TCP/IP e HTTP. O protocolo HTTP é um protocolo sem estado de conexão, ou seja, apenas uma solicitação é processada por conexão. Por outro lado, o protocolo TCP/IP é um protocolo de conexão com monitoração de estado que pode lidar com múltiplas solicitações ao mesmo tempo e garantir que os pacotes de dados cheguem em ordem. Portanto, qual protocolo de transmissão escolher é mais importante.

3. Visão geral da estrutura RPC

A estrutura RPC inclui os seguintes módulos principais:

  • Camada de transporte: Responsável pela transmissão da rede, como estabelecer conexões, desconectar, enviar e receber mensagens, etc.
  • Camada de protocolo: Responsável pela codificação e decodificação, como codificar uma string, um número inteiro, etc.
  • Camada de serialização: Responsável por converter objetos em fluxos de bytes, como API de serialização em Java e Pickle em Python.
  • Camada de rede: Acima da camada de transporte, mantém endereços de hosts remotos e é responsável pelo roteamento e encaminhamento.
  • Stub do Cliente: Este Stub é criado quando o usuário chama e é usado para enviar solicitações e receber respostas.
  • Esqueleto do servidor: o Skeleton recebe solicitações do cliente, chama serviços locais e, em seguida, encapsula os resultados em respostas e os retorna ao cliente.

A figura a seguir mostra um processo típico de estrutura RPC:

4. Projeto e implementação da estrutura RPC

O design e a implementação da estrutura RPC podem ser divididos nas quatro etapas a seguir:

  • Determinar o modo de comunicação
  • Determinar o protocolo de transporte
  • Projetar arquivo IDL
  • Implementar serialização e desserialização
  • Implementar protocolo de transporte
  • Realize a transmissão da rede
  • Implementar stub do cliente
  • Implementar o esqueleto do servidor

Essas etapas são explicadas em detalhes abaixo.

4.1 Determinar o modo de comunicação

RPC é um modo de comunicação bidirecional, ou seja, o cliente chama os serviços do servidor, e o servidor também permite que o servidor chame os serviços do cliente. Portanto, devemos primeiro considerar a direcionalidade das chamadas de serviço. Se o serviço chama o serviço do cliente, é denominado modo "procedimento"; caso contrário, é denominado modo "função". De modo geral, o modo "processo" é usado dentro do mesmo processo, e o modo "função" é usado entre processos diferentes.

4.2 Determine o protocolo de transmissão

A determinação do protocolo de transmissão geralmente envolve duas considerações. Primeiro, se a transmissão criptografada é necessária; segundo, que tipo de protocolo de transmissão é necessário. Normalmente, os protocolos de transmissão seguros são TLS/SSL, SSH, etc.; os protocolos de transmissão não seguros são como HTTP, TCP/IP, etc.

4.3 Projetar arquivo IDL

Para implementar o RPC, precisamos primeiro definir a interface do serviço, ou seja, os parâmetros de entrada e saída do serviço. Geralmente usamos arquivos .proto como arquivos de definição de interface, que contêm nomes de serviços, nomes de métodos e definições de tipos de parâmetros. Aqui está um exemplo de arquivo .proto:

// calculator.proto 文件
syntax = "proto3"; // 指定protobuf版本

package example; // 定义包名

message Request {
    int32 a = 1; // 参数定义
    int32 b = 2;
}

message Response {
    int32 c = 1; // 返回值定义
}

service Calculator { // 服务定义
    rpc add (Request) returns (Response); // 方法定义
    rpc sub (Request) returns (Response);
    rpc mul (Request) returns (Response);
    rpc div (Request) returns (Response);
}

O exemplo acima define um serviço de calculadora, que contém quatro métodos: add, sub, mul e div. Os parâmetros de entrada de cada método são Requeste o valor de retorno é Response.

4.4 Implementar serialização e desserialização

Para implementar a invocação remota de um serviço, primeiro você precisa serializar os parâmetros de chamada em um fluxo de bytes, depois transmitir o fluxo de bytes ao servidor por meio da rede e desserializar o valor de retorno. Geralmente, o processo de serialização e desserialização depende de uma linguagem de programação específica, como a API de serialização em Java e Pickle em Python. Claro, você também pode implementar a lógica de serialização e desserialização manualmente.

4.5 Implementando o protocolo de transmissão

De acordo com o protocolo de transmissão selecionado, precisamos implementar os componentes de transmissão de rede correspondentes, como estabelecer conexões, desconectar, receber mensagens, enviar mensagens, etc. Podemos usar projetos de código aberto existentes, como Netty, etc.

4.6 Implementar transmissão em rede

Após concluir a implementação do componente de transmissão da rede, o próximo passo é implementar o Stub do cliente e o Skeleton do servidor. O cliente Stub é responsável por enviar a solicitação de chamada local ao componente de transmissão da rede e aguardar a resposta do servidor; o servidor Skeleton é responsável por aceitar a solicitação de chamada do componente de transmissão da rede, chamar o serviço local para processar a solicitação e, finalmente, encapsular o resultado em uma resposta de retorno e enviá-lo ao cliente.

O caminho de comunicação entre o Stub cliente e o Skeleton do servidor pode ser dividido em três situações:

  1. Chamada de um salto: o cliente chama diretamente o servidor, envia a solicitação para a porta do componente de transmissão da rede e bloqueia a espera pela resposta do servidor.
  2. Chamada multi-hop: o cliente primeiro chama o middleware (como um centro de registro) para obter as informações de endereço de rede do servidor e, em seguida, chama o servidor. O middleware pode armazenar informações de endereço de serviço em cache para evitar consultas repetidas.
  3. Transmissão transparente de dados: O cliente envia a solicitação ao middleware por meio do componente de transmissão de rede. Após receber a solicitação, o middleware envia a solicitação ao servidor por meio do componente de transmissão de rede e bloqueia a espera pela resposta do servidor.

4.7 Implementar stub do cliente

O cliente Stub é responsável por enviar a solicitação de chamada local ao componente de transmissão da rede e aguardar a resposta do servidor. Primeiro, o Stub precisa analisar o arquivo IDL, obter o nome do serviço e do método e construir os parâmetros da solicitação. Em seguida, os parâmetros da solicitação são serializados em uma matriz de bytes e enviados ao servidor por meio do componente de transmissão de rede. Finalmente, ao receber a resposta do servidor, o Stub desserializa o array de bytes e retorna o resultado.

4.8 Implementar esqueleto do servidor

O esqueleto do servidor é responsável por aceitar a solicitação de chamada do componente de transmissão da rede, chamar o serviço local para processar a solicitação e, por fim, encapsular o resultado em uma resposta de retorno e enviá-la ao cliente. Primeiro, o Skeleton precisa analisar o fluxo de bytes solicitado e desserializá-lo para obter os parâmetros de chamada. Em seguida, ligue para o serviço local para processar a solicitação e obter o resultado. Finalmente, o resultado é serializado em uma matriz de bytes e retornado ao cliente através do componente de transmissão de rede.

4.9 Resumo

Através das etapas acima, implementamos uma estrutura RPC simples. É claro que, por razões de espaço, não há muitos detalhes técnicos subjacentes aqui. Se você estiver interessado nisso, continue lendo as informações relevantes.

Acho que você gosta

Origin blog.csdn.net/universsky2015/article/details/133504729
Recomendado
Clasificación