HyperLogLog、获取一个类的注解、运行lua脚本工具

本文介绍了如何使用Redis的HyperLogLog数据结构来统计独立访客(UV),通过Spring Data Redis操作HyperLogLog进行用户访问记录和UV统计。同时展示了如何读取和执行Lua脚本工具,以及获取类和方法注解的相关方法。内容涵盖了数据统计、Redis操作和Java编程技巧。
摘要由CSDN通过智能技术生成

一、HyperLogLog

统计 PV :每个网页一个独立的 Redis 计数器就可以了,这个计数器的 key 后缀加上当天的日期。这样来一个请求,incrby 一次,最终就可以统计出所有的 PV 数据。
统计UV: UV 不一样,它要去重,同一个用户一天之内的多次访问请求只能计数一次。这就要求每一个网页请求都需要带上用户的 ID,无论是登陆用户还是未登陆用户都需要一个唯一 ID 来标识。

import org.springframework.data.redis.core.HyperLogLogOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class HyperLogService {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 记录用户访问
     *
     * @param user
     */
    public long add(String Key, String user) {
        HyperLogLogOperations<String, String> hyperLogLog = redisTemplate.opsForHyperLogLog();
        return hyperLogLog.add(Key, user);
    }

    /**
     * 统计当前 UV
     *
     * @return
     */
    public long sum(String Key) {
        HyperLogLogOperations<String, String> hyperLogLog = redisTemplate.opsForHyperLogLog();
        return hyperLogLog.size(Key);
    }

    /**
     * 删除当前 key
     */
    public void clear(String Key) {
        HyperLogLogOperations<String, String> hyperLogLog = redisTemplate.opsForHyperLogLog();
        hyperLogLog.delete(Key);
    }
}
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ThreadLocalRandom;

@Slf4j
public class HyperLogTools {

    static class BitKeeper {
        private int maxBits;

        public void random() {
            // 这里的随机数可以当成一个对象的hashCode。long value = new Object().hashCode() ^ (2 << 32);
            long value = ThreadLocalRandom.current().nextLong(2L << 32);
            int bits = lowZeros(value);
            if (bits > this.maxBits) {
                this.maxBits = bits;
            }
        }

        /**
         * 低位有多少个连续0
         * 思路上 ≈ 倒数第一个1的位置
         *
         * @param value
         * @return
         */
        private int lowZeros(long value) {
            int i = 1;
            for (; i < 32; i++) {
                if (value >> i << i != value) {
                    break;
                }
            }
            return i - 1;
        }
    }

    static class Experiment {
        private int n;
        private BitKeeper keeper;

        public Experiment(int n) {
            this.n = n;
            this.keeper = new BitKeeper();
        }

        public void work() {
            for (int i = 0; i < n; i++) {
                this.keeper.random();
            }
        }

        public void debug() {
           log.info("%d %.2f %d\n", this.n, Math.log(this.n) / Math.log(2), this.keeper.maxBits);
        }
    }

    public static void main(String[] args) {
        for (int i = 1000; i < 100000; i += 100) {
            Experiment exp = new Experiment(i);
            exp.work();
            exp.debug();
        }
    }
}

二、运行lua脚本工具

import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Objects;


@Slf4j
public class ScriptUtil {

    /**
     * return lua script String
     *
     * @param path
     * @return
     */
    public static String getScript(String path) {
        StringBuilder sb = new StringBuilder();
        InputStream stream = ScriptUtil.class.getClassLoader().getResourceAsStream(path);
        try (BufferedReader br = new BufferedReader(new InputStreamReader(Objects.requireNonNull(stream)))){
            String str;
            while ((str = br.readLine()) != null) {
                sb.append(str).append(System.lineSeparator());
            }
        } catch (IOException e) {
           log.error("执行异常:{}",e);
        }
        return sb.toString();
    }


    public static void main(String[] args) {
        System.out.println(ScriptUtil.getScript("limit.lua"));
    }
}

三、获取一个类的注解,如果未获取到则获取父类

import org.joda.time.DateTime;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

public class ClassUtils {

