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

使用Redis统计网站的UV/DAU

HyperLogLog/BitMap

统计UV、DAU需要用到Redis的高级数据类型

M

public class RedisKeyUtil {private static final String PREFIX_UV = "uv";private static final String PREFIX_DAU = "dau";// a single day's UVpublic static String getUVKey(String date){return PREFIX_UV + SPLIT + date;}// a series of days' UVpublic static String getUVKey(String startDate, String endDate){return PREFIX_UV + SPLIT + startDate + SPLIT + endDate;}// get the DAU of a single daypublic static String getDAUKey(String date){return PREFIX_DAU + SPLIT + date;}// get the DAU of a series of dayspublic static String getDAUKey(String startDate, String endDate){return PREFIX_DAU + SPLIT + startDate + SPLIT + endDate;}
}

C

@Service
public class DataService {@Autowiredprivate RedisTemplate redisTemplate;private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");// add the specified IP address to the UVpublic void recordUV(String ip){String redisKey = RedisKeyUtil.getUVKey(dateFormat.format(new Date()));redisTemplate.opsForHyperLogLog().add(redisKey, ip);}// query the specified range of days' UVpublic long calculateUV(Date start, Date end) {if(start == null || end == null){throw new IllegalArgumentException("start and end must not be null");}if(start.after(end)){throw new IllegalArgumentException("start date should be before end date");}// get all the keys of those daysList<String> keyList = new ArrayList<>();Calendar calendar = Calendar.getInstance();calendar.setTime(start);while(!calendar.getTime().after(end)){String redisKey = RedisKeyUtil.getUVKey(dateFormat.format(calendar.getTime()));keyList.add(redisKey);// add calendar to 1calendar.add(Calendar.DATE, 1);}// merge all the UV of those daysString redisKey = RedisKeyUtil.getUVKey(dateFormat.format(start), dateFormat.format(end));redisTemplate.opsForHyperLogLog().union(redisKey, keyList.toArray());// return the statistics resultreturn redisTemplate.opsForHyperLogLog().size(redisKey);}// add the specified user to the DAUpublic void recordDAU(int userId){String redisKey = RedisKeyUtil.getDAUKey(dateFormat.format(new Date()));redisTemplate.opsForValue().setBit(redisKey, userId, true);}// query the specified range of days' DAVpublic long calculateDAU(Date start, Date end){if(start == null || end == null){throw new IllegalArgumentException("start and end must not be null");}if(start.after(end)){throw new IllegalArgumentException("start date should be before end date");}// get all the keys of those daysList<byte[]> keyList = new ArrayList<>();Calendar calendar = Calendar.getInstance();calendar.setTime(start);while(!calendar.getTime().after(end)){String key = RedisKeyUtil.getDAUKey(dateFormat.format(calendar.getTime()));keyList.add(key.getBytes());// add calendar to 1calendar.add(Calendar.DATE, 1);}// or operationreturn (long) redisTemplate.execute(new RedisCallback() {@Overridepublic Object doInRedis(RedisConnection connection) throws DataAccessException {String redisKey = RedisKeyUtil.getDAUKey(dateFormat.format(start), dateFormat.format(end));connection.stringCommands().bitOp(RedisStringCommands.BitOperation.OR,redisKey.getBytes(), keyList.toArray(new byte[0][0]));return connection.stringCommands().bitCount(redisKey.getBytes());}});}
}

使用拦截器记录访问

@Component
public class DataInterceptor implements HandlerInterceptor{@Autowiredprivate DataService dataService;@Autowiredprivate HostHolder hostHolder;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// record into the UVString ip = request.getRemoteHost();dataService.recordUV(ip);// record into the DAUUser user = hostHolder.getUser();if(user != null){dataService.recordDAU(user.getId());}return true;}
}

配置拦截器

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Autowiredprivate DataInterceptor dataInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(dataInterceptor).excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");}
}

V

