一、調用步驟:
將go代碼編譯成so庫 -> python中通過ctypes引用so庫並指定需要調用的函數(同時可指定傳入參數類型和返回值類型) -> 指定后按python使用函數方式調用。
需要注意的是:python和go之間參數傳遞是需要經過C的數據類型轉換的,因此需要了解python中ctypes數據類型和python數據類型以及C的數據類型對應關系
三種數據類型使用場景:1. ctypes數據類型為指定調用函數時的傳入參數和返回值的數據類型
2. python數據類型為調用函數時傳入的參數的數據類型
3. C的數據類型為go代碼中定義的函數所需的參數和返回值數據類型
類型對應如下:文檔傳送地址
由此舉例:當python傳入的參數需是string時,ctypes中指定的傳參參數類型需為c_wchar_p,go中需要指定接收的參數數據類型為 *C.wchar_t。
由於不知道go中如何將字符串和*C.wchar_t互相轉化,因此我這里將python傳入的參數指定為bytes,即ctypes中指定的傳參參數類型需為c_char_p,go中需要指定接收的參數數據類型為*C.char。
二、下面開始實踐:
1. 編寫go代碼
寫一個rocketmq的producer函數(main.go),封裝成Send函數如下:
1 package main 2 3 import ( 4 "C" 5 "context" 6 "github.com/apache/rocketmq-client-go/v2" 7 "github.com/apache/rocketmq-client-go/v2/primitive" 8 "github.com/apache/rocketmq-client-go/v2/producer" 9 "os" 10 ) 11 12 var ( 13 nameservs = []string{"192.168.2.1:9876"} 14 group = "demo.xy" 15 topic = "test" 16 ) 17 18 //export Send 19 func Send(cid, message *C.char) *C.char { 20 p, err := rocketmq.NewProducer( 21 producer.WithNsResolver(primitive.NewPassthroughResolver(nameservs)), 22 producer.WithRetry(2), 23 producer.WithGroupName(group), 24 ) 25 if err != nil { 26 return C.CString("create producer failed") 27 os.Exit(-1) 28 } 29 30 err = p.Start() 31 if err != nil { 32 return C.CString("start producer failed") 33 os.Exit(-1) 34 } else { 35 defer p.Shutdown() 36 } 37 38 msg := &primitive.Message{ 39 Topic: topic, 40 Body: []byte(C.GoString(message)), 41 } 42 msg.WithTag(C.GoString(cid)) 43 44 _, err = p.SendSync(context.Background(), msg) 45 if err != nil { 46 return C.CString("producer send message failed") 47 } else { 48 return C.CString("producer send message success") 49 } 50 } 51 52 func main() {}
需要注意:1). Go里面將C的char指針類型數據通過C.GoString()轉化成go中的字符串;反之通過C.CString()將go中的字符串轉化為C的char指針類型數據。具體類型轉化方式可查閱相關文檔(待補充)
2). python需要調用的函數必須在函數上方用 //export [函數名稱] 加上說明,不然python中會報AttrbuteError錯誤(symbol not found)
2. 將go代碼編譯成so動態鏈接庫
go build --buildmode=c-shared -o producer.so main.go
編譯好后會生成兩個文件:producer.so和producer.h
3. python中導入so文件並調用對應的函數
1 import ctypes 2 import json 3 4 # 指定go中的Send函數 5 SendSync = ctypes.CDLL("./producer.so").Send 6 # 指定調用函數時傳入的參數數據類型 7 SendSync.argtypes = [ctypes.c_char_p, ctypes.c_char_p] 8 # 指定調用函數返回值的數據類型 9 SendSync.restype = ctypes.c_char_p 10 11 cid = "123454321" 12 message = { 13 "cid": "123454321", 14 "cname": "指南朝北槍", 15 "age": 18, 16 "height": 1.88 17 } 18 19 result = SendSync(cid.encode("utf-8"), json.dumps(message).encode("utf-8")) 20 print(result, type(result))
需要注意的是:1). python2中的字節串是str;字符串是unicode。因此如果是python2調用SendSync函數時不需要使用encode,直接傳入str即可
2). python3中字節串是bytes;字符串是str。因此調用SendSync函數時需要將字符串str通過encode轉換成bytes
3). 指定的參數類型和傳入參數類型一定要一致,否者報ctypes.ArgumentError錯誤(wrong type)