金融创新业务基于容器云的微服务化实践
刘超︱网易云首席解决方案架构师
云计算发展至今,从普通的虚拟化到 OpenStack 再到容器云,如何为客户提供一个满意的解决方案成为一件越来越具有挑战性的事情。
虚拟化或者云计算的解决方案,大多从基础设施层面,讲清楚自己的产品就可以了。而容器云和微服务化的解决方案,则不但要了解客户的基础设施层,也需要了解客户的架构层,常常要和客户的技术人员真正的 “短兵相接”,了解客户的架构和痛点,以及流程中的问题,并给出建议,才能完成解决方案的工作。
金融行业对安全要求很高,传统金融业务对容器云解决方案大多都心存疑虑,但是传统金融行业也需要寻求创新,也在慢慢地进行互联网化,原因是互联网金融企业已经带来了很大的冲击,传统金融行业需要采取各种策略进行创新性的实验。接下来,我们就通过一个真实的金融客户案例,分享下传统金融客户进行创新业务的实践和经验。
这个客户有非常强的诉求去尝试创新业务,不但有着传统金融企业安全、高可用、容灾和简化运维的基本需求,还需要应对互联网化的新挑战,比如系统或应用的快速更新迭代,用户规模的急剧增长,市场活动带来的突发流量,以及互联网对用户体验的极致追求。
举个例子,这个客户的业务之前主要在本省,随着互联网化的发展,希望将业务扩展到更多的地区,但是经过计算发现原来的系统无法支撑未来的业务规划;此外,他们开始举办一些在线秒杀理财产品的活动,突发的流量让他们始料未及。
这时微服务架构及相关技术进入了他们的视线,由于缺乏相关技术人才和经验的支持,这个客户开始了和网易云的合作。
网易有很多内部的产品,如网易云音乐,云课堂,考拉,严选等都进行过微服务化改造,积累了丰富的经验。在这些内部应用上云的过程中,网易云也积累了丰富的容器化的经验。因而网易云除了提供高性能的容器平台以外,高并发场景下的微服务化和容器化经验,也作为知识体系进行输出。
经过和这个客户多次交流,根据客户的实际业务场景,网易云总结出了针对金融行业的微服务改造方案和规划,在这里分享给大家。
第一步:项目工程化与持续集成
项目工程化是指整个业务流程和上线流程必须要能够自动化,实现持续集成。如果不能做到这一点,盲目地去拆微服务,是十分危险的。这个客户最初工程化做得不是特别好,仅实现了部分的自动化。
微服务拆分的过程中会进行大规模的代码改动,改动的时候能否遵循代码规范是一个疑问,所以如果没有第二个步骤——代码审核,会使得代码质量失控。另一方面是单元测试的覆盖率太低,而且没有自动化的测试,微服务拆完之后功能还是不是原来的功能,如果没有足够的测试用例,很难证明。要做到自动化,环境部署和自动化部署又会成为一个问题。这个客户目前用脚本实现了自动化部署,在隔离性、端口冲突上都有一些问题。
第二步:框架的选择
目前有两个主流的微服务框架:Spring Cloud 和 Dubbo。Dubbo 诞生较早,所以用的人也更多,而 Spring Cloud 目前的关注度更高。
我认为应该从以下几个角度去考虑技术的选型:
业务相关性;
文档与社区支持,目前由于Dubbo热度的下降,社区的支持也在慢慢减少,从这个角度来说刘超更推荐Spring Cloud;
可扩展性;
许可证;
学习曲线;
框架流行度,如果你采用流行的框架会获取两个先天优势:容易招人,遇到问题也更容易在社区得到答案。
这个客户最终还是选择了 Dubbo,因为他们的技术负责人对 Dubbo 很熟悉。我也特别强调这一点:在项目管理中,不论选择哪个开发的框架,都要保证团队中至少有一个人对这个框架很熟,这会大幅度降低风险。
另外,服务间的调用,Spring Cloud 采用的是 Restful API,Dubbo 采用默认的 RPC 方式。我建议他们用 Restful API 的方式,因为如果用 RPC 会存在一个问题:客户端的调用方和被调用方要共享一个 Jar 包,负责对数据进行封装和解封装。但当微服务拆得非常多的时候,Jar 包的维护会变得非常困难,经常出现冲突。而 Restful API 基于 Jason,是一种松耦合的架构方式,相对来说可以比较好的解决 Java 中 Jar 包冲突的问题。
第三步:API 和 UI 界面的隔离
可能很多人觉得这是不必要的,实际上更好的设计方式是:
UI 层所有的展现方式都去调用后面的 API 层来做,API 层要尽量的原子化,这样的好处是可以在 API 层实现自动化测试;
可以实现 API 层的认证鉴权;
UI 层静态页面和动态页面分离,使用对象存储和 CDN 进行加速。
第四步:去状态化
容器化的前提是实现去状态化。所谓去状态化,就是将 Session 数据,文件数据,结构化数据保存在后端统一的存储中,从而实现跨机房迁移和弹性伸缩。拜访了多家金融客户后发现,大部分都已经实现了去状态化,可以直接进行后续的容器化。
第五步:容器化
容器有一个好处就是镜像可以分层,如果团队也按照分层来进行分工的话,可以提高整个团队的效率。比如,核心人员可以做偏向内核的镜像开发,外层人员基于内层做好的镜像,把 Jar 放进去就好了。这时只需要核心人员掌握容器的核心技术即可,降低了学习成本。
第六步:基于容器的持续集成
这个阶段暂时并不需要一个容器的管理平台,原来是用脚本来部署应用,现在用容器的方式来部署,原来在一台机器上启动 20 个进程,所有端口都是冲突的;现在启动 20 个容器所有端口都不冲突,每个 Tomcat 都可以用 8080 端口,可以相互调用,就可以进行自动化的测试,整个运维也会非常简单。并且容器使得整个开发、测试和生产的环境是一致的。
第七步:数据库读写分离与使用缓存
数据库永远是应用最关键的一环,数据库必须要高可用,同时越到高并发阶段,数据库往往成为瓶颈,如果数据库表和索引不在一开始就进行良好的设计,则后期数据库横向扩展,分库分表都会遇到困难。数据库往往写少读多,所以性能优化的第一步就是读写分离。
在高并发场景下,需要通过缓存来减少数据库的压力,使得大量的访问进来能够命中缓存,只有少量的需要到数据库层。由于缓存基于内存,可支持的并发量远远大于基于硬盘的数据库。所以对于高并发设计,缓存的设计是必不可少的一环。
第八步:API Gateway
在拆分之前,建议使用 API Gateway,如果有了服务网关,后面的服务不论怎么拆分与合并,对于客户端来讲都是透明的,方便后续的服务拆分。
第九步:服务拆分与服务发现
为什么需要服务拆分呢?
开发独立:代码耦合度比较高,修改代码通常会对多个模块产生影响,操控难度大,风险高;
上线独立:单次上线需求列表多,上线时间长,影响面大;
简化扩容: 由于业务多,每一次扩容需要增加的配置比较杂。一些不起眼的小业务虽然不是扩容的主要目的,也需要慎重考虑;
容灾降级:核心业务与非核心业务耦合,在关键时候互相影响。
如果进行服务拆分呢?
在原有工程中,先独立功能模块,声明接口,内部规范,形成服务内部的分离。
两套代码并存,用动态开关控制逻辑走向,实现随时恢复原来版本。
尽早新建工程,可以先不实现代码,只转流量,调用老的接口。
将老工程的接口复制到新工程。
优化新工程的逻辑,删除老工程的相关代码。
第十步:使用容器管理平台实现服务编排
例如一个应用包含四个服务 A,B,C,D,它们相互引用,相互依赖,如果使用了 kubernetes,则服务之间的服务发现就可以通过服务名进行了。例如 A 服务调用 B 服务,不需要知道 B 服务的 IP 地址,只需要在配置文件里面写入 B 服务服务名就可以了。如果中间的节点宕机了,kubernetes 会自动将上面的服务在另外的机器上启动起来。容器启动之后,容器的 IP 地址就变了,但是不用担心,kubernetes 会自动将服务名 B 和新的 IP 地址映射好,A 服务并无感知。这个过程叫做自修复和自发现。如果服务 B 遭遇了性能瓶颈,三个 B 服务才能支撑一个 A 服务,也不需要特殊配置,只需要将服务 B 的数量设置为 3,A 还是只需要访问服 务B,kubernetes 会自动选择其中一个进行访问,这个过程称为弹性扩展和负载均衡。
第十一步:统一日志收集
当单体应用拆分为多个微服务之后,不能再单独查看每个服务的日志,因为工作量太大。必须将日志汇集到统一的日志中心进行统一的管理,查询,排错,视图等。
第十二步:配置中心
应用多了就希望实现配置的集中下发,将配置放到统一的 consul 中,配置只要在代码中提交了,就可以自动下发。
第十三步:水平扩展
一旦容器化了之后,就可以实现全架构的弹性伸缩。
应用层因为无状态可以进行横向弹性扩展,从 5 个节点只要改一个数字就能变成 50 个节点。
缓存层可以通过集群机制进行横向扩展。数据库层可以通过分布式数据库 DDB 进行横向扩展。
第十四步:基于代码仓库的持续集成
线上的每一个环境都应该对应代码中的一个分支,不提倡也不允许用户在环境中进行手动修改,容易忘也不容易交接,所有操作都应该放在代码中做,通过提交代码,代码做自动发布,自动部署,使得新的配置或新的权限都能发布到新的环境。
第十五步:部署依赖与发布依赖
当单体应用拆分为多个微服务之后,服务的部署,更新,升级,回滚变的非常复杂。网易给出的方案是将编排文件保存在 Git 里面,在编排文件中维护不同服务之间的部署依赖和发布依赖,例如一次性发布 100 个应用其中的 5 个应用,可以通过在修改 Git 中编排文件中的 5 个服务进行,并在发现异常的时候,可通过 Git 回滚一个提交的方式,将 5 个应用一次性回滚。
- END -
精彩文章回顾
点击图片轻松阅读