@Controller
public class DataController {@Autowiredprivate DataService dataService;// the page of the statistics@RequestMapping(path = "/data", method = {RequestMethod.GET, RequestMethod.POST})public String getDataPage() {return "/site/admin/data";}// calculates the UV of the site
//    @PostMapping(path = "/data/uv")@RequestMapping(path = "/data/uv", method = {RequestMethod.GET/*, RequestMethod.POST*/})public String getUV(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start,@DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model) {long uv = dataService.calculateUV(start, end);model.addAttribute("uvResult", uv);model.addAttribute("uvStartDate", start);model.addAttribute("uvEndDate", end);// `forward` means this function just disposal a half of the request,// and the rest of request need to be executed by other functions// post request still be post after forwardingreturn "forward:/data";}// calculates the DAU of the site
//    @PostMapping(path = "/data/dau")@RequestMapping(path = "/data/dau", method = {RequestMethod.GET/*, RequestMethod.POST*/})public String getDAU(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start,@DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model) {long dau = dataService.calculateDAU(start, end);model.addAttribute("dauResult", dau);model.addAttribute("dauStartDate", start);model.addAttribute("dauEndDate", end);// `forward` means this function just disposal a half of the request,// and the rest of request need to be executed by other functions// post request still be post after forwardingreturn "forward:/data";}
}

相关文章:

使用Redis统计网站的UV/DAU

