一、背景
随着业务和技术的推进,在早期野蛮生长后,公司逐渐开始注重质量发展。
测试方面,为了满足需求的快速迭代上线,之前机票整个前台采用了 Scrum 敏捷开发流程,测试流程往往是人工进行。而随着业务增长,每次发布需要覆盖的 Case 越来越多,这时对所有 Case 都进行回归显然不太现实,人工测试也往往不能完全避免出现问题。
发布 Case 回归,除了本次发布的需求,其它 Case 的验证往往是重复的。因此我们开始加大自动化测试的比例,减少人工介入,以此来降低开发成本,提升发布质量。
二、简介
在开发流程中,我们逐渐引入了持续集成流程,整个流程包括单元测试,流量回放,Case 验证等等。
其中流量回放流程,要想达到模拟线上真实的请求结果,需要借助 Mock 系统和数据量比较大的线上日志来完成。将接口,Abtest 结果等第三方依赖结果 Mock 掉,尽量和线上的真实流程保持一致。
其中重要的一步是拉取线上日志用来做 Mock 使用,这关系到覆盖线上场景的多少,以及持续集成的有效性和可靠性。
这部分日志往往数量庞大,仅机票前台每天产生的日志就在 1T-2T 之间。出于数据安全的考虑,服务的各个环境做了隔离,这也使得拉取日志的成本较高。
之前的方案定时拉取日志,然后将其存储在 redis 进行缓存,每次进行拉取,进行日志数据准备往往需要半天的时间,成为持续集成的一个瓶颈。
三、场景回放
场景回放的目的在于覆盖线上业务场景,携程作为国内最大的 OTA,需要对接众多的国内和国际航司,大多数航司为了提升自家的票量,往往有着许多不同的需求,这也提升了我们业务的复杂度。
现在的方案是需要开发将用户预定流程经过的场景,通过埋点的方式将业务的场景埋到日志,存储到日志系统中,之后通过这些 Case 的埋点,来获取特定的日志报文。通过 Mock 系统,将 Soa 接口和 Ab 等第三方依赖 Mock 掉,使用线上的日志来重新发请求,通过比对线上返回报文和回放的返回报文的方式来进行线上的场景回放和验证,来达到覆盖线上业务场景的目的。
四、改造方案
新的方案采取 Flink 直接接收 Kafka 数据,对实时的数据进行预处理,在用户的每次请求中,都会生成一个唯一的 ID,把依赖的 SOA 接口通过 ID 进行了埋点和串联。
我们可以根据 ID 将主服务的日志和 SOA 的日志进行分组,聚合出一次请求的日志和依赖日志。并根据业务上的埋点,进行真实业务场景的换算,将场景换算成关键字,写入到 Es 中,利用 Es 中 Lucence 的分词和倒排索引进行检索,以提高查询效率。
方案设计之初我们提供了备用方案,如果 Es 不能满足预期,也可以对场景的日志自己维护一套索引,来达到快速检索的目的。
从目前的使用效果来看,Es 基本可满足需求。 在业务上的场景埋点字段类似于 A|B|C|D|E 这种,每个数字分别代表不同的场景含义,并且有可能是使用位操作来表示或者是一个特定的量词。
对场景埋点的处理,是将字段中每个数字的含义进行拆分,比如数字 A 每个位代表不同的含义,处理后的结果就为 ct_0=[A1];[A2];[A3],0 代表埋点中的位置,A1,A2 为具体的场景(举例如下),由于标点符号为天然的分隔符,可以利用这一点来进行分词,构建索引。
处理前 CaseTag:
处理后 CaseTag:
五、采取新方案后的效果
我们的日常发布一般是在晚上生产流量比较低的时候进行。之前进行流量回放,往往需要从早上开始准备,进行日志拉取,整个流程大致要 4 个小时以上(画外音:我的青春,我的泪)。
使用新方案后,我们的场景就可以使用索引来提高检索速度,这样每个场景的日志拉取可以做到在秒级返回,近乎实时的日志获取,大大提高了流量回放的效率。
六、使用 Flink 和 Es 需要注意的地方
在 Flink 的使用过程中有几个地方需要注意,我们使用的是 Flink 的 StreamApi,尽管 Flink 有内存溢出机制,但是实际使用过程中,由于每天产生的日志数据量在 1T-2T,即使是按照每 10 分钟,还是会有好几 G 的数据。另外我们会需要 SubTask 进行计算,每个 SubTask 都会备份数据,还是出现好几次内存溢出导致 TaskManager 死掉的情况。
在配置的时候一定要注意一下 TaskManagerJVM 内存大小的配置,生产环境我们最后将每个 TaskManager 的 JVM 堆的大小配置在了 7G 左右。另外为了集群的可靠性,部署建议采取 Yarn 模式。
在新版本的 Es 中默认支持 Mapping 自动创建,有利也有弊。
虽然我们不需要再手写 Mapping 文件来规范每个字段的类型,但必须要处理好每个字段的类型,比如如果存在多级对象并且在 Root 对象存在含有.的字段,Es 会认为这是一个对象字段而不是一个 String 或者 int 之类的基础字段。可能会导致 Mapping 类型冲突,导致写入失败。
因为 Es 是基于 JVM 的,Jdk 采用的是 32 位指针,内存的分配不要超过 32G。超过 32G 会发生指针扩容,造成效率降低,当然 JDK11 已经采用 64 位指针,不需要关心这个问题。
作者介绍:
姜传伟,携程机票高级软件工程师。机票前台服务端搬砖工,负责机票前台服务端基础框架。
本文转载自公众号携程技术(ID:ctriptech)。
原文链接:
https://mp.weixin.qq.com/s/D-nDqh9u_oKYJfTpEC6DCA
评论