Прежде всего давайте посмотрим на причины заставившие Twich релизить свою собственную версию ProtoBuf:
- Отсутствие поддержки HTTP 1.1. gRPS опирается на HTTP-трейлеры и полно-дуплексные потоки (full-duplex streams). Twirp поддерживает и HTTP 1.1 и HTTP/2, что очень важно потому что большое количество load-balancer-ов (как хардварных, так и софтверных) поддерживают только HTTP 1.1 — включая AWS Elastic Load Balancer. Но в отличии от gRPC Twirp не поддерживает стримингового RPC, что в случае когда Ваш API построен по принципу Request-Response и не требуется.
- Cложность реализации библиотеки grpc-go. Библиотека включает полную реализации HTTP/2, независимую от стандартных библиотек, что делает сложным ее пониманию и анализ возникающих ошибок.
- Cовместимость версий gRPC. В силу того, что gRPC довольно сложен, генерируемый Go код довольно простой и все запросы перенаправляются в grpc-go. Такая связанность приводит к тому, что клиент вынужден использовать ту же самую версию, что и сервер. И если у Вас большое количество клиентов и сервис взаимодействую друг с другом, то версия между ними должна быть идентичная. Понятно, что это приводит к сложностям в деплойменте и развертыванию микросервисов.
- Также Twitch указывают, что grpc-go требует определенную версию protobuf — github.com/golang/protobuf. Но для меня эта проблема кажется надуманной, так как protobuf имеет только один релиз версии v1.0.0, который используется всеми версиями grpc-go.
- gRPC поддерживает только бинарную форму сообщений и сниффинг сообщений очень сложным для анализа. Twirp поддерживает как бинарную форму сообщения в формате protobuf, так и в небинарные в формате JSON. Это вам дает преимущество, скажем если вы хотите взаимодействовать с сервисом через обычный HTTP Request посредством JSON
Как видите, простота это основновная причина, по которой Twich решили написать свою реализацию Protobuf.
Теперь давайте посмотрим, как же использовать эту библиотеку.
Если у вас уже настроена среда разработки на Go, то Вам нужно установить следующие пакеты
go get github.com/twitchtv/twirp/protoc-gen-twirp go get github.com/golang/protobuf/protoc-gen-go
Для пример напишем простой сервис, который инкрементит значение переданное в качестве параметра.
syntax = "proto3"; service Service { rpc Increment(Request) returns (Response); } message Request { int32 valueToIncrement = 1; // must be > 0 } message Response { int32 IncrementedValue = 1; // must be > 0 }
Сгенерируем код для нашего клиента выполнив следующую команду
protoc --proto_path=$GOPATH/src:. --twirp_out=. --go_out=. ./paperclips.proto
В результате буду созданы два файла
- Increment.pb.go — содержит кодо-генерацию для сообщений
- Increment.twirp.go — содержит интерфейсы и функции сервиса
Дальше добавим реализацию нашего сервиса
package main import ( "fmt" "log" "net/http" "context" pb "TwirpSample/Server/Twirp" ) // Server implements the Increment service type Server struct { value int32 } // NewServer creates an instance of our server func NewServer() *Server { return &Server{ value: 1, } } // Increment returns the incremented value of request.ValueToIncrement func (s *Server) Increment(ctx context.Context, request *pb.Request) (*pb.Response, error) { return &pb.Response{ IncrementedValue: request.ValueToIncrement + 1, }, nil } func main() { fmt.Printf("Starting Increment Service on :6666") server := NewServer() twirpHandler := pb.NewServiceServer(server, nil) log.Fatal(http.ListenAndServe(":6666", twirpHandler)) }
Теперь, если вы запустите клиента командой go run main.goм к сервису можно будет обратиться как по HTTP:
curl --request "POST" \ --location "http://localhost:6666/Service/Increment" \ --header "Content-Type:application/json" \ --data '{ValueToIncrement: 0}' \ --verbose Output: {"IncrementedValue":1}
Или в бинарном формате
package main import ( "fmt" rpc "TwirpSample/Server/Twirp" "net/http" "context" ) func main() { fmt.Println("Twirp Client Example.") client := rpc.NewServiceProtobufClient("http://localhost:6666", &http.Client{}) v, err := client.Increment(context.Background(), &rpc.Request{ValueToIncrement: 11}) if err != nil { fmt.Println(err.Error()) } fmt.Printf("Value: %d", v.IncrementedValue) } Output: Twirp Client Example. Value: 11
В целом сам фреймворк практически идентичен по подходам с gRPC, но прост в реализации и с одновременной поддержкой HTTP 1.1. На мой взгляд его применимость, если вам необходим RPC сервис, с который вы планируем одновременно взаимодействовать с UI посредством HTTP и между сервисами посредством Protobuf.
Ссылки:

