• 在Java中利用redis实现LBS服务

    发布:51Code 时间: 2018-01-05 10:29

  • 前言 LBS(基于位置的服务) 服务是现在移动互联网中比较常用的功能。例如外卖服务中常用的我附近的店铺的功能,通常是以用户当前的位置坐标为基础,查询一定距离范围类的店铺...

  • 前言

    LBS(基于位置的服务) 服务是现在移动互联网中比较常用的功能。例如外卖服务中常用的我附近的店铺的功能,通常是以用户当前的位置坐标为基础,查询一定距离范围类的店铺,按照距离远近进行倒序排序。

    自从 redis 4 版本发布后, lbs 相关命令正式内置在 redis 的发行版中。要实现上述的功能,主要用到 redis geo 相关的两个命令

    GEOADD 和 GEORADIOUS

    命令描述

    1、GEOADD

    GEOADD key longitude latitude member [longitude latitude member ...]

    这个命令将指定的地理空间位置(纬度、经度、名称)添加到指定的 key 中。

    有效的经度从-180度到180度。

    有效的纬度从-85.05112878度到85.05112878度。

    当坐标位置超出上述指定范围时,该命令将会返回一个错误。

    该命令可以一次添加多个地理位置点

    2、GEORADIOUS

    GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

    这个命令以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

    范围可以使用以下其中一个单位:

    · m 表示单位为米。

    · km 表示单位为千米。

    · mi 表示单位为英里。

    · ft 表示单位为英尺。

    在给定以下可选项时, 命令会返回额外的信息:

    · WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。

    · WITHCOORD: 将位置元素的经度和维度也一并返回。

    · WITHHASH: 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大。

    · ASC: 根据中心的位置, 按照从近到远的方式返回位置元素。

    · DESC: 根据中心的位置, 按照从远到近的方式返回位置元素。

    在默认情况下, GEORADIUS 命令会返回所有匹配的位置元素。 虽然用户可以使用 COUNT  选项去获取前 N 个匹配元素

    接口定义

    package com.x9710.common.redis;

    import com.x9710.common.redis.domain.GeoCoordinate;

    import com.x9710.common.redis.domain.Postion;

    import java.util.List;

    public interface LBSService {

    /**

    * 存储一个位置

    *

    * @param postion 增加的位置对象

    * @throws Exception

    */

    boolean addPostion(Postion postion);

    /**

    * 查询以指定的坐标为中心,指定的距离为半径的范围类的所有位置点

    *

    * @param center   中心点位置

    * @param distinct 最远距离,单位米

    * @param asc      是否倒序排序

    * @return 有效的位置

    */

    List<Postion> radious(String type, GeoCoordinate center, Long distinct, Boolean asc);

    }

    实现的接口

    package com.x9710.common.redis.impl;

    import com.x9710.common.redis.LBSService;

    import com.x9710.common.redis.RedisConnection;

    import com.x9710.common.redis.domain.GeoCoordinate;

    import com.x9710.common.redis.domain.Postion;

    import redis.clients.jedis.GeoRadiusResponse;

    import redis.clients.jedis.GeoUnit;

    import redis.clients.jedis.Jedis;

    import redis.clients.jedis.params.geo.GeoRadiusParam;

    import java.util.ArrayList;

    import java.util.List;

    public class LBSServiceRedisImpl implements LBSService {

    private RedisConnection redisConnection;

    private Integer dbIndex;

    public void setRedisConnection(RedisConnection redisConnection) {

    this.redisConnection = redisConnection;

    }

    public void setDbIndex(Integer dbIndex) {

    this.dbIndex = dbIndex;

    }

    public boolean addPostion(Postion postion) {

    Jedis jedis = redisConnection.getJedis();

    try {

    return (1L == jedis.geoadd(postion.getType(),

    postion.getCoordinate().getLongitude(),

    postion.getCoordinate().getLatitude(),

    postion.getId()));

    } finally {

    if (jedis != null) {

    jedis.close();

    }

    }

    }

    public List<Postion> radious(String type, GeoCoordinate center, Long distinct, Boolean asc) {

    List<Postion> postions = new ArrayList<Postion>();

    Jedis jedis = redisConnection.getJedis();

    try {

    GeoRadiusParam geoRadiusParam = GeoRadiusParam.geoRadiusParam().withCoord().withDist();

    if (asc) {

    geoRadiusParam.sortAscending();

    } else {

    geoRadiusParam.sortDescending();

    }

    List<GeoRadiusResponse> responses = jedis.georadius(type,

    center.getLongitude(),

    center.getLatitude(),

    distinct.doubleValue(),

    GeoUnit.M,

    geoRadiusParam);

    if (responses != null) {

    for (GeoRadiusResponse response : responses) {

    Postion postion = new Postion(response.getMemberByString(),

    type,

    response.getCoordinate().getLongitude(),

    response.getCoordinate().getLatitude());

    postion.setDistinct(response.getDistance());

    postions.add(postion);

    }

    }

    } finally {

    if (jedis != null) {

    jedis.close();

    }

    }

    return postions;

    }

    }

    测试用例

    package com.x9710.common.redis.test;

    import com.x9710.common.redis.RedisConnection;

    import com.x9710.common.redis.domain.GeoCoordinate;

    import com.x9710.common.redis.domain.Postion;

    import com.x9710.common.redis.impl.CacheServiceRedisImpl;

    import com.x9710.common.redis.impl.LBSServiceRedisImpl;

    import org.junit.Assert;

    import org.junit.Before;

    import org.junit.Test;

    import java.util.List;

    /**

    * LBS服务测试类

    *

    * @author 杨高超

    * @since 2017-12-28

    */

    public class RedisLBSTest {

    private CacheServiceRedisImpl cacheService;

    private LBSServiceRedisImpl lbsServiceRedis;

    private String type = "SHOP";

    private GeoCoordinate center;

    @Before

    public void before() {

    RedisConnection redisConnection = RedisConnectionUtil.create();

    lbsServiceRedis = new LBSServiceRedisImpl();

    lbsServiceRedis.setDbIndex(15);

    lbsServiceRedis.setRedisConnection(redisConnection);

    Postion postion = new Postion("2017122801", type, 91.118970, 29.654210);

    lbsServiceRedis.addPostion(postion);

    postion = new Postion("2017122802", type, 116.373472, 39.972528);

    lbsServiceRedis.addPostion(postion);

    postion = new Postion("2017122803", type, 116.344820, 39.948420);

    lbsServiceRedis.addPostion(postion);

    postion = new Postion("2017122804", type, 116.637920, 39.905460);

    lbsServiceRedis.addPostion(postion);

    postion = new Postion("2017122805", type, 118.514590, 37.448150);

    lbsServiceRedis.addPostion(postion);

    postion = new Postion("2017122806", type, 116.374766, 40.109508);

    lbsServiceRedis.addPostion(postion);

    center = new GeoCoordinate();

    center.setLongitude(116.373472);

    center.setLatitude(39.972528);

    }

    @Test

    public void test10KMRadious() {

    List<Postion> postions = lbsServiceRedis.radious(type, center, 1000 * 10L, true);

    Assert.assertTrue(postions.size() == 2 && exist(postions, "2017122802") && exist(postions, "2017122803"));

    }

    @Test

    public void test50KMRadious() {

    List<Postion> postions = lbsServiceRedis.radious(type, center, 1000 * 50L, true);

    Assert.assertTrue(postions.size() == 4

    && exist(postions, "2017122802")

    && exist(postions, "2017122803")

    && exist(postions, "2017122806")

    && exist(postions, "2017122804"));

    }

    private boolean exist(List<Postion> postions, String key) {

    if (postions != null) {

    for (Postion postion : postions) {

    if (postion.getId().equals(key)) {

    return true;

    }

    }

    }

    return false;

    }

    @Before

    public void after() {

    RedisConnection redisConnection = RedisConnectionUtil.create();

    cacheService = new CacheServiceRedisImpl();

    cacheService.setDbIndex(15);

    cacheService.setRedisConnection(redisConnection);

    cacheService.delObject(type);

    }

    }

    测试结果

    后记

    这样,我们通过 redis 就能简单实现一个我附近的小店的功能的 LBS服务。

    本文程序是在前面的文章《在 Java 中使用 redis》的基础上添加新的实现类的方式完成的。代码同步发布在 GitHub 仓库中
     

    本文原作者:高超杨
    文章来源:简书

  • 上一篇:Java单例模式我的看法

    下一篇:对Java程序猿的学习的建议

网站导航
Copyright(C)51Code软件开发网 2003-2018 , 沪ICP备05003035号