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

俄罗斯在线 网站制作百度推广步骤

俄罗斯在线 网站制作,百度推广步骤,秦皇岛手机网站制作多少钱,营销策划公司简介范文在互联网项目中,计数器有着广泛的应用场景。以技术派项目为例,诸如文章点赞数、收藏数、评论数以及用户粉丝数等都离不开计数器的支持。在技术派源码中,提供了基于数据库操作记录实时更新和基于 Redis 的 incr 特性实现计数器这两种方案&…

在互联网项目中,计数器有着广泛的应用场景。以技术派项目为例,诸如文章点赞数、收藏数、评论数以及用户粉丝数等都离不开计数器的支持。在技术派源码中,提供了基于数据库操作记录实时更新和基于 Redis 的 incr 特性实现计数器这两种方案,本文将重点探讨基于 Redis 的实现方式。

1 计数的业务场景

技术派中使用计数器的场景主要分为两大类(业务计数 + PV/UV),三个细分领域(用户、文章、站点):

  1. 用户的相关统计信息:包括文章数、文章总阅读数、粉丝数、关注作者数、文章被收藏数、被点赞数量等。
  2. 文章的相关统计信息:如文章点赞数、阅读数、收藏数、评论数等。
  3. 站点的 PV/UV 等统计信息:涵盖网站的总 PV/UV、某一天的 PV/UV 以及某个 URI 的 PV/UV 等。

2 Redis 计数器

Redis 计数器主要借助原生的 incr 指令来实现原子的 +1/-1 操作,且不仅 string 类型支持 incrhashzset 数据类型同样也支持。

2.1 incr 指令

Redis 的 Incr 命令用于将 key 中存储的数值增一。若 key 不存在,会先初始化为 0 再执行 INCR 操作;若值的类型错误或不能表示为数字,则返回错误;并且该操作的值限制在 64 位有符号数字表示之内。

  • 技术派的封装实现:在 RedisClient 中对 hIncr 方法进行了封装,用于实现 hash 类型数据的自增操作。代码如下:
    public static Long hIncr ( String key , String filed , Integer cnt ) {return template . execute ( ( RedisCallback <Long >) con ->con . hIncrBy ( keyBytes ( key ) , valBytes ( filed ) , cnt ) ) ;
    }
    

2.2 用户计数统计

在技术派项目中,每个用户的相关计数都存储在一个hash数据结构中。具体结构如下:

  • keyuser_statistic_${userId}
  • field 包含 followCount(关注数)、fansCount(粉丝数)、articleCount(已发布文章数)、praiseCount(文章点赞数)、readCount(文章被阅读数)、collectionCount(文章被收藏数)等。

在业务场景中,为避免计数器与业务代码强耦合,技术派采用消息机制。在 com.github.paicoding.forum.service.statistics.listener.UserStatisticEventListener 中,通过监听不同的消息事件(如 NotifyMsgEventArticleMsgEvent)来实现用户和文章的计数变更。例如,当收到点赞消息(PRAISE)时,会对用户和文章的点赞数进行相应的 +1 操作;取消点赞时则进行 -1 操作。

