1、什么是Protobuf
Protobuf是谷歌发布的一套协议格式,它规定了一系列的编码和解 码方法,比如对于数字,它要求根据数字的大小选择存储空间,小于等于15的数字只用1个字节来表示,大于15的数用2个字节表示,以此类推,这样要求可以尽可能地节省空间。Protobuf协议的一大特点是编码后的数据量很小,可以节省网络带宽。
上图展示了用pbc模块处理Protobuf协议的流程。我们需要先编写描述文件,描述文件有它特定的格式,再用名为protoc的软件将它转换成.pb格式的文件;最后使用pbc库提供的方法实现编码解码。从上图可以看出,Protobuf的编码长度很短,move协议仅仅占用了6个字节。
2、安装Protobuf和pbc
(1)安装protobuf
sudo apt-get install protobuf-c-compiler protobuf-compiler
(2)定位到skynet/3rd/目录下,并下载第三方库pbc的源码
cd skynet/3rd/
git clone https://github.com/cloudwu/pbc.git
(3)定位到pbc目录下,并编译
cd pbc
make
(4)编译成功后,打开skynet/3rd/pbc/binding/lua53/Makefile文件,修改里面的lua路径
CC = gcc
CFLAGS = -O2 -fPIC -Wall
LUADIR = ../../../lua #这个路劲就是skynet/3rd/lua
TARGET = protobuf.so
.PHONY : all clean
all : $(TARGET)
$(TARGET) : pbc-lua53.c
$(CC) $(CFLAGS) -shared -o $@ -I../.. -I$(LUADIR) -L../../build $^ -lpbc
clean :
rm -f $(TARGET)
(5)进入pbc的binding目录,它包含Skynet可用的C库源码
cd ./binding/lua53
(8)开始编译,成功后会在同目录下生成库文件protobuf.so
sudo make
(9)将protobuf.so和protobuf.lua分别放入对应的目录下
cp protobuf.so ../../../../luaclib/ #将protobuf.so复制到存放C模块的lualib目录中
cp protobuf.lua ../../../../lualib/ #将protobuf.lua复制到存放Lua模块的lualib目录中
3、编译proto文件
(1)编写proto文件
使用Protobuf的第一步是编写描述文件(即.proto文件),新建用于存放协议描述文件的目录 proto,并在里面创建描述文件login.proto。login.proto的内容如下代码所示:
package login;
message Login {
required int32 id = 1;
required string pw = 2;
optional int32 result = 3;
}
- 包名为“login”,协议名为“Login”,它包含id、pw、result三个属性;
- required:如果没有指定值,将采用默认值填充;
- optional:如果没有指定值,直接为空。
(2)编译proto文件
进入proto目录,用如下指令编译login.proto:
protoc --descriptor_set_out login.pb login.proto
编译成功后,将会出现名为login.pb的二进制文件
4、 编码和解码
pbc模块常用的API有“register_file”“encode”和“decode”。使用pbc编解码之前,需先用register_file注册编译文件(.pb文件),然后用encode方法编码、用decode方法解码。
(1)在skynet/examples目录下创建main_protobuf.lua:
local skynet = require "skynet"
local pb = require "protobuf"
--protobuf编码解码
function test4()
pb.register_file("./proto/login.pb")
--编码
local msg = {
id = 101,
pw = "123456",
}
local buff = pb.encode("login.Login", msg)
print("len:"..string.len(buff))
--解码
local umsg = pb.decode("login.Login", buff)
if umsg then
print("id:"..umsg.id)
print("pw:"..umsg.pw)
else
print("error")
end
end
skynet.start(function()
test4()
end)
pb.encode带有两个参数:
- 第一个参数代表协议名,由proto描述文件的包名和协议名组合而成
- 第二个参数代表协议对象
pb.decode也带有两个参数:
- 第一个参数代表协议名,
- 第二个参数是二进制数据。
如果解码失败,pb.decode会返回nil,如果解码成功,它会返回协议对象。 运行流程如下图所示:
(2)在skynet/examples目录下创建config_protobuf:
include "config.path"
thread = 8
logger = nil
logpath = "."
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "main_protobuf" -- main script
bootstrap = "snlua bootstrap" -- The service for bootstrap
standalone = "0.0.0.0:2013"
cpath = root.."cservice/?.so"
5、运行代码
进入skynet目录,输入如下指令:
./skynet examples/config_protobuf
运行结果如下图所示, 可以看到这里只占10字节。