Geo redis实现附近的车辆/人

我会带着你远行 2022-06-08 05:06 398阅读 0赞

前段时间业务需要实现一个用户查看附近车辆的服务:用户根据自己的定位坐标(经度、纬度)来快速搜索到附近的车辆,可以根据半径进行搜索。

根据需求也制定了几个技术方案,思路是这样的:将用户的坐标跟数据库中各个车辆的最新坐标进行距离计算,找到指定距离内的车辆并按照距离从小到大排序返回给业务。这种方法需要找到每辆车的最新定位,数据库中车辆定位数庞大,查询时数据库压力较大,并且每辆车需要计算一次距离,整个流程的耗时耗费数据库资源可想而知。考虑第二种方案,因为现在业务所有车辆的最新定位都保存在缓存中(memcached),可以采用把所有车辆的定位先从缓存中拿出来,然后进行距离计算,这种方法同样存在问题,就是操作缓存过于频繁。

以上两种方案,都是通过计算距离来判断的,后来发现geohash算法在这方面很高效(geohash原理参加 http://blog.jobbole.com/80633/)。于是将该思路应用到以上两个方案中,无非是多保存一份每个定位点的geohash值,然后根据该hash值来进行排序找到附近的车(当然该hash值在数据库中做索引)。沿着这个思路的技术实现也考虑了一些,等技术实现(具体实现思路就不啰嗦了)基本考虑好的时候,突然发现redis 自带的geo api能够完美地解决该问题。于是,啥也别说,直接使用现成的就好啦。

闲聊结束,直接操作,按照步骤来就能轻松搞定:

一、Jedis包引入项目

项目代码使用java编写,操作redis使用的是Jedis库,所以第一步先把Jedis找到并导入项目中。使用maven构建工程的话,在pom.xml中加入如下:

  1. <dependency>
  2. <groupId>redis.clients</groupId>
  3. <artifactId>jedis</artifactId>
  4. <version>2.9.0</version>
  5. </dependency>

二、Redis Geo Api使用

其实Redis Geo Api很多,本文只讲两个,因为实现这个功能就只需要要两个API(无非一个插入,一个搜索),非常简单。为了便于理解,将流程分为插入端以及搜索端。

注:两个API都存在于redis.clients.jedis.JedisCluster类中。

1、插入端(geoadd)

插入端很好理解,就是将车辆的经纬度插入redis(服务接收到各个车辆的定位数据,然后使用geoadd接口将数据存入redis)。当然这个跟普通的redis插入操作不同,这个不同体现在内部实现上不同(可以理解为内部实现了geo算法并且存储机制不同,以满足GEO功能)。

  1. # key:任意值。使用者可以根据车辆属性将车进行分类,key作为类别名称。例如按照城市划分,这样在搜索时知道车辆属于哪个城市时只在这个key中进行搜索
  2. # longitude: 经度
  3. # latitude: 纬度
  4. # member: 成员。这里就是车牌号
  5. public Long geoadd(final String key, final double longitude, final double latitude,final String member)

2、搜索端(georadius)

搜索端同样好理解,就是使用方嘛。用户给点一个定位坐标和搜索半径,列出附近的车辆以及经纬度。

  1. # key: 对应geoadd中的key
  2. # longitude: 经度
  3. # latitude: 纬度
  4. # unit: 距离单位(GeoUnit.M:米,GeoUnit.KM:千米, GeoUnit.MI:英里, GeoUnit.FT:英尺)
  5. # param: 参数 参见 http://redisdoc.com/geo/georadius.html
  6. public List<GeoRadiusResponse> georadius(String key, double longitude, double latitude,double radius, GeoUnit unit, GeoRadiusParam param)

可以看到,这个函数返回的是GeoRadiusResponse的集合,拿到这个集合整个工作就完成了。该集合即附近的车的集合,元素为车牌号以及对应的经纬度。

结束了,就这些,把这两个Jedis自带的函数看明白,然后使用在正确的地方就能实现附近的车辆服务了,很简单吧!

-——————————————————————————————————————————————————————————————————————————————————————————————————————————————————-

帮助理解,看一下GeoRadiusResponse这个类的结构:

  1. public class GeoRadiusResponse {
  2. private byte[] member;
  3. private double distance;
  4. private GeoCoordinate coordinate;
  5. public GeoRadiusResponse(byte[] member) {
  6. this.member = member;
  7. }
  8. public void setDistance(double distance) {
  9. this.distance = distance;
  10. }
  11. public void setCoordinate(GeoCoordinate coordinate) {
  12. this.coordinate = coordinate;
  13. }
  14. public byte[] getMember() {
  15. return member;
  16. }
  17. public String getMemberByString() {
  18. return SafeEncoder.encode(member);
  19. }
  20. public double getDistance() {
  21. return distance;
  22. }
  23. public GeoCoordinate getCoordinate() {
  24. return coordinate;
  25. }
  26. }

这里的member就是这里的车牌号,distance为用户跟这个车的距离,coordinate为经纬度坐标。

顺便看下GeoCoordinate这个类的结构:

  1. public class GeoCoordinate {
  2. private double longitude;
  3. private double latitude;
  4. public GeoCoordinate(double longitude, double latitude) {
  5. this.longitude = longitude;
  6. this.latitude = latitude;
  7. }
  8. public double getLongitude() {
  9. return longitude;
  10. }
  11. public double getLatitude() {
  12. return latitude;
  13. }
  14. @Override
  15. public boolean equals(Object o) {
  16. if (this == o) return true;
  17. if (!(o instanceof GeoCoordinate)) return false;
  18. GeoCoordinate that = (GeoCoordinate) o;
  19. if (Double.compare(that.longitude, longitude) != 0) return false;
  20. return Double.compare(that.latitude, latitude) == 0;
  21. }
  22. @Override
  23. public int hashCode() {
  24. // follows IntelliJ default hashCode implementation
  25. int result;
  26. long temp;
  27. temp = Double.doubleToLongBits(longitude);
  28. result = (int) (temp ^ (temp >>> 32));
  29. temp = Double.doubleToLongBits(latitude);
  30. result = 31 * result + (int) (temp ^ (temp >>> 32));
  31. return result;
  32. }
  33. @Override
  34. public String toString() {
  35. return "(" + longitude + "," + latitude + ")";
  36. }
  37. }

发表评论

表情:
评论列表 (有 0 条评论,398人围观)

还没有评论,来说两句吧...

相关阅读

    相关 redis实现附近

    最近在做一款交友软件的APP,现在有一个功能需要实现搜索附近的人。后来发现用redis 的GEO功能实现非常简单。 先说一下设计思路,每个用户在登陆的时候都会添加一下经纬度,