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

в 8:30, , рубрики: Go, proto3, protocol buffers, хранение данных

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

Подготовка

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

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

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

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

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

Пример

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

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, для хранения любых типов. Так гласит заметка к релизу, а на деле пока что так. Придётся подождать. Однако если взглянуть на вариант по ссылке выше, то принцип становиться ясен — дело за реализацией. Any выглядит так:

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

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


Референс

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

Автор: deep_orange

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js