当前位置: 首页 > news >正文

学校网站建设项目管理报告/网站的宣传推广方式

学校网站建设项目管理报告,网站的宣传推广方式,网站建设摊销年限,网站模板设计教程文章目录 I. 方案设计1. 业务场景说明2. 数据结构3. redis使用方案 II. 功能实现0. 前提准备1. 用户上传积分2. 获取个人排名3. 获取个人周边用户积分及排行信息4. 获取topn排行榜 III. 测试小结1. 测试2. 小结 I. 方案设计 在进行方案设计之前,先模拟一个真实的应…

文章目录

    • I. 方案设计
      • 1. 业务场景说明
      • 2. 数据结构
      • 3. redis使用方案
    • II. 功能实现
      • 0. 前提准备
      • 1. 用户上传积分
      • 2. 获取个人排名
      • 3. 获取个人周边用户积分及排行信息
      • 4. 获取topn排行榜
    • III. 测试小结
      • 1. 测试
      • 2. 小结

I. 方案设计

在进行方案设计之前,先模拟一个真实的应用场景,然后进行辅助设计与实现

1. 业务场景说明

以前一段时间特别🔥的跳一跳这个小游戏进行说明,假设我们这个游戏用户遍布全球,因此我们要设计一个全球的榜单,每个玩家都会根据自己的战绩在排行榜中获取一个排名,我们需要支持全球榜单的查询,自己排位的查询这两种最基本的查询场景;此外当我的分数比上一次的高时,我需要更新我的积分,重新获得我的排名;

此外也会有一些高级的统计,比如哪个分段的人数最多,什么分段是瓶颈点,再根据地理位置计算平均分等等

本篇博文主要内容将放在排行榜的设计与实现上;至于高级的功能实现,后续有机会再说

2. 数据结构

因为排行榜的功能比较简单了,也不需要什么复杂的结构设计,也没有什么复杂的交互,因此我们需要确认的无非就是数据结构 + 存储单元

存储单元

表示排行榜中每一位上应该持有的信息,一个最简单的如下

// 用来表明具体的用户
long userId;
// 用户在排行榜上的排名
long rank;
// 用户的历史最高积分,也就是排行榜上的积分
long score;

数据结构

排行榜,一般而言都是连续的,借此我们可以联想到一个合适的数据结构LinkedList,好处在于排名变动时,不需要数组的拷贝

请添加图片描述

上图演示,当一个用户积分改变时,需要向前遍历找到合适的位置,插入并获取新的排名, 在更新和插入时,相比较于ArrayList要好很多,但依然有以下几个缺陷

问题1:用户如何获取自己的排名?

使用LinkedList在更新插入和删除的带来优势之外,在随机获取元素的支持会差一点,最差的情况就是从头到尾进行扫描

问题2:并发支持的问题?

当有多个用户同时更新score时,并发的更新排名问题就比较突出了,当然可以使用jdk中类似写时拷贝数组的方案

上面是我们自己来实现这个数据结构时,会遇到的一些问题,当然我们的主题是借助redis来实现排行榜,下面则来看下,利用redis可以怎么简单的支持我们的需求场景

3. redis使用方案

这里主要使用的是redis的ZSET数据结构,带权重的集合,下面分析一下可能性

  • set: 集合确保里面元素的唯一性
  • 权重:这个可以看做我们的score,这样每个元素都有一个score;
  • zset:根据score进行排序的集合

从zset的特性来看,我们每个用户的积分,丢到zset中,就是一个带权重的元素,而且是已经排好序的了,只需要获取元素对应的index,就是我们预期的排名

II. 功能实现

再具体的实现之前,可以先查看一下redis中zset的相关方法和操作姿势:SpringBoot高级篇Redis之ZSet数据结构使用姿势

我们主要是借助zset提供的一些方法来实现排行榜的需求,下面的具体方法设计中,也会有相关说明

0. 前提准备

首先准备好redis环境,spring项目搭建好,然后配置好redisTemplate

/*** Created by @author yihui in 15:05 18/11/8.*/
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;}@Overridepublic byte[] serialize(Object o) throws SerializationException {return o == null ? null : String.valueOf(o).getBytes(charset);}@Overridepublic Object deserialize(byte[] bytes) throws SerializationException {return bytes == null ? null : new String(bytes, charset);}
}@Configuration
public class AutoConfig {@Bean(value = "selfRedisTemplate")public RedisTemplate<String, String> stringRedisTemplate(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;}
}

1. 用户上传积分

上传用户积分,然而zset中有一点需要注意的是其排行是根据score进行升序排列,这个就和我们实际的情况不太一样了;为了和实际情况一致,可以将score取反;另外一个就是排行默认是从0开始的,这个与我们的实际也不太一样,需要+1

/*** 更新用户积分,并获取最新的个人所在排行榜信息** @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);
}

上面的实现,主要利用了zset的两个方法,一个是添加元素,一个是查询排名,对应的redis操作方法如下,

@Resource(name = "selfRedisTemplate")
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);
}/*** 判断value在zset中的排名  zrank** 积分小的在前面** @param key* @param value* @return*/
public Long rank(String key, String value) {return redisTemplate.opsForZSet().rank(key, value);
}

