1. 程式人生 > 其它 >Redis都做了信無雙註冊哪些“變態”設計

Redis都做了信無雙註冊哪些“變態”設計

image.png

使用pro 安信6信無雙指定註冊地址 tobuf主要是兩個步驟,序列化和反序列化。 使用protobuf主要是兩個步驟,序列化和反序列化

關於Proto有哪些資料型別,然後如何編寫,此處就不贅述了,百度一下有很多。

此文主要是總結,python使用protobuf的過程,如何序列化和反序列化,對不同型別的欄位如何進行賦值。

下面將一一列舉各資料型別,在python中如何正確賦值。

首先,得把編譯包給匯入

import test_pb2 as pb

我分為兩部分,分別為未被repeated修飾的欄位 和 被repeated修飾後的欄位

無修飾符#

字串#

test.proto

message SearchService {

    string type = 1;

}

建立message物件,然後賦值即可。與python中,通過類建立例項,例項.屬性的方式進行賦值類似

search_service = pb.SearchService()
search_service.type = "request"

數字型#

test.proto

message SearchService {

    int32 id = 2;

}

與字串賦值一致

search_service = pb.SearchService()
search_service.id = 1

Message#

test.proto

message SearchService {

    // 定義一個message型別
    message SearchRequest {
    
        string content = 1;
        
        string keyword = 2;
    
    }
    
    //   型別         欄位名       序號
    SearchRequest searchRequest = 3;

}

我們看到在SearchService裡序號為3的欄位的型別為SearchRequest,這是我們新定義的message

如果把message看作是一個類,那麼我將其例項化,然後賦值給對應的欄位,可以嗎?

ok,這是不行的,錯誤示例:

search_service = pb.SearchService()
# 例項化SearchRequest
search_request = pb.SearchService.SearchRequest()
# 為search_request內部欄位賦值
search_request.content = "hello protobuf"
search_request.keyword = "mk"
# 為search_service的searchRequest欄位賦值
search_service.searchRequest = search_request

image-20210116211914264

不允許在協議訊息物件中分配複合字段“searchRequest”。

正確示例:

import test_pb2 as pb

search_service.searchRequest.content = "hello protobuf!"
search_service.searchRequest.keyword = "mk"

如果加上之前的那個欄位,那麼這樣的:

import test_pb2 as pb

search_service.type = "request"
search_service.id = 1
search_service.searchRequest.content = "hello protobuf!"
search_service.searchRequest.keyword = "mk"

Enum#

列舉型別,注意一點:必須包含一個含0的欄位

image-20210116212524288

test.proto

syntax = "proto3";

message SearchService {

    enum SearchType {

        A = 0;

        B = 1;
        
    }

    SearchType searchType = 4;

}

序號為4,型別為SearchType的列舉類,名為searchType的欄位

此處的列舉型別,你可以看作是網頁中的單選框,只能從給定的資料中選擇一個,不選則預設為0

# 手動選擇1
search_service.searchType = 1

# 或者是根據欄位名進行選擇
search_service.searchType = pb.SearchService.SearchType.A

被repeated修飾的欄位#

字串或數字#

test.proto

syntax = "proto3";

message SearchService {
    
    # 修飾符  型別  欄位名 序號
    repeated int32 uid = 5;
    
}    

uid的型別是int32,然後被repeated修飾,即這個欄位是可重複賦值的。

那麼,在python中應該怎麼賦值呢?

錯誤示例:

search_service.uid = 0

如果還是和之前一樣的賦值,就會報錯

AttributeError: Assignment not allowed to repeated field "uid" in protocol message object.

正確示例:

search_service.uid.append(1)
search_service.uid.append(2)

所以,你可以將被repeated修飾的欄位看作是一個空列表,往裡新增值即可!

Message#

test.proto

syntax = "proto3";

message SearchService {
    
    message Second {
        
        string type = 1;
        
        string word = 2;
        
    }
    
    repeated Second seconds = 6;
    
}    

seconds欄位是可重複的message型別,在python中該如何賦值?

# 例項化一個second
second = search_service.Second()
# 為second物件賦值
second.type = 'abc'
second.word = 'world'
# 新增至seconds列表中
search_service.seconds.append(second)

或者,你也可以這樣

search_service.seconds.append(
    search_service.Second(type='efg', word="world")
)

或者這樣:

seconds = [
    search_service.Second(type='1', word="world"),
    search_service.Second(type='2', word="world")
]
search_service.seconds.extend(seconds)

所以,repeated修飾的欄位,在python中,就是一個列表

Enum#

test.ptoto

syntax = "proto3";

message SearchService {
    
    enum SortOrder {
        
        key1 = 0;
        
        key2 = 1;
        
        key3 = 2;
        
    }
    
    repeated SortOrder sortOrder = 7;
    
}

