最近在做一款交友软件的APP,现在有一个功能需要实现搜索附近的人。后来发现用redis 的GEO功能实现非常简单。
先说一下设计思路,每个用户在登陆的时候都会添加一下经纬度,这个是APP端获取的,然后设置一下这个经纬度到mysql数据库中,最后把经纬度同步到redis数据库中。我们先来了解一下 redis GEO功能。
我们在用到redis的geo功能时候一定要把版本设置到3.2版本以上,3.2版本以上才有这些方法。
核心的用到的pom配置
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
核心方法
geoadd:增加某个地理位置的坐标。
geopos:获取某个地理位置的坐标。
geodist:获取两个地理位置的距离。
georadius:根据给定地理位置坐标获取指定范围内的地理位置集合。
georadiusbymember:根据给定地理位置获取指定范围内的地理位置集合。
geohash:获取某个地理位置的geohash值。
具体实现代码:
实体类:
package com.example.demo.entity;
import lombok.Data;
/**
* @author XuYangWei
* @Description:
* @Date 2021/7/21 11:41
*/
@Data
public class Coordinate {
double latitude;
double longitude;
String key;
}
工具包:
package com.example.demo.utils;
import com.example.demo.entity.Coordinate;
import redis.clients.jedis.*;
import redis.clients.jedis.params.GeoRadiusParam;
import java.util.List;
public class RedisUtil {
private static JedisPool jedisPool = null;
// Redis服务器IP
private static String ADDR = "127.0.0.1";
// Redis的端口号
private static int PORT = 6379;
// 访问密码
// private static String AUTH = "";
/**
* 初始化Redis连接池
*/
static {
try {
JedisPoolConfig config = new JedisPoolConfig();
// 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
config.setBlockWhenExhausted(true);
// 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");
// 是否启用pool的jmx管理功能, 默认true
config.setJmxEnabled(true);
// 最大空闲连接数, 默认8个 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
config.setMaxIdle(8);
// 最大连接数, 默认8个
config.setMaxTotal(200);
// 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
config.setMaxWaitMillis(1000 * 100);
// 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
config.setTestOnBorrow(true);
jedisPool = new JedisPool(config, ADDR, PORT, 3000);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取Jedis实例
*
* @return
*/
public synchronized static Jedis getJedis() {
try {
if (jedisPool != null) {
Jedis resource = jedisPool.getResource();
return resource;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 释放jedis资源
*
* @param jedis
*/
public static void close(final Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}
/**
* 添加坐标
* key 经度 维度 距离
* return m 表示单位为米*/
public static Long addReo(Coordinate coordinate) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//第一个参数可以理解为表名
return jedis.geoadd("test",coordinate.getLongitude(),coordinate.getLatitude(),coordinate.getKey());
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
if (null != jedis)
jedis.close();
}
return null;
}
/**
* 查询附近人
* key 经度 维度 距离
* return GeoRadiusResponse*/
public static List<GeoRadiusResponse> geoQuery(Coordinate coordinate) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//200F GeoUnit.KM表示km
return jedis.georadius("test",coordinate.getLongitude(),coordinate.getLatitude(),200F, GeoUnit.KM, GeoRadiusParam.geoRadiusParam().withDist());
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
if (null != jedis)
jedis.close();
}
return null;
}
}
测试方法:
package com.example.demo;
import com.example.demo.entity.Coordinate;
import com.example.demo.utils.RedisUtil;
import redis.clients.jedis.GeoRadiusResponse;
import redis.clients.jedis.Jedis;
import java.util.List;
import static com.example.demo.utils.RedisUtil.addReo;
import static com.example.demo.utils.RedisUtil.geoQuery;
/**
* @author XuYangWei
* @Description:
* @Date 2021/7/21 11:48
*/
public class TestRedis {
public static void main(String[] args) {
Jedis jedis = RedisUtil.getJedis();
java.text.DecimalFormat df = new java.text.DecimalFormat("#.######");//保留六位小数
//添加经纬度
Coordinate coordinate = new Coordinate();
coordinate.setLatitude(31.244803); //维度
coordinate.setLongitude(121.483671); //经度
coordinate.setKey("1"); //可以作为用户表的id
//添加经纬度
Coordinate coordinate1 = new Coordinate();
coordinate1.setLatitude(31.245321); //维度
coordinate1.setLongitude(121.485015); //经度
coordinate1.setKey("2"); //可以作为用户表的id
//添加经纬度
Coordinate coordinate2 = new Coordinate();
coordinate2.setLatitude(31.245456); //维度
coordinate2.setLongitude(121.485285); //经度
coordinate2.setKey("3"); //可以作为用户表的id
addReo(coordinate);
addReo(coordinate1);
addReo(coordinate2);
//添加经纬度
coordinate.setLatitude(31.244803); //维度
coordinate.setLongitude(121.483671); //经度
coordinate.setKey("1"); //用户表的id 以当前用户作为查询条件,查询他周围的人数
List<GeoRadiusResponse> list = geoQuery(coordinate);
for (GeoRadiusResponse geo : list) {
System.out.println(geo.getMemberByString()); //主键 有主键了个人信息就很简单了
System.out.println(df.format(geo.getDistance())); //距离多少米
}
RedisUtil.close(jedis);
}
}
打印效果: