百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 博客教程 > 正文

Protobuf 小试牛刀 小试牛刀的下一个阶段是什么

connygpt 2024-09-29 11:03 12 浏览

本文以PHP为例。

环境:

  • CentOS 6.8
  • proto 3.8
  • PHP 7.1.12
  • PHP protobuf扩展 3.8.0
  • go1.12.5 linux/amd64

本文示例仓库地址: https://github.com/52fhy/protobuf-sample

是什么

Protobuf是一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于网络通信和数据存储。

官方文档:https://github.com/protocolbuffers/protobuf

作为数据交换协议,常见的还有JSON、XML。相比JSON,Protobuf有更高的转化效率。一般JSON用于HTTP接口,Protobuf用于RPC比较多。以gRPC为例,默认就是使用Protobuf。

我们可以使用Protobuf:

  • 作为RPC的序列化数据结构的协议。类似于JSON
  • 定义proto文件,一键生成多语言代码。

安装

安装清单一览:

  • protoc
  • 各编程语言对应的protobuf库

安装protoc

为了将proto文件转成编程语言代码,需要安装编译工具。

地址:https://github.com/protocolbuffers/protobuf/releases/

Copy
wget https://github.com/protocolbuffers/protobuf/releases/download/v3.8.0/protoc-3.8.0-linux-x86_64.zip
unzip protoc-3.8.0-linux-x86_64.zip
cp bin/protoc /usr/bin/
cp -r include/google /usr/include/

注:最后一行是为了将proto的一些库复制到系统,例如google/protobuf/any.proto,如果不复制,编译如果用了里面的库例如Any,会提示:protobuf google.protobuf.Any not found 。

然后命令行输入 protoc可以查看帮助。

假设有一个 .proto格式的文件,需要编译成其它语言代码,以PHP为例则是:

Copy
mkdir php
protoc --php_out=php *.proto

其中--php_out=php表示编译成PHP代码,放在php目录。protof还支持:

Copy
$ protoc | grep "=OUT_DIR"
 --cpp_out=OUT_DIR Generate C++ header and source.
 --csharp_out=OUT_DIR Generate C# source file.
 --java_out=OUT_DIR Generate Java source file.
 --js_out=OUT_DIR Generate JavaScript source.
 --objc_out=OUT_DIR Generate Objective C header and source.
 --php_out=OUT_DIR Generate PHP source file.
 --python_out=OUT_DIR Generate Python source file.
 --ruby_out=OUT_DIR Generate Ruby source file.

后面有示例说明。

golang 代码编译支持

protoc --help 并没有--go_out参数说明, 如需编译golang目标代码,请执行以下步骤:

1、安装golang环境:yum install golang,其它系统查看 https://studygolang.com/dl (已安装请跳过)

2、go get github.com/golang/protobuf/protoc-gen-go;

3、复制扩展工具到/usr/bin:

Copy
cp `go env|grep 'GOPATH'|sed -e 's/GOPATH="//' -e 's/"//'`/bin/protoc-gen-go /usr/bin/

4、编译go目标代码: protoc --go_out=./go *.proto。

PHP扩展安装

php可以安装c扩展版本或者纯php代码版本。

C扩展版本

1、下载扩展源码:

Copy
wget https://pecl.php.net/get/protobuf-3.8.0.tgz
tar zxf protobuf-3.8.0.tgz
cd protobuf-3.8.0
phpize
./configure
make
sudo make install

或者直接使用 pecl 安装:

Copy
pecl install protobuf-3.8.0

2、 输入 php -i|grep php.ini 查看php.ini的路,修改php.ini, 增加:

Copy
extension=protobuf.so

3、检查是否安装成功:php --ri protobuf,安装成功会显示版本号。

纯PHP版本

使用 composer 安装即可:

Copy
composer require google/protobuf

下面说一下区别和注意事项:

1、截止到3.8.0版本,如果安装的是纯PHP版本,protobuf 里提供的序列化方法serializeToJsonString()不支持参数,c扩展版本支持,表示保留proto里定义的属性,不进行转大写;

2、c扩展版本无法使用var_dump等函数打印出protobuf对象里的对象的结构和内容,但是如果protobuf对象里的标量类型是可以打印出来的。

Go扩展库安装

golang如果使用protobuf,需要引入google.golang.org/grpc库。使用 go mod管理,可以编写规则做个映射:

Copy
replace google.golang.org/grpc => github.com/grpc/grpc-go v1.21.1

应用:protobuf创建Model

有时候我们需要根据数据库表结构生成一个Model,常规办法是手写,比较麻烦。有了protobuf,我们可以先编写一个proto 文件,然后编译成目标语言的代码。

