SpringBoot redis GEO 实战应用

   日期:2020-07-18     浏览:115    评论:0    
核心提示:上篇文章(Redis地理算法GEO解析和应用)我们对redis GEO算法进行解析以及相关shell命令的使用,这篇文章将带你进行实战应用。依赖注:jedis可以不引入,这里只是为了方便查看源码进行解析 _stringredistemplate georad

上篇文章(Redis地理算法GEO解析和应用)我们对redis GEO算法进行解析以及相关shell命令的使用,这篇文章将带你进行实战应用。

依赖

注:jedis可以不引入,这里只是为了方便查看源码进行解析

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.3.0.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <!--     方便查看源码   -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>

数据源配置

spring:
  redis:
    host: localhost
    port: 6379
    database: 4

编写实例

package com.gmall.user.server;

import com.gamll.user.base.BaseJunitTest;
import com.gmall.user.server.biz.UserBiz;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Slf4j
public class UserBizTest extends BaseJunitTest {
    @Autowired
    private UserBiz userBiz;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Test
    public void getCircleUser() {

        // 初始化数据
        Map<Double, Double> locMap = new HashMap<>(); // <纬度(x),经度(y)>
        locMap.put(121.576334, 31.168569);
        locMap.put(121.587664, 31.205503);
        locMap.put(121.60586, 31.221726);
        locMap.put(121.65238, 31.199703);
        locMap.put(121.544749, 31.204989);
        locMap.put(121.506297, 31.03268);

        Map<Double, String> cityMap = new HashMap<>();
        cityMap.put(121.576334, "上海希奥信息科技有限公司");
        cityMap.put(121.587664, "上海火车站");
        cityMap.put(121.60586, "上海人民广场");
        cityMap.put(121.65238, "上海中医药大学");
        cityMap.put(121.544749, "上海漕河泾经开区新创业园");
        cityMap.put(121.506297, "上海竹林工业园");
        System.out.println("--------------------------------- start to redis sync save Coordinate ---------------------------------");
        // 定义坐标信息
        for (Map.Entry<Double, Double> entry : locMap.entrySet()) {
            Point point = new Point(entry.getKey(), entry.getValue());
            redisTemplate.opsForGeo().add("user-local", point, cityMap.get(entry.getKey()));
        }

        // 设置检索范围
        Point point = new Point(121.587623,31.201719);
        Circle circle = new Circle(point, new Distance(5, Metrics.KILOMETERS));
        // 定义返回结果参数,如果不指定默认只返回content即保存的member信息
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortAscending().limit(5);
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius("user-local", circle,args);
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
        for (GeoResult<RedisGeoCommands.GeoLocation<String>> l1 : list) {
            System.out.println("name : " + l1.getContent().getName() +"  distance : " + l1.getDistance() +" point :" + l1.getContent().getPoint());
            //
        }

        System.out.println("--------------------------------- end to sync save Coordinate ---------------------------------");
    }

}

源码解析

1、坐标添加 add

注:支持单点、多点集合Map同时添加

  • point:元素属性x(经度),y(纬度)

	@Override
	public Long add(K key, Point point, M member) {

		byte[] rawKey = rawKey(key);
		byte[] rawMember = rawValue(member);

		return execute(connection -> connection.geoAdd(rawKey, point, rawMember), true);
	}

	
	@Override
	public Long add(K key, GeoLocation<M> location) {
		return add(key, location.getPoint(), location.getName());
	}

	
	@Override
	public Long add(K key, Map<M, Point> memberCoordinateMap) {

		byte[] rawKey = rawKey(key);
		Map<byte[], Point> rawMemberCoordinateMap = new HashMap<>();

		for (M member : memberCoordinateMap.keySet()) {
			byte[] rawMember = rawValue(member);
			rawMemberCoordinateMap.put(rawMember, memberCoordinateMap.get(member));
		}

		return execute(connection -> connection.geoAdd(rawKey, rawMemberCoordinateMap), true);
	}

	
	@Override
	public Long add(K key, Iterable<GeoLocation<M>> locations) {

		Map<M, Point> memberCoordinateMap = new LinkedHashMap<>();
		for (GeoLocation<M> location : locations) {
			memberCoordinateMap.put(location.getName(), location.getPoint());
		}

		return add(key, memberCoordinateMap);
	}

