2016 年下半年开始,FreeWheel 开始将其业务系统从 Rails 单体应用逐步迁移到微服务,同时技术栈从 Rails 改为 Golang,两年之后,整个迁移接近尾声,FreeWheel 业务系统技术团队对外分享了它们在微服务化过程中的经验。
原有架构的问题
FreeWheel 是一家为客户提供数字视频广告管理技术和服务的公司。其业务端产品需要对接客户,提供视频广告投放优化界面,类似于 Web ERP,该业务系统采用 Rails 技术栈开发,其架构是一个典型的三层架构。
这个系统经过近十年的研发和迭代,代码量达到数十万行,业务的特殊性和代码的复杂度让团队的维护和新功能研发越来越困难:
- 广告系统本身业务逻辑非常复杂,体现在它的数据实体很多,业务流程很长,分支逻辑很多。这样导致代码修改起来困难,可能改一个会影响好几个其它模块,同时维护也变困难,后来者不清楚模块具体实现细节,难以接手继续开发。
- Ruby 本身是一个高度动态的语言,通过解释执行。带来的坏处就是团队大了,维护成本很高,当发现问题的时候很难通过看代码知道问题出在哪儿,而且它也缺乏静态检查的工具,导致代码质量难以控制。
由于以上这些原因,FreeWheel 架构组决定将系统进行拆分,通过模块化来梳理和简化业务逻辑,当时微服务已经有不少公司采用,并取得了不错效果,架构组在调研之后决定采用微服务架构。同时团队还决定切换技术栈,选择更利于开发维护的语言和工具,基于种种理由,团队最后选择了 Golang。
从 Rails 到 Golang
在实施微服务时,很多团队都选择 Java 技术栈,毕竟有 Spring Cloud 等较为成熟的框架可以使用。FreeWheel 选择 Golang,则基于以下几个原因:
- Golang 本身上手门槛较低,有优秀的标准库可以快速开发应用,同时,Golang 生态也逐渐丰富,有不少大型项目采用;
- 微服务化过程中需要使用容器,FreeWheel 很早就确定使用 Docker 和 Kubernetes 作为容器化技术栈,而这两者都是 Golang 开发,不管是熟练掌握容器技术,还是要进行一些 K8S 扩展开发、定制,使用 Golang 都顺理成章;
- 新的技术栈不仅仅是业务系统团队使用,公司在研发新业务时也会使用,FreeWheel 之前各个系统的技术栈各不相同,有用 C++,有用 Java,也有用 Ruby 的,因此从大家都能接受的角度,Golang 也是一个较好的选择。
- 公司在美国的技术团队有过使用 Golang 的经验,并在内部做过分享,通过这种方式团队能快速了解这门新语言特点,遇到难题也可以向公司内部请教。
值得一提的是,FreeWheel 新的 Golang 应用都是由原来的 Rails 工程师研发的,为了让这些工程师顺利转移到新的技术栈,公司通过内部分享、黑客松等方式帮助团队成员快速上手,在短时间内即形成了战斗力。
迁移策略
FreeWheel 业务系统在决定重构时,新的需求仍然源源不断的产生,不能把需求停掉转而研发新系统;同时团队人力有限,不可能划出一部分人来转型微服务研发,因此,团队在一边研发原有 Rails 应用的同时,一边做新的系统研发。
在设计服务的粒度上,因为 FreeWheel 业务系统本身已经有了一套成熟的数据模型,这个数据模型可以反映经营的业务概念,也作为大家共同的语言,围绕这些数据模型是业务实体,团队在确认出三到四个核心业务实体后,围绕这些先创建了大概三到四个服务,最终创建的服务在 10 个左右,服务的粒度其实比较粗。FreeWheel 采用微服务的架构模式,其实主要也是吸收微服务架构中服务治理的一些实践经验,在初期服务设计上先从粗粒度开始。
在进行微服务改造时,团队还面临一个比较大的问题,就是原有架构使用单个数据库,里面有上千张表,数据分支很多,难以进行拆分。为了解决数据访问问题,专门设计了一个数据访问层,将原系统和新系统的数据库连接都通过数据访问层进行。数据访问层(内部简称 DAL)的设计目标包括:
- 对业务数据的访问进行全局路由,控制和优化,比如对不同客户的数据做软隔离,读写分离等;
- 在不分库的情况下,实现不同服务对数据的分治;
- 将应用与数据库具体实现隔离,为今后拆分数据库以及应用多种数据源做准备。
其基本架构如下图所示:
最后,FreeWheel 还需要解决上线问题,因为面向大 B 类客户,需要保证新老系统切换的平滑稳定。在这方面,对已开放的 Open API,FreeWheel 都保证接口和输入输入不变,通过添加一层适配器来将实现切换到新的服务上,同时通过完善的线下测试和 QA,以及线上的灰度发布来解决,并且,代码的单测覆盖率达到 80% 以上以保证代码质量,通过线下流量回放来保证原有系统的业务逻辑能够顺利完成。
微服务技术选型
由于 FreeWheel 业务系统功能较为单一,服务间的复杂交互不多,团队规模也不大,因此在进行微服务改造时较多依赖 Kubernetes 提供的原生能力,以及在它基础上开发的扩展来满足需求。
新的微服务架构如图:
具体组件的技术选型如下:
API 网关
API 网关包括认证、权限管理、限流、Metrics 等功能,这一部分为自研。它最初是为了将 FreeWheel 的自定义 token 认证切换到 Auth0 而研发的,一开始就是为了满足需求,逐渐扩展出其它功能,其请求处理流程如下:
服务框架
服务框架也为 FreeWheel 原 Rails 团队自研的轻量级框架,内部代号 Wheels,该框架借鉴了 Rails 脚手架的理念,基于 gPRC 进行了必要的扩展,比如在 protobuffer 对象中区分空值和未指定等,并集成了已有的数据访问,监控,消息,服务发现等服务,同时提供了一组单元测试,集成测试和面向 docker 和 kubernetes 的构建工具,使业务开发团队能快速开发出一个符合公司代码质量和服务治理标准的服务,而只需关注业务逻辑本身。下图展示了 Wheels 框架所包含的主要组件:
服务注册发现
这部分使用的 K8S 原生 DNS 组件 kube-dns,因为服务框架统一,所以无需进行额外配置或使用开源工具。
配置中心
FreeWheel 的配置中心组件基于 K8S 进行扩展而来,目前业界比较知名的配置中心开源组件有携程的 Apollo,但配置中心其实是 K8S 的一个核心功能,其它的开源库都是在它的 API 基础上封装而来。自研的配置中心与其它相比更有可控性,出了问题方便排查,该配置中心也无需面向业务,而由研发和 DevOps 负责操作,因此无需封装的太多。
数据总线
这个使用 Kafka 进行服务间的数据交换。Kafka 也是 FreeWheel 其它业务如大数据平台重要的基础设施。
日志
新的微服务架构里包含多个日志,包括 K8S 本身的日志、服务框架产生的日志、在业务中埋点产生的日志等,团队不仅依靠日志做监控和告警,也通过日志来做 debug。对日志收集处理的系统为 ELK。
监控与告警
使用 Prometheus,前段时间发布了 1.0 版本,也是目前主流的微服务监控框架。
Service Mesh
使用 Istio,网络治理对于微服务来说是非常关键的一环,而 Service Mesh 可以将开发人员从一些琐碎的关注点中解救出来。FreeWheel 团队主要利用了 Service Mesh 中的断路、连接管理、流量接管、自动重试、全链路跟踪、安全等功能。
微服务化成功要素总结
FreeWheel 的业务系统重构实践,可以说是一个很典型的中小型团队的微服务改造案例,它的经验值得其它团队学习,从中我们可以总结出一些成功经验:
一、选择合适的技术栈,可以做到事半功倍,在学习新的技术栈的同时,可以提升团队的整体实力。
二、对于一些功能较为单一的业务系统,用好 Kubernetes,微服务就成功了一半,团队里一定要有对 Kubernetes 非常熟悉、能够搞定它的各种问题的人。
三、微服务架构很多时候运维和研发已经不太能分的开,需要研发团队转型为 DevOps,设计并实现新的运维体系。
四、从自己的需求出发,简单的以及核心的需求可以自研,较为复杂的可以采用开源库,在研发能力和效率之间取得平衡。
受访嘉宾
张晗,现任 FreeWheel 首席架构师,全面负责核心业务系统开发,目前专注于业务系统的微服务化改造。
罗昕,现任 FreeWheel 首席工程师,目前负责公司微服务框架的开发工作。
杨谕黔,现任 FreeWheel 高级工程师,主要从事容器化相关的开发和推广工作。