定义proto

我们先定义一个 proto 文件:

Copy
// proto/User.proto
syntax = "proto3";
package Sample.Model; //namesapce
message User {
 int64 id = 1; //主键id
 string name = 2; //用户名
 string avatar = 3; //头像
 string address = 4; //地址
 string mobile = 5; //手机号
 map<string, string> ext = 6; //扩展信息
}
message UserList {
 repeated User list = 1; //用户列表
 int32 page = 2; //分页
 int32 limit = 3; //分页条数
}

以上分别创建了user和UserList两个Model。

编译proto

现在使用proto工具编译出来:

Copy
mkdir php
protoc --php_out=php proto/User.proto

会生成:

Copy
├── php
│ ├── GPBMetadata
│ │ └── User.php
│ └── Sample
│ └── Model
│ ├── UserList.php
│ └── User.php
├── proto
│ └── User.proto

UserList.php 代码部分示例:

测试编译生成的代码

接下来,我们写个例子看看如何使用生成的Model。在使用之前需要处理下GPBMetadata相关的命名空间问题,这里我们定义的命名空间是Sample\Model,但是 GPBMetadata/User.php以及Sample/Model/User.php的命名空间我们希望调整下,都以Sample\Model开头,而不是GPBMetadata。下面我们使用命令行处理:

Copy
cd protobuf-sample
#修改GPBMetadata命名空间
cd php
mv -f GPBMetadata Sample/Model/
find . -name '*.php' ! -name example.php -exec sed -i -e 's#GPBMetadata#Sample\\Model\\GPBMetadata#g' -e 's#\\Sample\\Model\\GPBMetadata\\Google#\\GPBMetadata\\Google#g' {} \;
cd -

接下来我们写个测试文件:

user.php

Copy
<?php
use Sample\Model\User;
use Sample\Model\UserList;
ini_set("display_errors", true);
error_reporting(E_ALL);
require_once "autoload.php";
$user = new User();
$user->setId(1)->setName("test");
$userList = new UserList();
$userList->setPage(1)->setLimit(5)->setList([$user]);
print_r($userList);
var_dump($userList->getPage());
print_r($userList->getList());
foreach ($userList->getList() as $key => $obj) {
 print_r($obj);
 echo $obj->getId() .PHP_EOL;
}

autoload.php是实现自动加载的。

我们运行:

Copy
$ php tests/user.php 
Sample\Model\UserList Object
/work/git/protobuf-sample/tests/user.php:15:
int(1)
Google\Protobuf\Internal\RepeatedField Object
(
)
Sample\Model\User Object
1
{"list":[{"id":1,"name":"test"}],"page":1,"limit":5}

可以看到使用var_dump、print_r等函数是打印不出来 protobuf生成的对象的,但是里面确实是有内容的,只有标量能打印出来,或者序列化为字符串。

我们也可以将一个字符串反序列化为protobuf对象:

user_merge.php

Copy
<?php 
use Sample\Model\UserList;
$json = '{"list":[{"id":1,"name":"test"}],"page":1,"limit":5}';
require_once "autoload.php";
$userList = new UserList();
$userList->mergeFromJsonString($json);
print_r($userList);
echo $userList->serializeToJsonString();

运行示例:

Copy
$ php tests/user_merge.php
Sample\Model\UserList Object
{"list":[{"id":1,"name":"test"}],"page":1,"limit":5}

proto语法

这里只将介绍简单的,如果需要细研究,请查看官方文档。

官方文档:https://developers.google.com/protocol-buffers/docs/overview

1、proto3

proto 有proto3 和 proto2。proto3 比 proto2 支持更多语言但 更简洁。去掉了一些复杂的语法和特性,更强调约定而弱化语法。如果是首次使用 Protobuf ,建议使用 proto3 。详见参考文献说明。

需要在proto头部申明:

Copy
syntax = "proto3";

如果你没有指定这个,编译器会使用proto2。

2、注释

使用 //,示例:

Copy
message UserList {
 repeated User list = 1; //用户列表
 int32 page = 2; //分页
 int32 limit = 3; //分页条数
}

其中写在每个属性后面的注释在生产的代码里面有保留。

3、message

message类似于结构体的概念,最终编译为代码在PHP、JAVA里就是一个类,在golang里是结构体。每一个属性都会生成对应的getXXX、setXXX方法。

4、字段规则

repeated表示这个属性重复N次,在相对应的编程语言中通常是一个空的list。PHP里对应数组。

reserved表示标识号保留暂时不用。

