2019年,当时计划用golang重写公司的Java api 网关, 网关用到了dubbo的泛化调用,底层是用hessian序列化协议, 但golang当时是没有比较稳定的实现,当时找到于雨实现的版本和viant/gohessian都存在bug, 于是就自己实现了一个版本, 并实现了golang api网关的功能,实测是原来Java网关3倍的性能,但因为诸多原因,网关也没有上线。 后来于雨找到我说一起去搞一个dubbo 的 golang hessian 开源版本 dubbo-go-hessian2,抱着对开源的热情便欣然同意了。
几年来一直断断续续不断重构改进该协议库,一有issue问题提出,就会去思考怎么解决。 有些问题并不好解决,但一直想着,总会想到解决方案。 有时候在睡梦中思路就想通了,抓着周末或下班时间赶紧修复改进。 一直维护到现在的原因,除了热情,另外就是责任。 想到有很多人在用这个库,出了问题是很着急的,可能要花比较多时间来熟悉,方案不一定考虑全面,还是自己花点时间解决比较妥当。
hessian协议本身是基于java设计的,夹带了一些java语言的特性。 hessian协议除了上手容易这个特点外,在与其他序列化协议比较中并不占优势,想着使用的人会慢慢减少。 但作为dubbo框架的默认序列号协议,随着dubbo的火爆,hessian协议也持续被广泛使用着,而且还有公司要继续去重新实现一遍golang hessian协议。 我想有必要整理一些之前设计遇到的问题和注意点,可以为大家做参考。
序列化协议库作为底层库,在API设计、特性功能设计上是要特别注意的,一旦发布了正式版本,则后续版本都要支持原有的设计,做到向后兼容。 底层库设计上的修改如果会影响上层应用,要求上层应用跟着改动,是会带来很大的民怨,是不符合开源软件的精神,对开源软件的发展也会带来不利的影响。
所以在设计上要谨慎,建议面向用户的API都应该准备好用户使用范例,经过团队讨论后,再最终敲定。 可以整理一份API列表、一份特性功能列表,这些设计将指引底层库细节设计的考虑,让编码、评审等工作目标更加明确。
比如定义对象名的方法名 JavaClassName
一旦确认了,后面都要继续支持使用该方法。
新增兼容方法可以解决问题,但会是实现变得复杂,同时会出现多重用法,给使用者增加了额外的认知负担。
特性范围也算是向后兼容要考虑的,单独列出来是因为对特性的选择两难的问题。 特性支持得多,对用户体验更友好,在用户选型时更容易拿高分。 但特性支持得多,底层设计越复杂,越容易出BUG。 要在使用体验和复杂性之间做权衡。 最好的选择是刚好满足用户的需求的同时底层设计又不那么复杂。
对于引用关系,Java里面是对象引用,Golang里面是通过指针,但golang里面有多层的指针。 hessian的golang实现要不要支持多层指针呢? 如果支持,对用户体验来说是更好,用户在struct定义上就没有指针层级的限制,但实现复杂度上了一个台阶,很多地方都要考虑。 也因为这个导致了很多issue。 如果不支持,绝大多数用户也不会用到,影响面不大,可以考虑作为协议支持特性进行说明,实现上就会简单很多。
如果一个协议要面向跨语言, 在API命名、特性设计上应该要考虑更通用,而不要包含只有某一个语言才有的功能。
hessian序列化协议规范的定义受到java语言特性的影响,以方便java语言序列化反序列化。 比如string编码格式使用16-bit unicode character, 是java内部字符编码格式。 比如object的定义叫class definition 。
为了跨语言的目标,同时兼容java中各种数据类型,需要建立一个类型映射表,以支持java中的特殊对象和类型。
底层协议库需要有完善的单元测试, 并能够达到一定的覆盖率。 可以考虑一开始把单元测试流程先建好,并把可能用例都写好,这样可以快速验证开发的功能是否满足需求,以及是否会影响其他功能点,提高开发效率。
鉴于java语言有完善的单元测试,以及跨语言的目标。 单元测试可以实现跨语言双向的编解码验证,可以更充分的验证协议库的兼容性。
hessian定义字符串编码为 16-bit unicode character, 而 golang 为UTF-8编码,需要做转换. 字符串作为主要的数据类型,编码转换的算法决定着协议库的性能。 开发过程中针对这部分也做过多次优化,最后版本相对还是比较满意。
hessian 协议比较特殊的一点是支持引用关系,需要建立上下文记录。 另外循环引用问题,也需要特别注意。
早期开发hessian golang协议库时还不支持泛型,代码中存在大量 switch case 和 if else 块,如果使用最新golang版本,使用泛型可以可以让代码更简洁。
Apache开源社群提倡包容的原则,一些新人刚参与社区并不能提交高质量的代码,要给予充分的支持和协助, 在没有bug的情况下,提交的代码尽量都予以接受,因为Apache把社区包容活跃放在首位,至于架构改善和优化可以慢慢去做。
如果又想要好的架构和高质量,建议是项目在一开始不要急着开源,而是在小范围内设计出最基本的架构,并且设置好自动化的CI质量检查。