2. 获取个人排名

获取个人排行信息,主要就是两个一个是排名一个是积分;需要注意的是当用户没有积分时(即没有上榜时),需要额外处理

/*** 获取用户的排行榜位置** @param userId* @return*/
public RankDO getRank(Long userId) {// 获取排行, 因为默认是0为开头,因此实际的排名需要+1Long 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);
}

上面的封装中,除了使用前面的获取用户排名之外,还有获取用户积分

/*** 查询value对应的score   zscore** @param key* @param value* @return*/
public Double score(String key, String value) {return redisTemplate.opsForZSet().score(key, value);
}

3. 获取个人周边用户积分及排行信息

有了前面的基础之后,这个就比较简单了,首先获取用户的个人排名,然后查询固定排名段的数据即可

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 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-1Set<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);
}

看下上面的实现,获取用户排名之后,就可以计算要查询的排名范围[Math.max(0, rank.getRank() - n - 1), rank.getRank() + n - 1]

其次需要注意的如何将返回的结果进行封装,上面写了个转换类,主要起始排行榜信息

4. 获取topn排行榜

上面的理解之后,这个就很简答了

/*** 获取前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);
}

III. 测试小结

首先准备一个测试脚本,批量的插入一下积分,用于后续的查询更新使用

public class RankInitTest {private Random random;private RestTemplate restTemplate;@Beforepublic void init() {random = new Random();restTemplate = new RestTemplate();}private int genUserId() {return random.nextInt(1024);}private double genScore() {return random.nextDouble() * 100;}@Testpublic void testInitRank() {for (int i = 0; i < 30; i++) {restTemplate.getForObject("http://localhost:8080/update?userId=" + genUserId() + "&score=" + genScore(),String.class);}}
}

1. 测试

上面执行完毕之后,排行榜中应该就有三十条数据,接下来我们开始逐个接口测试,首先获取top10排行

对应的rest接口如下

@RestController
public class RankAction {@Autowiredprivate RankListComponent rankListComponent;@GetMapping(path = "/topn")public List<RankDO> showTopN(int n) {return rankListComponent.getTopNRanks(n);}
}

请添加图片描述

接下来我们挑选第15名,获取对应的排行榜信息

@GetMapping(path = "/rank")
public RankDO queryRank(long userId) {return rankListComponent.getRank(userId);
}

首先我们从redis中获取第15名的userId,然后再来查询
请添加图片描述
rank

然后尝试修改下他的积分,改大一点,将score改成80分,则会排到第五名

@GetMapping(path = "/update")
public RankDO updateScore(long userId, float score) {return rankListComponent.updateRank(userId, score);
}

请添加图片描述

最后我们查询下这个用户周边2个的排名信息

@GetMapping(path = "/around")
public List<RankDO> around(long userId, int n) {return rankListComponent.getRankAroundUser(userId, n);
}

请添加图片描述

2. 小结

上面利用redis的zset实现了排行榜的基本功能,主要借助下面三个方法

  • range 获取范围排行信息
  • score 获取对应的score
  • range 获取对应的排名

虽然实现了基本功能,但是问题还是有不少的

  • 上面的实现,redis的复合操作,原子性问题
  • 由原子性问题导致并发安全问题
  • 性能怎么样需要测试
http://www.cadmedia.cn/news/625.html

相关文章:

  • 做毕业设计免费网站建设/怎么注册网站免费的
  • 自己可以做类似淘宝客网站吗/网站搭建外贸
  • 同德县公司网站建设/2022知名品牌营销案例100例
  • 那个公司做网站/西安疫情最新数据消息5分钟前
  • 网站 建设服务器/河南网站顾问
  • 福州建设网站公司/seo和sem的区别与联系
  • sql做网站后台/短网址生成网站
  • 微信网站建设电话/百度刷seo关键词排名
  • 广州公安局门户网站/中国今日新闻
  • 做网站一般长宽多少/惠州网络营销公司
  • 网站制作公司优势/企业网站推广技巧
  • 西安三桥网站建设/广告推广怎么做
  • 网站快速网站推广/百度网页版 入口
  • 天河区做网站公司/网络服务器的作用
  • 铜川免费做网站/苏州百度
  • 网站备案后有什么好处/百度下载官方下载安装
  • 做电商网站必需知道qc/百度小说风云榜排行榜官网
  • 温州专业微网站制作公司/怎么制作微信小程序
  • 网站做谷歌推广有效果吗/企业培训网
  • 多个网站集成在一个页面/线下推广活动策划方案
  • 健康中国app/上海seo顾问
  • 刷题网站建设/sem竞价推广托管
  • 南宁手机平台网网站建设/色盲和色弱的区别
  • 提升政务网站建设水平/河南网站建设哪个公司做得好
  • 专门做调研的网站/石家庄百度关键词优化
  • 大气的企业网站模板/网站app免费生成软件
  • 一个小型网站设计/淘宝运营培训课程
  • 购物网站计划书/seo职位要求
  • 专业上海网站建设公司/重庆关键词排名首页
  • 百度推广是给做网站吗/免费外链网站seo发布