    /**
     * 获取一个类的注解,如果未获取到则获取父类
     *
     * @param clazz      要获取的类
     * @param annotation 注解类型
     * @param <T>        注解类型泛型
     * @return 注解
     */
    public static <T extends Annotation> T getAnnotation(Class<?> clazz, Class<T> annotation) {
        T ann = clazz.getAnnotation(annotation);
        if (ann != null) {
            return ann;
        } else {
            if (clazz.getSuperclass() != Object.class) {
                //尝试获取父类
                return getAnnotation(clazz.getSuperclass(), annotation);
            }
        }
        return ann;
    }

    /**
     * 获取一个方法的注解,如果未获取则获取父类方法
     *
     * @param method     要获取的方法
     * @param annotation 注解类型
     * @param <T>        注解类型泛型
     * @return
     */
    public static <T extends Annotation> T getAnnotation(Method method, Class<T> annotation) {
        T ann = method.getAnnotation(annotation);
        if (ann != null) {
            return ann;
        } else {
            Class clazz = method.getDeclaringClass();
            Class superClass = clazz.getSuperclass();
            if (superClass != Object.class) {
                try {
                    //父类方法
                    Method suMethod = superClass.getMethod(method.getName(), method.getParameterTypes());
                    return getAnnotation(suMethod, annotation);
                } catch (NoSuchMethodException e) {
                    return null;
                }
            }
        }
        return ann;
    }

    public static Class<?> getGenericTypeByType(ParameterizedType genType, int index) {
        Type[] params = genType.getActualTypeArguments();
        if (index >= params.length || index < 0) {
            return null;
        }
        Object res = params[index];
        if (res instanceof Class) {
            return ((Class) res);
        }
        if (res instanceof ParameterizedType) {
            return (Class<?>) ((ParameterizedType) res).getRawType();
        }
        return null;
    }

    /**
     * 获取一个类的泛型类型,如果未获取到返回Object.class
     *
     * @param clazz 要获取的类
     * @param index 泛型索引
     * @return 泛型
     */
    public static Class<?> getGenericType(Class clazz, int index) {
        List<Type> arrys = new ArrayList<>();
        arrys.add(clazz.getGenericSuperclass());
        arrys.addAll(Arrays.asList(clazz.getGenericInterfaces()));
        return arrys.stream()
                .filter(Objects::nonNull)
                .map(type -> {
                    if (clazz != Object.class && !(type instanceof ParameterizedType)) {
                        return getGenericType(clazz.getSuperclass(), index);
                    }
                    return getGenericTypeByType(((ParameterizedType) type), index);
                })
                .filter(Objects::nonNull)
                .filter(res -> res != Object.class)
                .findFirst()
                .orElse((Class) Object.class);
    }

    /**
     * 获取一个类的第一个泛型的类型
     *
     * @param clazz 要获取的类
     * @return 泛型
     */
    public static Class<?> getGenericType(Class clazz) {
        return getGenericType(clazz, 0);
    }


    public static boolean instanceOf(Class clazz, Class target) {
        if (clazz == null) {
            return false;
        }
        if (clazz == target) {
            return true;
        }
        if (target.isInterface()) {
            for (Class aClass : clazz.getInterfaces()) {
                if (aClass == target) {
                    return true;
                }
            }
        }
        if (clazz.getSuperclass() == target) {
            return true;
        } else {
            if (clazz.isInterface()) {
                for (Class aClass : clazz.getInterfaces()) {
                    if (instanceOf(aClass, target)) {
                        return true;
                    }
                }
            }
            return instanceOf(clazz.getSuperclass(), target);
        }
    }

    public static final String YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";

