предисловие
Что такое RPC-сервисRPC — это аббревиатура удаленного вызова процедур, что в переводе на китайский язык означает удаленный вызов процедур. RPC — это служба, которая позволяет программе вызывать метод или функцию класса в другом адресном пространстве (обычно на другом компьютере). Это технология, которая построена на компьютерной сети и скрывает базовую сетевую технологию.Он может вызывать удаленную программу как локальную службу и повышать пропускную способность без больших затрат на кодирование.
Зачем использовать RPC-сервисыС быстрым развитием компьютерных технологий решения запуска служб на одной машине уже недостаточно для поддержки все большего количества сетевых запросов.Начали появляться распределенные решения.Бизнес-сценарий можно разделить и запустить на нескольких машинах. машина выполняет только один или несколько бизнес-модулей соответственно. Чтобы позволить другим машинам использовать методы бизнес-модуля на машине, существует служба RPC, которая представляет собой службу, основанную на протоколе, специально реализующем удаленный вызов методов. В настоящее время многие основные языки поддерживают службы RPC, такие как Dubbo для Java, net/rpc и RPCX для Go и gRPC для Google.
О gRPCБольшинство RPC реализованы на основе сокетов, которые могут быть более эффективными, чем HTTP-запросы. gRPC – это высокопроизводительная платформа, разработанная Google с открытым исходным кодом для реализации сервисов RPC. Она основана на протоколе http2.0 и в настоящее время поддерживает C, C++, Java, Node.js, Python, Ruby, Objective-C, PHP и C# и так далее язык. Для передачи вызовов методов, параметров вызова, параметров ответа и т. д. между двумя серверами эти параметры необходимо сериализовать.gRPC использует синтаксис буфера протокола (check proto), который может быть определен синтаксисом proto.Методы, параметры и ответ форматы могут легко выполнять удаленные вызовы методов и очень полезны для расширения и обновления параметров.
Быстрый запуск GRPC.
Прежде чем использовать GRPC для реализации вызова удаленного метода, нам необходимо понять синтаксис буфера протокола, установить инструмент для поддержки синтаксиса буфера протокола в файл .proto, затем завершить сервер GRPC (поставщик удаленного метода) и клиент (вызывающий) и упаковка.
Узнать о буферах протоколов
Protocol Buffers — это межъязыковой, кроссплатформенный, расширяемый механизм Google для сериализации структурированных данных — более компактный, быстрый и простой формат данных, чем XML. Вы можете определить структуру данных, таких как имена методов, параметры и форматы ответов и т. д., а затем вы можете легко записывать и читать структурированные данные на разных языках в различных потоках данных, используя исходный код, сгенерированный соответствующим языком. инструменты.
Использование грамматики
- определить тип сообщения
package test;
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
Приведенный выше пример представляет собой файл .proto. В первой строке файла указывается имя пакета, которое удобно для вас, чтобы импортировать определение этого файла в другие файлы proto. Вторая строка указывает на то, что вы используете синтаксис proto3: если вы не делайте этого, protobuf Компилятор предположит, что вы используете proto2. Это должна быть первая непустая строка файла без комментариев, в настоящее время рекомендуется синтаксис proto3. SearchRequest — имя тела сообщения, указание трех полей, указание типа и порядка полей соответственно, порядок должен начинаться с 1 и не может повторяться;
- Поле, указанное в правилах Поле сообщения может быть одним из следующих:
单数(默认):格式良好的消息可以包含该字段中的零个或一个(但不超过一个)。 repeated:此字段可以在格式良好的消息中重复任意次数(包括零)。将保留重复值的顺序。 Например:
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
repeated Body body = 4;
}
message Body {
int32 id = 1;
string number = 2;
}
Приведенный выше пример на самом деле определяет формат, который выражается в нашем обычном формате json:
{
"query": str,
"page_number":int,
"result_per_page":int,
"body":[
{
"id":int,
"number":str
}
],
}
- Тип скалярного значения Поле скалярного сообщения может иметь один из следующих типов — в таблице указан тип, указанный в файле .proto, и соответствующий тип в автоматически сгенерированном классе:
| .proto Type | Примечание | Python Typ |
|---|---|---|
| double | float | |
| float | float | |
| int32 | Используйте кодировку переменной длины, которая неэффективна для отрицательных значений. Если в вашем поле могут быть отрицательные значения, используйте вместо этого sint64. | int |
| uint32 | Использовать кодировку переменной длины | int/long |
| uint64 | Использовать кодировку переменной длины | int/long |
| sint32 | Используйте кодировки переменной длины, которые намного эффективнее, чем int32 для отрицательных значений. | int |
| sint64 | Целочисленное значение со знаком, использующее кодировку переменной длины. Более эффективен, чем обычный int64 при кодировании. | int/long |
| fixed32 | Всегда 4 байта, этот тип более эффективен, чем uint32, если значение всегда больше 228. | int |
| fixed64 | Всегда 8 байт, этот тип более эффективен, чем uint64, если значение всегда больше 256. | int/long |
| sfixed32 | всегда 4 байта | int |
| sfixed64 | всегда 8 байт | int/long |
| bool | Логическое значение | bool |
| string | Строка должна быть в кодировке UTF-8 или 7-битном кодированном тексте ASCII. | str/unicode |
| bytes | Может содержать байтовые данные в любом порядке. | str |
- По умолчанию При синтаксическом анализе сообщения, если закодированное сообщение не содержит определенного единственного элемента, соответствующее поле в объекте синтаксического анализа будет установлено на значение по умолчанию для этого поля. Эти значения по умолчанию зависят от типа:
- Для строк по умолчанию используется пустая строка.
- Для байтов по умолчанию используются нулевые байты.
- Для логических значений значение по умолчанию равно false.
- Для числовых типов значение по умолчанию равно нулю.
- Для перечислений значением по умолчанию является первое определенное значение перечисления, которое должно быть равно 0.
- Значение по умолчанию для повторяющихся полей пусто (обычно это пустой список для соответствующего языка).
- тип перечисления
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
Первая константа перечисления Корпуса сопоставляется с нулем: каждое определение перечисления должно содержать константу, которая сопоставляется с нулем в качестве первого элемента. Это потому что:
- Должно быть нулевое значение, чтобы мы могли использовать 0 в качестве числа по умолчанию.
- Нулевое значение должно быть первым элементом для совместимости с семантикой proto2, где первое значение перечисления всегда используется по умолчанию.
- определить метод
service SearchService {
rpc Search(SearchRequest)returns(SearchResponse);
}
Вышеприведенный оператор определяет имя метода Поиск для удаленного вызова.После компиляции исходного кода соответствующего языка можно использовать удаленный вызов, напримерИнициализируйте метод SearchService в Python, затем выполните метод Search, который должен использовать формат SearchRequest для вызова метода удаленного компьютера, а затем вернуть результат вызова в определенном формате SearchResponse.. Согласно определению синтаксиса proto, этот вид удаленного вызова можно использовать даже на разных платформах и языках.
Используйте инструменты для создания исходного кода для соответствующего языка
В соответствии с фактическими рабочими потребностями для создания файлов .proto пользовательских типов сообщений Java, Python, C++, Go, Ruby, Objective-C или C# на следующих языках необходимо запустить протокол компилятора protobuf на .proto. Если компилятор еще не установлен, загрузите пакет и следуйте инструкциям в файле readme. Компилятор Protobuf вызывается следующим образом:
protoc --proto_path = IMPORT_PATH --cpp_out = DST_DIR --java_out = DST_DIR --python_out = DST_DIR --go_out = DST_DIR --ruby_out = DST_DIR --objc_out = DST_DIR --csharp_out = DST_DIR path / to / file .proto
Python генерирует соответствующий исходный код
- Установите исходный пакет Python gRPC grpcio, который используется для выполнения различных базовых протоколов и методов запроса ответа gRPC.
- Установите grpcio-tools, инструмент для создания исходного кода Python на основе прототипа gRPC.
sudo python -m pip install grpcio
python -m pip install grpcio-tools
- Выполните компиляцию, чтобы сгенерировать исходный код протокола сериализации прототипа Python:
# 编译 proto 文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. test.proto
python -m grpc_tools.protoc: python 下的 protoc 编译器通过 python 模块(module) 实现, 所以说这一步非常省心
--python_out=. : 编译生成处理 protobuf 相关的代码的路径, 这里生成到当前目录
--grpc_python_out=. : 编译生成处理 grpc 相关的代码的路径, 这里生成到当前目录
-I. test.proto : proto 文件的路径, 这里的 proto 文件在当前目录
Исходный код, сгенерированный после компиляции:
- test_pb2.py: используется для взаимодействия с данными protobuf, это файл структуры данных в формате python, созданный в соответствии с типом структуры данных, определенным в файле proto.
- test_pb2_grpc.py: используется для взаимодействия с grpc, это класс, который определяет метод rpc, включая параметры запроса и ответы класса и т. д., которые могут быть непосредственно созданы и вызваны python.
Создание службы Python gRPC
После создания класса gRPC, который python может напрямую создавать и вызывать, мы можем приступить к созданию сервера RPC (поставщик удаленных вызовов) и клиента (вызывающий).
- Собрать сервер server.py
from concurrent import futures
import time
import grpc
import test_pb2
import test_pb2_grpc
# 实现 proto 文件中定义的 SearchService
class RequestRpc(test_pb2_grpc.SearchService):
# 实现 proto 文件中定义的 rpc 调用
def doRequest(self, request, context):
return test_pb2.Search(query = 'hello {msg}'.format(msg = request.name)) # return的数据是符合定义的SearchResponse格式
def serve():
# 启动 rpc 服务,这里可定义最大接收和发送大小(单位M),默认只有4M
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10), options=[
('grpc.max_send_message_length', 100 * 1024 * 1024),
('grpc.max_receive_message_length', 100 * 1024 * 1024)])
test_pb2_grpc.add_SearchServiceServicer_to_server(RequestRpc(), server)
server.add_insecure_port('[::]:50051')
server.start()
try:
while True:
time.sleep(60*60*24) # one day in seconds
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
serve()
- Собрать клиент client.py
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def run():
# 连接 rpc 服务器
channel = grpc.insecure_channel('localhost:50051')
# 调用 rpc 服务
stub = test_pb2_grpc.SearchServiceStub(channel)
response = stub.doRequest(test_pb2.SearchRequest(query='henry'))
print("client received: ", response)
if __name__ == '__main__':
run()
Лучшие практики
- При написании прото-файлов обратите внимание на определение формата данных и подумайте о масштабируемости.Например, api_version можно определить, чтобы различать версии, предотвращая совместимость будущих версий при обновлении больших форматов данных;
- Для неизменяемых типов рекомендуется использовать перечисление, например запрос типа поля, когда значение фиксировано, можно использовать тип перечисления;
- Для записи сервера и клиента рекомендуется указывать максимальные размеры приема и отправки, чтобы избежать исключений переполнения данных;
- gRPC время от времени отключается и повторно подключается, поэтому следует добавить механизм обработки исключений, чтобы отловить проблему сбоя удаленного вызова, вызванного переподключением, а затем можно выполнить повторную попытку (подробно будет объяснено в следующей статье);
- gRPC может использовать протокол SSL или TLS для реализации зашифрованной передачи http2.0 и повышения безопасности системы (подробно будет объяснено в следующей статье);
- Для сервисов с большим трафиком и параллелизмом некоторые приложения или компоненты микросервисов (такие как istio) можно использовать для обеспечения плавкости трафика, ограничения тока и т. д. для повышения стабильности.
Преимущества gRPC
представление
Сообщения gRPC сериализуются с использованием protobuf, эффективного двоичного формата сообщений. Сериализация Protobuf на сервере и клиенте выполняется очень быстро. Серийные сообщения Protobuf имеют небольшой размер и полезный объем, что важно в сценариях с ограниченной пропускной способностью, таких как мобильные приложения.
gRPC был разработан для HTTP/2, основной версии HTTP со значительными преимуществами в производительности по сравнению с HTTP 1.x:
- Бинарное кадрирование и сжатие. Протокол HTTP/2 компактен и эффективен как при отправке, так и при получении.
- Мультиплексирование нескольких вызовов HTTP/2 по одному TCP-соединению. Мультиплексирование устраняет блокировку очереди.
генерация кода
Все платформы gRPC обеспечивают первоклассную поддержку генерации кода. Основным файлом разработки gRPC является файл *.proto, который определяет соглашения о службах и сообщениях gRPC. В соответствии с этим файлом платформа gRPC будет генерировать базовые классы услуг, сообщения и полный клиентский код.
Благодаря обмену файлами *.proto между сервером и клиентом сообщения и клиентский код могут генерироваться от начала до конца. Генерация кода на стороне клиента устраняет дублирование сообщений на клиенте и сервере и создает для вас строго типизированный клиент. Устранение необходимости написания кода на стороне клиента экономит много времени на разработку приложений с большим количеством служб.
строгая спецификация
Официальной спецификации для HTTP API с JSON не существует. Разработчикам не нужно обсуждать лучший формат для URL-адресов, HTTP-команд и кодов ответов. (Подумайте об этом, лучше ли использовать Post или Get? Лучше использовать Get или Put? Когда вы думаете о фобии выбора, вы открываете запутанность и тратите много времени впустую)
Спецификация gRPC определяет формат, которому должны следовать службы gRPC. gRPC устраняет аргументы и экономит время разработчиков, поскольку gRPC един для всех платформ и реализаций.
поток
HTTP/2 обеспечивает основу для долгосрочных потоков связи в реальном времени. gRPC обеспечивает первоклассную поддержку потоковой передачи через HTTP/2.
Служба gRPC поддерживает все комбинации потоков:
- Унарный (без потоковой передачи)
- поток от сервера к клиенту
- поток от клиента к серверу
- Двусторонняя потоковая передачаКрайний срок/тайм-аут и отменаgRPC позволяет клиентам указать, как долго они готовы ждать завершения RPC. Крайний срок отправляется на сервер, который может решить, какие действия предпринять, когда крайний срок превышен. Например, сервер может отменить выполняемый запрос gRPC/HTTP/базы данных по истечении времени ожидания.
Обращение к крайним срокам и отменам через суб-gRPC помогает обеспечить соблюдение ограничений на использование ресурсов.
Сценарии, в которых рекомендуется gRPC
- Микросервисы — gRPC предназначен для связи с низкой задержкой и высокой пропускной способностью. gRPC отлично подходит для облегченных микросервисов, где эффективность имеет решающее значение. Одноранговая связь в режиме реального времени — gRPC отлично поддерживает двунаправленную потоковую передачу. Службы gRPC могут отправлять сообщения в режиме реального времени без опроса. Многоязычная смешанная среда разработки. Инструменты gRPC поддерживают все популярные языки разработки, что делает gRPC идеальным решением для многоязычных сред разработки.
- Среда с ограниченным доступом к сети — сериализованные сообщения GRPC с использованием Protobuf (облегченный формат сообщений). Сообщения GRPC всегда меньше, чем эквивалентные сообщения JSON.