предисловие
Всем привет, меня зовут Jianyu. В этой главе я буду использовать Go для написания сервера gRPC и клиента для связи друг с другом. Кроме того, будут использоваться следующие библиотеки:
- google.golang.org/grpc
- github.com/golang/protobuf/protoc-gen-go
Установить
gRPC
go get -u google.golang.org/grpc
Protocol Buffers v3
wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.zip
unzip protobuf-all-3.5.1.zip
cd protobuf-3.5.1/
./configure
make
make install
Проверьте, прошла ли установка успешно
protoc --version
Если возникает следующая ошибка, выполнитеldconfig
Именование решает эту проблему
protoc: error while loading shared libraries: libprotobuf.so.15: cannot open shared object file: No such file or directory
Protoc Plugin
go get -u github.com/golang/protobuf/protoc-gen-go
Если есть проблема со средой установки, обратитесь к моей предыдущей статье«Введение и установка среды»В нем есть подробные вступления, поэтому я не буду их повторять.
gRPC
В этом разделе начинается формальное написание программ, связанных с gRPC, давайте вместе сядем в автобус 😄
значок
Структура каталогов
$ tree go-grpc-example
go-grpc-example
├── client
├── proto
│ └── search.proto
└── server.go
IDL
написать
В файле search.proto в папке proto напишите следующее:
syntax = "proto3";
package proto;
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse) {}
}
message SearchRequest {
string request = 1;
}
message SearchResponse {
string response = 1;
}
генерировать
Выполните следующую команду в папке proto:
$ protoc --go_out=plugins=grpc:. *.proto
- plugins=plugin1+plugin2: указывает список подплагинов для загрузки
Определим проточный файл участвует в службе RPC, и по умолчанию не собирается генерировать код RPC, и поэтому необходимо датьplugins
параметр, переданныйprotoc-gen-go
, подскажите, пожалуйста, поддержите RPC (здесь указан gRPC)
- --go_out=.: Установить каталог для вывода кода Go
Эта команда загрузит плагин protoc-gen-go для генерации кода Go, а сгенерированный файл будет иметь суффикс .pb.go.
- : (двоеточие)
В качестве разделителя выступает двоеточие, за которым следует требуемый набор параметров. Если здесь не задействован RPC, команду можно упростить до:
$ protoc --go_out=. *.proto
Примечание: я предлагаю вам посмотреть на файлы .pb.go, сгенерированный двумя командами, в чем разница?
после генерации
После выполнения команды вы получите файл .pb.go со следующим содержимым:
type SearchRequest struct {
Request string `protobuf:"bytes,1,opt,name=request" json:"request,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SearchRequest) Reset() { *m = SearchRequest{} }
func (m *SearchRequest) String() string { return proto.CompactTextString(m) }
func (*SearchRequest) ProtoMessage() {}
func (*SearchRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_search_8b45f79ee13ff6a3, []int{0}
}
func (m *SearchRequest) GetRequest() string {
if m != nil {
return m.Request
}
return ""
}
Прочитав эту часть кода, вы можете узнать, что в основном задействованы следующие аспекты:
- Преобразование имен полей из нижнего регистра подчеркивания в верхний регистр верблюжьего (экспорт поля)
- Создайте набор методов Getters для облегчения обработки некоторых значений нулевого указателя.
- Метод ProtoMessage реализует интерфейс proto.Message.
- Создание методов Rest для восстановления структур Protobuf до нулевых значений
- Повторное преобразование в фрагмент
type SearchRequest struct {
Request string `protobuf:"bytes,1,opt,name=request" json:"request,omitempty"`
}
func (*SearchRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_search_8b45f79ee13ff6a3, []int{0}
}
type SearchResponse struct {
Response string `protobuf:"bytes,1,opt,name=response" json:"response,omitempty"`
}
func (*SearchResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_search_8b45f79ee13ff6a3, []int{1}
}
...
func init() { proto.RegisterFile("search.proto", fileDescriptor_search_8b45f79ee13ff6a3) }
var fileDescriptor_search_8b45f79ee13ff6a3 = []byte{
// 131 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x4e, 0x4d, 0x2c,
0x4a, 0xce, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x4a, 0x9a, 0x5c, 0xbc,
0xc1, 0x60, 0xe1, 0xa0, 0xd4, 0xc2, 0xd2, 0xd4, 0xe2, 0x12, 0x21, 0x09, 0x2e, 0xf6, 0x22, 0x08,
0x53, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x55, 0xd2, 0xe1, 0xe2, 0x83, 0x29, 0x2d,
0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x15, 0x92, 0xe2, 0xe2, 0x28, 0x82, 0xb2, 0xa1, 0x8a, 0xe1, 0x7c,
0x23, 0x0f, 0x98, 0xc1, 0xc1, 0xa9, 0x45, 0x65, 0x99, 0xc9, 0xa9, 0x42, 0xe6, 0x5c, 0x6c, 0x10,
0x01, 0x21, 0x11, 0x88, 0x13, 0xf4, 0x50, 0x2c, 0x96, 0x12, 0x45, 0x13, 0x85, 0x98, 0xa3, 0xc4,
0x90, 0xc4, 0x06, 0x16, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xf3, 0xba, 0x74, 0x95, 0xc0,
0x00, 0x00, 0x00,
}
Эта часть кода в основном вращается вокругfileDescriptor
продолжайте, здесьfileDescriptor_search_8b45f79ee13ff6a3
Представляет скомпилированный прото-файл, и каждый метод содержит метод Descriptor, что означает, что этот метод находится вfileDescriptor
Конкретное поле сообщения в
Server
В этом разделе будет написан базовый шаблон сервера gRPC для завершения вызова метода. Напишите на server.go следующее:
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "github.com/EDDYCJY/go-grpc-example/proto"
)
type SearchService struct{}
func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) {
return &pb.SearchResponse{Response: r.GetRequest() + " Server"}, nil
}
const PORT = "9001"
func main() {
server := grpc.NewServer()
pb.RegisterSearchServiceServer(server, &SearchService{})
lis, err := net.Listen("tcp", ":"+PORT)
if err != nil {
log.Fatalf("net.Listen err: %v", err)
}
server.Serve(lis)
}
- Создайте объект сервера gRPC, вы можете понимать его как абстрактный объект на стороне сервера.
- Зарегистрируйте SearchService (который содержит интерфейс сервера, который необходимо вызвать) во внутреннем реестре сервера gRPC. Таким образом, при получении запроса через обнаружение внутренней службы интерфейс сервера может быть обнаружен и передан для логической обработки.
- Создать Listen, слушать на порту TCP
- gRPC Server запускает lis.Accept до Stop или GracefulStop
Client
Затем напишите базовый шаблон gRPC Go Client, откройте файл client/client.go и напишите следующее:
package main
import (
"context"
"log"
"google.golang.org/grpc"
pb "github.com/EDDYCJY/go-grpc-example/proto"
)
const PORT = "9001"
func main() {
conn, err := grpc.Dial(":"+PORT, grpc.WithInsecure())
if err != nil {
log.Fatalf("grpc.Dial err: %v", err)
}
defer conn.Close()
client := pb.NewSearchServiceClient(conn)
resp, err := client.Search(context.Background(), &pb.SearchRequest{
Request: "gRPC",
})
if err != nil {
log.Fatalf("client.Search err: %v", err)
}
log.Printf("resp: %s", resp.GetResponse())
}
- Создайте взаимодействие соединения с заданной целью (сервер)
- Создайте клиентский объект для SearchService
- Отправьте запрос RPC, дождитесь синхронного ответа и верните ответ после получения обратного вызова.
- вывод результата ответа
проверять
запустить сервер
$ pwd
$GOPATH/github.com/EDDYCJY/go-grpc-example
$ go run server.go
Запустить клиент
$ pwd
$GOPATH/github.com/EDDYCJY/go-grpc-example/client
$ go run client.go
2018/09/23 11:06:23 resp: gRPC Server
Суммировать
В этой главе мы познакомимся с Protobuf и клиентом/сервером gRPC соответственно. Надеюсь вы напишете Демо для более глубокого понимания на основе содержания в статье, оно точно будет лучше 🤔
?
Если у вас есть какие-либо вопросы или ошибки, добро пожаловать вissuesЗадавайте вопросы или вносите исправления, если вам нравится или вам помогают, добро пожаловатьStar, является своеобразным поощрением и продвижением автора.