От интерфейса к серверу: использование gRPC (бонус: как использовать gRPC с хуками React!)
Что такое gRPC?
Если вы похожи на меня, вы, возможно, даже никогда не слышали о gRPC, не говоря уже о том, чтобы знать, каковы некоторые из его преимуществ и недостатков. gRPC — это рекурсивная аббревиатура, обозначающая удаленный вызов процедур gRPC. Это комбинация протокола HTTP 2.0 и буферов протокола, также известных как протобуферы.
Для чего нужен gRPC?
Основное преимущество gRPC заключается в том, что это универсальный коммуникационный протокол, поскольку протобуферы закодированы в двоичный код (проще не бывает!). Таким образом, он идеально подходит для распределенных вычислений и обмена сообщениями между системами, которые написаны на разных языках или платформах, но, тем не менее, должны взаимодействовать. Хотя для этой межсистемной связи можно использовать что-то вроде протокола HTTP, это сопряжено с накладными расходами, поскольку каждая система также должна иметь сервер, предоставляющий какую-то службу REST или REST-подобную службу с допустимыми конечными точками и так далее. Protobufs, будучи на некоторых уровнях даже более строгими, чем REST через HTTP, могут быть в коде столь же простыми, как однострочный код.
gRPC также намного быстрее, чем традиционный HTTP — опять же благодаря двоичному формату передаваемых сообщений. Это делает его идеальным для связи между службами с малой задержкой.
Недостатки gRPC
Как и все в технологиях, ничто не является серебряной пулей, и gRPC не является исключением, даже с его впечатляющим списком преимуществ. По состоянию на 13 июня 2022 г., когда был опубликован этот пост, у gRPC были основные недостатки (все проблемы на стороне клиента, особенно в Интернете):
- Поддержка TypeScript остается экспериментальной функцией gRPC.
- Для связи веб-клиентов с сервером, на котором работает gRPC, требуется прокси, и для этого прокси есть только два варианта: а. Невероятный клиент gRPC-Web или б. Клиент Google gRPC-Web
- Кроме того, в настоящее время ни одна из реализаций полностью не соответствует полной спецификации gRPC.
- Поддержка WebSocket в качестве транспортного уровня доступна только в клиенте Improbable gRPC-Web и остается экспериментальной, не рекомендуемой для использования в производстве.
(Кстати, если вы раздумываете, что выбрать, на данный момент только Невероятный клиент gRPC-Web поддерживает двунаправленную потоковую передачу, что ближе к полной спецификации gRPC.)
Начать
Бэкэнд-реализация
Во-первых, я начал с рекомендованного gRPC начального репозитория для изучения gRPC, их helloworld
пример, который является частью официального репозитория gRPC.
Используя этот код, я добавил в Go новый служебный метод под названием Reverse
:
package utils // Reverse returns its argument string reversed rune-wise left to right. func Reverse(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r) }
И изменил исходную реализацию функции SayHello
, чтобы использовать эту функцию Reverse
:
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { log.Printf("Received: %v", in.GetName()) log.Printf("Returning: %v", utils.Reverse(in.GetName())) return &pb.HelloReply{Message: utils.Reverse(in.GetName())}, nil }
Реализация внешнего интерфейса
Я создал приложение React с помощью create-react-app
, удалил все лишнее (стили, изображения и т. д.) и добавил некоторый текст описания с помощью <textarea>
.
Теперь интересная часть: я решил использовать пользовательский React Hook для управления состоянием сообщения, ответа, и взаимодействия с вызовом gRPC. Ловушка прослушивает значение сообщения, которое предоставляет пользователь, и всякий раз, когда оно изменяется, вызывается служба реверса метода gRPC. Это довольно кратко выглядит как хук, который я назвал useGreeterService
:
import { useEffect, useState } from "react"; import { sayHello } from "../services/greeterService"; export const useGreeterService = (): [ string, React.Dispatch<React.SetStateAction<string>>, string ] => { const [message, setMessage] = useState(""); const [responseMessage, setResponseMessage] = useState(""); useEffect(() => { sayHello(message, setResponseMessage); }, [message]); return [message, setMessage, responseMessage]; };
Обратите внимание на неуловимую мощь этого вызова sayHello
— однострочника! Никакого fetch
, никакой настройки заголовков, файлов cookie, HTTP-методов или создания какой-либо пользовательской службы API — gRPC абстрагирует все это для нас.
Использование useGreeterService
в App.tsx
выглядит следующим образом:
import { useState } from "react" import { useGreeterService } from "./hooks/useGreeterService"; const App = () => { const [message, setMessage, responseMessage] = useGreeterService(); return ( <> <h1>Simple message reverser over gRPC</h1> <p>(Don't ask me why you would actually want to reverse a string server-side, this is just for fun!</p> <label title="Type a message in realtime over the gRPC wire">Type a message in realtime over the gRPC wire:</label> <br/> <textarea value={message} onChange={(event) => setMessage(event.target.value)}/> <p>And see the response message here: {responseMessage}</p> </> ); } export default App;
Веб-прокси gRPC
После того, как ваш серверный и внешний интерфейс запущены, остается добавить еще один элемент: прокси-сервер gRPC. К счастью, у нас есть прокси Improbable, готовый к использованию. Сначала установите его с помощью:
GOPATH=~/go ; export GOPATH git clone https://github.com/improbable-eng/grpc-web.git $GOPATH/src/github.com/improbable-eng/grpc-web cd $GOPATH/src/github.com/improbable-eng/grpc-web dep ensure # after installing dep go install ./go/grpcwebproxy # installs into $GOPATH/bin/grpcwebproxy
Затем запустите его с помощью:
GOPATH=~/go ; export GOPATH $GOPATH/bin/grpcwebproxy \ --backend_addr=localhost:50051 \ --run_tls_server=false \ --use_websockets \ --allow_all_origins
*Примечание: в моем первоначальном эксперименте я хотел использовать WebSockets в качестве транспортного уровня для оптимальной производительности. Тем не менее, после дальнейшего изучения gRPC (и, как упоминалось выше), использование WebSockets в качестве транспортного уровня технически все еще является экспериментальной функцией, поэтому я бы рекомендовал удалить флаг --use_websockets
из приведенной выше команды при запуске веб-прокси в рабочей среде.
(Хотя мне не совсем понятно, что является экспериментальным в использовании WebSockets в качестве транспортного уровня. В этом простом примере у меня не возникло никаких проблем!)
Пример кода
Код этого небольшого эксперимента можно найти на GitHub. README может дать вам еще более подробную информацию о том, как все это настроить и запустить.
Должен сказать, что после того, как я начал дурачиться, я был поражен увиденным. С моей точки зрения, я не мог бы сказать вам, что видел какую-либо разницу по сравнению с простым вызовом string.reverse()
в клиенте при каждом нажатии клавиши, и тем не менее эти строки были созданы двумя вызовами gRPC, совершающими круговое путешествие - сначала вызов сервера со строкой, а сервер отвечает обратной строкой! (Конечно, все это работало на локальном хосте, поэтому я не уверен, как это будет выглядеть на реальном сервере, но я могу представить, что это почти так же быстро). Но, как я уже сказал, поначалу кажется, что это быстро!
Спасибо!
Если вы хотели погрузиться в мир gRPC, я надеюсь, что этот пост убедил вас попробовать и показал, как легко приступить к работе с вызовом gRPC полного стека!
Ваше здоровье! 🍻
-Крис
У меня есть задача научить 1 000 000 многообещающих разработчиков работать с реальным программным обеспечением! Подробнее читайте в моем блоге:
и мой профиль и курсы Udemy: