程序性能優化之網絡傳輸與數據存儲優化(五)上

阿里P7移動互聯網架構師進階視頻(每日更新中)免費學習請點擊:space.bilibili.com/474380680java

本篇文章將先從google protobuf來介紹網絡傳輸與數據存儲優化:python

簡介

protobuf也叫protocol buffer是google 的一種數據交換的格式,它獨立於語言,獨立於平臺。google 提供了多種語言的實現:java、c#、c++、go 和 python,每一種實現都包含了相應語言的編譯器以及庫文件。因爲它是一種二進制的格式,比使用 xml 、json進行數據交換快許多。能夠把它用於分佈式應用之間的數據通訊或者異構環境下的數據交換。做爲一種效率和兼容性都很優秀的二進制數據傳輸格式,能夠用於諸如網絡傳輸、配置文件、數據存儲等諸多領域。 protobuf在各類rpc的實現上都佔據重要角色。linux

優勢

 性能好/效率高
 代碼生成機制
 支持「向後兼容」和「向前兼容」
 支持多種編程語言ios

缺點

 應用不夠廣(相比xml和json)
 二進制格式致使可讀性差
 缺少自描述c++

安裝

 github源代碼下載地址:github.com/google/prot… 源碼包中的src/README.md, 有詳細的安裝說明,安裝過程以下: 一、解壓壓縮包:unzip protobuf-master.zip 二、進入解壓後的文件夾:cd protobuf-master 三、安裝所需工具:sudo apt-get install autoconf automake libtool curl make g++ unzip 四、自動生成configure配置文件:./autogen.sh 五、配置環境:./configure 六、編譯源代碼(時間比較長):make 七、安裝:sudo make install 八、刷新動態庫:sudo ldconfiggit

編譯.proto文件

 protoc:protobuf自帶的編譯工具,將.proto文件生成指定的類
 –cpp_out:將生成的C++代碼文件放到等號後面指定的目錄,這裏也指定當前目錄github

經過protoc工具編譯.proto文件時,編譯器將生成所選擇語言的代碼,這些代碼能夠操做在.proto文件中定義的消息類型,包括獲取、設置字段值,將消息序列化到一個輸出流中,以及從一個輸入流中解析消息。對C++來講,編譯器會爲每一個.proto文件生成一個.h文件和一個.cc文件,.proto文件中的每個消息有一個對應的類。編程

編譯代碼

g++ *.cpp *.c *.cc `pkg-config --cflags --libs protobuf`
複製代碼

 反引號(` ):反引號的做用就是將反引號內的linux命令執行
 pkg-config 是經過庫提供的一個.pc文件得到庫的各類必要信息的,包括版本信息、編譯和鏈接須要的參數等。
 pkg-config –cflags protobuf:列出指定共享庫的預處理和編譯flags
 pkg-config –libs protobuf:列出指定共享庫的連接flagsjson

在.proto文件中定義消息格式

消息由至少一個字段組合而成,相似於C語言中的結構體,每一個字段都有必定的格式:c#

數據類型 字段名稱 = 惟一的編號標籤值;

syntax = "proto3"; //指定版本信息,不指定會報錯

message Person  //message爲關鍵字,做用爲定義一種消息類型
{
    string name = 1;    //姓名
    int32 id = 2;       //id
    string email = 3;   //郵件
}
複製代碼

數據類型

proto文件即消息協議原型定義文件,在該文件中咱們能夠經過使用描述性語言,來良好的定義咱們程序中須要用到數據格式。經過查看頭文件,能夠發現針對每一個字段都會大體生成以下幾種函數,以name爲例。能夠看出,對於每一個字段會生成一個clear清除函數(clear_name)、set函數(set_name)、get函數(name和mutable_name)。

void clear_name();
void set_name(const ::std::string& value);
void set_name(const char* value);
void set_name(const char* value, size_t size);
const ::std::string& name() const;
::std::string* mutable_name();
複製代碼

C數組的序列化和反序列化

#include <iostream>
#include "person.pb.h"

using namespace std;

int main()
{
    char buf[1024];
    int len;

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    Person obj;
    obj.set_name("gongluck");
    obj.set_id(1);
    *obj.mutable_email() = "http://blog.csdn.net/gongluck93";
    len = obj.ByteSize();
    cout << "len = " << len << endl;
    obj.SerializeToArray(buf, len);

    Person obj2;
    obj2.ParseFromArray(buf, len);
    cout << "name = " << obj2.name() << endl;
    cout << "id = " << obj2.id() << endl;
    cout << "email = " << obj2.email() << endl;

    google::protobuf::ShutdownProtobufLibrary();

    return 0;
}
複製代碼

C++ String的序列化和反序列化

#include <iostream>
#include "person.pb.h"

using namespace std;

int main()
{
    string str;

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    Person obj;
    obj.set_name("gongluck");
    obj.set_id(1);
    *obj.mutable_email() = "http://blog.csdn.net/gongluck93";
    obj.SerializeToString(&str);

    Person obj2;
    obj2.ParseFromString(str);
    cout << "name = " << obj2.name() << endl;
    cout << "id = " << obj2.id() << endl;
    cout << "email = " << obj2.email() << endl;

    google::protobuf::ShutdownProtobufLibrary();

    return 0;
}
複製代碼

文件描述符序列化和反序列化

#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "person.pb.h"

using namespace std;

int main()
{
    int fd = open("./testFileDesc.xxx", O_CREAT|O_TRUNC|O_RDWR, 0664);

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    Person obj;
    obj.set_name("gongluck");
    obj.set_id(1);
    *obj.mutable_email() = "http://blog.csdn.net/gongluck93";
    obj.SerializeToFileDescriptor(fd);
    fsync(fd);
    lseek(fd, 0, SEEK_SET);

    Person obj2;
    obj2.ParseFromFileDescriptor(fd);
    cout << "name = " << obj2.name() << endl;
    cout << "id = " << obj2.id() << endl;
    cout << "email = " << obj2.email() << endl;

    google::protobuf::ShutdownProtobufLibrary();

    return 0;
}
複製代碼

C++ stream 序列化和反序列化

#include <iostream>
#include <fstream>
#include "person.pb.h"

using namespace std;

int main()
{
    fstream file("testStream.xxx", ios::in|ios::out|ios::trunc|ios::binary);

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    Person obj;
    obj.set_name("gongluck");
    obj.set_id(1);
    *obj.mutable_email() = "http://blog.csdn.net/gongluck93";
    obj.SerializeToOstream(&file);
    file.flush();
    file.seekg(0, ios::beg);

    Person obj2;
    obj2.ParseFromIstream(&file);
    cout << "name = " << obj2.name() << endl;
    cout << "id = " << obj2.id() << endl;
    cout << "email = " << obj2.email() << endl;

    google::protobuf::ShutdownProtobufLibrary();

    file.close();

    return 0;
}
複製代碼

repeated限定修飾符

repeated 表明可重複,咱們能夠理解爲數組。

syntax = "proto3";

message Person
{
    string name = 1;
    int32 id = 2;
    string email = 3;
}

message AddressBook
{
    repeated Person people = 1;
}
複製代碼

而對於字段修飾符爲repeated的字段生成的函數,則稍微有一些不一樣,如people字段,則編譯器會爲其產生以下的代碼:

int people_size() const;
void clear_people();
const ::Person& people(int index) const;
::Person* mutable_people(int index);
::Person* add_people();
::google::protobuf::RepeatedPtrField< ::Person >* mutable_people();
const ::google::protobuf::RepeatedPtrField< ::Person >& people() const;
複製代碼

例子

#include <iostream>
#include <fstream>
#include "person.pb.h"

using namespace std;

int main()
{
    fstream file("testStream.xxx", ios::in|ios::out|ios::trunc|ios::binary);

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    AddressBook obj;

    Person* p1 = obj.add_people();
    p1->set_name("gongluck");
    p1->set_id(1);
    *(p1->mutable_email()) = "http://blog.csdn.net/gongluck93";

    Person* p2 = obj.add_people();
    p2->set_name("panzhikun");
    p2->set_id(2);
    *(p2->mutable_email()) = "panzhikun@gg.com";

    obj.SerializeToOstream(&file);
    file.flush();
    file.seekg(0, ios::beg);

    AddressBook obj2;
    obj2.ParseFromIstream(&file);
    for(int i= 0; i< obj.people_size(); ++i)
    {
        Person per = obj2.people(i);
        cout << "name = " << per.name() << endl;
        cout << "id = " << per.id() << endl;
        cout << "email = " << per.email() << endl;
    }

    google::protobuf::ShutdownProtobufLibrary();

    file.close();

    return 0;
}
複製代碼

枚舉

syntax = "proto3";

message Person
{
    string name = 1;
    int32 id = 2;
    string email = 3;

    enum PhoneType
    {
        MOBLIE = 0;//首成員必須爲0
        HOME = 1;
        WORK = 2;
    }
    message PhoneNumber
    {
        string number = 1;
        PhoneType type = 2;
    }
    repeated PhoneNumber phones = 4;
}

message AddressBook
{
    repeated Person people = 1;
}
複製代碼

·例子

#include <iostream>
#include <fstream>
#include "person.pb.h"

using namespace std;

int main()
{
    fstream file("testStream.xxx", ios::in|ios::out|ios::trunc|ios::binary);

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    AddressBook obj;

    Person* p1 = obj.add_people();
    p1->set_name("gongluck");
    p1->set_id(1);
    *(p1->mutable_email()) = "http://blog.csdn.net/gongluck93";

    Person::PhoneNumber* phone1 = p1->add_phones();
    phone1->set_number("110");
    phone1->set_type(Person::MOBLIE);
    Person::PhoneNumber* phone2 = p1->add_phones();
    phone2->set_number("120");
    phone2->set_type(Person::WORK);

    obj.SerializeToOstream(&file);
    file.flush();
    file.seekg(0, ios::beg);

    AddressBook obj2;
    obj2.ParseFromIstream(&file);
    for(int i= 0; i< obj.people_size(); ++i)
    {
        Person per = obj2.people(i);
        cout << "name = " << per.name() << endl;
        cout << "id = " << per.id() << endl;
        cout << "email = " << per.email() << endl;
        for(int j=0; j< per.phones_size(); ++j)
        {
            Person::PhoneNumber phonenum= per.phones(j);
            switch(phonenum.type())
            {
            case Person::MOBLIE:
                cout << "mobile : " ;
                break;
            case Person::WORK:
                cout << "work : ";
                break;
            case Person::HOME:
                cout << "home : ";
                break;
            default:
                cout << "Not Know : ";
                break;
            }
            cout << phonenum.number() << endl;
        }
    }

    google::protobuf::ShutdownProtobufLibrary();

    file.close();

    return 0;
}
複製代碼

 .proto文件新增一個可選的package聲明符,用來防止不一樣的消息類型有命名衝突。包的聲明符會根據使用語言的不一樣影響生成的代碼。對於C++,產生的類會被包裝在C++的命名空間中。

syntax = "proto3";

package Test;//package

message Person
{
    string name = 1;
    int32 id = 2;
    string email = 3;

    enum PhoneType
    {
        MOBLIE = 0;//首成員必須爲0
        HOME = 1;
        WORK = 2;
    }
    message PhoneNumber
    {
        string number = 1;
        PhoneType type = 2;
    }
    repeated PhoneNumber phones = 4;
}

message AddressBook
{
    repeated Person people = 1;
}
複製代碼

例子

using namespace Test;
#include <iostream>
#include <fstream>
#include "person.pb.h"

using namespace std;
using namespace Test;

int main()
{
    fstream file("testStream.xxx", ios::in|ios::out|ios::trunc|ios::binary);

    GOOGLE_PROTOBUF_VERIFY_VERSION;

    AddressBook obj;

    Person* p1 = obj.add_people();
    p1->set_name("gongluck");
    p1->set_id(1);
    *(p1->mutable_email()) = "http://blog.csdn.net/gongluck93";

    Person::PhoneNumber* phone1 = p1->add_phones();
    phone1->set_number("110");
    phone1->set_type(Person::MOBLIE);
    Person::PhoneNumber* phone2 = p1->add_phones();
    phone2->set_number("120");
    phone2->set_type(Person::WORK);

    obj.SerializeToOstream(&file);
    file.flush();
    file.seekg(0, ios::beg);

    AddressBook obj2;
    obj2.ParseFromIstream(&file);
    for(int i= 0; i< obj.people_size(); ++i)
    {
        Person per = obj2.people(i);
        cout << "name = " << per.name() << endl;
        cout << "id = " << per.id() << endl;
        cout << "email = " << per.email() << endl;
        for(int j=0; j< per.phones_size(); ++j)
        {
            Person::PhoneNumber phonenum= per.phones(j);
            switch(phonenum.type())
            {
            case Person::MOBLIE:
                cout << "mobile : " ;
                break;
            case Person::WORK:
                cout << "work : ";
                break;
            case Person::HOME:
                cout << "home : ";
                break;
            default:
                cout << "Not Know : ";
                break;
            }
            cout << phonenum.number() << endl;
        }
    }

    google::protobuf::ShutdownProtobufLibrary();

    file.close();

    return 0;
}
複製代碼

導入定義

syntax = "proto3";//指定版本信息,不指定會報錯

import "info.proto"; //導入定義

package tutorial; //package聲明符

message Person //message爲關鍵字,做用爲定義一種消息類型
{
    string name = 1;    //姓名
    int32 id = 2;       //id
    string email = 3; //郵件

    enum PhoneType //枚舉消息類型
    {
        MOBILE = 0; //proto3版本中,首成員必須爲0,成員不該有相同的值
        HOME = 1;
        WORK = 2;
    }

    message PhoneNumber
    {
        string number = 1;
        PhoneType type = 2;
    }

    repeated PhoneNumber phones = 4; //phones爲數組

    //info定義在"info.proto"
    //類型格式:包名.信息名
    infopack.info tmp = 5;
}

message AddressBook
{
    repeated Person people = 1;
}
複製代碼

原文連接 cloud.tencent.com/developer/a…
阿里P7移動互聯網架構師進階視頻(每日更新中)免費學習請點擊:space.bilibili.com/474380680

相關文章
相關標籤/搜索