goc/doc/design/protocol.md

104 lines
3.3 KiB
Markdown
Raw Normal View History

2021-06-03 15:30:01 +00:00
# 通信协议设计
## 背景
v1 版本中,被插桩的服务会暴露一个 HTTP 接口,由 goc server 来访问获取覆盖率。
该方式要求被插桩的服务要有一个外界可访问的 ip + port。
如果被测服务躲在 NAT 网络下,该方式就不可行了,典型的就是被测服务由 docker 拉起,而 goc server 部署在另外的网络。
## 新设计选择
2021-06-25 14:43:57 +00:00
新设计只需要暴露 goc server 的地址,由插桩服务发起连接,然后保持长连接,在该长连接上构建 goc 自己的业务逻辑。
2021-06-03 15:30:01 +00:00
### 自行设计 TCP 应用层协议
go 语言做网络编程非常适合,非阻塞地处理“粘包”也不麻烦。但设计出来不管是纯二进制的、还是类似 HTTP 的,都不会是通用协议,后续维护和扩展估计是个大坑。
2021-06-20 14:29:13 +00:00
### websocket + net/rpc
`websocket + jsonrpc2` 的区别就是没有流式调用,纯 rpc 调用。
在这种模式下agent 和 goc server 的角色和 rpc 中的角色并不对应。agent 是 rpc server goc server 是 rpc client。由于 websocket 是长连接,上述角色的倒换是可行的。
1. goc server 发起获取覆盖率 rpcagent 响应 rpcgoc server 汇总覆盖率
2. watch 模式下agent 再开一条 websocket 连接到 goc server这条连接中角色不再颠倒goc server 就是 rpc server
2021-06-03 15:30:01 +00:00
### websocket + jsonrpc2
websocket + jsonrpc2 有流式调用,消息边界。非常适合
我找到 `github.com/goriila/websocket``github.com/sourcegraph/jsonrpc2` 库,后者 import 了前者,前者没有 import 任何其它外部库,全是标准库。把两个库的代码合并一下:`github.com/lyyyuna/jsonrpc2` 就是一个无任何外部库引用的 jsonrpc 实现。
这非常适合由插桩代码来使用,因为该库没有再引用其它库,**不会污染原服务的依赖关系**。
### gRPC
老实说 gRPC 在这里更适合作为通信协议来使用,更快更通用,流式调用也有,上一小节的 `github.com/sourcegraph/jsonrpc2` 使用广度就很低。
但 gRPC 的 go 实现有一个很大的缺点,用了一些非标准库,且有版本依赖。我们不清楚原服务是不是有特定 gRPC 要求,或是 goc 插入的 gRPC 库会导致编译依赖冲突,或者是编译后运行冲突。
所以不适合。
### 结论
2021-06-20 14:29:13 +00:00
先使用 websocket + net/rpc 来做吧。
2021-06-03 15:30:01 +00:00
## 协议内容
### 注册
2021-09-10 03:20:05 +00:00
agent 首先通过以下 url 完成注册:
2021-06-20 14:29:13 +00:00
```
2021-09-10 03:20:05 +00:00
GET /internal/register?cmdline=.%2Fcmd&hostname=nuc&pid=1699804
2021-06-20 14:29:13 +00:00
```
注册信息为:
1. 完整的命令行
2. hostname
3. 进程 PID
2021-09-10 03:20:05 +00:00
goc server 会生成一个自增 id (全局唯一) 和 token (全局唯一) 返回给 agent。
### websocket 连接
agent 通过以下 url 发起 websocket 连接:
```
/internal/ws/rpcstream?id=[id]&token=[token]
/internal/ws/watchstream?id=[id]&token=[token]
```
分别代表 rpc 通道和 watch 通道。
服务端会校验 id 和 token 是否存在及是否匹配。不匹配返回错误。
agent 收到错误会重新注册。
2021-06-20 14:29:13 +00:00
2021-06-03 15:30:01 +00:00
### 获取覆盖率
2021-06-20 14:29:13 +00:00
```
GocAgent.GetProfile
ProfileReq: getprofile
```
2021-06-03 15:30:01 +00:00
### 清空覆盖率
2021-06-20 14:29:13 +00:00
```
GocAgent.ResetProfile
ProfileReq: resetprofile
```
2021-06-03 15:30:01 +00:00
### watch
2021-06-20 14:29:13 +00:00
### 异常处理
2021-09-10 03:20:05 +00:00
1. goc server 端遇到 err 就关闭对应 agent 的 websocket 连接。
2. goc server 会将 id 和 token 持久化重启后agent 再次上报时可直接校验,若成功保持 id 不变。