乐趣区

关于springboot:个人学习系列-Spring-Boot使用RedisGeo实现位置查找功能

最近应用团油的时候总是感觉他的那个依照间隔排序的性能很好,所以就试着钻研一下。

1. 新建 spring boot 我的项目

1.1 pom.xml

增加 redis 依赖和 lombok 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
    <scope>provided</scope>
</dependency>

1.2 application.yml

# Redis 数据库索引(默认为 0)spring:
  redis:
    database: 0
    # Redis 服务器地址
    host: 127.0.0.1
    # Redis 服务器连贯端口
    port: 6379
    # Redis 服务器连贯明码(默认为空)password:
    # 连接池最大连接数(应用负值示意没有限度)jedis:
      pool:
        max-active: 20
        # 连接池最大阻塞等待时间(应用负值示意没有限度)max-wait: -1
        # 连接池中的最大闲暇连贯
        max-idle: 10
        # 连接池中的最小闲暇连贯
        min-idle: 0
    # 连贯超时工夫(毫秒)timeout: 1000

1.3 新建实体类

/**
 * 油站实体类
 * @author zhouzhaodong
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ServiceStation implements Serializable {

    /** 油站 */
    private String serviceStationName;

    /** 经度 */
    private Double longitude;

    /** 纬度 */
    private Double latitude;

}

1.4 新建 service

/**
 * 服务接口定义
 * @author zhouzhaodong
 */
public interface RedisGeoService {

    /**
     * 把油站信息保留到 Redis 中
     * @param serviceStations {@link ServiceStation}
     * @return 胜利保留的个数
     * */
    Long saveServiceStationToRedis(Collection<ServiceStation> serviceStations);

    /**
     * 获取给定油站的坐标
     * @param serviceStations 给定油站 key
     * @return {@link Point}s
     * */
    List<Point> getServiceStationPos(String[] serviceStations);

    /**
     * 获取两个油站之间的间隔
     * @param serviceStation1 第一个油站
     * @param serviceStation2 第二个油站
     * @param metric {@link Metric} 单位信息, 能够是 null
     * @return {@link Distance}
     * */
    Distance getTwoServiceStationDistance(String serviceStation1, String serviceStation2, Metric metric);

    /**
     * 依据给定地理位置坐标获取指定范畴内的地理位置汇合
     * @param within {@link Circle} 中心点和间隔
     * @param args {@link RedisGeoCommands.GeoRadiusCommandArgs} 限度返回的个数和排序形式, 能够是 null
     * @return {@link RedisGeoCommands.GeoLocation}
     * */
    GeoResults<RedisGeoCommands.GeoLocation<String>> getPointRadius(Circle within, RedisGeoCommands.GeoRadiusCommandArgs args);

    /**
     * 依据给定地理位置获取指定范畴内的地理位置汇合
     * @param member 油站名称
     * @param distance 间隔范畴
     * @param args {@link RedisGeoCommands.GeoRadiusCommandArgs} 限度返回的个数和排序形式, 能够是 null
     * @return
     */
    GeoResults<RedisGeoCommands.GeoLocation<String>> getMemberRadius(String member, Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args);

    /**
     * 获取某个地理位置的 geohash 值
     * @param serviceStations 给定油站 key
     * @return city geohashs
     * */
    List<String> getServiceStationGeoHash(String[] serviceStations);

}

1.5 新建 service 实现类

/**
 * 服务接口实现
 * @author zhouzhaodong
 */
@Service
@Slf4j
public class RedisGeoServiceImpl implements RedisGeoService {

    /**
     * redis 的 key
     */
    private final String GEO_KEY = "ah-cities";

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public Long saveServiceStationToRedis(Collection<ServiceStation> serviceStation) {log.info("start to save station info: {}.", serviceStation);

        GeoOperations<String, String> ops = redisTemplate.opsForGeo();
        Set<RedisGeoCommands.GeoLocation<String>> locations = new HashSet<>();
        // 将坐标转为坐标点
        serviceStation.forEach(ci -> locations.add(new RedisGeoCommands.GeoLocation<>(ci.getServiceStationName(), new Point(ci.getLongitude(), ci.getLatitude())
        )));
        log.info("done to save station info.");
        return ops.add(GEO_KEY, locations);
    }

    @Override
    public List<Point> getServiceStationPos(String[] serviceStations) {GeoOperations<String, String> ops = redisTemplate.opsForGeo();
        // 依据油站名称获取油站的坐标
        return ops.position(GEO_KEY, serviceStations);
    }

