写点什么

Ousta 如何在两分钟的测试周期内模拟客流

  • 2016-12-05
  • 本文字数:3432 字

    阅读完需:约 11 分钟

主要结论

  • Ousta 的模拟服务会监听系统事件,并通过司机专用应用程序的 API 模拟车队司机做出回应。
  • 事件驱动的架构使用了“发布 - 订阅”模式,一个发布方可以有一个或多个订阅方。
  • 用微服务代表系统中每个可测试和可部署的单位,提供不同功能。
  • 弱耦合的系统在测试方面的操作更简单。

Ousta 是埃及领先的 Ride hailing 公交公司(译注:这是一种不设立固定站点,乘客随上随下的公交运营模式。详见: https://en.wikipedia.org/wiki/Hail_and_ride ),该公司的乘客数和车辆数正在经历飞速增长。我们的系统包含两个移动应用,应用可以与基于微服务的事件驱动的架构进行交互。我们通过配合使用 EDA 和微服务搭建了自动化的模拟系统,借此打造了一个可供我们快速演进的环境。

定义问题

我们分别针对乘客和司机开发了两个应用。在开始实现这些应用时,我们决定先从面向客户的乘客应用着手,乘客可以通过这个应用约车。

开发可以实时更新的应用程序会遇到额外的挑战。每次需要对约车过程进行测试时,都需要模拟下列高层事件:

  • 需要由司机应用接受约车请求
  • 车辆抵达时通知乘客
  • 将乘客送达目的地
  • 结束行程

每次移动应用或后端服务发生变化后,我们往往很难对整个流程进行测试,因为我们手头只有乘客应用和 API。

自动化!

我们开始考虑通过自动化解决方案在测试环境中测试整个流程。可是该如何针对乘客应用中进行的操作执行司机 API?我们曾考虑使用外部微服务执行司机 API,监听由于乘客操作产生的系统事件。在我们所用的事件驱动的架构帮助下,这一切可以很容易地实现。

事件驱动的架构

事件驱动的架构极为强大,针对系统执行的每个操作都会产生一个 JSON 对象形式的事件,其中包含了所需的全部信息。

例如约车请求会产生一个事件,其中包含了乘客信息、上车位置、价格等信息。JSON 事件内容如下:

复制代码
{
// Meta part contains information about the event type and category.
"meta":{
"messageCategory":"event_TripInfo",
"messageType":"newTrip"
},
"body":{
"tripId":"d2eac36c-8131-4409-8c37-863b0725fb4d",
"status":"processing",
"riderId":"4524b80a-f82a-4f1c-ad77-69a620c5c628",
"requestTime":"2016-09-09T15:42:32+0000",
"requestTimestamp":1473435752,
"currencyCode":"EGP",
"rider":{
"riderId":"4524b80a-f82a-4f1c-ad77-69a620c5c626",
"firstName":"Mostafa",
"lastName":"Saeed",
"emailAddress":"msaeedatteya@gmail.com",
"mobileNumber":"+201272509147",
"pictureUrl":null,
"rating":"5"
},
"service":{
"serviceId":"7f888b1f-14e4-4b7e-8f8d-1c1ee1df074c",
"displayName":"Super Saver",
"description":"Ousta super saver service",
"code":"Super Saver",
"image":"https:\/\/s3-eu-west-1.amazonaws.com\/oustaridersapp\/services\/ico_cartype_economy.png",
"carPictureUrl":"https:\/\/s3-eu-west-1.amazonaws.com\/oustaridersapp\/services\/img_onmap_car_taxi.png",
"priceDetails":{
"costPerDistance":"1.45",
"costPerMinute":"0.25",
"distanceUnit":"KM",
"base":"4.0",
"cancellation":"10",
"minimum":"10",
"currencyCode":"EGP",
"commissionPercentage":"0.2",
"commissionFees":"0",
"commissionType":"percentage"
}
},
"city":{
"name":"Cairo",
"code":"CA",
"location":{
"latitude":"30.0594699",
"longitude":"31.1884239"
}
},
"pickupLocation":{
"latitude":"30.067615031955",
"longitude":"31.210990101099",
"text":"Tahrir Square, Meret Basha, Qasr an Nile, Cairo Governorate"
},
// Driver & vehicle are null because ride is not accepted by a driver yet.
"driver": null,
"vehicle": null,
"paymentInfo":{
"paymentMethodId":"4496839d-8cd2-4f98-98bb-e059ca7ac5c1",
"type":"cash",
}
}
}

该事件将由多个其他微服务来处理,例如发送推送通知、短信、邮件,或处于分析需要将乘客信息存储在其他位置。我们使用 Amazon SNS 发布事件,使用 Amazon SQS 接收事件,并将消息队列中的客户事件持久存储。

  1. 使用 Amazon SNS(Simple Notification Service):AWS SNS 是亚马逊提供的发布订阅服务,可用于创建与系统逻辑有关的话题(Topic),随后可在发生特定事件后使用 AWS SDK 将事件发布到这些话题。例如当乘客需要约车时,负责预定车程的微服务为该事件创建 JSON 对象,并将事件发布至 TripEvents SNS 话题。
  2. 使用 Amazon SQS(Simple Queueing Service):AWS SQS 是一种消息队列服务,可用于订阅特定 SNS 话题,这样发布到该话题的任何消息即可持久保存在消息队列中,直到最终被服务使用。

下图展示了系统中的事件从发布到最终被不同微服务处理的完整事件流过程。

(点击放大图像)

如图所示,事件驱动的架构使得我们可以轻松地在系统中加入不同用途的新增微服务,整个过程只需要下列步骤:

  1. 新增一个 SQS 队列
  2. 将该队列订阅至特定 SNS 话题
  3. 新增监听该队列的微服务

模拟器微服务

我们曾实现过一种模拟器微服务。通过侦听 TripEvents SNS 话题,该服务可以很容易地根据产生的事件执行必要的操作。例如当收到的事件表示某位司机准备接受或拒绝某个约车请求后,该服务会通过调用 API 代表司机应用接受该行程,具体过程如上图所示。

最开始我们只实现了一种较为简单的版本,涵盖了从乘客发出请求到行程结束的整个过程。不同调用按顺序执行,每个调用之间等待 10 秒钟。完整的行程过程如下所示:

(点击放大图像)

遍布 Qusta 总部四周的“假”司机

我们希望让模拟的过程更真实,因此开始通过模拟司机在不同街道上的活动对模拟器进行优化:

  • 接到乘车请求之前
  • 行驶至上车位置
  • 到达乘客的目的地

随后我们考虑到可以在测试环境中模拟在我们位于开罗市区的总部周围四处行驶的司机。我们希望每隔 x 秒调用一次司机 API,借此向后端系统告知司机当前位置的经纬度。

模拟司机的驾驶过程

由于希望让模拟的过程显得更真实,我们还对模拟服务进行了优化,用一系列代表经纬度的坐标点代表实际路程,这些坐标点代表了从司机接受行程时的位置到乘客上车位置的全过程,以及从上车位置到乘客(请求者)在应用程序中指定的最终目的地的全过程。为此我们使用 Google Distance Matrix API 获取坐标点列表形式代表的两点间完整路径,这样在司机从 A 点到 B 点的行驶过程中模拟器就可以按顺序发送这些点的坐标。

位置更新触发器

最麻烦的部分在于,该如何设置安排好的触发器,以便将每 x 秒一次的位置更新发送给每位模拟的司机。我们考虑了一种解决方案,使用一个可自动触发给服务的 Cron 作业,但这种方式存在局限,最小间隔为一分钟。因此我们需要用一种特殊的方法使用 Amazon SQS。SQS 提供了一个名为 Delay Queues 的强大功能,使得加入队列的消息可以在实际使用前维持 x 秒的不可见状态。下图展示了我们使用这一功能模拟司机行驶过程的方法。

(点击放大图像)

位置更新的采样率

根据应用程序的需求,我们选择了每四秒一次的采样率。精确频繁的位置更新对我们的应用程序和乘车体验很重要,因为我们需要通过这样的更新计算行程的实际距离。

模拟流程总结

(点击放大图像)

独立进行的应用程序测试

模拟服务使得我们可以在将司机应用程序开发完成之前随时测试乘客应用程序的所有功能,这一特性解决了我们开发工作中一个非常大的依赖性问题,无需同时使用这两个应用程序就可以对整个行程进行测试。

两分钟内完成整个测试周期

借助上述方法,我们打造了一个完整的自动化测试解决方案,可以帮助我们更放心地发布新功能,应用变更,修复现有问题,并让每天多次的生产部署变为可能。

关于本文作者

Mostafa Saeed是 Ousta 公司的软件架构师,这是一家位于埃及,正在经历飞速增长的公交公司。Mostafa 领导着开发团队打造了一套事件驱动,可容错,基于微服务的架构。同时他也是 AWS 顾问,帮助大量初创公司应用有关高可用云,以及缩放能力方面的最佳实践。

作者 Mostafa Saeed 阅读英文原文 How Ousta Simulates Rides within a Two-Minute Test Cycle

2016-12-05 17:011759
用户头像

发布了 283 篇内容, 共 119.2 次阅读, 收获喜欢 63 次。

关注

评论

发布
暂无评论
发现更多内容

android开发面试题,字节跳动Android三面凉凉,手慢无

欢喜学安卓

android 程序员 面试 移动开发

企业如何做数字化转型?想要资产状况及时把控,它的作用至关重要!

一只数据鲸鱼

数字化 数据可视化 资产管理

区块链电子政务——不动产综合服务平台

电微13828808271

SpringBoot Admin2.0 集成 Java 诊断神器 Arthas 实践

阿里巴巴云原生

Java 运维 云原生 中间件 Arthas

交易所跟单软件搭建,合约跟单系统开发

视频云全球创新挑战赛 —— 视频目标分割经典算法解析

阿里云CloudImagine

阿里云 计算机视觉

技术人如何调研和选型第三方 SDK?全文干货

融云 RongCloud

26天吃透算法笔记,面试字节,面试官朝我比了个“ok”

比伯

Java 编程 架构 算法 技术宅

回归主流:区块链新大门的“密钥”

CECBC

区块链

Hadoop的发展及其架构

五分钟学大数据

hadoop 4月日更

Javacv 音视频小工具 - 下载抖音无水印视频

张音乐

Java 音视频 ffmpeg 抖音 javacv

跟着源码学IM(八):万字长文,手把手教你用Netty打造IM聊天

JackJiang

Netty 即时通讯 IM

BERT和GAN咋压缩,且看咱PaddleSlim新利器—— OFA

百度大脑

百度 飞桨

聪明人的训练(十二)

Changing Lin

4月日更

专业开发者眼中的HarmonyOS:专访资深软件工程师李宁

Geek_283163

华为

面试官:请说说什么是BFC?大白话讲清楚

蛙人

CSS 大前端

EGG Network阿凡提 公链EFTalk全球首创POTP二叉交叉共识机制

币圈那点事

阿里高级架构师纯手打832页Java全栈知识点笔记,吃透后成功七面上岸滴滴!

Java架构追梦

Java 阿里巴巴 架构 面试 成长笔记

Redis单线程已经很快,为何6.0要引入多线程?有啥优势?

Java架构师迁哥

积极研发区块链技术落地应用业务 这家A股上市企业试水云算力挖矿

CECBC

区块链

如何将区块链打造为城市底层基础设施

CECBC

区块链

情指勤指挥调度平台搭建,公安重点人员管控平台搭建

程序员去大公司面试,我的头条面试经历分享,搞懂这些直接来阿里入职

欢喜学安卓

android 程序员 面试 移动开发

“区块链+电子商务”,电商能否再创辉煌?

电微13828808271

攻击区块链网络的都有哪些方式方法

CECBC

区块链

思维导图学《Java性能权威指南》

Yano

Java 性能 思维导图

混音新手必备软件:FL Studio,用它简单制作混音

奈奈的杂社

极智网络告警关联规则挖掘

鲸品堂

方法论 解决方案

大数据前置知识-服务器及磁盘

大数据技术指南

大数据 4月日更

一文读懂容器存储接口 CSI

阿里巴巴云原生

容器 云原生 k8s 存储 调度

用 JavaScript 实现三次贝塞尔动画库 - 前端组件化

三钻

JavaScript 大前端 动画 组件化

Ousta如何在两分钟的测试周期内模拟客流_语言 & 开发_Mostafa Saeed_InfoQ精选文章