使用方法與之前的完全一致

sortFields = [
    # 此處key1 根據關鍵詞,獲取列舉值
    search_service.SortOrder.key1,
    search_service.SortOrder.key2
]
search_service.sortOrder.extend(sortFields)

現在我們已經全部賦值好了,接著就是序列化了

b = search_service.SerializeToString()

print(b)

# b'\n\x07request\x10\x01\x1a\x15\n\x0fhello protobuf!\x12\x02mk 
# \x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg
# \x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'

SerializeToString

Serializes the protocol message to a binary string.

序列化此協議訊息為二進位制串

反序列化#

現在,我們是接收方,我們收到了一串二進位制。

首先,我們需要使用將其反序列化,同樣使用編譯包。

search_service = pb.SearchService()
b = b'\n\x07request\x10\x01\x1a\x15\n\x0fhello protobuf!\x12\x02mk \x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg\x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'
search_service.ParseFromString(b)
# 訪問屬性值
print(search_service.type) # 輸出:request

ParseFromString解析函式

此時,search_service就已經含有傳輸過來的全部資料了。如果你不想使用物件.屬性的方式呼叫,或者想使用類似json.loads直接轉為python中的字典,那麼你可以使用protobuf_to_dict將其轉為字典。

安裝protobuf3_to_dict`

pip install protobuf3_to_dict
# 呼叫
from protobuf_to_dict import protobuf_to_dict 

b = b'\n\x07request\x10\x01\x1a\x15\n\x0fhello protobuf!\x12\x02mk \x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg\x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'
search_service.ParseFromString(b)
# print(search_service.type)
d = protobuf_to_dict(search_service)
print(d, type(d))

# {'type': 'request', 'id': 1, 'searchRequest': {'content': 'hello protobuf!', 'keyword': 'mk'}, 'searchType': 2, 'uid': [1, 2], 'seconds': [{'type': 'abc', 'word': 'world'}, {'type': 'efg', 'word': 'world'}, {'type': '1', 'word': 'world'}, {'type': '2', 'word': 'world'}], 'sortOrder': [0, 1]} <class 'dict'>

小小嚐試#

本文中例子,我做了一個介面。

介面地址:http://47.101.154.110:8000/

image.png

使用protobuf主要是兩個步驟,序列化和反序列化。

關於Proto有哪些資料型別,然後如何編寫,此處就不贅述了,百度一下有很多。

此文主要是總結,python使用protobuf的過程,如何序列化和反序列化,對不同型別的欄位如何進行賦值。

序列化#

下面將一一列舉各資料型別,在python中如何正確賦值。

首先,得把編譯包給匯入

importtest_pb2aspb

我分為兩部分,分別為未被repeated修飾的欄位 和 被repeated修飾後的欄位

無修飾符#

字串#

test.proto

messageSearchService{

stringtype=1;

}

建立message物件,然後賦值即可。與python中,通過類建立例項,例項.屬性的方式進行賦值類似

search_service=pb.SearchService()
search_service.type="request"

數字型#

test.proto

messageSearchService{

int32id=2;

}

與字串賦值一致

search_service=pb.SearchService()
search_service.id=1

Message#

test.proto

messageSearchService{

//定義一個message型別
messageSearchRequest{

stringcontent=1;

stringkeyword=2;

}

//型別欄位名序號
SearchRequestsearchRequest=3;

}

我們看到在SearchService裡序號為3的欄位的型別為SearchRequest,這是我們新定義的message

如果把message看作是一個類,那麼我將其例項化,然後賦值給對應的欄位,可以嗎?

ok,這是不行的,錯誤示例:

search_service=pb.SearchService()#例項化SearchRequestsearch_request=pb.SearchService.SearchRequest()#為search_request內部欄位賦值search_request.content="helloprotobuf"search_request.keyword="mk"#為search_service的searchRequest欄位賦值search_service.searchRequest=search_request

image-20210116211914264

不允許在協議訊息物件中分配複合字段“searchRequest”。

正確示例:

importtest_pb2aspb

search_service.searchRequest.content="helloprotobuf!"search_service.searchRequest.keyword="mk"

如果加上之前的那個欄位,那麼這樣的:

importtest_pb2aspb

search_service.type="request"search_service.id=1search_service.searchRequest.content="helloprotobuf!"search_service.searchRequest.keyword="mk"

Enum#

列舉型別,注意一點:必須包含一個含0的欄位

image-20210116212524288

test.proto

syntax="proto3";

messageSearchService{

enumSearchType{

A=0;

B=1;

}

SearchTypesearchType=4;

}

序號為4,型別為SearchType的列舉類,名為searchType的欄位

此處的列舉型別,你可以看作是網頁中的單選框,只能從給定的資料中選擇一個,不選則預設為0

#手動選擇1search_service.searchType=1#或者是根據欄位名進行選擇search_service.searchType=pb.SearchService.SearchType.A

被repeated修飾的欄位#

字串或數字#

test.proto

syntax="proto3";

messageSearchService{

#修飾符型別欄位名序號
repeatedint32uid=5;

}

uid的型別是int32,然後被repeated修飾,即這個欄位是可重複賦值的。

那麼,在python中應該怎麼賦值呢?

錯誤示例:

search_service.uid=0

如果還是和之前一樣的賦值,就會報錯

AttributeError: Assignment not allowed to repeated field "uid" in protocol message object.

正確示例:

search_service.uid.append(1)
search_service.uid.append(2)

所以,你可以將被repeated修飾的欄位看作是一個空列表,往裡新增值即可!

Message#

test.proto

syntax="proto3";

messageSearchService{

messageSecond{

stringtype=1;

stringword=2;

}

repeatedSecondseconds=6;

}

seconds欄位是可重複的message型別,在python中該如何賦值?

#例項化一個secondsecond=search_service.Second()#為second物件賦值second.type='abc'second.word='world'#新增至seconds列表中search_service.seconds.append(second)

或者,你也可以這樣

search_service.seconds.append(
search_service.Second(type='efg',word="world")
)

或者這樣:

seconds=[
search_service.Second(type='1',word="world"),
search_service.Second(type='2',word="world")
]
search_service.seconds.extend(seconds)

所以,repeated修飾的欄位,在python中,就是一個列表

Enum#

test.ptoto

syntax="proto3";

messageSearchService{

enumSortOrder{

key1=0;

key2=1;

key3=2;

}

repeatedSortOrdersortOrder=7;

}

使用方法與之前的完全一致

sortFields=[#此處key1根據關鍵詞,獲取列舉值
search_service.SortOrder.key1,
search_service.SortOrder.key2
]
search_service.sortOrder.extend(sortFields)

現在我們已經全部賦值好了,接著就是序列化了

b=search_service.SerializeToString()

print(b)#b'\n\x07request\x10\x01\x1a\x15\n\x0fhelloprotobuf!\x12\x02mk#\x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg#\x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'

SerializeToString

Serializes the protocol message to a binary string.

序列化此協議訊息為二進位制串

反序列化#

現在,我們是接收方,我們收到了一串二進位制。

首先,我們需要使用將其反序列化,同樣使用編譯包。

search_service=pb.SearchService()
b=b'\n\x07request\x10\x01\x1a\x15\n\x0fhelloprotobuf!\x12\x02mk\x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg\x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'search_service.ParseFromString(b)#訪問屬性值print(search_service.type)#輸出:request

ParseFromString解析函式

此時,search_service就已經含有傳輸過來的全部資料了。如果你不想使用物件.屬性的方式呼叫,或者想使用類似json.loads直接轉為python中的字典,那麼你可以使用protobuf_to_dict將其轉為字典。

安裝protobuf3_to_dict`