    @Override
    public Distance getTwoServiceStationDistance(String serviceStations1, String serviceStations2, Metric metric) {GeoOperations<String, String> ops = redisTemplate.opsForGeo();
        return metric == null ?
                ops.distance(GEO_KEY, serviceStations1, serviceStations2) : ops.distance(GEO_KEY, serviceStations1, serviceStations2, metric);
    }

    @Override
    public GeoResults<RedisGeoCommands.GeoLocation<String>> getPointRadius(Circle within, RedisGeoCommands.GeoRadiusCommandArgs args) {GeoOperations<String, String> ops = redisTemplate.opsForGeo();
        return args == null ?
                ops.radius(GEO_KEY, within) : ops.radius(GEO_KEY, within, args);
    }

    @Override
    public GeoResults<RedisGeoCommands.GeoLocation<String>> getMemberRadius(String member, Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args) {GeoOperations<String, String> ops = redisTemplate.opsForGeo();
        return args == null ?
                ops.radius(GEO_KEY, member, distance) : ops.radius(GEO_KEY, member, distance, args);
    }

    @Override
    public List<String> getServiceStationGeoHash(String[] serviceStations) {GeoOperations<String, String> ops = redisTemplate.opsForGeo();
        return ops.hash(GEO_KEY, serviceStations);
    }

}

1.6 测试代码

@SpringBootTest
class RedisGeoApplicationTests {


    @Autowired
    private RedisGeoService geoService;

    /**
     * 测试 把油站信息保留到 Redis 中
     * */
    @Test
    public void testSaveServiceStationToRedis() {List<ServiceStation> serviceStations = new ArrayList<>();
        serviceStations.add(new ServiceStation("金盾", 117.17, 31.52));
        serviceStations.add(new ServiceStation("中石油", 117.02, 30.31));
        serviceStations.add(new ServiceStation("中石化", 116.47, 33.57));
        serviceStations.add(new ServiceStation("山东石化", 116.58, 33.38));
        serviceStations.add(new ServiceStation("青岛石化", 115.48, 32.54));
        serviceStations.add(new ServiceStation("壳牌", 117.21, 32.56));
        serviceStations.add(new ServiceStation("中国化工", 118.18, 29.43));
        System.out.println(geoService.saveServiceStationToRedis(serviceStations));
    }

    /**
     * 测试 获取给定油站的坐标
     * 如果传递的 city 在 Redis 中没有记录, 会返回什么呢 ? 例如, 这里传递的 xxx
     * */
    @Test
    public void testGetServiceStationPos() {

        System.out.println(geoService.getServiceStationPos(Arrays.asList("中石油", "中石化", "xxx").toArray(new String[3])
        ));
    }

    /**
     * 测试 获取两个油站之间的间隔
     * */
    @Test
    public void testGetTwoServiceStationDistance() {System.out.println(geoService.getTwoServiceStationDistance("壳牌", "金盾", null).getValue());
        System.out.println(geoService.getTwoServiceStationDistance("壳牌", "金盾", Metrics.KILOMETERS).getValue());
    }

    /**
     * 测试 依据给定地理位置坐标获取指定范畴内的地理位置汇合
     * */
    @Test
    public void testGetPointRadius() {Point center = new Point(117.17, 31.52);
        Distance radius = new Distance(140, Metrics.KILOMETERS);
        Circle within = new Circle(center, radius);

        System.out.println(geoService.getPointRadius(within, null));

        // 获取前两个油站地位, 同时返回间隔中心点的间隔
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending();
        System.out.println(geoService.getPointRadius(within, args));
    }

    /**
     * 测试 依据给定地理位置获取指定范畴内的地理位置汇合
     * */
    @Test
    public void testGetMemberRadius() {Distance radius = new Distance(200, Metrics.KILOMETERS);

        System.out.println(geoService.getMemberRadius("金盾", radius, null));

        // order by 间隔 limit 2, 同时返回间隔中心点的间隔
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending();
        System.out.println(geoService.getMemberRadius("金盾", radius, args));
    }

    /**
     * 测试 获取某个地理位置的 geohash 值
     * */
    @Test
    public void testGetServiceStationGeoHash() {

        System.out.println(geoService.getServiceStationGeoHash(Arrays.asList("中石化", "中石油", "xxx").toArray(new String[3])
        ));
    }
}

结束!

集体博客

http://www.zhouzhaodong.xyz/a…

源代码地址

https://github.com/zhouzhaodo…

退出移动版