• 180705-一个简单的幂等工具类实现


    logo

    一个简单的幂等工具类

    在日常的工作中,业务的去重幂等场景属于比较常见的需求,一般来讲简单的幂等工具类可以基于内存或者基于redis进行,本篇简单介绍下,如何使用Guava的缓存来实现一个幂等工具类

    I. 基本思路与实现

    利用Guava的内存缓存来缓存,如果执行完毕,则在缓存中添加一个标识,每次执行之前,判断是否执行过,从而实现简单的幂等逻辑

    1. 基本实现

    基于此,一个简单的工具来就出炉了

    public static final String NOT_HIT_TAG = "UNHIT_TAG";
    
    private static LoadingCache<String, Object> idempotentCache =
        CacheBuilder.newBuilder().expireAfterAccess(3, TimeUnit.MINUTES).build(new CacheLoader<String, Object>() {
            @Override
            public Object load(String key) throws Exception {
                return NOT_HIT_TAG;
            }
    });
    
    public static Object getObject(String uuid) {
        return idempotentCache.getUnchecked(uuid);
    }
    

    上面的代码比较简单,这个幂等工具类,key为唯一标识,value为上次计算的结果,因此在下次再次执行时,直接拿这个结果即可,适用于需要获取计算结果作为他用的业务场景中。那么在实际使用中,直接这么用是否可行?

    答案却是不行,在实际使用的时候,有几个地方需要注意

    • 如果某次计算结果返回的null怎么办?
    • 内存是否会爆掉?

    2. null值问题

    针对返回结果为null的场景,也好解决,就是利用一个符号来代替null,简单的变形如下

    public static final String NOT_HIT_TAG = "UNHIT_TAG";
    public static final String NULL_TAG = "NULL_TAG";
    private static LoadingCache<String, Object> idempotentCache =
            CacheBuilder.newBuilder().expireAfterAccess(3, TimeUnit.MINUTES).build(new CacheLoader<String, Object>() {
                @Override
                public Object load(String key) throws Exception {
                    return NOT_HIT_TAG;
                }
            });
    
    public static Object getObject(String uuid) {
        Object obj = idempotentCache.getUnchecked(uuid);
        if (obj instanceof String) {
            if (NULL_TAG.equals(obj)) {
                return null;
            }
        }
    
        return obj;
    }
    
    public static void putObject(String uuid, Object val) {
        if (val == null) {
            val = NULL_TAG;
        }
    
        idempotentCache.put(uuid, val);
    }
    

    在上面使用中,有一点需要注意,在取出数据之后,首先判断下是否为未命中状态?为什么未命中要这么干?而言看博文

    3. 内存问题

    虽然上面设置了失效时间为3min,但在jdk8的场景下,很容易发现内存疯狂上涨,不见到有回收? why?这块可能与gauva的内存回收机制有关系,因为jdk8取消了永久代,使用了元空间,当没有设最大值时,会一直上涨,使用系统的内存

    简单的解决方案就是主动回收掉无效的数据

    public static final String NOT_HIT_TAG = "UNHIT_TAG";
    public static final String NULL_TAG = "NULL_TAG";
    private static LoadingCache<String, Object> idempotentCache =
            CacheBuilder.newBuilder().expireAfterAccess(3, TimeUnit.MINUTES).build(new CacheLoader<String, Object>() {
                @Override
                public Object load(String key) throws Exception {
                    return NOT_HIT_TAG;
                }
            });
    
    public static Object getObject(String uuid) {
        Object obj = idempotentCache.getUnchecked(uuid);
        if (obj instanceof String) {
            if (NULL_TAG.equals(obj)) {
                return null;
            }
        }
    
        return obj;
    }
    
    public static void putObject(String uuid, Object val) {
        if (val == null) {
            val = NULL_TAG;
        }
    
        idempotentCache.put(uuid, val);
    }
    
    public static void remove(String uuid) {
        idempotentCache.invalidate(uuid);
    }
    
    public static void registerScheduleClearTask() {
        ScheduledExecutorService task =
                Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("idempotent"));
        task.scheduleAtFixedRate(() -> idempotentCache.cleanUp(), 1, 1, TimeUnit.MINUTES);
    }
    

    II. 其他

    1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

    一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

    2. 声明

    尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

    3. 扫描关注

    QrCode

  • 相关阅读:
    Codeforces Round #456 (Div. 2)
    Codeforces Round #455 (Div. 2)
    Codeforces Round #453 (Div. 1)
    Codeforces Round #450 (Div. 2)
    退役了
    退役了
    这个博客不想要了
    Hello!The familiar and strange world.
    真正的退役了。
    bzoj4231: 回忆树
  • 原文地址:https://www.cnblogs.com/yihuihui/p/9270539.html
一二三 - 开发者的网上家园