Redis 3.2 contains significant changes to the API and implementation of Redis. A new set of commands for Geo indexing was added (GEOADD, GEORADIUS and related commands).
关于 Geo,需要知道
- Redis 的 Geo 是在 3.2 版本才有的
- 使用 geohash 保存地理位置的坐标
- 使用有序集合(zset)保存地理位置的集合
- GEOADD:增加某个地理位置的坐标
- GEOPOS:获取某个地理位置的坐标
- GEODIST:获取两个地理位置的距离
- GEORADIUS:根据给定地理位置坐标获取指定范围内的地理位置集合
- GEORADIUSBYMEMBER:根据给定地理位置获取指定范围内的地理位置集合
- GEOHASH:获取某个地理位置的 geohash 值
- 官网
- 黄健宏的文章
说明:以城市信息为目标,使用 StringRedisTemplate 操作 Redis 提供的关于 Geo 的6个命令。
- JDK 版本
1.8
- SpringBoot 版本
org.springframework.boot
spring-boot-starter-parent
2.0.2.RELEASE
- redis starter
org.springframework.boot
spring-boot-starter-data-redis
- vo 对象定义
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CityInfo {
private String city;
private Double longitude;
private Double latitude;
}
- 服务接口定义
import com.imooc.ad.vo.CityInfo;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import java.util.Collection;
import java.util.List;
public interface IGeoService {
Long saveCityInfoToRedis(Collection cityInfos);
List getCityPos(String[] cities);
Distance getTwoCityDistance(String city1, String city2, Metric metric);
GeoResults> getPointRadius(
Circle within, RedisGeoCommands.GeoRadiusCommandArgs args);
GeoResults> getMemberRadius(
String member, Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args);
List getCityGeoHash(String[] cities);
}
- 服务接口实现
import com.alibaba.fastjson.JSON;
import com.imooc.ad.service.IGeoService;
import com.imooc.ad.vo.CityInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metric;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Slf4j
@Service
public class GeoServiceImpl implements IGeoService {
private final String GEO_KEY = "ah-cities";
private final StringRedisTemplate redisTemplate;
@Autowired
public GeoServiceImpl(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public Long saveCityInfoToRedis(Collection cityInfos) {
log.info("start to save city info: {}.", JSON.toJSONString(cityInfos));
GeoOperations ops = redisTemplate.opsForGeo();
Set> locations = new HashSet<>();
cityInfos.forEach(ci -> locations.add(new RedisGeoCommands.GeoLocation(
ci.getCity(), new Point(ci.getLongitude(), ci.getLatitude())
)));
log.info("done to save city info.");
return ops.add(GEO_KEY, locations);
}
@Override
public List getCityPos(String[] cities) {
GeoOperations ops = redisTemplate.opsForGeo();
return ops.position(GEO_KEY, cities);
}
@Override
public Distance getTwoCityDistance(String city1, String city2, Metric metric) {
GeoOperations ops = redisTemplate.opsForGeo();
return metric == null ?
ops.distance(GEO_KEY, city1, city2) : ops.distance(GEO_KEY, city1, city2, metric);
}
@Override
public GeoResults> getPointRadius(
Circle within, RedisGeoCommands.GeoRadiusCommandArgs args
) {
GeoOperations ops = redisTemplate.opsForGeo();
return args == null ?
ops.radius(GEO_KEY, within) : ops.radius(GEO_KEY, within, args);
}
@Override
public GeoResults> getMemberRadius(
String member, Distance distance, RedisGeoCommands.GeoRadiusCommandArgs args
) {
GeoOperations ops = redisTemplate.opsForGeo();
return args == null ?
ops.radius(GEO_KEY, member, distance) : ops.radius(GEO_KEY, member, distance, args);
}
@Override
public List getCityGeoHash(String[] cities) {
GeoOperations ops = redisTemplate.opsForGeo();
return ops.hash(GEO_KEY, cities);
}
}
- 测试用例
import com.alibaba.fastjson.JSON;
import com.imooc.ad.Application;
import com.imooc.ad.vo.CityInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class GeoServiceTest {
private List cityInfos;
@Autowired
private IGeoService geoService;
@Before
public void init() {
cityInfos = new ArrayList<>();
cityInfos.add(new CityInfo("hefei", 117.17, 31.52));
cityInfos.add(new CityInfo("anqing", 117.02, 30.31));
cityInfos.add(new CityInfo("huaibei", 116.47, 33.57));
cityInfos.add(new CityInfo("suzhou", 116.58, 33.38));
cityInfos.add(new CityInfo("fuyang", 115.48, 32.54));
cityInfos.add(new CityInfo("bengbu", 117.21, 32.56));
cityInfos.add(new CityInfo("huangshan", 118.18, 29.43));
}
@Test
public void testSaveCityInfoToRedis() {
System.out.println(geoService.saveCityInfoToRedis(cityInfos));
}
@Test
public void testGetCityPos() {
System.out.println(JSON.toJSONString(geoService.getCityPos(
Arrays.asList("anqing", "suzhou", "xxx").toArray(new String[3])
)));
}
@Test
public void testGetTwoCityDistance() {
System.out.println(geoService.getTwoCityDistance("anqing", "suzhou", null).getValue());
System.out.println(geoService.getTwoCityDistance("anqing", "suzhou", Metrics.KILOMETERS).getValue());
}
@Test
public void testGetPointRadius() {
Point center = new Point(cityInfos.get(0).getLongitude(), cityInfos.get(0).getLatitude());
Distance radius = new Distance(200, Metrics.KILOMETERS);
Circle within = new Circle(center, radius);
System.out.println(JSON.toJSONString(geoService.getPointRadius(within, null)));
// order by 距离 limit 2, 同时返回距离中心点的距离
RedisGeoCommands.GeoRadiusCommandArgs args =
RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending();
System.out.println(JSON.toJSONString(geoService.getPointRadius(within, args)));
}
@Test
public void testGetMemberRadius() {
Distance radius = new Distance(200, Metrics.KILOMETERS);
System.out.println(JSON.toJSONString(geoService.getMemberRadius("suzhou", radius, null)));
// order by 距离 limit 2, 同时返回距离中心点的距离
RedisGeoCommands.GeoRadiusCommandArgs args =
RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(2).sortAscending();
System.out.println(JSON.toJSONString(geoService.getMemberRadius("suzhou", radius, args)));
}
@Test
public void testGetCityGeoHash() {
System.out.println(JSON.toJSONString(geoService.getCityGeoHash(
Arrays.asList("anqing", "suzhou", "xxx").toArray(new String[3])
)));
}
}
欢迎关注课程:
基于 SpringCloud 微服务架构下 广告系统设计与实现
JAVA分布式优惠券系统后台 手把手实战开发



