- PVSM.RU - https://www.pvsm.ru -

Go и Protocol Buffers толика практики (или быстрый старт, для тех кто ещё не знаком)

Весьма короткий пример как использовать Protocol Buffers в Go. Речь пойдёт о proto3, т.е. 3-ей версии протокола (на текущим момент альфа), обобщённо пример справедлив и для второй версии. Не будет описания самого Protocol Buffers. Впрочем чего тянуть

Подготовка

Документации по третьей версии нет, поэтому идём сюда [1] читаем и качаем. Устанавливаем (там всё просто). Теперь что касается Go — всё здесь [2]. Собственно установка

go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

Может понадобиться -f если в ~/.gitconfig у Вас указано

[url "ssh://git@github.com/"]
        insteadOf = https://github.com/

Если Вы ещё не знакомы с Protocl Buffers, то здесь [3] (англ.) описание 2-ой версии. Для третьей версии пока нет документации, остаётся довольствоваться лишь этим [1].

Пример

Для примера будем сохранять массив из чисел и строку, а потом читать их обратно. Далее будем считать что мы находимся в корне нашего нового проекта. Прото-файл будет выглядеть так

msg/msg.proto

// комментарии как в C/C++
/*
     и многострочные тоже
*/
// синтаксис версии 3, она пока что альфа - будем думать о будующем
syntax = "proto3";

// имя пакета, в результирующем go-файле это сохраниться
package msg;

// тип данных, которые мы будем сохранять
message Msg {
        // тип имя_поля = номер_поля
        string key = 1;
        // repeated означает slice
        repeated int64 value = 2;
}
/*
 в третьей версии нет required полей и extensions
 вместо extensions реализован (пока ещё не) тип Any о нём попозже
*/
// кстати для Sublime есть подсветка синтаксиса

Теперь необходимо скомпилировать прото-файл

 protoc --go_out=. msg/*.proto

В результате получим такой во файлик

msg/msg.pb.go

// комментарии выпилены
package msg

import proto "github.com/golang/protobuf/proto"

var _ = proto.Marshal

/*
Структура обрела такой вид. Обратите внимание, что автоматически добавлены тэги для JSON
*/
type Msg struct {
        Key   string  `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"`
        Value []int64 `protobuf:"varint,2,rep,name=value" json:"value,omitempty"`
}

// методы необходимы, чтобы структура соответствовала интерфейсу proto.Message
func (m *Msg) Reset()         { *m = Msg{} }
func (m *Msg) String() string { return proto.CompactTextString(m) }
func (*Msg) ProtoMessage()    {}

func init() {
}

Теперь создадим структуру, запишем её байты, и прочитаем обратно

main.go

package main

import (
	"log"

	"./msg"
	"github.com/golang/protobuf/proto"
)

func main() {
	// создадим новое "сообщение"
	msg1 := &msg.Msg{
		Key:   "Hello Protocol Buffers",
		Value: []int64{1, 2, 3, 4},
	}
	// структуру в байты
	data, err := proto.Marshal(msg1)
	if err != nil {
		log.Fatal("marshaling error: ", err)
		return
	}
	// сколько же она занимает памяти?
	log.Printf("data length: %d", len(data))
	// байты в структуру
	msg2 := new(msg.Msg)
	err = proto.Unmarshal(data, msg2)
	if err != nil {
		log.Fatal("unmarshaling error: ", err)
	}
	// теперь обе структкры должны быть равны
	if msg1.Key != msg2.Key {
		log.Printf("unexpected value, expected '%s', got '%s'", msg1.Key, msg2.Key)
	}
	for i := 0; i < 4; i++ {
		if msg1.Value[i] != msg2.Value[i] {
			log.Printf("unexpected value, expected %d, got %d", msg1.Value[i], msg2.Value[i])
		}
	}
	log.Println("Done")
}

Как видите «проще пареной репы». Если копнуть глубже, допустим есть желание создать некую базу, хранящую «сообщения» — так, что тип «сообщения» изначально не определён, причём сохранять эти «сообщения» в членстве некой структуры. Иными словами иметь библиотеку, которая будет сохранять то, что мы ей дадим в определённом формате. В proto3 — третьей версии protocol buffers — реализован тип Any, для хранения любых типов. Так гласит заметка к релизу, а на деле пока что так [4]. Придётся подождать. Однако если взглянуть на вариант по ссылке выше, то принцип становиться ясен — дело за реализацией. Any выглядит так:

message Any {
  string type_url = 1; // тип
  bytes value = 2; // содержимое типа в байтах
}

Что означает… Впрочем долго рассказывать, взгляните на пример [5]. По сути это регистрация всех используемых типов и двойной маршалинг — маршалинг некоего типа, а затем маршалинг базовой структуры. Всё это дико поперчёно рефлексией. Да — рефлексия это долго, тут ничего не поделаешь. На этом всё.


Референс

Релизы Protocol Buffers на GitHub [1]
Там же, трекер [6]
Одна из реализаций Protocol Buffers для Go [2]
Путеводитель по Protocol Buffers v2 [3]
Там же, базовые типы данных [7]

Автор: deep_orange

Источник [8]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/hranenie-danny-h/85320

Ссылки в тексте:

[1] сюда: https://github.com/google/protobuf/releases

[2] здесь: https://github.com/golang/protobuf

[3] здесь: https://developers.google.com/protocol-buffers/docs/proto

[4] так: https://github.com/google/protobuf/issues/198

[5] пример: https://github.com/logrusorgru/gopb3any

[6] Там же, трекер: https://github.com/google/protobuf/issues

[7] Там же, базовые типы данных: https://developers.google.com/protocol-buffers/docs/proto#scalar

[8] Источник: http://habrahabr.ru/post/252455/