pipinstallprotobuf3_to_dict
#呼叫fromprotobuf_to_dictimportprotobuf_to_dict

b=b'\n\x07request\x10\x01\x1a\x15\n\x0fhelloprotobuf!\x12\x02mk\x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg\x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'search_service.ParseFromString(b)#print(search_service.type)d=protobuf_to_dict(search_service)
print(d,type(d))#{'type':'request','id':1,'searchRequest':{'content':'helloprotobuf!','keyword':'mk'},'searchType':2,'uid':[1,2],'seconds':[{'type':'abc','word':'world'},{'type':'efg','word':'world'},{'type':'1','word':'world'},{'type':'2','word':'world'}],'sortOrder':[0,1]}<class'dict'>

小小嚐試#

本文中例子,我做了一個介面。

介面地址:http://47.101.154.110:8000/

請求頭請求體請求方式
必須指定Content-Type: application/grpc-web+proto序列化後的二進位制POST

你可以使用postman提交資料,來檢視結果

456

也可以使用Python傳送請求

importrequests

headers={'Content-Type':'application/grpc-web+proto'}

b=b'\n\x07request\x10\x01\x1a\x15\n\x0fhelloprotobuf!\x12\x02mk\x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg\x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'resp=requests.post('http://47.101.154.110:8000/',data=b,headers=headers)

print(resp.text)

完整的test.proto

syntax="proto3";