2、删除坐标remove

注:根据元素名称进行删除,支持多个同时删除

	
	@Nullable
	Long remove(K key, M... members);

3、查询周边radius

注:支持多种条件检索周边

Circle对象属性:

  • Point:需要检索的中心点坐标;
  • Distance:value为检索半径范围;Metric枚举类型为检索范围单位
KILOMETERS(6378.137, "km"), MILES(3963.191, "mi"), NEUTRAL(1, "");

GeoRadiusCommandArgs对象方法:

注:作用指定返回数据结果参数

  • includeCoordinates:返回结果包含坐标信息

  • includeDistance:返回结果包含具中心坐标距离信息

  • sortAscending:按照距离升序排序

  • sortDescending:按照距离降序排序

  • limit:返回结果数量限制


		public static GeoRadiusCommandArgs newGeoRadiusArgs() {
			return new GeoRadiusCommandArgs();
		}

		
		public GeoRadiusCommandArgs includeCoordinates() {

			flags.add(Flag.WITHCOORD);
			return this;
		}

		
		public GeoRadiusCommandArgs includeDistance() {

			flags.add(Flag.WITHDIST);
			return this;
		}

		
		public GeoRadiusCommandArgs sortAscending() {

			sortDirection = Direction.ASC;
			return this;
		}

		
		public GeoRadiusCommandArgs sortDescending() {

			sortDirection = Direction.DESC;
			return this;
		}

		
		public GeoRadiusCommandArgs limit(long count) {

			Assert.isTrue(count > 0, "Count has to positive value.");
			limit = count;
			return this;
		}

radius源码解析:


	@Nullable
	GeoResults<GeoLocation<M>> radius(K key, Circle within);


	
	@Nullable
	GeoResults<GeoLocation<M>> radius(K key, Circle within, GeoRadiusCommandArgs args);

	
	
	@Nullable
	GeoResults<GeoLocation<M>> radius(K key, M member, double radius);


	
	@Nullable
	GeoResults<GeoLocation<M>> radius(K key, M member, Distance distance);


	
	@Nullable
	GeoResults<GeoLocation<M>> radius(K key, M member, Distance distance, GeoRadiusCommandArgs args);

异常现象

Caused by: io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'GEOADD'


org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'GEOADD'

	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:54)
	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:52)
	at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
	at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
	at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
	at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:273)
	at org.springframework.data.redis.connection.lettuce.LettuceGeoCommands.convertLettuceAccessException(LettuceGeoCommands.java:424)
	at org.springframework.data.redis.connection.lettuce.LettuceGeoCommands.geoAdd(LettuceGeoCommands.java:78)
	at org.springframework.data.redis.connection.DefaultedRedisConnection.geoAdd(DefaultedRedisConnection.java:1187)
	at org.springframework.data.redis.connection.DefaultStringRedisConnection.geoAdd(DefaultStringRedisConnection.java:2908)
	at org.springframework.data.redis.core.DefaultGeoOperations.lambda$add$0(DefaultGeoOperations.java:60)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228)
	at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188)
	at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96)
	at org.springframework.data.redis.core.DefaultGeoOperations.add(DefaultGeoOperations.java:60)
	at com.gmall.user.server.UserBizTest.getCircleUser(UserBizTest.java:52)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'GEOADD'
	at io.lettuce.core.ExceptionFactory.createExecutionException(ExceptionFactory.java:135)
	at io.lettuce.core.ExceptionFactory.createExecutionException(ExceptionFactory.java:108)
	at io.lettuce.core.protocol.AsyncCommand.completeResult(AsyncCommand.java:120)
	at io.lettuce.core.protocol.AsyncCommand.complete(AsyncCommand.java:111)
	at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:654)
	at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:614)
	at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:565)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:748)

解决方案:

更换redis版本,因为redis GEO是从>=3.2版本之后开始支持

最后

源码地址:https://gitee.com/mackjie/gmall-service 

项目中gmall-batch -->test-->com.gmall.user.server.UserBizTest#getCircleUser()

这个技能 今天你学到了吗?^_^

 

 
 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服