问题:
目前在项目中采用 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 May 19, 2024
用 grpc 定义两个 service?
|
2
InDom May 19, 2024 | ver | route id | body len | body |
然后 body 才是你的 Protobuf 结构。 或者 | ver | route len | route str | body len | body | |
3
uiosun OP |
4
cloud107202 May 19, 2024 抛弃路由的概念,用 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 May 19, 2024 第二种是直接用 pb 的高阶用法,oneof 字段。参考这里 https://zhuanlan.zhihu.com/p/453913153 例子,可以避开对 bytes 的 raw byte manipulation. 有兴趣研读 pb 文档的话,我推荐第二种
|
6
cloud107202 May 19, 2024 针对你的困扰出发,核心就是这也许是你头一次针对 websocket 场景编程。这里跟 HTTP 的语义封装没关系,尤其是没有请求-响应的通讯模式,没有路由的概念。先定义消息类型(完全由你自行定义),把消息发跟收分开处理就好,各自独立
|
7
kiracyan May 19, 2024 建议用 2 个 key 区分功能 内容用 bytebuff
|
8
Nazz May 19, 2024 via Android
在消息头用两个字节(uint16)标识路由
|
9
Nazz May 19, 2024 via Android 用字符串标识路由更好些,开头的两个字节表示路由长度. ws 库可以使用 gws, 它的 payload 是 bytes.buffer 类型.
|
10
sunny352787 May 19, 2024 via Android 对于你的技术储备,我建议直接 grpc 最省事,有切割二进制流的功夫 grpc 写好多功能了
|
11
kuanat May 19, 2024 via Android 这种通信场景一般没有路由的说法吧,都是用协议这个词。
如楼上说得都挺好了。我有个建议,你可以看看用 unix domain socket 做 IPC 通信一般是怎么做的。ws 就是把本地变远程,protobuf 就是 socket 的具体实现(协议/路由)。 在 web 编程里是匹配路由然后把请求交给对应的 handler ,在 socket 编程里硬要说路由或者协议的话就是某个字节代表特定的类型,然后每个类型有一个专门的 handler 来响应。 |
12
gamexg May 19, 2024 路由简单,
如前面回复,在包内容前面加点路由字段就行. 但是其他麻烦还有很多, 计划是否允许并行请求(前一个接口 1 请求未响应就发送新的接口 2 请求)? 如果允许并行请求,那么能处理响应顺序和请求顺序不一致吗? 另一个情况,比如发送消息,第一个消息还没返回响应,第二个消息又发送了. 那么之后收到的响应可能是第一个也可能是第二个的响应.虽然加个 id 也能处理,但是加上超时/连接断开重发请求等情况会很麻烦. 自己去实现这些很麻烦. |
13
uiosun OP @cloud107202 @kiracyan @Nazz @kuanat 谢谢大佬们,你们好强!
@gamexg @sunny352787 也谢谢两位大佬,你们也好强!这个项目是学点新东西,所以不介意费事。 我先读读 gRPC 流的知识,看能不能快速实现我的需求。再次感谢各位! |
14
Nazz May 20, 2024 做 IM 应该用 WebSocket 而不是 gRPC Stream, io.Reader 切割二进制流很简单的, 发送成功确认做起来麻烦些
|
15
Nazz May 20, 2024
对性能没有高要求的话应该使用 JSON
|
16
Nazz May 20, 2024
或者 MsgPack
|