    /**
     * 将对象转为指定的类型
     * <br/>
     * 支持日期,数字,boolean类型转换
     *
     * @param value 需要转换的值
     * @param type  目标类型
     * @return 转换后的值
     */
    public static final <T> T cast(Object value, Class<T> type) {
        if (value == null) {
            return null;
        }
        Object newVal = null;
        if (ClassUtils.instanceOf(value.getClass(), type)) {
            newVal = value;
        } else if (type == Integer.class || type == int.class) {
            newVal = toInt(value);
        } else if (type == Double.class || type == double.class || type == Float.class || type == float.class) {
            newVal = toDouble(value);
        } else if (type == Long.class || type == long.class) {
            newVal = toLong(value);
        } else if (type == Boolean.class || type == boolean.class) {
            newVal = isTrue(value);
        } else if (type == Date.class) {
            newVal = formatUnknownString2Date(value.toString());
        } else if (type == String.class) {
            if (value instanceof Date) {
                newVal = format(((Date) value), YEAR_MONTH_DAY_HOUR_MINUTE_SECOND);
            } else {
                newVal = String.valueOf(value);
            }
        }
        return (T) newVal;
    }

    public static final Set<Class> basicClass = new HashSet<>();

    static {
        basicClass.add(int.class);
        basicClass.add(double.class);
        basicClass.add(float.class);
        basicClass.add(byte.class);
        basicClass.add(short.class);
        basicClass.add(char.class);
        basicClass.add(String.class);
    }

    public static boolean isBasicClass(Class clazz) {
        return basicClass.contains(clazz);
    }

    public static int toInt(Object object) {
        return toInt(object, 0);
    }

    /**
     * 将对象转为int值,如果对象无法进行转换,则使用默认值
     *
     * @param object       要转换的对象
     * @param defaultValue 默认值
     * @return 转换后的值
     */
    public static int toInt(Object object, int defaultValue) {
        if (object instanceof Number) {
            return ((Number) object).intValue();
        }
        if (isInt(object)) {
            return Integer.parseInt(object.toString());
        }
        if (isDouble(object)) {
            return (int) Double.parseDouble(object.toString());
        }
        return defaultValue;
    }


    /**
     * 参数是否是有效整数
     *
     * @param obj 参数(对象将被调用string()转为字符串类型)
     * @return 是否是整数
     */
    public static boolean isInt(Object obj) {
        if (StringUtils.isEmpty(obj)) {
            return false;
        }
        if (obj instanceof Integer) {
            return true;
        }
        return obj.toString().matches("[-+]?\\d+");
    }

    /**
     * 字符串参数是否是double
     *
     * @param obj 参数(对象将被调用string()转为字符串类型)
     * @return 是否是double
     */
    public static boolean isDouble(Object obj) {
        if (StringUtils.isEmpty(obj)) {
            return false;
        }
        if (obj instanceof Double || obj instanceof Float) {
            return true;
        }
        return compileRegex("[-+]?\\d+\\.\\d+").matcher(obj.toString()).matches();
    }


    /**
     * 编译后的正则表达式缓存
     */
    private static final Map<String, Pattern> PATTERN_CACHE = new ConcurrentHashMap<>();

    /**
     * 编译一个正则表达式,并且进行缓存,如果换成已存在则使用缓存
     *
     * @param regex 表达式
     * @return 编译后的Pattern
     */
    public static final Pattern compileRegex(String regex) {
        Pattern pattern = PATTERN_CACHE.get(regex);
        if (pattern == null) {
            pattern = Pattern.compile(regex);
            PATTERN_CACHE.put(regex, pattern);
        }
        return pattern;
    }


    /**
     * 将对象转为Double,如果对象无法转换,将使用默认值0
     *
     * @param object 要转换的对象
     * @return 转换后的值
     */
    public static double toDouble(Object object) {
        return toDouble(object, 0);
    }


    /**
     * 将对象转为Double,如果对象无法转换,将使用默认值
     *
     * @param object       要转换的对象
     * @param defaultValue 默认值
     * @return 转换后的值
     */
    public static double toDouble(Object object, double defaultValue) {
        if (object instanceof Number) {
            return ((Number) object).doubleValue();
        }
        if (isNumber(object)) {
            return Double.parseDouble(object.toString());
        }
        if (null == object) {
            return defaultValue;
        }
        return 0;
    }


    /**
     * 参数是否是有效数字 (整数或者小数)
     *
     * @param obj 参数(对象将被调用string()转为字符串类型)
     * @return 是否是数字
     */
    public static boolean isNumber(Object obj) {
        if (obj instanceof Number) {
            return true;
        }
        return isInt(obj) || isDouble(obj);
    }

