为什么需要 sidecar
稍微大一点的公司,不管架构图画的多好看,内部可能都是下面这样的现状:
如果想在公司内新做一套配置推送系统,本来只需要做一遍的 sdk 工作就会需要乘以语言数量:
程序员也不傻,总还是有解决办法的:
出自 《Designing Distributed Systems》。
狗坐的位置就是 sidecar(笑
总之,我们把自己的工作的大头集中在了 sidecar 里,SDK 的工作虽没有完全省掉,但已被极大简化,只剩下比如:
-
读取文件,替换内存中的 map
-
向本地的 sidecar 发一个 http 请求。
是在一个面试的时间内就能完成的代码。热更新相关的知识:
-
无锁双 buffer
-
fsnotify
为什么需要 mesh
嗯,你说为什么呢?
服务网格(Service Mesh)是处理服务间通信的基础设施层。它负责构成现代云原生应用程序的复杂服务拓扑来可靠地交付请求。在实践中,Service Mesh 通常以轻量级网络代理阵列的形式实现,这些代理与应用程序代码部署在一起,对应用程序来说无需感知代理的存在。
有了 mesh 以后,变成了这样:
功能没有消失,只是从 sdk 搬家到了独立的外部进程。
从工程角度来讲,下沉的好处:
-
语言相关的轮子变成了语言无关的公共轮
-
业务团队忙业务,基础社区团队忙基础设施,模块解耦,分工细化
-
不用求你的客户来升级了,自己就能搞定。爸爸变回了同事。
mesh 的麻烦
协议太多
就像 RPC 框架万年统一不了一样,一个公共的流量层就需要去对接五花八门的协议:
-
HTTP
-
HTTP/2
-
HTTP/3
-
dubbo
-
sofarpc
-
gRPC
-
thrift
如果一个公司内存在多种 RPC 协议,可能会比较痛苦。但我们可以给我们认识的协议简单分个类。根据协议的交互方式,可以分为 pingpong,和 multiplex 两种类型。
pingpong
请求发送完毕后,需要阻塞等待响应。一个请求的响应处理完才能发送下一个请求。典型的比如:
-
HTTP
-
redis 协议
-
MySQL 协议
这种模型比较简单,实现客户端也简单:
encode payload → write data to connection → read block until data ready → decode
multiplex
向连接发送请求完毕后,不用等待响应就可以继续发送请求。
对于客户端来说,比以前复杂了,请求和响应处理不是顺序流程,所以一般会有一个 map 来记录请求和响应的关系:
type http2ClientConn struct {
....
streams map[uint32]*http2clientStream // 就是这里
nextStreamID uint32
....
}
响应解析完毕后,用 stream id 去 map 里找到对应的 request 并唤醒相应的 client。
这种响应模型在 Go 里还是比较容易想到怎么做的,我们只要在 stream 对象里放一个 channel 就行了:
type http2clientStream struct {
....
resc chan http2resAndError // 就是这里
....
}
其它类型的 RPC 协议和 http1,http2 其实没什么本质区别。
除了交互方式有差不多,具体的协议设计其实都差不了多少。
mosn 这样的 mesh 项目是怎么落地的
项目一出生就上 istio 的贼船的话,那你这辈子都上不了线了。
比较聪明的做法,在一个模块里把控制面和数据面的工作都先集成了。让公司原有的服务发现能平滑融入到新的 mesh 模块中:
continuous profiling
Google 2010 的老论文:https://research.google/pubs/pub36575/,和一篇更说人话的文章,https://www.opsian.com/blog/what-is-continuous-profiling/,对 continuous profiling 进行了说明。
Google 把这种 continuous profiling 包装成产品在 Google Cloud 上卖钱,不过还是好心地给出了文章:https://medium.com/google-cloud/continuous-profiling-of-go-programs-96d4416af77b,还有 client 端的代码: https://github.com/googleapis/google-cloud-go/tree/master/profiler
continuous profiling 能给我们带来的益处:
快速定位性能恶化问题
模块上线过程中,从 metrics 里发现突然炸了。这时候通常的做法是回滚,在预发环境研究为什么新版本上线导致爆炸。
这个过程是很浪费时间的,并且导致问题的原因又不一定复现得出来(比如因为你和线上机器的负载不一样,环境有细微差别)。
定位不出来你就没法上线,不上线问题又复现不了,尴尬不尴尬。
在线上系统进行实时问题定位同样麻烦,即使你能拿到当前出问题的 profile,没有历史的 profile 做对比,你看不出来异常情况和正常情况有什么差别,因为通过一个 profile 文件,你不知道某个 dag 图上的节点到底是正常的还是异常的。(即使用 pprof 的 --base 来做 diff,其实也还是挺麻烦的,公司的线上实例想 dump 要各种权限审批。)