欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

荐 SpringBoot redis GEO 实战应用

程序员文章站 2022-10-03 17:24:02
上篇文章(Redis地理算法GEO解析和应用)我们对redis GEO算法进行解析以及相关shell命令的使用,这篇文章将带你进行实战应用。依赖注:jedis可以不引入,这里只是为了方便查看源码进行解析 ...

上篇文章(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;

/**
 * @author HL.Wu
 * @date 2020/5/28 17:54
 * Copyright ©https://blog.csdn.net/qq_31150503 Copyright@2009-2020 AII Right Reserve
 */
@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 ---------------------------------");
    }

}

荐
                                                        SpringBoot redis GEO 实战应用

源码解析

1、坐标添加 add

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

  • point:元素属性x(经度),y(纬度)
/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.GeoOperations#add(java.lang.Object, org.springframework.data.geo.Point, java.lang.Object)
	 */
	@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);
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.GeoOperations#add(java.lang.Object, org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation)
	 */
	@Override
	public Long add(K key, GeoLocation<M> location) {
		return add(key, location.getPoint(), location.getName());
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.GeoOperations#add(java.lang.Object, java.util.Map)
	 */
	@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);
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.redis.core.GeoOperations#add(java.lang.Object, java.lang.Iterable)
	 */
	@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

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

	/**
	 * Remove the {@literal member}s.
	 *
	 * @param key must not be {@literal null}.
	 * @param members must not be {@literal null}.
	 * @return Number of elements removed. {@literal null} when used in pipeline / transaction.
	 * @since 2.0
	 */
	@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:返回结果数量限制

/**
		 * Create new {@link GeoRadiusCommandArgs}.
		 *
		 * @return never {@literal null}.
		 */
		public static GeoRadiusCommandArgs newGeoRadiusArgs() {
			return new GeoRadiusCommandArgs();
		}

		/**
		 * Sets the {@link Flag#WITHCOORD} flag to also return the longitude, latitude coordinates of the matching items.
		 *
		 * @return
		 */
		public GeoRadiusCommandArgs includeCoordinates() {

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

		/**
		 * Sets the {@link Flag#WITHDIST} flag to also return the distance of the returned items from the specified center.
		 *
		 * @return never {@literal null}.
		 */
		public GeoRadiusCommandArgs includeDistance() {

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

		/**
		 * Sort returned items from the nearest to the furthest, relative to the center.
		 *
		 * @return never {@literal null}.
		 */
		public GeoRadiusCommandArgs sortAscending() {

			sortDirection = Direction.ASC;
			return this;
		}

		/**
		 * Sort returned items from the furthest to the nearest, relative to the center.
		 *
		 * @return never {@literal null}.
		 */
		public GeoRadiusCommandArgs sortDescending() {

			sortDirection = Direction.DESC;
			return this;
		}

		/**
		 * Limit the results to the first N matching items.
		 *
		 * @param count
		 * @return never {@literal null}.
		 */
		public GeoRadiusCommandArgs limit(long count) {

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

radius源码解析:

/**
	 * Get the {@literal member}s within the boundaries of a given {@link Circle}.
	 *
	 * @param key must not be {@literal null}.
	 * @param within must not be {@literal null}.
	 * @return never {@literal null} unless used in pipeline / transaction.
	 * @since 2.0
	 * @see <a href="https://redis.io/commands/georadius">Redis Documentation: GEORADIUS</a>
	 */
	@Nullable
	GeoResults<GeoLocation<M>> radius(K key, Circle within);


	/**
	 * Get the {@literal member}s within the boundaries of a given {@link Circle} applying {@link GeoRadiusCommandArgs}.
	 *
	 * @param key must not be {@literal null}.
	 * @param within must not be {@literal null}.
	 * @param args must not be {@literal null}.
	 * @return never {@literal null} unless used in pipeline / transaction.
	 * @since 2.0
	 * @see <a href="https://redis.io/commands/georadius">Redis Documentation: GEORADIUS</a>
	 */
	@Nullable
	GeoResults<GeoLocation<M>> radius(K key, Circle within, GeoRadiusCommandArgs args);

	
	/**
	 * Get the {@literal member}s within the circle defined by the {@literal members} coordinates and given
	 * {@literal radius}.
	 *
	 * @param key must not be {@literal null}.
	 * @param member must not be {@literal null}.
	 * @param radius
	 * @return never {@literal null} unless used in pipeline / transaction.
	 * @since 2.0
	 * @see <a href="https://redis.io/commands/georadiusbymember">Redis Documentation: GEORADIUSBYMEMBER</a>
	 */
	@Nullable
	GeoResults<GeoLocation<M>> radius(K key, M member, double radius);


	/**
	 * Get the {@literal member}s within the circle defined by the {@literal members} coordinates and given
	 * {@literal radius} applying {@link Metric}.
	 *
	 * @param key must not be {@literal null}.
	 * @param member must not be {@literal null}.
	 * @param distance must not be {@literal null}.
	 * @return never {@literal null} unless used in pipeline / transaction.
	 * @since 2.0
	 * @see <a href="https://redis.io/commands/georadiusbymember">Redis Documentation: GEORADIUSBYMEMBER</a>
	 */
	@Nullable
	GeoResults<GeoLocation<M>> radius(K key, M member, Distance distance);


	/**
	 * Get the {@literal member}s within the circle defined by the {@literal members} coordinates and given
	 * {@literal radius} applying {@link Metric} and {@link GeoRadiusCommandArgs}.
	 *
	 * @param key must not be {@literal null}.
	 * @param member must not be {@literal null}.
	 * @param distance must not be {@literal null}.
	 * @param args must not be {@literal null}.
	 * @return never {@literal null} unless used in pipeline / transaction.
	 * @since 2.0
	 * @see <a href="https://redis.io/commands/georadiusbymember">Redis Documentation: GEORADIUSBYMEMBER</a>
	 */
	@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()

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

 

 

本文地址:https://blog.csdn.net/qq_31150503/article/details/107381051