PythonGoogleプロトコルバッファ

この記事では、主にPython言語でGoogle Protocol Buffer(以下、PBと略します)を使用する方法を紹介します。

なぜPBを使用するのですか? #

PB(Protocol Buffer)は、Tencent Cloud LogServiceの標準的な書き込み形式としてGoogleが開発した構造化データ交換形式です。したがって、ログデータの書き込みに使用する前に、元のログデータをPBデータストリームにシリアル化してから、APIを介してサーバーに書き込む必要があります。ただし、各エンドクラスプログラムでPB形式を操作するのは不便であるため、エンドクラスとログサービスの間にPB変換レイヤーを追加する必要があります。

もちろん、PB形式には、主にシンプルで高速な独自の利点もあります。具体的なテスト結果については、[Googleシリアル化ベンチマーク分析](https://github.com/eishay/jvm-serializers/wiki)を参照してください。

Google PBをインストールする#

PythonでPBを使用する場合は、PBコンパイラprotocをインストールして.protoファイルをコンパイルする必要があります。インストール方法は次のとおりです。

最新のprotobufリリースパッケージをダウンロードしてインストールします。現在のバージョンは3.5.1です。インストール手順は次のとおりです。

wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.tar.gz
tar xvfz protobuf-all-3.5.1.tar.gz
cd protobuf-3.5.1/./configure --prefix=/usr
make
make check
make install

すべてのチェックステップに合格すると、コンパイルに合格したことになります。

protobufのpythonモジュールのインストールを続行します

cd ./python 
python setup.py build 
python setup.py test 
python setup.py install

protocコマンドを確認するためのインストールが完了しました

root@ubuntu:~# protoc --version
libprotoc 3.5.1

protobufのデフォルトのインストール場所は/ usr / localであり、/ usr / local / libはUbuntuシステムのデフォルトのLD_LIBRARY_PATHにありません。Ubuntuシステムでの構成中にインストールパスが / usrとして指定されていない場合、次のエラーが発生します。

protoc: error while loading shared libraries: libprotoc.so.8: cannot open shared object file: No such file or directory

ldconfigコマンドを使用して解決できます。[Protobufが共有ライブラリを見つけることができません](https://stackoverflow.com/questions/25518701/protobuf-cannot-find-shared-libraries)を参照してください。このエラーは、インストールパッケージのREADMEに記載されています。もちろん、再インストールできます

Pythonモジュールが正しくインストールされていることを確認します

import google.protobuf

上記のインポートでpythonインタープリターでエラーが報告されない場合、インストールは正常です。

カスタム.protoファイル#

まず、プログラムで処理する必要のある構造化データを定義するために、protoファイルを作成する必要があります。protobufの用語では、構造化データはメッセージと呼ばれます。 protoファイルは、javaまたはC ++言語のデータ定義と非常によく似ています。プロトサンプルファイル cls.Log.protoは次のとおりです。

syntax ="proto2";package cls;
message Log
{
 optional uint64 time =1;// UNIX Time Format
 required string topic_id =2;
 required string content =3;}

. protoファイルの先頭は、さまざまなプロジェクトでの名前の競合を防ぐためのパッケージの宣言です。 Pythonでは、パッケージは通常ディレクトリ構造によって決定されるため、この.protoファイルによって定義されたパッケージは実際のPythonコードには影響しません。ただし、公式の推奨事項は、このステートメントの宣言を主張することです。主な機能は、PBネームスペースでの名前の競合を防ぐことです。パッケージ名はclsで、メッセージログを定義します。メッセージには3つのメンバーがあり、各メンバーの意味は次のとおりです。

フィールド名 タイプ 場所 必須 意味
time uint64 body No log time(指定されていない場合)、サーバーが要求を受信した時刻が使用されます
topic_id string body Yes ログによって報告されたログトピックのID
content string body is log content

プロトファイルのファイル名を真剣に受け止めるのが良い習慣です。たとえば、命名規則は次のように設定されます: packageName.MessageName.proto

.protoファイル#をコンパイルします

コンパイラプロトコルを使用して直接コンパイルします。ソースファイルパスとターゲットファイルパスを指定する必要があります

SRC_DIR=/tmp/src_dir
DST_DIR=/tmp/dst_dir
protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/cls.Log.proto

Pythonクラスを生成するときは --python_outオプションを使用し、C ++クラスを生成するときは --cpp_outオプションを使用します

ターゲットpyファイルを解析します#

ターゲットフォルダに生成されたファイルディレクトリは、次のものに対応します。

root@ubuntu:/tmp/dst_dir# tree
.
└── cls
 └── Log_pb2.py

1 directory,1 file

Log_pb2.pyファイルの内容は次のとおりです(編集は許可されていません)。

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: cls.Log.proto

import sys
_ b=sys.version_info[0]<3and(lambda x:x)or(lambda x:x.encode('latin1'))from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
# @@ protoc_insertion_point(imports)

_ sym_db = _symbol_database.Default()

DESCRIPTOR = _descriptor.FileDescriptor(
 name='cls.Log.proto',package='cls',
 syntax='proto2',
 serialized_pb=_b('\n\rcls.Log.proto\x12\x03\x63ls\"6\n\x03Log\x12\x0c\n\x04time\x18\x01 \x01(\x04\x12\x10\n\x08topic_id\x18\x02 \x02(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x02(\t'))

_ LOG = _descriptor.Descriptor(
 name='Log',
 full_name='cls.Log',
 filename=None,
 file=DESCRIPTOR,
 containing_type=None,
 fields=[
 _ descriptor.FieldDescriptor(
  name='time', full_name='cls.Log.time', index=0,
  number=1, type=4, cpp_type=4, label=1,
  has_default_value=False, default_value=0,
  message_type=None, enum_type=None, containing_type=None,
  is_extension=False, extension_scope=None,
  options=None, file=DESCRIPTOR),
 _ descriptor.FieldDescriptor(
  name='topic_id', full_name='cls.Log.topic_id', index=1,
  number=2, type=9, cpp_type=9, label=2,
  has_default_value=False, default_value=_b("").decode('utf-8'),
  message_type=None, enum_type=None, containing_type=None,
  is_extension=False, extension_scope=None,
  options=None, file=DESCRIPTOR),
 _ descriptor.FieldDescriptor(
  name='content', full_name='cls.Log.content', index=2,
  number=3, type=9, cpp_type=9, label=2,
  has_default_value=False, default_value=_b("").decode('utf-8'),
  message_type=None, enum_type=None, containing_type=None,
  is_extension=False, extension_scope=None,
  options=None, file=DESCRIPTOR),],
 extensions=[],
 nested_types=[],
 enum_types=[],
 options=None,
 is_extendable=False,
 syntax='proto2',
 extension_ranges=[],
 oneofs=[],
 serialized_start=22,
 serialized_end=76,)

DESCRIPTOR.message_types_by_name['Log']= _LOG
_ sym_db.RegisterFileDescriptor(DESCRIPTOR)

Log = _reflection.GeneratedProtocolMessageType('Log',(_message.Message,),dict(
 DESCRIPTOR = _LOG,
 __ module__ ='cls.Log_pb2'
 # @@ protoc_insertion_point(class_scope:cls.Log)))
_ sym_db.RegisterMessage(Log)

# @@ protoc_insertion_point(module_scope)

pbによって生成されたpyファイルのソースコードの分析は一時的に棚上げされます。添付の情報を参照できます。

シリアル化と逆シリアル化#

#! /usr/bin/env python
# - *- coding: utf-8-*-"""
Created on 1/30/184:23 PM
@ author: Chen Liang
@ function: pb test
"""

import sys

reload(sys)
sys.setdefaultencoding('utf-8')import Log_pb2
import json

def serialize_to_string(msg_obj):
 ret_str = msg_obj.SerializeToString()return ret_str

def parse_from_string(s):
 log = Log_pb2.Log()
 log.ParseFromString(s)return log

if __name__ =='__main__':
 # serialize_to_string
 content_dict ={"live_id":"1239182389648923","identify":"zxc_unique"}
 tencent_log = Log_pb2.Log()
 tencent_log.time =1510109254
 tencent_log.topic_id ="John Doe"
 tencent_log.content = json.dumps(content_dict)
 ret_s =serialize_to_string(tencent_log)print(type(ret_s))print(ret_s)

 # parse_from_string
 log_obj =parse_from_string(ret_s)print(log_obj)

重要な操作は、メッセージオブジェクトの書き込みと読み取り、シリアル化関数 SerializeToStringと逆シリアル化関数 ParseFromStringです。

より複雑なメッセージ#

これまでのところ、ログをアップロードする簡単な例のみを示してきました。実際のアプリケーションでは、多くの場合、より複雑なメッセージを定義する必要があります。 「複雑」という言葉は、数の観点からより多くのフィールドまたはより多くのタイプのフィールドを指すだけでなく、より複雑なデータ構造を指すために使用します。

以下は別途紹介します

メッセージのネスト##

ネスティングは魔法の概念です。ネスティング機能があれば、メッセージを表現する機能は非常に強力になります。ネストされたメッセージの具体例は次のとおりです

message Person { 
 required string name =1; 
 required int32 id =2;// Unique ID number for this person. 
 optional string email =3;enum PhoneType { 
 MOBILE =0; 
 HOME =1; 
 WORK =2;} 
 
 message PhoneNumber { 
 required string number =1; 
 optional PhoneType type =2[default= HOME];} 
 repeated PhoneNumber phone =4;}

Message Personでは、ネストされたメッセージPhoneNumberが定義され、Personメッセージの電話フィールドを定義するために使用されます。これにより、より複雑なデータ構造を定義できます。

Import Message

.protoファイルでは、Importキーワードを使用して、他の.protoファイルで定義されているメッセージをインポートすることもできます。これはImportMessageまたはDependencyMessageと呼ばれます。特定のインポートメッセージの例は次のとおりです

import common.header; 
 
message youMsg{ 
 required common.info_header header =1; 
 required string youPrivateData =2;}

その中で、 common.info_header common.headerパッケージで定義されています。

Import Messageの主な目的は、C言語のヘッダーファイルと同様に、便利なコード管理メカニズムを提供することです。パッケージにいくつかの一般的なメッセージを定義してから、他の.protoファイルにパッケージをインポートして、その中でメッセージ定義を使用することができます。

Google Protocol Bufferは、ネストされたメッセージとメッセージの導入を適切にサポートできるため、複雑なデータ構造を定義する作業が非常に簡単で快適になります。

動的コンパイル#

通常の状況では、Protobufを使用する人は、最初に.protoファイルを作成し、次にProtobufコンパイラを使用して、ターゲット言語に必要なソースコードファイルを生成します。これらの生成されたコードをアプリケーションでコンパイルします。

ただし、特定の状況では、人々は.protoファイルを事前に知ることができず、いくつかの未知の.protoファイルを動的に処理する必要があります。たとえば、一般的なメッセージ転送ミドルウェアでは、処理する必要のあるメッセージの種類を予測できません。これには、.protoファイルを動的にコンパイルし、その中でメッセージを使用する必要があります。

詳細な説明については、[Googleプロトコルバッファの使用と原則](https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/)を参照してください。

参照:

  1. https://developers.google.com/protocol-buffers/docs/reference/python/
  2. https://developers.google.com/protocol-buffers/docs/reference/python-generated
  3. http://hzy3774.iteye.com/blog/2323428
  4. https://github.com/google/protobuf/tree/master/python
  5. https://github.com/google/protobuf/tree/master/examples
  6. https://blog.csdn.net/losophy/article/details/17006573
  7. https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/
  8. https://github.com/google/protobuf
  9. https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.tar.gz
  10. Python Google Protocol Buffer: https://developers.google.com/protocol-buffers/docs/pythontutorial

Recommended Posts

PythonGoogleプロトコルバッファ
Python関数バッファー
GooglePythonプログラミングスタイルガイド