この記事では、主にPython言語でGoogle Protocol Buffer(以下、PBと略します)を使用する方法を紹介します。
PB(Protocol Buffer)は、Tencent Cloud LogServiceの標準的な書き込み形式としてGoogleが開発した構造化データ交換形式です。したがって、ログデータの書き込みに使用する前に、元のログデータをPBデータストリームにシリアル化してから、APIを介してサーバーに書き込む必要があります。ただし、各エンドクラスプログラムでPB形式を操作するのは不便であるため、エンドクラスとログサービスの間にPB変換レイヤーを追加する必要があります。
もちろん、PB形式には、主にシンプルで高速な独自の利点もあります。具体的なテスト結果については、[Googleシリアル化ベンチマーク分析](https://github.com/eishay/jvm-serializers/wiki)を参照してください。
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ファイルを作成する必要があります。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
コンパイラプロトコルを使用して直接コンパイルします。ソースファイルパスとターゲットファイルパスを指定する必要があります
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
オプションを使用します
ターゲットフォルダに生成されたファイルディレクトリは、次のものに対応します。
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メッセージの電話フィールドを定義するために使用されます。これにより、より複雑なデータ構造を定義できます。
.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/)を参照してください。
参照: