go的go protobuff这个问题是不是一个bug

Protobuf对于Golang通过插件进行支持,因些需要安装protoc的执行环境,下面我们来一步步看下,如何搭建一个编译环境。
1. 安装protoc
2. 下载并安装protobuf-go插件
从github上下载插件,并解压(/golang/protobuf),得到以下的目录
drwxr-xr-x 6 root root 4096 Jun 16 15:45 .
drwxr-xr-x 3 root root 4096 Jun 16 15:48 ..
-rw-r--r-- 1 root root
173 Jun 15 06:31 AUTHORS
-rw-r--r-- 1 root root
170 Jun 15 06:31 CONTRIBUTORS
drwxr-xr-x 3 root root 4096 Jun 15 06:31 jsonpb
-rw-r--r-- 1 root root 1583 Jun 15 06:31 LICENSE
-rw-r--r-- 1 root root 2080 Jun 15 06:31 Makefile
-rw-r--r-- 1 root root 1955 Jun 15 06:31 Make.protobuf
drwxr-xr-x 4 root root 4096 Jun 15 06:31 proto
drwxr-xr-x 7 root root 4096 Jun 16 15:42 protoc-gen-go
drwxr-xr-x 8 root root 4096 Jun 15 06:31 ptypes
-rw-r--r-- 1 root root 7149 Jun 15 06:31 README.md
这时,执行make install,多半是不会成功的,一般会报找不到对应的文件,原因在于go源文件中指定的目录位置是这样的
&io/ioutil&
&/golang/protobuf/proto&
&/golang/protobuf/protoc-gen-go/generator&
因此,要求我们把当面下载的文件放到$GOROOT对应的目录下,并且把目录名改成指定的名称,比如我的GOROOT=/usr/local/go,那我就把解压后的目录改名为protobuf,并在/usr/local/go下创建/usr/local/go//golang/目录,把protobuf目录整体mv过去,再执行make install,执行结果如下:
[root@SH-todo- /usr/local/go//golang/protobuf]# make install
go install ./proto ./jsonpb ./ptypes
go install ./protoc-gen-go
说明执行成功了。
3. 用法举例
下面我们来说明如何把*.proto文件生成*.go文件,同时在程序中序列及反序列化
a) 创建一个test.proto文件
enum FOO { X = 17; };
message Test {
required string label = 1;
optional int32 type = 2 [default=77];
repeated int64 reps = 3;
optional group OptionalGroup = 4 {
required string RequiredField = 5;
执行protoc --go_out=. test.proto,得到test.pb.go
测试代码如下:
package main
&/golang/protobuf/proto&
// test.pb.go 的路径
func main() {
// 创建一个消息 Test
test := &example.Test{
// 使用辅助函数设置域的值
Label: proto.String(&hello&),
proto.Int32(17),
Optionalgroup: &example.Test_OptionalGroup{
RequiredField: proto.String(&good bye&),
fmt.Printf(&Label:%s Type:%d\n&, test.GetLabel(), test.GetType())
*(test.Label) = &hello go&
*(test.Type) = 18
// 进行编码
data, err := proto.Marshal(test)
if err != nil {
log.Fatal(&marshaling error: &, err)
fmt.Printf(&Binary Len:%d\n&, len(data))
// 进行解码
newTest := &example.Test{}
err = proto.Unmarshal(data, newTest)
if err != nil {
log.Fatal(&unmarshaling error: &, err)
fmt.Printf(&Label:%s Type:%d\n&, test.GetLabel(), test.GetType())
// 测试结果
if test.GetLabel() != newTest.GetLabel() {
log.Fatalf(&data mismatch %q != %q&, test.GetLabel(), newTest.GetLabel())
执行结果如下:
Label:hello Type:17
Binary Len:24
Label:hello go Type:18
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:169365次
积分:2575
积分:2575
排名:第10345名
原创:74篇
转载:66篇
评论:26条
(3)(6)(2)(1)(5)(2)(2)(1)(1)(1)(1)(2)(2)(2)(3)(1)(9)(5)(13)(5)(4)(4)(7)(31)(4)(5)(3)(2)(3)(11)5357人阅读
C/C++(77)
golang(18)
首先,安装google的protobuf。安装流程请参见我以前的博客
http://blog.csdn.net/eclipser1987/article/details/8525383
安装proto的go语言插件
go get /p/goprotobuf/{proto,protoc-gen-go}
编写一个简单的事例,go与c++网络通信:
RegMessage.proto
package cn.vicky.model.
message RegMessage {
required int32 id = 1; // 主键,唯一
required string username = 2; // 帐号
required string password = 3; // 密码
optional string email = 4; // 邮箱(可选)
protoc --cpp_out=. RegMessage.proto
protoc --go_out=. RegMessage.proto
生成对应的c++源文件和go源文件
RegMessage.pb.h
RegMessage.pb.cc
RegMessage.pb.go
go语言做客户端代码:
// Code generated by protoc-gen-go.
// source: RegMessage.proto
// DO NOT EDIT!
package main
import &fmt&
import &os&
import &net&
import proto &/p/goprotobuf/proto&
import json &encoding/json&
import math &math&
// Reference proto, json, and math imports to suppress error if they are not otherwise used.
var _ = proto.Marshal
var _ = &json.SyntaxError{}
var _ = math.Inf
type RegMessage struct {
`protobuf:&varint,10001,req,name=id& json:&id,omitempty&`
*string `protobuf:&bytes,2,req,name=username& json:&username,omitempty&`
*string `protobuf:&bytes,3,req,name=password& json:&password,omitempty&`
*string `protobuf:&bytes,4,opt,name=email& json:&email,omitempty&`
XXX_unrecognized []byte
`json:&-&`
func (m *RegMessage) Reset()
{ *m = RegMessage{} }
func (m *RegMessage) String() string { pactTextString(m) }
func (*RegMessage) ProtoMessage()
func (m *RegMessage) GetId() int32 {
if m != nil && m.Id != nil {
return *m.Id
func (m *RegMessage) GetUsername() string {
if m != nil && m.Username != nil {
return *m.Username
func (m *RegMessage) GetPassword() string {
if m != nil && m.Password != nil {
return *m.Password
func (m *RegMessage) GetEmail() string {
if m != nil && m.Email != nil {
return *m.Email
func init() {
func main() {
regMessage := &RegMessage{
proto.Int32(10001),
Username: proto.String(&vicky&),
Password: proto.String(&123456&),
proto.String(&&),
buffer, err := proto.Marshal(regMessage)
if err != nil {
fmt.Printf(&failed: %s\n&, err)
pTCPAddr, err := net.ResolveTCPAddr(&tcp&, &192.168.1.98:7070&)
if err != nil {
fmt.Fprintf(os.Stderr, &Error: %s&, err.Error())
pTCPConn, err := net.DialTCP(&tcp&, nil, pTCPAddr)
if err != nil {
fmt.Fprintf(os.Stderr, &Error: %s&, err.Error())
pTCPConn.Write(buffer)
正常情况下是不建议修改protoc生成的源文件的,我们这里图省事,直接在生成的RegMessage.pb.go添加网络发送代码。
使用C++编写简单的socket服务器:
* Author: Vicky.H
#include &cstdio&
#include &cstdlib&
#include &iostream&
#include &sys/socket.h&
#include &netinet/in.h&
#include &arpa/inet.h&
#include &pthread.h&
#include &RegMessage.pb.h&
void* connHandler(void *pConn) {
int* _pConn = static_cast&int*& (pConn);
for (;;) {
void* buf = malloc(1024);
int n = read(*_pConn, buf, 1024);
cn::vicky::model::seri::RegMessage regM
regMessage.ParseFromArray(buf, n);
std::cout && regMessage.id() && std::
std::cout && regMessage.username() && std::
std::cout && regMessage.password() && std::
std::cout && regMessage.email() && std::
close(*_pConn);
free(buf);
int main(void) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
perror(&创建Socket失败!&);
return EXIT_FAILURE;
struct sockaddr_
addr.sin_port = htons(7070);
inet_pton(AF_INET, &192.168.1.98&, &addr.sin_addr);
result = bind(sock, (struct sockaddr*) &addr, sizeof (struct sockaddr_in));
if (result != 0) {
perror(&绑定地址失败!&);
return EXIT_FAILURE;
result = listen(sock, 10);
if (result != 0) {
perror(&监听失败!&);
return EXIT_FAILURE;
for (;;) {
int conn = accept(sock, NULL, NULL);
if (conn == -1) {
perror(&与客户端建立连接失败!&);
pthread_create(&thread, NULL, connHandler, (void*) &conn);
需要连接 -lpthread -lprotobuf&
分别运行c++程序,go run&RegMessage.pb.go
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:602312次
积分:7831
积分:7831
排名:第1807名
原创:207篇
转载:37篇
评论:58条
(1)(2)(3)(4)(3)(13)(8)(1)(3)(1)(4)(1)(2)(1)(15)(6)(3)(2)(4)(2)(8)(14)(3)(7)(1)(3)(6)(3)(7)(3)(6)(1)(7)(6)(11)(9)(10)(9)(18)(8)(21)(4)在接收到&protobuf&数据之后,如何自动创建具体的&Protobuf&Message&对象,再做反序列化。&自动&的意思主要有两个方面:(1)当程序中新增一个&protobuf&Message&类型时,这部分代码不需要修改,不需要自己去注册消息类型,不需要重启进程,只需要提供protobuf文件;(2)当protobuf&Message修改后,这部分代码不需要修改,不需要自己去注册消息类型,不需要重启进程只需要提供修改后protobuf文件。
Protobuf的入门可以参考或者IBM&developerwor上的文章。
protobuf的动态解析在google&protobuf&buffer官网并没有什么介绍。通过google出的一些参考文档可以知道,其实,Google&Protobuf&本身具有很强的反射(reflection)功能,可以根据&type&name&创建具体类型的&Message&对象,我们直接利用即可,应该就可以满足上面的需求。
实现可以参考淘宝的文章,里面对protobuf的动态解析的原理做了详细的介绍,在此我介绍一下Protobuf&&class&diagram。
大家通常关心和使用的是图的左半部分:MessageLite、Message、Generated&Message&Types&(Person,&AddressBook)&等,而较少注意到图的右半部分:Descriptor,&DescriptorPool,&MessageFactory。
上图中,其关键作用的是&Descriptor&class,每个具体&Message&Type&对应一个&Descriptor&对象。尽管我们没有直接调用它的函数,但是Descriptor在&根据&type&name&创建具体类型的&Message&对象&中扮演了重要的角色,起了桥梁作用。上图的红色箭头描述了根据&type&name&创建具体&Message&对象的过程。
先直接上代码,这个代码来自于:
#include&&iostream&
#include&&google/protobuf/descriptor.h&
#include&&google/protobuf/descriptor.pb.h&
#include&&google/protobuf/dynamic_message.h&
#include&&google/protobuf/compiler/importer.h&
using&namespace&
using&namespace&google::
using&namespace&google::protobuf::
int&main(int&argc,const&char&*argv[])
&&&&DiskSourceTree&sourceT
&&&&//look&up&.proto&file&in&current&directory
&&&&sourceTree.MapPath("","./");
&&&&Importer&importer(&sourceTree,&NULL);
&&&&//runtime&compile&foo.proto
&&&&importer.Import("foo.proto");
const&Descriptor&*descriptor&=&&&&importer.pool()-&
&&&&&&FindMessageTypeByName("Pair");
&&&&cout&&&&descriptor-&DebugString();
&&&&//&build&a&dynamic&message&by&"Pair"&proto
&&&&DynamicMessageFactory&
&&&&const&Message&*message&=&factory.GetPrototype(descriptor);
&&&&//&create&a&real&instance&of&"Pair"
&&&&Message&*pair&=&message-&New();
&&&&//&write&the&"Pair"&instance&by&reflection
&&&&const&Reflection&*reflection&=&pair-&GetReflection();
&&&&const&FieldDescriptor&*field&=&NULL;
&&&&field&=&descriptor-&FindFieldByName("key");
&&&&reflection-&SetString(pair,&field,"my&key");
&&&&field&=&descriptor-&FindFieldByName("value");
&&&&reflection-&SetUInt32(pair,&field,&1111);
&&&&cout&&&&pair-&DebugString();
&&&&delete&
&&&&return0;
那我们就来看看上面的代码
1)把本地地址映射为虚拟地址
DiskSourceTree&sourceT
&&&&//look&up&.proto&file&in&current&directory
sourceTree.MapPath("","./");
2)构造DescriptorPool
Importer&importer(&sourceTree,&NULL);
&&&&//runtime&compile&foo.proto
importer.Import("foo.proto");
3)获取Descriptor
const&Descriptor&*descriptor&=&importer.pool()-&FindMessageTypeByName("Pair");
4)通过Descriptor获取Message
const&Message&*message&=&factory.GetPrototype(descriptor);
5)根据类型信息使用出这个类型的一个空对象
Message&*pair&=&message-&New();
6)通过的操作的各个字段
& const&Reflection&*reflection&=&pair-&GetReflection();&
&&&&const&FieldDescriptor&*field&=&NULL;
&&&&field&=&descriptor-&FindFieldByName("key");
&&&&reflection-&SetString(pair,&field,"my&key");
&&&&field&=&descriptor-&FindFieldByName("value");
reflection-&SetUInt32(pair,&field,&1111);
直接copy上面代码看起来我们上面的需求就满足了,只是唯一的缺点就是每次来个包加载一次配置文件,当时觉得性能应该和读取磁盘的性能差不多,但是经过测试性能极差,一个进程每秒尽可以处理1000多个包,经过分析性能瓶颈不在磁盘,而在频繁调用malloc和free上。
看来我们得重新考虑实现,初步的实现想法:只有protobuf描述文件更新时再重新加载,没有更新来包只需要使用加载好的解析就可以。这个方案看起来挺好的,性能应该不错,经过测试,性能确实可以,每秒可以处理3万左右的包,但是实现中遇到了困难。要更新原来的Message,必须更新Importer和Factory,那么要更新这些东西,就涉及到了资源的释放。经过研究这些资源的释放顺序特别重要,下面就介绍一下protobuf相关资源释放策略。
动态的Message是我们用DynamicMessageFactory构造出来的,因此销毁Message必须用同一个DynamicMessageFactory。&动态更新.proto文件时,我们销毁老的并使用新的DynamicMessageFactory,在销毁DynamicMessageFactory之前,必须先删除所有经过它构造的Message。
&&原理:DynamicMessageFactory里面包含DynamicMessage的共享信息,析构DynamicMessage时需要用到。生存期必须保持Descriptor&DynamicMessageFactory&DynamicMessage。&
释放顺序必须是:释放所有DynamicMessage,释放DynamicMessageFactory,释放Importer。
资源释放前,必须要了解资源的构造原理,通过构造原理反推释放顺序,这样就少走弯路、甚至不走。
阅读(...) 评论()在 Golang 中使用 Protobuf - 推酷
在 Golang 中使用 Protobuf
项目为 Golang 提供了
安装 goprotobuf
获取 Protobuf 编译器 protoc(可下载到 Windows 下的二进制版本)
获取 goprotobuf 提供的 Protobuf 编译器插件 protoc-gen-go(被放置于 $GOPATH/bin 下,$GOPATH/bin 应该被加入 PATH 环境变量,以便 protoc 能够找到 protoc-gen-go)
go get /p/goprotobuf/protoc-gen-go
此插件被 protoc 使用,用于编译 .proto 文件为 Golang 源文件,通过此源文件可以使用定义在 .proto 文件中的消息。
获取 goprotobuf 提供的支持库,包含诸如编码(marshaling)、解码(unmarshaling)等功能
go get /p/goprotobuf/proto
使用 goprotobuf
这里通过一个例子来说明用法。先创建一个 .proto 文件 test.proto:
enum FOO { X = 17; };
message Test {
required string label = 1;
optional int32 type = 2 [default=77];
repeated int64 reps = 3;
optional group OptionalGroup = 4 {
required string RequiredField = 5;
编译此 .proto 文件:
protoc --go_out=. *.proto
这里通过 –go_out 来使用 goprotobuf 提供的 Protobuf 编译器插件 protoc-gen-go。这时候我们会生成一个名为 test.pb.go 的源文件。
在使用之前,我们先了解一下每个 Protobuf 消息在 Golang 中有哪一些可用的接口:
每一个 Protobuf 消息对应一个 Golang 结构体
消息中域名字为 camel_case 在对应的 Golang 结构体中为 CamelCase
消息对应的 Golang 结构体中不存在 setter 方法,只需要直接对结构体赋值即可,赋值时可能使用到一些辅助函数,例如:
msg.Foo = proto.String(&hello&)
消息对应的 Golang 结构体中存在 getter 方法,用于返回域的值,如果域未设置值,则返回一个默认值
消息中非 repeated 的域都被实现为一个指针,指针为 nil 时表示域未设置
消息中 repeated 的域被实现为 slice
访问枚举值时,使用“枚举类型名_枚举名”的格式(更多内容可以直接阅读生成的源码)
使用 proto.Marshal 函数进行编码,使用 proto.Unmarshal 函数进行解码
现在我们编写一个小程序:
package main
&/p/goprotobuf/proto&
// test.pb.go 的路径
func main() {
// 创建一个消息 Test
test := &example.Test{
// 使用辅助函数设置域的值
Label: proto.String(&hello&),
proto.Int32(17),
Optionalgroup: &example.Test_OptionalGroup{
RequiredField: proto.String(&good bye&),
// 进行编码
data, err := proto.Marshal(test)
if err != nil {
log.Fatal(&marshaling error: &, err)
// 进行解码
newTest := &example.Test{}
err = proto.Unmarshal(data, newTest)
if err != nil {
log.Fatal(&unmarshaling error: &, err)
// 测试结果
if test.GetLabel() != newTest.GetLabel() {
log.Fatalf(&data mismatch %q != %q&, test.GetLabel(), newTest.GetLabel())
关于 goprotobuf 更为详尽的使用文档见:
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致

我要回帖

更多关于 go protobuf repeated 的文章

 

随机推荐