Удаленный вызов процедур или RPC позволяет программе, работающей на одном компьютере, выполнять код на другом компьютере. Это создает иллюзию, что процедура выполняется локально.

Идея RPC была разработана в 1970-х годах. С годами он развивался в различных формах — XML-RPC, SOAP, gRPC, Thrift и т. д. Эти технологии основаны на основной идее RPC, но различаются форматами сериализации, транспортными протоколами и т. д.

В этой статье мы рассмотрим некоторые основные платформы RPC, их плюсы и минусы, а также случаи их использования.

# A simple RPC client in Python

import xmlrpc.client

# XML-RPC server URL 
URL = "http://localhost:8000"

# Proxy object to interact with server 
proxy = xmlrpc.client.ServerProxy(URL)  

# Call the `add` RPC method
result = proxy.add(1, 2)  
print(result) # Prints 3

В приведенном выше коде показан простой клиент на Python, который взаимодействует с сервером XML-RPC. Он вызывает удаленный метод add и печатает результат.

RPC позволяет распределенным системам функционировать бесперебойно. Основная сложность сетевого взаимодействия и сериализации абстрагируется от разработчика.

В следующем разделе мы рассмотрим XML-RPC и SOAP — два самых ранних стандарта RPC, основанных на XML.

II. XML-RPC и SOAP

XML-RPC и SOAP — две из первых платформ RPC, получившие широкое распространение. Как следует из названий, они оба используют XML для сериализации.

XML-RPC

XML-RPC был одной из первых платформ RPC, созданной в 1998 году. Он использует запросы HTTP POST для передачи данных в формате XML. Запрос будет содержать XML с именем метода и параметрами, а ответ будет XML с результатом.

Например, вот запрос на суммирование двух чисел:

<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>sum</methodName>
  <params>
    <param>
      <value><int>5</int></value>
    </param> 
    <param>
      <value><int>10</int></value>
    </param>
  </params> 
</methodCall>

Ответ будет таким:

<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
  <params>
    <param>
      <value><int>15</int></value>
    </param>
  </params>   
</methodResponse>

Плюсы:

  • Независимая платформа. Использует стандартные форматы XML и HTTP.
  • Знакомые технологии. Легко начать.

Минусы:

  • Подробный формат XML, медленный анализ.
  • Громоздкие запросы и ответы.
  • Привязан к XML, не имеет строгой типизации.

SOAP построен на основе XML-RPC, с добавлением поддержки определений сервисов WSDL, заголовков SOAP и т. д. Однако он страдал от тех же проблем: медленный, громоздкий и тесно связанный с XML.

Популярность XML-RPC и SOAP сегодня снизилась в пользу более быстрых двоичных протоколов, таких как gRPC и Thrift.

III. gRPC

gRPC — это высокопроизводительная платформа RPC с открытым исходным кодом, разработанная Google. Он использует буферы протоколов в качестве языка определения интерфейса (IDL) для определения служб RPC и сериализации данных. gRPC поддерживает потоковую передачу, аутентификацию, сжатие, балансировку нагрузки и т. д.

Некоторые из основных плюсов gRPC:

  • Это происходит быстро благодаря использованию протокольных буферов, формата двоичной сериализации.
  • Он имеет строгую типизацию за счет использования файлов .proto.
  • Он имеет клиентские библиотеки на многих основных языках, таких как C++, Java, Python, Go, C# и т. д.

Некоторые потенциальные недостатки:

  • Он привязан к формату Protobuf и файлам .proto.
  • Спецификация Protobuf и файлы .proto требуют некоторого обучения.

Вот простой пример сервера и клиента gRPC на Python:

# server.py
import grpc

import hello_pb2
import hello_pb2_grpc

class HelloServicer(hello_pb2_grpc.HelloServicer):
    def SayHello(self, request, context):
        return hello_pb2.HelloReply(message='Hello, {}'.format(request.name))