HyperLogLog/BitMap 统计UV、DAU需要用到Redis的高级数据类型 M public class RedisKeyUtil {private static final String PREFIX_UV "uv";private static final String PREFIX_DAU "dau";// a single days UVpublic static String getUVKey(String …...

【python】报错:ImportError: DLL load failed: 找不到指定的模块 的详细解决办法

原因&#xff1a;安装的包与python版本不一致 解决方法&#xff1a; 查看python版本&#xff1a; #python / #python -V Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:58:18) [MSC v.1900 64 bit (AMD64)] on win32只查看python第三方模块&#xff08;库、包&…...

SemrushBot蜘蛛爬虫屏蔽方式

查看访问日志时候发现有SemrushBot爬虫 屏蔽方法&#xff1a; 使用robots.txt文件是一种标准的协议,用于告诉搜索引擎哪些页面可以和不能被爬取,如想禁止Googlebot爬取整个网站的话,可以在该文件中添加以下内容: User-agent: Googlebot Disallow: / 对于遵循robots协议的蜘蛛…...

6 ssh面密登录

1. 首先进入自己的家目录&#xff0c;执行命令 [atguiguhadoop102 .ssh]$ ssh-keygen -t rsa然后敲&#xff08;三个回车&#xff09;&#xff0c;就会生成两个文件id_rsa&#xff08;私钥&#xff09;、id_rsa.pub&#xff08;公钥&#xff09; 2. 将公钥拷贝到要免密登录的…...

基于微信小程序的汽车租赁系统的设计与实现ljx7y

汽车租赁系统&#xff0c;主要包括管理员、用户二个权限角色&#xff0c;对于用户角色不同&#xff0c;所使用的功能模块相应不同。本文从管理员、用户的功能要求出发&#xff0c;汽车租赁系统系统中的功能模块主要是实现管理员后端&#xff1b;首页、个人中心、汽车品牌管理、…...

优化学习体验的在线考试系统

随着互联网的发展&#xff0c;在线教育逐渐成为学习的主要方式之一。在线考试系统作为在线教育的重要组成部分&#xff0c;对于学习者提供了更为便捷和灵活的学习方式。但是&#xff0c;如何优化学习体验&#xff0c;提高学习效果&#xff0c;仍然是在线考试系统需要解决的问题…...

1267. 统计参与通信的服务器

题目描述&#xff1a; 这里有一幅服务器分布图&#xff0c;服务器的位置标识在 m * n 的整数矩阵网格 grid 中&#xff0c;1 表示单元格上有服务器&#xff0c;0 表示没有。 如果两台服务器位于同一行或者同一列&#xff0c;我们就认为它们之间可以进行通信。 请你统计并返回能…...

【考研数学】矩阵、向量与线性方程组解的关系梳理与讨论

文章目录 引言一、回顾二、梳理齐次线性方程组非齐次线性方程组 写在最后 引言 两个原因让我想写这篇文章&#xff0c;一是做矩阵题目的时候就发现这三货经常绑在一起&#xff0c;让人想去探寻其中奥秘&#xff1b;另一就是今天学了向量组的秩&#xff0c;让我想起来了之前遗留…...

打造个人的NAS云存储-通过Nextcloud搭建私有云盘实现公网远程访问

文章目录 摘要1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 摘要 Nextcloud,它是ownCloud的一个分支,是一个文件共享服…...

FFI绕过disable_functions

文章目录 FFI绕过disable_functions[RCTF 2019]NextphpPHP7.4 FFI参考 FFI绕过disable_functions [RCTF 2019]Nextphp 首先来看这道题目 index.php <?php if (isset($_GET[a])) {eval($_GET[a]); } else {show_source(__FILE__); }查看一下phpinfo 发现过滤了很多函数&…...

53 个 CSS 特效 2

53 个 CSS 特效 2 这里是第 17 到 32 个&#xff0c;跟上一部分比起来多了两个稍微大一点的首页布局&#xff0c;上篇&#xff1a;53 个 CSS 特效 1&#xff0c;依旧&#xff0c;预览地址在 http://www.goldenaarcher.com/html-css-js-proj/&#xff0c;git 地址&#xff1a; …...

ubuntu学习(六)----文件编程实现cp指令

1 思路 Linux要想复制一份文件通常指令为&#xff1a; cp src.c des.c 其中src.c为源文件&#xff0c;des.c为目标文件。 要想通过文件编程实现cp效果&#xff0c;思路如下 1 首先打开源文件 src.c 2 读src到buf 3 创建des.c 4 将buf写入到des.c 5 close两个文件 2 实现 vi …...

wireshark过滤器的使用

目录 wiresharkwireshark的基本使用wireshark过滤器的区别 抓包案例 wireshark wireshark的基本使用 抓包采用 wireshark&#xff0c;提取特征时&#xff0c;要对 session 进行过滤&#xff0c;找到关键的stream&#xff0c;这里总结了 wireshark 过滤的基本语法&#xff0c;…...

Zookeeper 脑裂问题

什么是脑裂&#xff1f; 脑裂(split-brain)就是“大脑分裂”&#xff0c;也就是本来一个“大脑”被拆分了两个或多个“大脑”&#xff0c;如果一个人有多个大脑&#xff0c;并且相互独立的话&#xff0c;那么会导致人体“手舞足蹈”&#xff0c;“不听使唤”。 脑裂通常会出现…...

计算机网络高频面试题解(一)

1. OSI七层模型 2. TCP/IP五层模型 3. TCP、UDP区别 4. TCP三次握手 5. TCP四次挥手 6. TCP状态转换图 7.TCP状态中TIME_WAIT作用 8. TCP连接建立为什么不是两次握手 9. TCP第三次握手失败会出现什么 10. TCP长连接和短链接及优缺点...

从0-1的docker镜像服务构建

文章目录 摘要一、环境准备1、docker安装2、docker-compose安装 二、镜像制作2.1、编写Dockerfile文件2.1.1、熟悉常用Dockerfile命令2.1.2、制作php镜像案例 2.2、build镜像 三、docker-compose管理容器3.1、编写docker-compose.ymal配置文件3.2、编写systemctl配置 摘要 由于…...

RabbitMQ、Kafka、RocketMQ:特点和适用场景对比

推荐阅读 AI文本 OCR识别最佳实践 AI Gamma一键生成PPT工具直达链接 玩转cloud Studio 在线编码神器 玩转 GPU AI绘画、AI讲话、翻译,GPU点亮AI想象空间 资源分享 史上最全文档AI绘画stablediffusion资料分享 AI绘画关于SD,MJ,GPT,SDXL百科全书 「java、python面试题」…...

【实战】十一、看板页面及任务组页面开发(四) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十六)

文章目录 一、项目起航&#xff1a;项目初始化与配置二、React 与 Hook 应用&#xff1a;实现项目列表三、TS 应用&#xff1a;JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…...

解决docker无法执行定时任务问题

背景 在docker里面想创建定时任务&#xff0c;但是发现时间到了并没有执行&#xff0c;第一时间想到应该是没有开启crond服务&#xff0c;然后执行systemctl status crond.service报错如下所示&#xff1a; System has not been booted with systemd as init system (PID 1).…...

【FreeRTOS】【STM32】中断详细介绍

文章目录 一、三种优先级的概念辨析1. 先理清楚两个概念&#xff1a;CPU 和 MPU2. Cortex-M3 内核与 STM32F1XX 控制器有什么关系3. 优先级的概念辨析① Cortex-M3 内核和 STM32F1XX 的中断优先级② FreeRTOS 的任务的优先级 二、 Cortex-M3 内核的中断优先级1. 中断编号2. 优先…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据&#xff0c;你需要完成以下配置步骤&#xff1a; ✅ 一、在 SQL Server 端配置&#xff08;服务器设置&#xff09; 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到&#xff1a;SQL Server 网络配…...