Рис.1: Ответ gRPC сервера на любой вопрос.
Краткое содержание для непрограммистов: gRPC круто и быстро и знать об этом незачем. Всего хорошего!
При написании коннектора к любой бирже на 99% везде используется два основных вида способа передачи запросов и получения данных — это через отдельные REST-вызовы (например, «биржа, дай мне список инструментов») и через веб-сокеты (например, «биржа, дай мне поток обезличенных сделок по Газпромнефти»).
В этих «обычных» случаях всё общение происходит через JSON-запросы, то есть, говоря по-русски, в текстовом виде.
Рис. 2: пример запроса списка продуктов и ответа рептилоидов с сервера BitGet
Запросы нужно отсылать по заранее известным адресам API.
Но время от времени появляются необычные протоколы и вот у Тинькофф инвестиций попался gRPC.
Лезем в гугл разбираться что это за зверь и выясняем:
Рис. 3: мой стек длиннее твоего.
* gRPC работает быстрее за счет более нового и производительного стандарта http/2 (обновления данных станут более риалтаймные)
* в отличие от REST/WebSockets, у gRPC все данные передаются в бинарном виде, что быстрее и экономнее в плане размера
* gRPC работает с четкой типизацией, что означает, что сериализация/десериализация происходят «автоматически» и не надо гадать как преобразовывать данные в текст и обратно
* gRPC это все еще RPC, удаленный вызов процедур, то есть используется все так, будто ты не к удаленному серверу обращаешься, а делаешь вызов на локальной машине.
Много непонятных букв, но давайте приведу один практический пример.
Рис. 4: меняем пару буковок в строчке, которую отравляем на сервер.
Например, нам нужно получить информацию о доступных бумагах на сервере.
REST-запрос на Битгете будет выглядеть примерно так: GET запрос на адрес api.bitget.com/api/spot/v1/public/products
или в коде:
Рис. 5: Запрос доступных бумаг в OsEngine (коннектор Битгет)
Здесь мы видим сначала формирование запроса, отправку, а потом десериализацию ответа (это когда из текста мы создаем объекты данных).
Дальше объекты передаются в UpdateSecurity, где происходит окончательное формирование полей:
Рис. 6: разбор из строки в объект.
Чтобы разобрать при помощи библиотеки JSON, нам понадобится вручную создать класс, соответствующий ответу сервера:
Рис. 7: Разработчик сделал класс, где строки ответа разложены по полочкам.
Но при этом приходится в коде приводить все к правильным типам данных.
А теперь посмотрим как все делается в gRPC?
Рис. 8: перерыв на обед.
В gRPC всё известно заранее благодаря .proto файлам, в которых описаны методы, предоставляемые сервером:
Рис. 9: методы получения данных по инструментам из instruments.proto Тинькофф инвестиций.
Эти прото-файлы скачиваются с сайта брокера и подключаются в проект.
А дальше происходит небольшая магия.
Из этих прото-файлов компилятор Protobuf генерит файлы с нужными функциями и объявлениями классов. Всё, что нужно, чтобы пользоваться API.
В отличие от REST, тут эти функции/типы не нужно писать самому.
Просто начинаем их использовать:
Рис. 10: получаем список акций.
То есть тип SharesResponse, InstrumentsRequest, метод Shares — все это было сгенерировано автоматически из прото-файлов.
В результате мы получаем ответ с набором объектов, у которых уже объявлены типы данных:
Рис. 11: Заполняем новый объект бумаги в коннекторе OsEngine/Tinkoff
Стоит обратить внимание, что разбираются уже объекты типа Share и да, его определение тоже было автоматом сгенерировано из прото-файла.
А еще в gRPC есть потоковая передача, которая по обещаниям должна работать лучше и быстрее вебсокетов. Но для начального знакомства это уже слишком (:
До встречи в следующем эпизоде!
gRPC не спасает от глупых изменений. То есть если у вас в JSON пришедшему по веб-сокету что-то изменилось и ваше приложение крашнулось, то оно точно так же даст сбой при gRPC. Единственное удобство gRPC это то что там единый формат файлов с настройками из которых потом генерится нативный код в нужном языке и вам не надо делать свои конвертации.
П.С. И да, типизированным языкам неудобно работать с JSON. Какой-нибудь PHP съест любой json и не сломается.
В целом понятно, что это лишь удобство, а вовсе не гарантия защиты от ошибок когда.
REST вполне себе может работать через http/2 Вебсокеты поддерживают бинарные данные. При нормальном дизайне rest коннектора — тоже ни о чем гадать не нужно — есть строго типизированный объект, где каждое поле можно однозначно разобрать. Да, писать придется руками (хотя если есть swagger — можно тоже автоматом все сгенерировать) То что удаленный вызов спрятан ничего не меняет, ровно так же можно все спрятать и для реста У вас кровь из глаз от такого не течет? Зачем разбирать сначала в строки, а потом героически их парсить? Почему бы не разобрать сразу в нужные типы?