server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
hello_pb2_grpc.add_HelloServicer_to_server(HelloServicer(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
# client.py 
import grpc

import hello_pb2
import hello_pb2_grpc

channel = grpc.insecure_channel('localhost:50051')
client = hello_pb2_grpc.HelloStub(channel)
response = client.SayHello(hello_pb2.HelloRequest(name='you'))
print("Greeter client received: " + response.message)

Файл .proto, определяющий службу:

syntax = "proto3";

package hello;

service Hello {
  rpc SayHello (HelloRequest) returns (HelloReply) {} 
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Надеюсь, это поможет вам в написании раздела gRPC! Дайте мне знать, если у вас есть еще вопросы.

Апач бережливый

Apache Thrift — это платформа RPC, разработанная Facebook для масштабируемой разработки межъязыковых сервисов. Он использует Thrift IDL (язык определения интерфейса) для определения интерфейсов, которые можно скомпилировать для генерации кода для различных языков. Thrift поддерживает несколько форматов сериализации, таких как Буферы протокола, JSON, Компактный двоичный формат и т. д.

К основным преимуществам бережливости можно отнести:

  • Высокая гибкость благодаря поддержке нескольких форматов сериализации.
  • Зрелый, со стабильным API и привязками для многих языков, таких как C++, Java, Python, PHP, Ruby, C#, Node.js и т. д.
  • Генерирует типобезопасные API на основе определений IDL.

Однако у Thrift есть и недостатки:

  • Может быть сложная конфигурация для установки
  • Разнообразие вариантов может затруднить выбор
  • Отсутствие массовой популярности по сравнению с gRPC.
  • Немного более медленная производительность по сравнению с двоичными форматами, такими как протокольные буферы.

Вот простой IDL Thrift, определяющий API для службы блогов:

namespace java com.example.blogging
namespace py example.blogging

service BlogService {
    list<string> getPosts()
    Post getPost(1: i32 id)
    void createPost(1: string title, 2: string content)
}

struct Post {
    1: i32 id,
    2: string title,
    3: string content    
}

Его можно скомпилировать для создания заглушек на выбранном вами языке. Вот пример клиента на Python:

from example.blogging import BlogService

client = BlogService.Client('localhost', 6000)
client.createPost(title='My First Post', content='Hello World!')
posts = client.getPosts() 
post = client.getPost(1) 
print(post.title)  # My First Post

И сервер на Java:

public class BlogServer implements BlogService.Iface {
  private Map<Integer, Post> posts = new HashMap<>();

  public List<String> getPosts() { ... }
  public Post getPost(int id) { ... }  
  public void createPost(String title, String content) { ... }  
}

Подводя итог, можно сказать, что Thrift — это надежная и зрелая среда RPC, обеспечивающая большую гибкость в разработке. Однако с дополнительной сложностью возникают некоторые накладные расходы на обучение и настройку. Для большинства микросервисов я бы рекомендовал gRPC вместо Thrift из-за его производительности и простоты использования. Но Thrift остается отличным вариантом, если вам нужна очень широкая языковая поддержка или очень гибкий IDL.

В. JSON-RPC

JSON-RPC — это простой и легкий протокол RPC, который использует JSON (нотацию объектов JavaScript) для сериализации.

Плюсы

  • Использует популярный формат JSON, который изначально поддерживается на многих языках. Это упрощает реализацию JSON-RPC на любом языке.
  • Очень простой и легкий. Легко настроить и начать работу.

Минусы

  • Не строго типизировано. Все данные сериализуются в общие объекты JSON, массивы и строки, поэтому вы теряете преимущества статической типизации.
  • Нет поддержки потоковой передачи или отправки на сервер. Все запросы одноразовые.
  • Анализ и сериализация JSON обычно выполняются медленнее, чем двоичные форматы, такие как Protobuf, используемые в gRPC.

Например, вот простой запрос JSON-RPC:

{
  "jsonrpc": "2.0",
  "method": "sum",
  "params": [1, 2],
  "id": 1
}

И соответствующий ответ:

{
  "jsonrpc": "2.0",
  "result": 3,
  "id": 1 
}

Поле method содержит имя вызываемой удаленной процедуры, params содержит входные параметры, а result содержит результат вызова.

JSON-RPC — хороший выбор, если вам нужна простая связь API через JSON без сложностей других платформ RPC. Он хорошо работает для внутренних API, сред тестирования или некритических систем, где производительность не имеет значения.

Многие клиенты и серверы поддерживают JSON-RPC, например Postman, cURL, Spring, Django и т. д. Поэтому его легко реализовать во многих стеках.

В целом, JSON-RPC предоставляет простой способ реализации RPC с использованием очень знакомого формата, но ему могут не хватать некоторых расширенных функций других вариантов. В большинстве базовых случаев использования он может отлично работать, но для высокопроизводительных или сложных систем лучше выбрать gRPC или Thrift.

Когда что использовать?

Теперь, когда мы изучили некоторые популярные платформы и форматы RPC, давайте обсудим, когда какой из них использовать.

gRPC

gRPC идеально подходит для высокопроизводительных микросервисов, где важны эффективность и строгая типизация. Некоторые примеры:

  • Связь между внутренними серверными службами
  • Случаи использования потоковой передачи в реальном времени, такие как чат-приложения, онлайн-игры и т. д. Преимущества Protobuf в производительности и поддержка потоковой передачи делают gRPC отличным выбором.
# Example gRPC client/server in Python

import grpc

import helloworld_pb2
import helloworld_pb2_grpc

...

# Client 
channel = grpc.insecure_channel('localhost:50051')
stub = helloworld_pb2_grpc.GreeterStub(channel) 
response = stub.SayHello(helloworld_pb2.HelloRequest(name='John'))
print("Greeter client received: " + response.message)

# Server        
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))  
helloworld_pb2_grpc.add_GreeterServicer_to_server(GreeterServicer(), server) 
server.add_insecure_port('[::]:50051')
server.start()

Бережливость

Thrift хорошо подходит для нужд RPC, где:

  • Гибкость и широкая языковая поддержка являются ключевыми факторами. Например. Служба API, используемая различными внутренними и внешними клиентами.
  • Эффективность важна, но не является главным приоритетом.
  • Накладные расходы на настройку приемлемы для полученной гибкости.

Поддержка нескольких форматов сериализации и множества языковых привязок делает Thrift подходящим для этих случаев использования.

# Example Thrift server in Python

import thriftpy2
from thriftpy2.rpc import make_server

from thrift_service import Processor     # implements the Thrift service

server = make_server(Processor, '127.0.0.1', 6000)  # Processor handles requests
server.serve()

JSON-RPC

JSON-RPC хорош для простой связи RPC через JSON, особенно:

  • При взаимодействии с системами, поддерживающими только JSON
  • Для общедоступных REST-подобных API из-за простоты и популярности JSON.

Использование JSON упрощает реализацию и работу с ним за счет некоторой производительности и типизации.

# Example JSON-RPC server in Python

import json
from jsonrpcserver import method, middlewares

@method
def add(x, y):
    return x + y

server = middlewares.JsonRpcMiddleware(middlewares.JsonRpcMethods())
server.register(add) 
server.serve(('127.0.0.1', 8000))

Запрос JSON-RPC будет следующим:

{"jsonrpc": "2.0", "method": "add", "params": [1, 2], "id": 1}

И ответ:

{"jsonrpc": "2.0", "result": 3, "id": 1}

VII. Заключение

Мы подробно изучили различные структуры и форматы RPC. Подведем итоги основных выводов:

  • XML-RPC и SOAP — это старые протоколы RPC на основе XML. Они не зависят от платформы и используют знакомые технологии, такие как HTTP и XML. Однако они многословны, громоздки и медленны из-за анализа XML.
  • gRPC – это современная платформа RPC, разработанная Google. Он использует буферы протокола для быстрой сериализации и поддерживает потоковую передачу. У него есть клиенты на многих языках, но он привязан к формату Protobuf. gRPC идеально подходит для высокопроизводительных типизированных микросервисов.
  • Apache Thrift разработан Facebook и поддерживает различные форматы сериализации. Он гибкий и зрелый, но может иметь тяжелую конфигурацию и работать медленнее, чем gRPC. Экономия хороша для широкой языковой поддержки и гибкости.
  • JSON-RPC – это простой протокол RPC, использующий JSON. Он легкий и с ним легко работать, но ему не хватает поддержки потоковой передачи, типов и производительности. JSON-RPC подходит для простой связи API через JSON.
  • Будущее RPC выглядит светлым, поскольку новые стандарты, такие как GraphQL и REST, также набирают популярность. RPC будет продолжать развиваться, чтобы удовлетворить потребности современных программных систем, которым требуется скорость, эффективность, гибкость и широкие возможности подключения.

В общем, выбирайте:

  • gRPC для высокопроизводительных типизированных микросервисов
  • Бережливость к гибкости, широкая языковая поддержка
  • JSON-RPC для простой связи API через JSON.

Благодаря обзору различных вариантов вы теперь можете принять обоснованное решение в соответствии с потребностями вашего проекта. Надеюсь, это поможет вам в выборе инфраструктуры RPC! Дайте мне знать, если у вас есть еще вопросы.

Надеюсь, эта статья была для вас полезна! Если статья оказалась для вас полезной, поддержите меня: 1) нажмите несколько хлопков и 2) поделитесь этой историей в своей сети. Дайте мне знать, если у вас есть какие-либо вопросы по обсуждаемому содержанию.

Не стесняйтесь обращаться ко мне по адресу coderhack.com(at)xiv.in.