Copy
message Foo {
 reserved 2, 15, 9 to 11;
 reserved "foo", "bar";
}

在消息定义中,每个字段都有唯一的一个数字标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。

5、支持的数据类型

详情参看官方文档:https://developers.google.com/protocol-buffers/docs/proto3#scalar

6、默认值说明

  • string类型,默认值是空字符串
  • bytes类型,默认值是空bytes
  • bool类型,默认值是false
  • 数字类型,默认值是0
  • 枚举类型,默认值是第一个枚举值,即0
  • repeated修饰的属性,默认值是空.

7、枚举

使用enum关键字定义枚举,值必须从0开始:

Copy
enum Corpus {
 UNIVERSAL = 0;
 WEB = 1;
 IMAGES = 2;
 LOCAL = 3;
 NEWS = 4;
 PRODUCTS = 5;
 VIDEO = 6;
}

8、引用类型

上面的UserList就引用了User类型。大家可以看一下。

9、import

如果一个proto文件引用了另外一个proto文件,那么可以使用import关键字在头部申明:

Copy
import "User.proto";

10、Map类型

proto支持map属性类型的定义,语法如下:

Copy
map<key_type,value_type> map_field = N;

示例:

Copy
map<string, string> ext = 6; //扩展信息

这个map对于PHP来说就是关联数组,对于golang来说就是Map。

10、Any

Any类型允许包装任意的message类型,可以通过pack()和unpack()(方法名在不同的语言中可能不同)方法打包/解包:

Copy
import "google/protobuf/any.proto";
message Response {
 google.protobuf.Any data = 1;
}

PHP开发的同学可能觉得Any没必要,因为数组里任何类型都可以放,但是对于强类型语言,数组里的值类型必须是一致的,使用Any类型可以解决这个问题。Any相当于把值包装了一层,这样都是Any类型。

11、服务定义

Copy
service UserService {
 // 方法名 方法参数 返回值
 rpc GetUser(Request) returns (Response); 
}

这相当于定义了一个类,里面有一个对外的GetUser()方法。这个通常用于定义RPC服务,与gRPC结合使用。

12、从.proto文件生成了什么?

当用protocol buffer编译器来运行.proto文件时,编译器将生成所选择语言的代码,这些代码可以操作在.proto文件中定义的消息类型,包括获取、设置字段值,将消息序列化到一个输出流中,以及从一个输入流中解析消息。

  • PHP:每一个Message或者Enum生成一个类,另外还会生成GPBMetadata。
  • C++:编译器会为每个.proto文件生成一个.h文件和一个.cc文件,.proto文件中的每一个消息有一个对应的类。
  • Java:编译器为每一个消息类型生成了一个.java文件,以及一个特殊的Builder类(该类是用来创建消息类接口的)。
  • Python:Python编译器为.proto文件中的每个消息类型生成一个含有静态描述符的模块,该模块与一个元类(metaclass)在运行时(runtime)被用来创建所需的Python数据访问类。
  • go:编译器会位每个消息类型生成了一个.pd.go文件。
  • Ruby:编译器会为每个消息类型生成了一个.rb文件。
  • Objective-C:编译器会为每个消息类型生成了一个pbobjc.h文件和pbobjcm文件,.proto文件中的每一个消息有一个对应的类。
  • C#:编译器会为每个消息类型生成了一个.cs文件,.proto文件中的每一个消息有一个对应的类。

其它

IDE插件

1、JetBrains PhpStorm 可以在插件里找到Protobuf安装,重启IDE后就支持proto格式语法了。

2、VScode 在扩展里搜索 Protobuf,安装即可。

3、protobuf的 php 扩展类在ide中没有提示,可将https://github.com/protocolbuffers/protobuf/tree/master/php/src 目录下载到本地,将此目录加到ide的include_path中即可。

常见问题

1、 protoc 编译输出php文件时遇到一个错误:protobuf google.protobuf.Any not found。

原因:安装proto的时候没有把include/google复制到/usr/include/。

解决:重新下载protoc-3.8.0-linux-x86_64.zip并将解压后的include/google复制到/usr/include/。

2、Mac下执行phpize报如下错误:

Copy
grep: /usr/include/php/main/php.h: No such file or directory
grep: /usr/include/php/Zend/zend_modules.h: No such file or directory
grep: /usr/include/php/Zend/zend_extensions.h: No such file or directory

解决方法:

Copy
xcode-select --instal

参考

1、protoc2 与 protoc3 区别 - 简书

https://www.jianshu.com/p/cdedcf696e9e

2、gRPC之proto语法 - 简书