messageSearchService{

stringtype=1;

int32id=2;

//定義一個message型別
messageSearchRequest{

stringcontent=1;

stringkeyword=2;

}

//型別欄位名序號
SearchRequestsearchRequest=3;

enumSearchType{

A=0;

B=1;

}

SearchTypesearchType=4;

repeatedint32uid=5;

messageSecond{

stringtype=1;

stringword=2;

}

repeatedSecondseconds=6;

enumSortOrder{

key1=0;

key2=1;

key3=2;

}

repeatedSortOrdersortOrder=7;

}

完整的賦值示例

importtest_pb2aspbfromprotobuf_to_dictimportprotobuf_to_dict


search_service=pb.SearchService()


search_service.type="request"search_service.id=1search_service.searchRequest.content="helloprotobuf!"search_service.searchRequest.keyword="mk"#search_service.searchType=pb.SearchService.SearchType.Asearch_service.searchType=2search_service.uid.append(1)
search_service.uid.append(2)

second=search_service.Second()
second.type='abc'second.word='world'search_service.seconds.append(second)

search_service.seconds.append(search_service.Second(type='efg',word="world"))

seconds=[
search_service.Second(type='1',word="world"),
search_service.Second(type='2',word="world")
]
search_service.seconds.extend(seconds)

sortFields=[
search_service.SortOrder.key1,
search_service.SortOrder.key2
]
search_service.sortOrder.extend(sortFields)


b=search_service.SerializeToString()

print(b)

推薦模組#

在使用編譯包時,沒有程式碼提示,還有點不習慣。

這裡,推薦安裝mypy-protobuf

pipinstallmypy-protobuf

使用方法:

在你使用protoc命令編譯proto檔案時,新增一個引數mypy-out=,就像這樣

protoc--python_out=.--mypy-out=.test.proto

此時會生成兩個檔案,並將他們拖入專案中的同一目錄

test_pb2.py:我們需要匯入使用的編譯包

test_pb2.pyi:存根檔案,在編輯器中會有程式碼提示(想了解存根檔案,可以看最下面的參考文章)

效果演示:

演示

你可以使用postman提交資料,來檢視結果

456

也可以使用Python傳送請求

import requests

headers = {
    'Content-Type': 'application/grpc-web+proto'
}

b = b'\n\x07request\x10\x01\x1a\x15\n\x0fhello protobuf!\x12\x02mk \x02*\x02\x01\x022\x0c\n\x03abc\x12\x05world2\x0c\n\x03efg\x12\x05world2\n\n\x011\x12\x05world2\n\n\x012\x12\x05world:\x02\x00\x01'

resp = requests.post('http://47.101.154.110:8000/', data=b, headers=headers)

print(resp.text)

完整的test.proto

syntax = "proto3";

message SearchService {

    string type = 1;

    int32 id = 2;

    // 定義一個message型別
    message SearchRequest {
    
        string content = 1;
        
        string keyword = 2;
    
    }
    
    //   型別         欄位名       序號
    SearchRequest searchRequest = 3;

    enum SearchType {

        A = 0;

        B = 1;

    }

    SearchType searchType = 4;

    repeated int32 uid = 5;

    message Second {

        string type = 1;

        string word = 2;

    }

    repeated Second seconds = 6;

    enum SortOrder {

        key1 = 0;

        key2 = 1;

        key3 = 2;

    }

    repeated SortOrder sortOrder = 7;

}

完整的賦值示例

import test_pb2 as pb
from protobuf_to_dict import protobuf_to_dict


search_service = pb.SearchService()


search_service.type = "request"
search_service.id = 1
search_service.searchRequest.content = "hello protobuf!"
search_service.searchRequest.keyword = "mk"
# search_service.searchType = pb.SearchService.SearchType.A
search_service.searchType = 2

search_service.uid.append(1)
search_service.uid.append(2)

second = search_service.Second()
second.type = 'abc'
second.word = 'world'
search_service.seconds.append(second)

search_service.seconds.append(search_service.Second(type='efg', word="world"))

seconds = [
    search_service.Second(type='1', word="world"),
    search_service.Second(type='2', word="world")
]
search_service.seconds.extend(seconds)

sortFields = [
    search_service.SortOrder.key1,
    search_service.SortOrder.key2
]
search_service.sortOrder.extend(sortFields)


b = search_service.SerializeToString()

print(b)

推薦模組#

在使用編譯包時,沒有程式碼提示,還有點不習慣。

這裡,推薦安裝mypy-protobuf

pip install mypy-protobuf

使用方法:

在你使用protoc命令編譯proto檔案時,新增一個引數mypy-out=,就像這樣

protoc --python_out=. --mypy-out=. test.proto

此時會生成兩個檔案,並將他們拖入專案中的同一目錄

test_pb2.py:我們需要匯入使用的編譯包

test_pb2.pyi:存根檔案,在編輯器中會有程式碼提示(想了解存根檔案,可以看最下面的參考文章)

效果演示:

演示