当前位置: 首页 > 科技观察

使用Redis和Python构建自行车共享应用

时间:2023-03-16 14:57:09 科技观察

了解如何使用Redis和Python构建位置感知应用。我经常旅行。但不是汽车狂热者,所以当我有空闲时间时,我更喜欢在城市里步行或骑自行车。我去过的许多城市都有共享单车系统,你可以在那里租一辆自行车几个小时。大多数系统都有一个应用程序来帮助用户找到和租用他们的自行车,但对于像我这样的用户来说,将城市中所有可供出租的自行车集中在一个地方会更有帮助。为了解决这个问题并展示开源的力量并为Web应用程序添加位置感知,我结合了可用的公共自行车共享数据、Python编程语言和开源Redis内存数据结构服务进行索引查询地理空间数据。由此产生的共享单车APP整合了很多不同共享系统的数据,包括纽约市的CitiBike共享单车系统(LCTT译注:CitiBike是纽约市的一个私人公共自行车系统,于2013年5月27日正式投入运营,CitiBike是美国最大的公共自行车系统,CitiBike的名字有两个意思,Citi是项目发起人花旗银行的名字,同时Citi和英文“city”的发音相同)。它利用Citibike提供的GeneralBikeshareFeed,并使用其数据来演示使用Redis地理空间数据索引的一些功能。Citibike数据可根据Citibike数据许可协议获得。GeneralBikeshareFeedSpecification(GBFS)是北美共享单车协会制定的开放数据规范,旨在让地图程序和交通程序更容易将共享单车系统添加到相应的平台中台。该规范目前被全球60多个不同的共享系统所使用。提要流由几个简单的JSON数据文件组成,其中包含有关系统状态的信息。提要流以引用子流URL的***JSON文件开头:{"data":{"en":{"feeds":[{"name":"system_information","url":"https://gbfs.citibikenyc.com/gbfs/en/system_information.json"},{"name":"station_information","url":"https://gbfs.citibikenyc.com/gbfs/en/station_information.json"},...]}},"last_updated":1506370010,"ttl":10}第一步是利用system_information和station_information的数据,将共享单车站点的信息加载到Redis中。system_information提供系统ID,这是一种短编码,可用于命名Redis键名。GBFS规范没有规定系统ID的格式,但保证它是全局唯一的。许多自行车共享流使用短名称,如“coastbikeshare”、“boisegreenbike”或“topekametro_bikes”作为系统ID。其他人使用常见的地理缩写,例如NYC或BA,并使用通用唯一标识符(UUID)。自行车共享应用程序使用此标识符作为前缀来为给定系统构建唯一键。station_information流提供有关构成整个系统的共享单车站点的静态信息。站由具有多个字段的JSON对象表示。站点对象中有几个必填字段,用于提供物理自行车站点的ID、名称和位置。还有几个可选字段提供有用的信息,例如最近的十字路口、接受的付款方式。这是共享单车APP这部分的主要信息来源。设置数据库我编写了一个示例应用程序loadstationdata.py,它模拟了从外部源加载数据时后端进程中发生的情况。要查找共享单车站点,首先要从GitHub上GBFS存储库中的systems.csv文件加载共享单车数据。存储库中的systems.csv文件提供了已注册自行车共享系统和可用GBFS流的发现URL。这个发现URL是处理共享单车信息的起点。load_station_data程序获取在系统文件中找到的每个发现URL,并使用它来查找两个子数据流的URL:系统信息和站信息。系统信息提供了一条关键信息:系统的唯一ID。(注意:systems.csv文件中也提供了系统ID,但文件中的某些标识符与数据流中的标识符不匹配,所以我总是从数据流中获取标识符。)有关系统的详细信息,如共享单车网址、电话号码、邮箱等,可以在程序的后续版本中添加,所以使用键${system_id}:system_info将数据存储在Redis中。装载站数据站信息提供系统中每个站的数据,包括系统的位置。load_station_data程序迭代站点数据流中的每个站点,并使用${system_id}:station:${station_id}形式的键将每个站点的数据存储到Redis中。使用GEOADD命令将每个站点的位置添加到共享单车的地理空间索引中。更新数据在后续运行中,我不希望代码从Redis中删除所有提要数据并将其重新加载到一个空的Redis数据库中,因此我仔细考虑了如何处理数据的就地更新。该代码首先将系统需要处理的所有共享单车站点信息数据集加载到内存中。当一个站点的信息被加载时,该站点将根据Redis键名从内存中的站点集合中删除。加载所有站点数据后,我们剩下一个集合,其中包含必须从系统中删除的所有站点数据。该程序遍历数据集并创建删除站信息的事务,从地理空间索引中删除站的键名,并从系统的站列表中删除站。代码要点示例代码中有一些值得注意的地方。首先,所有数据项都使用GEOADD命令添加到地理空间索引,并使用ZREM命令删除。由于地理空间类型的底层实现使用排序集,因此需要ZREM来删除数据项。注意:为简单起见,示例代码演示了如何在单个Redis节点上工作;为了在集群环境中运行,需要重构事务块。如果您使用的是Redis4.0(或更高版本),则可以在代码中使用DELETE和HMSET命令。Redis4.0提供了UNLINK命令来替代异步版本的DELETE命令。UNLINK命令将从键空间中删除键,但它会在单独的线程中回收内存。在Redis4.0中,HMSET命令已被弃用,HSET命令现在接受可变参数(即它接受可变数量的参数)。通知客户处理完成后,通知将发送给依赖我们数据的客户。使用Redis发布/订阅机制,通知将通过geobike:station_changed通道与系统ID一起发送。数据模型在Redis中构建数据时,最重要的考虑因素是如何查询信息。自行车共享程序需要支持的两个主要查询是:查找我们附近的站点显示有关站点的信息Redis提供了两种主要的数据类型来存储数据:哈希和有序集。哈希类型很好地映射到表示站点的JSON对象;由于Redis哈希不使用固定数据结构,因此它们可用于存储可变站点信息。当然,从地理上查找站点需要地理空间索引来搜索相对于某些坐标的站点。Redis提供了几个使用有序集数据结构构建地理空间索引的命令。我们使用格式为${system_id}:station:${station_id}的键来存储站点相关信息,并使用格式为${system_id}:stations:location的键来查找站点的地理空间索引。获取用户的位置构建应用程序的下一步是确定用户的当前位置。大多数应用程序通过操作系统提供的内置服务来实现这一点。操作系统可以为应用程序提供基于设备内置GPS硬件的位置,或来自设备可用WiFi网络的大概位置。查找站点找到用户的位置后,下一步就是查找附近的共享单车站点。Redis的地理空间功能可以返回用户当前坐标给定距离内的所有站点的信息。以下是使用Redis命令行界面的示例。想象一下,我在纽约市第五大道的AppleStore,我想去市中心西37街的MOOD布料店见我的好朋友Swatch。我可以坐出租车或地铁,但我更喜欢骑自行车。附近有没有我可以使用的自行车共享站?AppleStore位于40.76384,-73.97297。根据地图,零售店方圆500英尺内有两个自行车停靠点(地图上方的蓝色区域),即ArmyPlazaCentralParkSouth和East58thStreetMadison。我可以使用RedisGEORADIUS命令查询500英尺半径范围内车站的NYC系统索引:127.0.0.1:6379>GEORADIUSNYC:stations:location-73.9729740.76384500ft1)"NYC:station:3457"2)"NYC:station:281"Redis返回在该半径范围内找到的两个共享单车站点,使用地理空间索引中的元素作为特定站点元数据的键。下一步是查找两个车站的名称:127.0.0.1:6379>hgetNYC:station:281name"GrandArmyPlaza&CentralParkS"127.0.0.1:6379>hgetNYC:station:3457name"E58St&MadisonAve”这些关键名称对应于上面地图上标识的车站。如果需要,可以将更多标志添加到GEORADIUS命令以获取元素列表、每个元素的坐标以及它们与当前点的距离:127.0.0.1:6379>GEORADIUSNYC:stations:location-73.9729740.76384500ftWITHDISTWITHCOORDASC1)1)"NYC:station:281"2)"289.1995"3)1)"-73.97371262311935425"2)"40.76439830559216659"2)1)"NYC:station:3457"2)"383.1782"3)1)"-73.97209256887435913"2)"40.76302702144496237"查找与这些键名称关联的名称会生成一个有序的站点列表,我可以从中进行选择。Redis不提供方向和方向,因此我使用设备操作系统的方向功能绘制从当前位置到所选自行车站的路线。GEORADIUS函数可以在您最喜欢的开发框架的API中轻松实现,从而为应用程序添加定位功能。其他查询命令除了GEORADIUS命令外,Redis还提供了另外三个查询索引数据的命令:GEOPOS、GEODIST和GEORADIUSBYMEMBER。GEOPOS命令可以为地理散列geohash中的给定元素提供坐标。例如,如果我知道在8West38thStreet有一个ID为523的自行车共享站,那么该站的元素名称是NYC:station:523。使用Redis,我可以找到车站的经度和纬度:127.0.0.1:6379>geoposNYC:stations:locationNYC:station:5231)1)"-73.99138301610946655"2)"40.75466497634030105"GEODIST命令提供了两者之间的距离.如果我想找出ArmyPlaza的中央公园南自行车站和东58街的Madison自行车站之间的距离,我会使用以下命令:127.0.0.1:6379>GEODISTNYC:stations:locationNYC:station:281NYC:station:3457ft"671.4900"***,GEORADIUSBYMEMBER命令类似于GEORADIUS命令,但该命令不是采用一组坐标,而是采用索引的另一个成员的名称并返回所有成员。要查找ArmyPlazaCentralParkSouthStation1000英尺以内的所有车站,请输入以下内容:127.0.0.1:6379>GEORADIUSBYMEMBERNYC:stations:locationNYC:station:2811000ftWITHDIST1)1)"NYC:station:281"2)"0.0000"2)1)"NYC:station:3132"2)"793.4223"3)1)"NYC:station:2006"2)"911.9752"4)1)"NYC:station:3136"2)"940.3399"5)1)"NYC:station:3457"2)"671.4900"虽然此示例侧重于使用Python和Redis解析数据并构建自行车共享系统位置的索引,但它可以很容易地推导出来定位餐厅,公共交通,或开发人员希望帮助用户找到的任何其他类型的地方。