    /**
     * 将对象转为 long值,如果无法转换,则转为0
     *
     * @param object 要转换的对象
     * @return 转换后的值
     */
    public static long toLong(Object object) {
        return toLong(object, 0);
    }


    /**
     * 将对象转为long类型,如果对象无法转换,将返回默认值
     *
     * @param object       要转换的对象
     * @param defaultValue 默认值
     * @return 转换后的值
     */
    public static long toLong(Object object, long defaultValue) {
        if (object instanceof Number) {
            return ((Number) object).longValue();
        }
        if (isInt(object)) {
            return Long.parseLong(object.toString());
        }
        if (isDouble(object)) {
            return (long) Double.parseDouble(object.toString());
        }
        return defaultValue;
    }


    /**
     * 对象是否为true
     *
     * @param obj
     * @return
     */
    public static boolean isTrue(Object obj) {
        return "true".equals(String.valueOf(obj));
    }

    public static final String REG_EXP_DATE = "^((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29))\\s+([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$";


    /**
     * 自动解析多种格式的时间字符串为时间对象<br>
     * 支持格式为:yyyy-MM-dd HH:mm:ss 支持多种分隔符,以及多种日期精度。 如yyyy年MM月。 HH时mm分ss秒
     *
     * @param dateString 时间字符串 <br>
     * @return 格式正确则返回对应的java.util.Date对象 格式错误返回null
     */
    public static Date formatUnknownString2Date(String dateString) {
        try {
            if (StringUtils.isEmpty(dateString)) {
                return null;
            }
            dateString = dateString.replace("T", " ");
            String hms = "00:00:00";
            dateString = dateString.trim();
            if (dateString.contains(" ")) {
                // 截取时分秒
                hms = dateString.substring(dateString.indexOf(" ") + 1);
                // 重置日期
                dateString = dateString.substring(0, dateString.indexOf(" "));
                // 多中分隔符的支持
                hms = hms.replace(":", ":");
                hms = hms.replace("时", ":");
                hms = hms.replace("分", ":");
                hms = hms.replace("秒", ":");
                hms = hms.replace("-", ":");
                hms = hms.replace("-", ":");
                // 时间不同精确度的支持
                if (hms.endsWith(":")) {
                    hms = hms.substring(0, hms.length() - 1);
                }
                if (hms.split(":").length == 1) {
                    hms += ":00:00";
                }
                if (hms.split(":").length == 2) {
                    hms += ":00";
                }
            }
            String[] hmsarr = hms.split(":");
            // 不同日期分隔符的支持
            dateString = dateString.replace(".", "-");
            dateString = dateString.replace("/", "-");
            dateString = dateString.replace("-", "-");
            dateString = dateString.replace("年", "-");
            dateString = dateString.replace("月", "-");
            dateString = dateString.replace("日", "");
            // 切割年月日
            String yearStr, monthStr, dateStr;
            // 截取日期
            String[] ymd = dateString.split("-");
            // 判断日期精确度
            yearStr = ymd[0];
            monthStr = ymd.length > 1 ? ymd[1] : "";
            dateStr = ymd.length > 2 ? ymd[2] : "";
            monthStr = monthStr == "" ? Integer.toString(1) : monthStr;
            dateStr = dateStr == "" ? Integer.toString(1) : dateStr;
            String dtr = (yearStr + "-" + monthStr + "-" + dateStr + " " + hms);
            if (!dtr.matches(REG_EXP_DATE)) {
                return null;
            }
            // 返回日期
            return new DateTime(Integer.parseInt(yearStr.trim()), Integer.parseInt(monthStr.trim()), Integer.parseInt(dateStr.trim()), Integer.parseInt(hmsarr[0].trim()), Integer.parseInt(hmsarr[1].trim()), Integer.parseInt(hmsarr[2].trim()), 0).toDate();
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 格式化日期时间
     *
     * @param date    Date对象
     * @param pattern 模式
     * @return 格式化后的日期时间字符串
     */
    public static String format(Date date, String pattern) {
        if (date == null) {
            return "";
        }
        return new DateTime(date).toString(pattern);
    }



}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值