网站 UV
- 统计结果 0
活跃用户
- 统计结果 0
独立访客,需通过用户IP 排重统计数据每次访问都要进行统计。
HyperLogLog,性能好,且存储空间小。
日活跃用户,需通过用户ID排重统计数据访问过一次,则认为其活跃。
Bitmap,性能好、且可以统计精确的结果
定义的redisKey常量
public static final String PREFIX_UV="uv"; //独立用户 public static final String PREFIX_DAU="dau"; //活跃用户private static final String SPLIT=":";
//获取用户key
public static String getUserKey(int userId){
return PREFIX_USER+SPLIT+userId;
}
//获取单日uv的key
public static String getUVKey(String date){
return PREFIX_UV+SPLIT+date;
}
//获取区间uv的key
public static String getUVKey(String startDate,String endDate){
return PREFIX_UV+SPLIT+startDate+SPLIT+endDate;
}
//获取单日dau的key
public static String getDAUKey(String date){
return PREFIX_DAU+SPLIT+date;
}
//获取区间dau的key
public static String getDAUKey(String startDate,String endDate){
return PREFIX_DAU+SPLIT+startDate+SPLIT+endDate;
}
@Controller
public class DataController {
@Autowired
private DataService dataService;
@RequestMapping(value = "/data",method = {RequestMethod.GET,RequestMethod.POST})
public String getDataPage(){
return "/site/admin/data";
}
//统计独立访客
@RequestMapping(value = "/data/uv",method = 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);
return "forward:/data"; //转发:在一个请求中
}
//统计活跃用户
@RequestMapping(value = "/data/dau",method = RequestMethod.POST)
public String getDAU(@DateTimeFormat(pattern = "yyyy-MM-dd")Date start,
@DateTimeFormat(pattern = "yyyy-MM-dd")Date end, Model model){
long uv = dataService.calculateDAU(start,end);
model.addAttribute("dauResult",uv);
model.addAttribute("dauStartDate",start);
model.addAttribute("dauEndDate",end);
return "forward:/data"; //转发:在一个请求中
}
}
@Service
public class DataService {
@Autowired
private RedisTemplate redisTemplate;
private static SimpleDateFormat df=new SimpleDateFormat("yyyyMMdd"); //统一处理日期格式
//将指定的ip计入uv
//使用hyperloglog
public void recordUV(String ip){
//构建key
String redisKey = RedisKeyUtil.getUVKey(df.format(new Date()));
redisTemplate.opsForHyperLogLog().add(redisKey,ip);
}
//统计指定范围内的uv
public long calculateUV(Date start,Date end){
if(start==null||end==null){
throw new RuntimeException("参数不能为空!");
}
//获取指定日期内key的集合
List keyList=new ArrayList<>();
//获取获取系统的当前日历对象
Calendar calendar=Calendar.getInstance();
calendar.setTime(start);//将时间对象data设置为新的日历
while(!calendar.getTime().after(end)){ //从当前时间到end之前的这一段时间
//获取当前时间的key,并将key装到集合中去
String key = RedisKeyUtil.getUVKey(df.format(calendar.getTime()));
keyList.add(key);
calendar.add(calendar.DATE,1);
}
//合并这些数据,需要用新的值去接收
String redisKey = RedisKeyUtil.getUVKey(df.format(start), df.format(end));
redisTemplate.opsForHyperLogLog().union(redisKey,keyList.toArray());
return redisTemplate.opsForHyperLogLog().size(redisKey);
}
//将用户计入dau中
//使用bitmap
public void recordDAU(int userId){
//构建key
String redisKey = RedisKeyUtil.getDAUKey(df.format(new Date()));
redisTemplate.opsForValue().setBit(redisKey,userId,true);
}
//获取指定日期内的dau
public long calculateDAU(Date start,Date end){
if(start==null||end==null){
throw new RuntimeException("参数不能为空!");
}
List keyList=new ArrayList<>();
Calendar calendar=Calendar.getInstance();
calendar.setTime(start);
while(!calendar.getTime().after(end)){
String key = RedisKeyUtil.getDAUKey(df.format(calendar.getTime()));
keyList.add(key.getBytes());
calendar.add(calendar.DATE,1);
}
//做or运算
return (long) redisTemplate.execute(new RedisCallback() {
@Override
public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
String redisKey = RedisKeyUtil.getDAUKey(df.format(start), df.format(end));
redisConnection.bitOp(RedisStringCommands.BitOperation.OR,
redisKey.getBytes(),keyList.toArray(new byte[0][0]));//返回指定类型的数组
return redisConnection.bitCount(redisKey.getBytes());
}
});
}
}
在请求中获取ip
@Component
public class DataInterceptor implements HandlerInterceptor {
@Autowired
private DataService dataService;
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//统计uv
String ip=request.getRemoteUser();
dataService.recordUV(ip);
//统计dau
User user = hostHolder.getUser();
if(user!=null){
int userId = user.getId();
dataService.recordDAU(userId);
}
return true;
}
}
网站 UV
-
统计结果
0
活跃用户
-
统计结果
0
另:该功能只有管理员才能访问,因此需要在之前写过的springsecurity进行相关授权配置即可