由于公司的一系列(拥抱)变化,导致公司需要将原来的 Python 为主的技术栈,用 Java 全部重写替换掉,于是乎,去年 10 月份,我就跳出来从这个 Java 化大潮中,接了一部分 Python2Java 的活,主要原因当然是因为我想写 Java 啦。然后我就顺理成章的 owner 了这部分服务的 Java 化工作。

因为我们组有 8 个人力可以出来投入 Java 化,所以我自信满满的觉得我们能在 19 年 12 月底完成我这部分服务的 Java 化,并且全部上线,后来发现我是太 naive 了,最终延迟到了 2 月底才全量上线。

迁移过程

因为 Java 化一直都在进行,之前是做网关层的工作,所以对于其他同事的 Java 化,对于如何灰度,如何完美的替换掉 Python 服务,都有了一定的了解。
由于公司 Python 和 Java 服务用的是不同的 RPC 协议,所以直接替换肯定是不行的,但是公司框架组提供的 Java 框架,支持混合协议,在 RPC 握手时,可以选择不同的协议。
于是基于此,和同事讨论后设计了如下的迁移方案,最重要的步骤就是将 Java 混合协议写的服务替换掉原来正在线上跑的 Python 服务,并且做到对业务来说影响最小。

迁移过程中的架构

迁移前

Python2Java.001

迁移前的架构就跟大多数公司的架构一样。
接手的服务已经有部分接口用 Java 重写了,并且已经有了部分流量了。这次 Java 化,主要是针对 Service A,其中有 3 个服务,并且其中两个服务部分业务功能重叠,其中有一个是用 Go 已经重写了部分,但是由于某些原因,肯定是也要转成 Java 的。下文中,统一用 Service A 表示要迁移的 3 个 thrift 服务。

迁移中

迁移过程中中分为两步,第一步是首先将老的 Python (有流量的)接口用 Java 全部重写掉,让 Service A 成为一个代理层,将流量全量转发到 Service A 的 Java 服务。
这样做的好处是对于 readonly 的流量可以做流量比对,避免了大量的测试问题。

当然,基于二八定律,将 Python 大流量,并且依赖方少的接口直接在Java(Service B)里写掉,这个过程就需要人肉推动调用方改造了,所以是选择流量大,依赖方少和依赖方好说话和熟悉的来迁移。也就下图中的红色线条的流量。

通过 Python 的 Service A 做流量转发,这样就可以逐步的迁移接口,并且能将已经迁移好的接口线上灰度,避免要全部灰度完后再去替换整个服务。

在迁移过程中的第一阶段的架构如下图所示,Java 的 Service A 单独部署,并且将 Service A 的流量按照接口级别,代理到 Java 版的 Service A。

Python2Java.002

待到接口全部写完之后,就到了第二阶段了,也就是混合部署 Java 版的 Service A 和 Python 版的 Service A。

Python2Java.003-1

如上图,红色线条流量就是当 Java 版 Service A 部署并且注册到 Service A 的服务注册中心里面,并且对外都是提供 thrift 协议,这样 consumer 就可以按照注册的 Java 版 Service A 的比例,将流量打到 Java 版 Service A。这样就可以验证整个服务的可行性和可用性。

迁移后

当 Python 版的 Service A 被 Java 版的 Service A 全部替换后,就变成了下图的架构了。

Python2Java.004

踩过的坑

迁移过程当然不是一帆风顺的,中间也是踩过一点坑,主要是两大类坑,一类是人的因素,一类是规范的坑。

规范

首先说下私有规范的坑,公司的 thrift 协议,在原本的基础上,扩展了一些黑科技,并且修改了 thrift 里面函数的参数的默认 required 类型,大多数 thrift 库,对于函数上的参数都是 required,而公司因为某些不知名的原因,改成了 optional,而迁移时使用的 Java 框架,没有遵循公司这一特性。最终解决方案只能是框架那边改进,支持上这一特性了。

关于规范还有个坑就是公司的 Java 框架,没有使用 thrift 的 message id,全部当作 1 来处理,这样可以提升性能,但是 gateway 使用的 thrift 库是按照 thrift 规范实现的,虽然没有支持 thrift 的多路复用,但是也用到 message id。最终也是 Java 框架妥协了。

对于这个,我觉得如果有规范还是按照规范来实现的好,如果是公司内部约定俗成的,应该落实到文档中,避免后人踩坑。

人是一个感性的动物,并且拥有丰富的感情,这个就会导致一个团队里面,出现各种差异,因为每个人的做事风格都不一样。而且人与人之间的差异会很大,也会导致能力差距很大,从木桶原理来看,如果能力差距过大,整个项目会因为短板的那个地方,导致延期,如果在预估项目时效性以团队平均能力来预估的化,就会让涝的涝死,旱的旱死。当你发现某个人不能完成某件事时,要做的是及时止损,而不是放任自流。