问题:
目前在项目中采用 WebSocket 长连接做服务,Protobuf 作为请求/响应的数据结构。
当存在多个路由时,如何定义它们呢?
举个例子:聊天室
接口 1:(按条件)筛选聊天内容 接口 2:发送消息
请教各位,长连接服务在这种场景,如何与客户端通讯(怎么定义路由,或者如果不定义,有什么其他办法吗?)
谢谢各位!
背景:我对于 Protobuf 、长连接的使用经验比较匮乏,之前只做过 HTTP/RPC + Protobuf 的项目。
我的思路历程:
最简单的就是 Protobuf 封装一个标准路由结构:
message IRoute {
string key = 1;
string jsonRes = 2;
}
key
做路由键,jsonRes
就是 JSON 字符串,只有响应时才返回纯 Protobuf 对象,但这就买椟还珠了吧?(不如直接用 JSON )
后面又想定义 Interface 来设计,先将数据转化为标准对象,然后判断 key
来将数据转化为实际路由,但对二进制数据,我做不到只转化部分内容……
最后,目前打算使用 text 作为路由,自己手动分割,然后响应时返回 Protobuf 对象。
1
ryc111 216 天前
用 grpc 定义两个 service?
|
2
InDom 216 天前 1
| ver | route id | body len | body |
然后 body 才是你的 Protobuf 结构。 或者 | ver | route len | route str | body len | body | |
3
uiosun OP |
4
cloud107202 216 天前 1
抛弃路由的概念,用 pb 定义消息结构就好。举个例子
message FetchChatHistoryRequest(id, count, start, end, e.g.) message FetchChatHistoryResponse(repeat string xxx) message SendChatMsg(string target, string content) 在实现里收到消息,解析类型,派发给对应 Type 的逻辑做业务逻辑处理,他们的逻辑当是独立离散的。 理解这一层之后,收发两端都有个需要,就是识别一个 raw bytes 比如 Java 语言会接收到 byte[] 作为消息包,要知道它具体是什么。 这里有两种思路: 第一是像二楼这样,外层用 TCP 的 TV 或 TLV 来包装一下,就是 type-length-value 这种。前两个字段一定要定长,比如 type 是 4byte 的数字类型,自己给上述消息定义好类型 id 。lenght 是 8byte 的长整形,数值是后面 value 部分的长度。value 里就是 pb 消息,encoded pb bytes 。 自己写个简单的 encoding / decoding 逻辑 |
5
cloud107202 216 天前 1
第二种是直接用 pb 的高阶用法,oneof 字段。参考这里 https://zhuanlan.zhihu.com/p/453913153 例子,可以避开对 bytes 的 raw byte manipulation. 有兴趣研读 pb 文档的话,我推荐第二种
|
6
cloud107202 216 天前 1
针对你的困扰出发,核心就是这也许是你头一次针对 websocket 场景编程。这里跟 HTTP 的语义封装没关系,尤其是没有请求-响应的通讯模式,没有路由的概念。先定义消息类型(完全由你自行定义),把消息发跟收分开处理就好,各自独立
|
7
kiracyan 216 天前 1
建议用 2 个 key 区分功能 内容用 bytebuff
|
8
Nazz 216 天前 via Android
在消息头用两个字节(uint16)标识路由
|
9
Nazz 216 天前 via Android 2
用字符串标识路由更好些,开头的两个字节表示路由长度. ws 库可以使用 gws, 它的 payload 是 bytes.buffer 类型.
|
10
sunny352787 216 天前 via Android 1
对于你的技术储备,我建议直接 grpc 最省事,有切割二进制流的功夫 grpc 写好多功能了
|
11
kuanat 216 天前 via Android 1
这种通信场景一般没有路由的说法吧,都是用协议这个词。
如楼上说得都挺好了。我有个建议,你可以看看用 unix domain socket 做 IPC 通信一般是怎么做的。ws 就是把本地变远程,protobuf 就是 socket 的具体实现(协议/路由)。 在 web 编程里是匹配路由然后把请求交给对应的 handler ,在 socket 编程里硬要说路由或者协议的话就是某个字节代表特定的类型,然后每个类型有一个专门的 handler 来响应。 |
12
gamexg 216 天前 2
路由简单,
如前面回复,在包内容前面加点路由字段就行. 但是其他麻烦还有很多, 计划是否允许并行请求(前一个接口 1 请求未响应就发送新的接口 2 请求)? 如果允许并行请求,那么能处理响应顺序和请求顺序不一致吗? 另一个情况,比如发送消息,第一个消息还没返回响应,第二个消息又发送了. 那么之后收到的响应可能是第一个也可能是第二个的响应.虽然加个 id 也能处理,但是加上超时/连接断开重发请求等情况会很麻烦. 自己去实现这些很麻烦. |
13
uiosun OP @cloud107202 @kiracyan @Nazz @kuanat 谢谢大佬们,你们好强!
@gamexg @sunny352787 也谢谢两位大佬,你们也好强!这个项目是学点新东西,所以不介意费事。 我先读读 gRPC 流的知识,看能不能快速实现我的需求。再次感谢各位! |
14
Nazz 215 天前 2
做 IM 应该用 WebSocket 而不是 gRPC Stream, io.Reader 切割二进制流很简单的, 发送成功确认做起来麻烦些
|
15
Nazz 215 天前
对性能没有高要求的话应该使用 JSON
|
16
Nazz 215 天前
或者 MsgPack
|