https://www.jianshu.com/p/da7ed5914088

3、Protobuf3语法详解 - 望星辰大海 - 博客园

https://www.cnblogs.com/tohxyblog/p/8974763.html

本文首发于公众号"飞鸿影的博客(fhyblog)",欢迎关注。博客地址:https://52fhy.cnblogs.com

(本文完)

相关推荐

3分钟让你的项目支持AI问答模块,完全开源!

hello,大家好,我是徐小夕。之前和大家分享了很多可视化,零代码和前端工程化的最佳实践,今天继续分享一下最近开源的Next-Admin的最新更新。最近对这个项目做了一些优化,并集成了大家比较关注...

干货|程序员的副业挂,12个平台分享

1、D2adminD2Admin是一个完全开源免费的企业中后台产品前端集成方案,使用最新的前端技术栈,小于60kb的本地首屏js加载,已经做好大部分项目前期准备工作,并且带有大量示例代码,助...

Github标星超200K,这10个可视化面板你知道几个

在Github上有很多开源免费的后台控制面板可以选择,但是哪些才是最好、最受欢迎的可视化控制面板呢?今天就和大家推荐Github上10个好看又流行的可视化面板:1.AdminLTEAdminLTE是...

开箱即用的炫酷中后台前端开源框架第二篇

#头条创作挑战赛#1、SoybeanAdmin(1)介绍:SoybeanAdmin是一个基于Vue3、Vite3、TypeScript、NaiveUI、Pinia和UnoCSS的清新优...

搭建React+AntDeign的开发环境和框架

搭建React+AntDeign的开发环境和框架随着前端技术的不断发展,React和AntDesign已经成为越来越多Web应用程序的首选开发框架。React是一个用于构建用户界面的JavaScrip...

基于.NET 5实现的开源通用权限管理平台

??大家好,我是为广大程序员兄弟操碎了心的小编,每天推荐一个小工具/源码,装满你的收藏夹,每天分享一个小技巧,让你轻松节省开发效率,实现不加班不熬夜不掉头发,是我的目标!??今天小编推荐一款基于.NE...

StreamPark - 大数据流计算引擎

使用Docker完成StreamPark的部署??1.基于h2和docker-compose进行StreamPark部署wgethttps://raw.githubusercontent.com/a...

教你使用UmiJS框架开发React

1、什么是Umi.js?umi,中文可发音为乌米,是一个可插拔的企业级react应用框架。你可以将它简单地理解为一个专注性能的类next.js前端框架,并通过约定、自动生成和解析代码等方式来辅助...

简单在线流程图工具在用例设计中的运用

敏捷模式下,测试团队的用例逐渐简化以适应快速的发版节奏,大家很早就开始运用思维导图工具比如xmind来编写测试方法、测试点。如今不少已经不少利用开源的思维导图组件(如百度脑图...)来构建测试测试...

【开源分享】神奇的大数据实时平台框架,让Flink&amp;Spark开发更简单

这是一个神奇的框架,让Flink|Spark开发更简单,一站式大数据实时平台!他就是StreamX!什么是StreamX大数据技术如今发展的如火如荼,已经呈现百花齐放欣欣向荣的景象,实时处理流域...

聊聊规则引擎的调研及实现全过程

摘要本期主要以规则引擎业务实现为例,陈述在陌生业务前如何进行业务深入、调研、技术选型、设计及实现全过程分析,如果你对规则引擎不感冒、也可以从中了解一些抽象实现过程。诉求从硬件采集到的数据提供的形式多种...

【开源推荐】Diboot 2.0.5 发布,自动化开发助理

一、前言Diboot2.0.5版本已于近日发布,在此次发布中,我们新增了file-starter组件,完善了iam-starter组件,对core核心进行了相关优化,让devtools也支持对IAM...

微软推出Copilot Actions,使用人工智能自动执行重复性任务

IT之家11月19日消息,微软在今天举办的Ignite大会上宣布了一系列新功能,旨在进一步提升Microsoft365Copilot的智能化水平。其中最引人注目的是Copilot...

Electron 使用Selenium和WebDriver

本节我们来学习如何在Electron下使用Selenium和WebDriver。SeleniumSelenium是ThoughtWorks提供的一个强大的基于浏览器的开源自动化测试工具...

Quick &#39;n Easy Web Builder 11.1.0设计和构建功能齐全的网页的工具

一个实用而有效的应用程序,能够让您轻松构建、创建和设计个人的HTML网站。Quick'nEasyWebBuilder是一款全面且轻巧的软件,为用户提供了一种简单的方式来创建、编辑...