iT邦幫忙

0

有關golang使用protobuf進行反序列化

  • 分享至 

  • xImage

想請教一下,我現在在學習使用golang websocket跟protobuf使用架設server,目前遇到問題就是protobuf直接限定了發送跟接收的struct結構,可是應該我要使用websocket接收而不是用grpc所以導致了我不能每次都用特定的格式去接收[]byte,想問一下有什麼方法可以不特定結構的反序列化接收的data呢?

我所有設定的struct的request裡面都有一個變數叫做messageCode,想利用這個來分辨,以下是我的proto檔
https://ithelp.ithome.com.tw/upload/images/20230518/20160296iLjzxFC68J.png

//補充
抱歉,可能是我表達得還不夠清楚,我已經寫到接收的部分了,以上面給的proto檔來說,目前接收的request就兩種,Ping跟GetDataRequesthttps://ithelp.ithome.com.tw/upload/images/20230519/20160296TiXoKHV1dW.png
問題是在proto.Unmarshal這裡,如果我request傳的是Ping,那我Unmarshal的結構就一定要用Ping來接才能反序列化成功,可是問題是我有可能傳Ping也有可能傳GetDataRequest,如果在傳GetDataRequest的話反序列化就會失敗,我找了很久都沒有找到一個通用的結構兩個都可以反序列化成功,又沒有辦法只提取出messageCode來確認是什麼。

froce iT邦大師 1 級 ‧ 2023-05-19 08:13:57 檢舉
看不懂你的問題,但gRPC和websocket從根本上就是兩個通信協定,應該是互不相關。
不是使用grpc,而是websocket傳送的資料格式使用protobuf的方式去定義
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 個回答

0
whitefloor
iT邦研究生 2 級 ‧ 2023-05-19 10:57:33

看了一下感覺可以啊
我的理解是你想用protobuf當IDL
然後用websocket當通訊協定溝通
所以

  1. marshal data,return type 是 []byte
data, err := proto.Marshal(s)
if err != nil {
	log.Fatal(err)
}
  1. websocket write msg,傳入的 data 剛好接收 []byte
c, _, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8899/echo", nil)
if err != nil {
	log.Fatal("dial:", err)
}
defer c.Close()

err = c.WriteMessage(websocket.TextMessage, data)
if err != nil {
	log.Println(err)
	return
}
  1. 要接收在 unmarshal 就反過來而已

https://jiepeng.me/2018/07/25/protocol-buffers-intro
https://ithelp.ithome.com.tw/articles/10208531

看更多先前的回應...收起先前的回應...

抱歉,可能是我表達得還不夠清楚,我已經寫到接收的部分了,以上面給的proto檔來說,目前接收的request就兩種,Ping跟GetDataRequesthttps://ithelp.ithome.com.tw/upload/images/20230519/20160296TiXoKHV1dW.png
問題是在proto.Unmarshal這裡,如果我request傳的是Ping,那我Unmarshal的結構就一定要用Ping來接才能反序列化成功,可是問題是我有可能傳Ping也有可能傳GetDataRequest,如果在傳GetDataRequest的話反序列化就會失敗,我找了很久都沒有找到一個通用的結構兩個都可以反序列化成功,又沒有辦法只提取出messageCode來確認是什麼。

froce iT邦大師 1 級 ‧ 2023-05-19 13:24:33 檢舉

看能不能用泛型(go >= 1.18)解決,不能你就用個interface{}去接,然後接到函數內做類型判斷。

https://alankrantas.medium.com/%E7%B0%A1%E5%96%AE%E7%8E%A9-go-1-18-%E6%B3%9B%E5%9E%8B-1d09da07b70

兩個我都試過了,都沒辦法的樣子,一直卡在所謂的protouface.MessageV1https://ithelp.ithome.com.tw/upload/images/20230519/2016029616b1v9DqnX.png

https://ithelp.ithome.com.tw/upload/images/20230519/201602963a6tuMKsbU.png

froce iT邦大師 1 級 ‧ 2023-05-19 14:50:25 檢舉

你interface{}接收完要自己去做類型斷言/反射,去判斷類型啊,直接丟到Unmarshal當然會出錯。
泛型我是不知道Unmarshal有沒有支援,有的話你前面也得加類型,反正你都離不開類型判斷。

另外實務上這種不同參數類型要共用一條路由的狀況很少,了不起就是optional value的差別,通常會開另外一條路由去專一處理,因為這違反SOLID。
你function會希望功能專一,路由當然也會希望這樣,網頁系統的路由請你當function name+parameter這樣寫,因為看網頁系統第一步一定是看路由。

whitefloor iT邦研究生 2 級 ‧ 2023-05-19 16:37:56 檢舉

我直接講結論,沒人會照你的情境這樣寫API,用錯方法
一個API就是對到一個struct
不會有人寫一個API還要判斷是哪種struct去做unmarshal的
API功能都已經確定了,怎麼可能會有兩種以上的struct包含在一起?
除了將定義好的struct透過embed的方式不算。

你想做的東西是動態解析,而且場景只適用於你不知道input是什麼,卻要解析成你的struct,json package就是在做這件事

或是你想透過像query的方式處理根據field處理input/output,那就是GraphQL

我有寫過短連接用的API所以我可以理解一個API對應到一個struct

目前的情況是前端傳給我的資料就只有一個object,不像呼叫api就只傳對應的struct,如果是這樣當然好判斷

而在使用websocket的情況下一直都是同一條連線,而且沒有指定要打哪個API這件事情,,我只能自己接收資料去判斷這個object的messageCode要執行哪個函式,感覺是websocket部分我還不夠熟。

以前的做法是使用json接收資料沒錯,所以unmarshal後不管其他資料至少messageCode是可以得到讓我判斷,但現在要求換成protobuffer來傳輸我這邊就卡住了

froce iT邦大師 1 級 ‧ 2023-05-22 10:00:25 檢舉
whitefloor iT邦研究生 2 級 ‧ 2023-05-22 12:00:44 檢舉

這連結看起來是可以用,不過限制也很多
https://github.com/gogo/protobuf/blob/master/custom_types.md
而且還要用json在unmarshal,這樣用protobuf的優點就消失了啊XD
我是在想如果已經確定了N種資料類型的話
定義proto file的時候寫embed會不會比較簡單?

froce iT邦大師 1 級 ‧ 2023-05-22 14:46:25 檢舉

因為不知道他用的框架是啥,但我猜他的問題websocket一樣可以用route去做,以gin來說分拆出 path parameter 對對應的command做單一struct的對應,這只是在websocket handler 做個switch case的事。
原生的話應該是比較麻煩一點。
其實我上上一個回覆就是在提醒他該從router著手。

或是像你說的,在定義的時候把這種需求考慮進去,弄成embed的。

比較糟的作法是用泛型/interface{}、或是像上面的動態解析json的方式。
這種通常是後端沒法溝通的時候才會做的事,但以protobuf的正常的使用場景,應該很少機會會去用到這個終極手段才對。

我要發表回答

立即登入回答