• SpringBoot集成Redis实现排行榜


    SpringBoot继承Redis实现排行榜

    项目文件结构

    1、修改maven文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.wu</groupId>
        <artifactId>rank</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>rank</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!--redis的依赖包-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.38</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

      

    2、redis配置文件

    spring.application.name=spring-boot-redis
    
    # 配置redis
    
    # Redis数据库索引(默认为0)
    spring.redis.database=0
    # Redis服务器地址
    spring.redis.host=127.0.0.1
    # Redis服务器连接端口
    spring.redis.port=6379
    # Redis服务器连接密码(默认为空)
    spring.redis.password=foobared
    # 连接池最大连接数(使用负值表示没有限制)
    spring.redis.pool.max-active=8
    # 连接池最大阻塞等待时间(使用负值表示没有限制)
    spring.redis.pool.max-wait=-1
    # 连接池中的最大空闲连接
    spring.redis.pool.max-idle=8
    # 连接池中的最小空闲连接
    spring.redis.pool.min-idle=0
    # 连接超时时间(毫秒)
    spring.redis.timeout=200 # 这个地方需要
    

    这里上面的redis的密码需要根据实际情况进行修改

    3、RedisConfig文件

    package com.wu.rank.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    
    @Configuration
    public class RedisConfig {
    
        @Bean
        public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            StringRedisTemplate redis = new StringRedisTemplate();
    
            redis.setConnectionFactory(redisConnectionFactory);
    
            // 设置redis的String/value的默认序列化方式
            DefaultSerializer stringRedisSerializer = new DefaultSerializer();
            redis.setKeySerializer(stringRedisSerializer);
            redis.setValueSerializer(stringRedisSerializer);
            redis.setHashKeySerializer(stringRedisSerializer);
            redis.setHashValueSerializer(stringRedisSerializer);
    
            redis.afterPropertiesSet();
    
            return redis;
        }
    }
    

      

    4、DefaultSerializer文件

    package com.wu.rank.config;
    
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.SerializationException;
    import org.springframework.util.Assert;
    
    import java.nio.charset.Charset;
    
    public class DefaultSerializer implements RedisSerializer<Object> {
        private final Charset charset;
    
        public DefaultSerializer() {
            this(Charset.forName("UTF8"));
        }
    
        public DefaultSerializer(Charset charset) {
            Assert.notNull(charset, "Charset must not be null!");
            this.charset = charset;
        }
    
    
        @Override
        public byte[] serialize(Object o) throws SerializationException {
            return o == null ? null : String.valueOf(o).getBytes(charset);
        }
    
        @Override
        public Object deserialize(byte[] bytes) throws SerializationException {
            return bytes == null ? null : new String(bytes, charset);
        }
    }
    

      

    component文件夹

    5、RedisComponent代码

    package com.wu.rank.component;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.core.ZSetOperations;
    import org.springframework.stereotype.Component;
    
    import java.util.Set;
    
    @Component
    public class RedisComponent {
    
        @Autowired
        private StringRedisTemplate redisTemplate;
    
        /**
         * 添加一个元素, zset与set最大的区别就是每个元素都有一个score,因此有个排序的辅助功能;  zadd
         *
         * @param key
         * @param value
         * @param score
         */
        public void add(String key, String value, double score) {
            redisTemplate.opsForZSet().add(key, value, score);
        }
    
        /**
         * 删除元素 zrem
         *
         * @param key
         * @param value
         */
        public void remove(String key, String value) {
            redisTemplate.opsForZSet().remove(key, value);
        }
    
        /**
         * score的增加or减少 zincrby
         *
         * @param key
         * @param value
         * @param score
         */
        public Double incrScore(String key, String value, double score) {
            return redisTemplate.opsForZSet().incrementScore(key, value, score);
        }
    
        /**
         * 查询value对应的score   zscore
         *
         * @param key
         * @param value
         * @return
         */
        public Double score(String key, String value) {
            return redisTemplate.opsForZSet().score(key, value);
        }
    
        /**
         * 判断value在zset中的排名  zrank
         *
         * 积分小的在前面
         *
         * @param key
         * @param value
         * @return
         */
        public Long rank(String key, String value) {
            return redisTemplate.opsForZSet().rank(key, value);
        }
    
        /**
         * 查询集合中指定顺序的值, 0 -1 表示获取全部的集合内容  zrange
         *
         * 返回有序的集合,score小的在前面
         *
         * @param key
         * @param start
         * @param end
         * @return
         */
        public Set<String> range(String key, long start, long end) {
            return redisTemplate.opsForZSet().range(key, start, end);
        }
    
        /**
         * 查询集合中指定顺序的值和score,0, -1 表示获取全部的集合内容
         *
         * @param key
         * @param start
         * @param end
         * @return
         */
        public Set<ZSetOperations.TypedTuple<String>> rangeWithScore(String key, long start, long end) {
            return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
        }
    
        /**
         * 查询集合中指定顺序的值  zrevrange
         *
         * 返回有序的集合中,score大的在前面
         *
         * @param key
         * @param start
         * @param end
         * @return
         */
        public Set<String> revRange(String key, long start, long end) {
            return redisTemplate.opsForZSet().reverseRange(key, start, end);
        }
    
        /**
         * 根据score的值,来获取满足条件的集合  zrangebyscore
         *
         * @param key
         * @param min
         * @param max
         * @return
         */
        public Set<String> sortRange(String key, long min, long max) {
            return redisTemplate.opsForZSet().rangeByScore(key, min, max);
        }
    
        /**
         * 返回集合的长度
         *
         * @param key
         * @return
         */
        public Long size(String key) {
            return redisTemplate.opsForZSet().zCard(key);
        }
    
    }
    

      

    6、RankListComponent

    package com.wu.rank.component;
    
    import com.wu.rank.model.RankDO;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.ZSetOperations;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Set;
    
    @Component
    public class RankListComponent {
    
        @Autowired
        private RedisComponent redisComponent;
    
        private static final String RANK_PREFIX = "global_rank";
    
        private List<RankDO> buildRedisRankToBizDO(Set<ZSetOperations.TypedTuple<String>> result, long offset) {
            List<RankDO> rankList = new ArrayList<>(result.size());
            long rank = offset;
            for (ZSetOperations.TypedTuple<String> sub : result) {
                rankList.add(new RankDO(rank++, Math.abs(sub.getScore().floatValue()), Long.parseLong(sub.getValue())));
            }
            return rankList;
        }
    
        /**
         * 获取前n名的排行榜数据
         *
         * @param n
         * @return
         */
        public List<RankDO> getTopNRanks(int n) {
            Set<ZSetOperations.TypedTuple<String>> result = redisComponent.rangeWithScore(RANK_PREFIX, 0, n - 1);
            return buildRedisRankToBizDO(result, 1);
        }
    
    
        /**
         * 获取用户所在排行榜的位置,以及排行榜中其前后n个用户的排行信息
         *
         * @param userId
         * @param n
         * @return
         */
        public List<RankDO> getRankAroundUser(Long userId, int n) {
            // 首先是获取用户对应的排名
            RankDO rank = getRank(userId);
            if (rank.getRank() <= 0) {
                // fixme 用户没有上榜时,不返回
                return Collections.emptyList();
            }
    
            // 因为实际的排名是从0开始的,所以查询周边排名时,需要将n-1
            Set<ZSetOperations.TypedTuple<String>> result =
                    redisComponent.rangeWithScore(RANK_PREFIX, Math.max(0, rank.getRank() - n - 1), rank.getRank() + n - 1);
            return buildRedisRankToBizDO(result, rank.getRank() - n);
        }
    
    
        /**
         * 获取用户的排行榜位置
         *
         * @param userId
         * @return
         */
        public RankDO getRank(Long userId) {
            // 获取排行, 因为默认是0为开头,因此实际的排名需要+1
            Long rank = redisComponent.rank(RANK_PREFIX, String.valueOf(userId));
            if (rank == null) {
                // 没有排行时,直接返回一个默认的
                return new RankDO(-1L, 0F, userId);
            }
    
            // 获取积分
            Double score = redisComponent.score(RANK_PREFIX, String.valueOf(userId));
            return new RankDO(rank + 1, Math.abs(score.floatValue()), userId);
        }
    
        /**
         * 更新用户积分,并获取最新的个人所在排行榜信息
         *
         * @param userId
         * @param score
         * @return
         */
        public RankDO updateRank(Long userId, Float score) {
            // 因为zset默认积分小的在前面,所以我们对score进行取反,这样用户的积分越大,对应的score越小,排名越高
            redisComponent.add(RANK_PREFIX, String.valueOf(userId), -score);
            Long rank = redisComponent.rank(RANK_PREFIX, String.valueOf(userId));
            return new RankDO(rank + 1, score, userId);
        }
    
    }
    

      

    Model文件夹

    7、RankDO

    package com.wu.rank.model;
    
    import java.io.Serializable;
    
    public class RankDO implements Serializable {
        private static final long serialVersionUID = 4804922606006935590L;
    
        /**
         * 排名
         */
        private Long rank;
    
        /**
         * 积分
         */
        private Float score;
    
        /**
         * 用户id
         */
        private Long userId;
    
    
        public RankDO(Long rank, Float score, Long userId) {
            this.rank = rank;
            this.score = score;
            this.userId = userId;
        }
    
        public static long getSerialVersionUID() {
            return serialVersionUID;
        }
    
        public Long getRank() {
            return rank;
        }
    
        public void setRank(Long rank) {
            this.rank = rank;
        }
    
        public Float getScore() {
            return score;
        }
    
        public void setScore(Float score) {
            this.score = score;
        }
    
        public Long getUserId() {
            return userId;
        }
    
        public void setUserId(Long userId) {
            this.userId = userId;
        }
    }
    

      

    8、controller文件夹

    RankAction

    package com.wu.rank.controller;
    
    import com.wu.rank.component.RankListComponent;
    import com.wu.rank.model.RankDO;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    
    @RestController
    public class RankAction {
    
        @Autowired
        private RankListComponent rankListComponent;
    
        @GetMapping(path = "/topn")
        public List<RankDO> showTopN(int n) {
            return rankListComponent.getTopNRanks(n);
        }
    
        @GetMapping(path = "/update")
        public RankDO updateScore(long userId, float score) {
            return rankListComponent.updateRank(userId, score);
        }
    
        @GetMapping(path = "/rank")
        public RankDO queryRank(long userId) {
            return rankListComponent.getRank(userId);
        }
    
        @GetMapping(path = "/around")
        public List<RankDO> around(long userId, int n) {
            return rankListComponent.getRankAroundUser(userId, n);
        }
    
    }
    

      

    主程序代码

    package com.wu.rank;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class RankApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RankApplication.class, args);
        }
    
    }
    

      

    测试

    1、先运行redis服务器

    2、打开postman进行测试

    参考:

    1、https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-case/120-redis-ranklist 

  • 相关阅读:
    输出1到100内前五个可以被3整除的数 while for
    Java:运用while()与do....while与for()
    break & continue
    while循环
    for循环例子
    if语句条件
    上位机开发二----第一个程序hallo world
    c语言获取数组长度的三种方法
    halcon标定后畸变校正与测量
    海康相机SDK联合c++标定
  • 原文地址:https://www.cnblogs.com/wylwyl/p/10819447.html
Copyright © 2020-2023  润新知