作者简介
Fenlon,携程资深后端开发工程师,负责国际业务多语言中台部分客户端和服务端研发。
Shark是携程IBU国际化进程中孵化的集多语言内容管理、多语言翻译、多语言内容下发等功能为一体的多语言平台,目前为携程海外几十个站点和上千个业务应用提供稳定的多语言内容管理和下发服务。本文主要分享携程IBU多语言团队在提升系统稳定性过程中的一些实践和总结,希望给大家一些参考和帮助。
随着公司国际化业务的深入推进,业务越来越多元化,支持的国际化站点越来越多,接入Shark的业务方也随之增加。内部Shark自身支持的业务场景和业务端越来越多,Shark应用架构也越来越复杂。但提供稳定的服务能力一直是Shark的首要目标,从Shark诞生以来,服务端主要进行过大概两轮优化升级,分别为缓存建设和文件冗余方案。两次升级都是结合了shark自身的业务特点进行针对性的方案设计。
在介绍服务端系统演进之前,先介绍一下shark目前整体的应用架构。shark应用架构分为5层,分别为应用层、sdk层、交互层、服务层、存储层。本文讲述的服务端主要分布在服务层,涉及多个微服务。
3.1 初期(1.0-2.0)
在项目初期因业务接入方少,shark系统自身也处于快速迭代期,平台管理的多语言内容数据量较小。服务端架构较为简单,主要依赖数据库的服务能力。除根据业务特点做了读写分离外没有过度的冗余设计。
3.2 缓存建设(3.0)
随着国际化的逐步推进,shark接入的数据量和业务方都有了大幅的增加,服务响应耗时和db压力都逐渐增加。压测显示系统能够支持的最大qps大概在4k左右,而当时系统平均qps已经接近2k。db服务器网卡出口流量高峰在300M左右,如果发生大规模全量数据拉取则可能会将网卡资源耗尽;此外db受离线job和portal端批量数据更新的影响偶尔会有性能抖动,因此降低db压力提高系统响应为首要的优化目标。
1)近实时
shark不同于配置系统,主要是提供近实时的多语言数据下发服务,对实时性没有严格要求,能接受1-3分钟数据延迟。
2)读多写少
shark作为一个多语言内容管理和下发平台,本质上是一个读多写少的服务。当业务方业务稳定后,对多语言内容的修改较少。此外提供给业务方的sdk主要是通过轮询的方式获取最新的多语言信息,则读的请求量会很大。
3)请求重复性
对业务方应用集群而言,同一时间段内发起的后端查询往往是重复的。如在发布过程中数据初始化,每台机器执行的操作都是一致的,因此服务端缓存能够有效解决这种重复请求。
4)缓存方式
shark较大部分查询场景为多维度数据匹配的范围查询,服务端难以全面预知客户端的具体请求条件,因此难以通过异步方式提前建立缓存,最终选择被动式缓冲。
5)数据实时性优化
被动式缓存主要是通过设置过期时间来自动过期的,因此为了保证数据的实时性和保证缓存的命中率,缓存时间应保持和sdk轮询周期大致一致。服务端也可以通过监听数据变化来主动过期缓存,保证数据实时性。
3.3 文件冗余(4.0)
缓存建设虽然降低了数据库压力,提高了系统响应时间,但导致服务端的稳定性又间接的依赖缓存系统的稳定性,一般情况下业务系统对缓存的依赖为弱依赖。虽然可以通过DR来提高缓存系统的可用性,但DR切换阶段仍会有短时间内的服务不可用,另外DR也会造成硬件资源的浪费。
当前客户端的主要流程如下:
当前客户端存在以下问题:
冗余设计是提高可靠性最笨的办法,同时也是最有效的方法。冗余设计又称余度设计技术,是指在系统或设备完成任务起关键作用的地方,增加一套以上完成相同功能的功能通道、工作元件或部件,以保证当该部分出现故障时,系统或设备仍能正常工作,减少系统或者设备的故障概率,提高系统可靠性。
对于服务端来说,希望拥有一个冗余的数据拉取通道来分摊服务端的压力。对于客户端来说,希望能拥有一份冗余的本地多语言资源,来降低对服务端的强依赖提高客户端的可用性。一般情况下业务方业务迭代完成后,不会频繁的变更多语言内容,所以对于冗余的资源文件,无需非常频繁的刷新内容。即便文件内容不是最新的,对客户端来说仍然能通过增量接口进行数据修正。最终方案如下:
服务端主要是通过离线任务定时生成多语言资源文件,然后将资源文件刷新至文件系统中,同时在数据库中维护多语言文件的元数据信息,以便更好的对多语言文件进行管理和通知客户端进行资源更新。
通过调研最终我们选择了ceph作为shark资源文件存储系统,ceph在携程运行多年,有较为丰富的运维经验,具有较高的稳定性,在多IDC都有部署,且有成熟的DR方案。
因⽣成的多语言内容和配置文件都是较小的⽂本⽂件,⽆需过度考虑压缩⽐和解压缩性能。为了减少sdk对外部的依赖,我们更倾向于选择jdk内置的压缩方式,因Gzip不支持直接对文件夹进行压缩,最终我们选择了通⽤性较好的zip压缩⽅式。
客户端主要是通过本地冗余多语言文件来降低对服务端的依赖,当服务端不可用时客户端可通过本地冗余的全量内容提供降级服务,避免造成业务方应用完全不可用。客户端多语言资源冗余主要有两个阶段:应用编译打包阶段离线初始化;应用运行阶段异步更新。
java项目一般是通过maven来进行项目管理和构建的,maven将一个整体任务划分为一个个的阶段,类似于流程图,然后按顺序依次执行。所以我们只需要开发一个插件将其绑定在maven的default生命周期的某一个阶段即可。当最终打包时便可将业务方依赖的多语言资源注入到业务的发布包中。
多语言资源包的目标是注入到业务发布包中,而一般一个服务会依赖多个maven-module。且公司发布系统针对java项目默认是推荐war包发布,所以我们可以在插件内对packaging类型进行过滤,只支持war类型进行资源注入即可。
目前插件需要业务开发显示依赖的,开发阶段会进行频繁的编译和测试。为了减少开发阶段频繁的资源下载,结合maven default生命周期各阶段的主要特性,最终将插件绑定在prepare-package阶段。
同上为了降低开发阶段不必要的多语言资源下载,插件目前不支持开发常用的Mac和Windows环境。
java
<plugin>
<groupId>com.ctrip.xxxx</groupId>
<artifactId>shark-maven-plugin</artifactId>
<version>x.x.x</version>
<executions>
<execution>
<goals>pack-download</goals>
</execution>
</executions>
</plugin>
sdk针对不同的数据源分别了封装了RemoteFileLoader、LocaleFileLoader和RemoteSOALoader。sdk主要是通过时间戳判定资源是否有更新,如有更新则从远程文件系统下载最新资源文件更新本地备份文件。为了保证sdk的可用性和降低对服务端的请求压力,sdk-loader模块的全量数据加载流程如下:
Shark多语言系统是一个复杂的面向多端提供多语言内容解决方案的系统平台,本文只是大致描述了Shark平台的服务端和java-sdk端遇到的问题和演进方向,具体实现细节和详细的业务逻辑并未过多的描述。Shark当前为多语言中台中重要的一员,后续Shark的方向主要为提升多语言翻译效率和支持更多的机器翻译场景。