@EventListener(classes = NotifyMsgEvent.class)
@Async
public void notifyMsgListener(NotifyMsgEvent msgEvent) {switch (msgEvent.getNotifyType()) {case COMMENT:case REPLY:// 评论/回复CommentDO comment = (CommentDO) msgEvent.getContent();RedisClient.hIncr(CountConstants.ARTICLE_STATISTIC_INFO + comment.getArticleId(), CountConstants.COMMENT_COUNT, 1);break;case DELETE_COMMENT:case DELETE_REPLY:// 删除评论/回复comment = (CommentDO) msgEvent.getContent();RedisClient.hIncr(CountConstants.ARTICLE_STATISTIC_INFO + comment.getArticleId(), CountConstants.COMMENT_COUNT, -1);break;case COLLECT:// 收藏UserFootDO foot = (UserFootDO) msgEvent.getContent();RedisClient.hIncr(CountConstants.USER_STATISTIC_INFO + foot.getDocumentUserId(), CountConstants.COLLECTION_COUNT, 1);RedisClient.hIncr(CountConstants.ARTICLE_STATISTIC_INFO + foot.getDocumentId(), CountConstants.COLLECTION_COUNT, 1);break;case CANCEL_COLLECT:// 取消收藏foot = (UserFootDO) msgEvent.getContent();RedisClient.hIncr(CountConstants.USER_STATISTIC_INFO + foot.getDocumentUserId(), CountConstants.COLLECTION_COUNT, -1);RedisClient.hIncr(CountConstants.ARTICLE_STATISTIC_INFO + foot.getDocumentId(), CountConstants.COLLECTION_COUNT, -1);break;case PRAISE:// 点赞foot = (UserFootDO) msgEvent.getContent();RedisClient.hIncr(CountConstants.USER_STATISTIC_INFO + foot.getDocumentUserId(), CountConstants.PRAISE_COUNT, 1);RedisClient.hIncr(CountConstants.ARTICLE_STATISTIC_INFO + foot.getDocumentId(), CountConstants.PRAISE_COUNT, 1);break;case CANCEL_PRAISE:// 取消点赞foot = (UserFootDO) msgEvent.getContent();RedisClient.hIncr(CountConstants.USER_STATISTIC_INFO + foot.getDocumentUserId(), CountConstants.PRAISE_COUNT, -1);RedisClient.hIncr(CountConstants.ARTICLE_STATISTIC_INFO + foot.getDocumentId(), CountConstants.PRAISE_COUNT, -1);break;case FOLLOW:UserRelationDO relation = (UserRelationDO) msgEvent.getContent();// 主用户粉丝数 + 1RedisClient.hIncr(CountConstants.USER_STATISTIC_INFO + relation.getUserId(), CountConstants.FANS_COUNT, 1);// 粉丝的关注数 + 1RedisClient.hIncr(CountConstants.USER_STATISTIC_INFO + relation.getFollowUserId(), CountConstants.FOLLOW_COUNT, 1);break;case CANCEL_FOLLOW:relation = (UserRelationDO) msgEvent.getContent();// 主用户粉丝数 - 1RedisClient.hIncr(CountConstants.USER_STATISTIC_INFO + relation.getUserId(), CountConstants.FANS_COUNT, -1);// 粉丝的关注数 - 1RedisClient.hIncr(CountConstants.USER_STATISTIC_INFO + relation.getFollowUserId(), CountConstants.FOLLOW_COUNT, -1);break;default:}
}@Async
@EventListener(ArticleMsgEvent.class)
public void publishArticleListener(ArticleMsgEvent<ArticleDO> event) {ArticleEventEnum type = event.getType();if (type == ArticleEventEnum.ONLINE || type == ArticleEventEnum.OFFLINE || type == ArticleEventEnum.DELETE) {Long userId = event.getContent().getUserId();int count = articleDao.countArticleByUser(userId);RedisClient.hSet(CountConstants.USER_STATISTIC_INFO + userId, CountConstants.READ_COUNT, count);}
}

2.3 用户统计信息查询

查询用户的统计信息时,直接使用 hgetall 命令即可获取用户对应的所有统计数据,源码路径:com.github.paicoding.forum.service.statistics.service.impl.CountServiceImpl#queryUserStatisticInfo

@Override
public UserStatisticInfoDTO queryUserStatisticInfo(Long userId) {Map<String, Integer> ans = RedisClient.hGetAll(CountConstants.USER_STATISTIC_INFO + userId, Integer.class);UserStatisticInfoDTO info = new UserStatisticInfoDTO();info.setFollowCount(ans.getOrDefault(CountConstants.FOLLOW_COUNT, 0));info.setArticleCount(ans.getOrDefault(CountConstants.ARTICLE_COUNT, 0));info.setPraiseCount(ans.getOrDefault(CountConstants.PRAISE_COUNT, 0));info.setCollectionCount(ans.getOrDefault(CountConstants.COLLECTION_COUNT, 0));info.setReadCount(ans.getOrDefault(CountConstants.READ_COUNT, 0));info.setFansCount(ans.getOrDefault(CountConstants.FANS_COUNT, 0));return info;
}

2.4 缓存一致性

为保证缓存与实际数据的一致性,技术派采用简单的定时同步方案,每天对用户统计信息和文章统计信息进行全量同步。

  • 用户统计信息每天全量同步com.github.paicoding.forum.service.statistics.service.impl.CountServiceImpl#autoRefreshAllUserStatisticInfo

     /*** 每天4:15分执行定时任务,全量刷新用户的统计信息*/@Scheduled(cron = "0 15 4 * * ?")public void autoRefreshAllUserStatisticInfo() {Long now = System.currentTimeMillis();log.info("开始自动刷新用户统计信息");Long userId = 0L;int batchSize = 20;while (true) {List<Long> userIds = userDao.scanUserId(userId, batchSize);userIds.forEach(this::refreshUserStatisticInfo);if (userIds.size() < batchSize) {userId = userIds.get(userIds.size() - 1);break;} else {userId = userIds.get(batchSize - 1);}}log.info("结束自动刷新用户统计信息,共耗时: {}ms, maxUserId: {}", System.currentTimeMillis() - now, userId);}
    
  • 文章统计信息每天全量同步com.github.paicoding.forum.service.sitemap.service.impl.SitemapServiceImpl#initSiteMap以及com.github.paicoding.forum.service.statistics.service.impl.CountServiceImpl#refreshArticleStatisticInfo

    /*** fixme: 加锁初始化,更推荐的是采用分布式锁*/
    private synchronized void initSiteMap() {long lastId = 0L;RedisClient.del(SITE_MAP_CACHE_KEY);while (true) {List<SimpleArticleDTO> list = articleDao.getBaseMapper().listArticlesOrderById(lastId, SCAN_SIZE);// 刷新文章的统计信息list.forEach(s -> countService.refreshArticleStatisticInfo(s.getId()));// 刷新站点地图信息Map<String, Long> map = list.stream().collect(Collectors.toMap(s -> String.valueOf(s.getId()), s -> s.getCreateTime().getTime(), (a, b) -> a));RedisClient.hMSet(SITE_MAP_CACHE_KEY, map);if (list.size() < SCAN_SIZE) {break;}lastId = list.get(list.size() - 1).getId();}
    }
    
    /**
    *刷新文章的统计信息
    */public void refreshArticleStatisticInfo(Long articleId) {ArticleFootCountDTO res = userFootDao.countArticleByArticleId(articleId);if (res == null) {res = new ArticleFootCountDTO();} else {res.setCommentCount(commentReadService.queryCommentCount(articleId));}RedisClient.hMSet(CountConstants.ARTICLE_STATISTIC_INFO + articleId,MapUtils.create(CountConstants.COLLECTION_COUNT, res.getCollectionCount(),CountConstants.PRAISE_COUNT, res.getPraiseCount(),CountConstants.READ_COUNT, res.getReadCount(),CountConstants.COMMENT_COUNT, res.getCommentCount()));}
    

3 小结

基于 Redis 的 incr 特性能够轻松实现计数相关的需求。使用 Redis 实现计数器,相较于直接使用数据库原始数据进行统计,在项目发展到一定阶段、面临高并发访问时,性能更强,能直接在展示层获取最终结果。但数据库统计方式在项目初期或简单项目中也有其优势,如实现简单、迅速且不易出问题。实际选型时,应根据项目具体情况,优先选择实现代价最小的方案,同时也可预留重构的可能性。

4 思维导图

在这里插入图片描述

5 参考链接

  1. 技术派Redis实现计数统计
  2. 项目仓库(GitHub)
  3. 项目仓库(码云)
http://www.dtcms.com/wzjs/275330.html

相关文章:

  • 怎么做新闻网站蜘蛛搜索引擎
  • 网站设计与建设ppt如何做好营销推广
  • 免费微场景制作网站销售找客户最好的app
  • wordpress autumn默认主页网奇seo赚钱培训
  • 扁平网站设计青岛网站建设制作
  • 网页设计心得体会50字东莞seo外包公司哪家好
  • 静安区网站开发个人怎么做网站
  • 做外贸网站空间多少g整合营销传播成功案例
  • 门户网站建设方案是什么意思快速seo关键词优化方案
  • 厦门网站开发排名线在科技成都网站推广公司
  • c2c网站的建设百度指数可以查询到哪些内容
  • 怎么做福彩网站百度一下你就知道官方
  • 有账号密码网站怎么建设百度下载官方下载安装
  • 哈尔滨整站大庆黄页查询电话
  • 潍坊优化网站排名杭州小程序建设公司
  • 网站的建设与推广网站 推广
  • 如何提高网站百度权重地推拉新app推广怎么做
  • 本地ecshop网站怎么上传到服务器青岛网络seo公司
  • 做百度网上搜索引擎推广最好网站创新营销方式有哪些
  • 企业网站运行通知网上营销推广
  • 网站导航三角怎么做百度指数关键词
  • 如何在社交网站做销售seo网站排名优化公司哪家
  • wordpress谷歌字体加载慢郑州seo技术培训班
  • 自己能不能做个网站网络优化工程师骗局
  • 深圳 建设银行国际互联网站建站公司哪个好
  • 怎么做网站热线电话网站seo关键词优化
  • 外贸独立网站建设哪里能搜索引擎优化
  • 企业网站后台管理模板盘多多搜索引擎入口
  • jeecg 做网站企业建站模板
  • 南山网站建设关键词调词平台哪个好