HTTP Channel 与 GRPC Channel

在开始之前,让我们探索一下 HTTP 和 RPC 的区别。这里讨论 gRPC 是因为已经没有人用普通的 RPC 啦。
HTTP 是常见的业务编码使用的通信方式,普及程度不用多说,作为一个 Web 程序员,HTTP Server 编程是其核心技能。RPC 在微服务中也不可或缺。
那么对于熟悉其中一个编码的程序员,是否能够快速上手另一种传输方式的业务编码呢?

毕竟业务逻辑是一致的,看起来只是网络传输上有所区别。
做过的同学会知道,在业务编码上差异还不小,尽管在设计时通过抽象层级,会将不同约束在传输层上,然而并没有一个框架来在实现上屏蔽其不同之处。因此,编码的同学反而要深入,自行处理。
举例就是还要多学习 protoc, proto 文件,学习如何给请求,返回值编码,如何在业务中用特定的 protobuf 去解析消息包。

抽象层级上可以屏蔽的差异,在实现上还是继续去编写细节差异,这就是一部分程序员可以被框架取代的操作。

Top 程序员与入门、新手、白痴编码员

框架的作用在于不用思考也能做出正确编码行为。

对于现行的语言,进行各类型的编码有足够的生态库帮助编写,而缺少时,还可以通过参照其他语言移植一些生态减少工作量。然而这样的行为并不是每一个程序员都能随时做到的。
想必不能指望遇到问题查 CSDN,甚至 SO,CSDN 都不知道的程序员完成现代应用程序编程。
Google is a friend。而且业务编码经常会因为不知道的事情而陷入困境,以至于不管技能或智力如何,都没法解决问题。

业务上会将一些编码工作比喻成搬砖,程序员被形容成将代码从这里搬到那里的体力工种,然而有人参与的环节,错误概率还会与人的状态好坏,通过墨菲定律可以认知到错误在这些环节是必定发生的。如何减少个人的决策以保证流水线编码人力的高质量高产出?

要把自己当做白痴编码员,容易出错的地方交给工具,框架因此产生了巨大的效益。

没有什么是建立在石头上的;一切都建立在沙子上,但我们必须把沙子当作石头来建造。

——豪尔赫·路易斯·博尔赫斯

以下为几个方面的例子:

代码 review:架构师们不仅要制定流程标准,还需要监督执行,代码 review 是工作量的大头,然而千人千面,代码的编写者都有自己的想法,甚至会出现一个设计上内聚的功能,实现上散落在各个层级,review 时更是拖累效率。通过框架能进行约束,这也是软件工程的智慧,通过增加限制,制定标准,提供效率。

使用的最佳实践:业务代码通常使用的功能都是数据、目标资源的简单增删改查,同时会有一些共性的功能性要求,比如 JWT ,框架能屏蔽这些差异,比如 JWT 只是 HTTP 携带的 token 种类不同,ORM 给增删查改屏蔽了后台实际的数据存储软件接口。这是计算机科学的另一个智慧,通过增加一个中间层解决问题。框架的使用者不需要思考就能切换,加入不同的实现。

如果框架提供的最佳实践无法满足需求,就到文档展现作用的时候了。面向技术人员的文档,只有在出问题的时候才有用。

抽象层级与抽象泄露带来的微服务的进退维艰

《Google 软件工程 》中提到编程和软件工程之间的三个关键区别:时间、范围和权衡取舍
然而框架的理想足够美好,现实中的实现却充满了取舍和委曲求全。
即使排除掉业务侧特定工期的奇怪需求。设计上也并不能一蹴而就,完成一个完美的抽象设计。

“抽象泄漏”是软件开发时,本应隐藏实现细节的抽象化不可避免地暴露出底层细节与局限性。

更不要说一个完整系统也不止一两个层级叻,如何做出合理抽象,推广作为标准,在众多微服务框架和编码领域中都是长期实践和变更的问题。

抽象意味着统一,而抽象层级的背后通常意味着有差异特性的实际服务,取这些服务的并集还是交集做抽象?是考虑扩展兼容性还是考虑功能性。
这方面可以参考写的另一篇文章 Mongo Doc 接入设计,是实践上的经验。同时 Dapr 的 API。 Golang 的众多 Interface (IO、SQL、Net)都可以看到抽象的现实实践。

举一个概例来说,设计人员会在某个功能上纠结是否提供给外部,为此做了不少的工作提供出来,然而在功能使用上,其可能是伪需求,或者简单的屏蔽,然而实际场景中又非要 lower layer 的功能不可,抽象层级依旧被击穿。

讲到这里,大家其实明白,又落在了具体场景,具体问题具体分析上了,所以一个持久经过考研的微服务框架,一定在目标场景解决了不少问题了。
这让我想起研发人员总是追求新的技术,新的微服务框架通常都寄予厚望,希望他能完全解决之前实践中遇到但是旧框架无法解决的问题,最后期望常常落空。为什么能期望一个新的不经过考验的框架能满足经过了多次设计,多次实践修改的技术框架在特定领域的需求呢?

回到我们开篇的问题,是否有一个框架统一统一了 HTTP Channel 和 gRPC Channel ,只需要编写 handler 内部代码,无需关注其他工作呢?现代框架中,Dapr 确实完成了这一点。那么抽象的代价呢?则是丢失了 Protobuf 中的字段类型,统一认为是一种 payload,在 handler 中自行处理类型的不同,这样就和 HTTP 做出一致抽象了。
真的只是现在才想出这样的抽象层吗?对计算机科学了解深入一些,会发现一些过去的理念在新场景下大放异彩,时间正是最大的变数(举例:以前的程序员需要扣字节,现在个人 PC、云上编码还需要在意内存不足吗?)。

总结

以上讲述了经验中思考的微服务框架的相关问题,光提出问题是耍流氓,我的观点和建议在文中提到不少,做一个总结则是:
提供傻瓜式自动化的微服务框架,让程序员的搬砖工作做越少的决策越好,把节省的时间用来做业务环节的创新,业务模式的创新,不圉于环境搭建等非创造性工作的劳动,才会让工作者能感到创新的价值与自我成就。