如何设计一个打车系统

[TOC]

需求和功能

1.乘客可以发起乘车请求
2.司机需要汇报位置接单,然后完成请求。
3.乘客可以取消订单。
4.司机可以放下乘客,然后完成任务。
我们假设有20W司机同时在线。
司机每4秒汇报一次位置。
那么QPS 200K/4 = 50K
安全起见,*5,因为新年夜是打车高峰。我大概要支持250K 的QPS
下面计算存储。

下面存储就是要不要存历史记录。
我们先算每秒单条的存储大小大概是200字节
200B * 50K = 10 M
那么每天 100K*50M =1T

服务

1.GEOSERVICE,这个负责做地理位置的记录。如乘客在哪打车,当前司机的车在哪个位置。
2.dispatchService : 这个是用来匹配乘客和司机的服务。

img

image.png

存储

DISPATCH SERVICE 既然是匹配司机和乘客,那么存储就应该是一个个TRIP ORDER。
GEO SERVICE 应该存的是 一个人-》地址的映射。
所以TRIP 应该有
TRIP ID
DRIVER ID
RIDER ID
SOURCE (LAT,LNG)
DESTINATION (LAT,LNG)
CREATED AT
status(new,wait , in trip, cancelled , ended)

LOCATION 有
Driver ID,
current pos (lat, lng)
updated at

地理位置信息的存储与查询

GEOHASH
公共前缀越长,两个点越近。

那么是如何生成的呢?

img

image.png

img

image.png

img

image.png

这里如果我们用SQL 数据库,那么我们
需要对GEOHASH 建立索引。
select * from location where geohash like ‘9q9hv%’

nosql cassandra
可以用GEO HASH 设为 COLUMN KEY
随后用RANGE QUERY(9q9hv0,9q9hvz)

nosql redis
对DRIVER的位置,分别分级存储
DRIVER 的位置如果是9q9hvt, 我们可以存储在9q9hvt,9q9hv,9q9h 这3个KEY里
VALUE 就是SET OF DRIVER。

随后我们比较分析一下。
SQL 和 CASSANDRA 都是一个能用的选择,但是性能不够好为什么?

  1. 即使有INDEX, LIKE QUERY也是比较慢的。
    2.DRIVER需要实时UPDATE 自己的地理位置,被INDEX COLUMN 并不适合经常修改。
    CASSANDRA的问题也是一样,COLUMN KEY 有INDEX,不适合经常修改。
    所以REDIS比较好。
    因为数据可持久化,原生支持SET VALUE。读写速度接近内存访问速度,单机支持100K QPS。
司机汇报自己的位置

根据坐标算出GEOHASH,如果变化则更新

司机接受打车请求

修改TRIP状态
在DRIVER TABLE里标记为不可用。

司机完成打车请求

修改TRIP状态。
在DRIVER TABLE里标记为可用。

一个完整的流程。
乘客发出打车请求,服务器创建一个TRIP。
乘客每隔几秒根据TRIP ID询问是否匹配成功。
服务器找到匹配的司机,写入TRIP,状态更新。
同时修改DRIVER TABLE中的司机为不可用。同时记录TRIP ID
司机汇报位置,在RESPONSE里得到TRIP ID。

img

image.png

优化

虽然一台REDIS够用,但是挂了后果严重。我们依然要做DB SHARDING。
2个优点,可以分摊流量,以及避免单点失效。
这里比较TRICK 的是按照城市来SHARDING。
因为不同城市的政策和计费可能不一样。所以我们要根据司机的经纬度,来决定哪个城市然后去对应的服务器。
在数据方面的容错。首先我们可以用REDIS 的 REPLICA ,就是它的主从模式。
其次我们在底层存储接口,使得每份数据写三份。
这样sharding key 从本来的 CITY ID
变成 CITY ID _0, CITY ID_1, CITY ID_2
读取的时候从任意一个备份读

UBER 美食外卖怎么实现

我们注册一个外卖的DRIVER,一般是骑助动车的。给他们派单。这里面就要研究顺路单。也就是意味着,一个外送员,接到一个单的时候,如果附近0.6KM正好有另一个单,可以再让他接一下。
拼车同理。

作者:西部小笼包
链接:https://www.jianshu.com/p/093075cd05b7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。