高效缓存的实现及其应用于接口幂等性的尝试

1,前言

该缓存为Java并发编程实战中实现的,主要是为了将性能瓶颈并转变为可伸缩瓶颈,用于提升单线程的性能;该缓存主要是为了解决高开销高耗时接口的性能提升。而基于此接口,能够尝试实现接口的幂等性。

2,代码

1,缓存的实现

class Memorizer<A,V> implements Computable<A,V> {

    private final Map<A, FutureTask<V>> cache = new ConcurrentHashMap<>();
    //通过组合实现接口的调用
    private Computable<A,V> origin;

    public Memorizer(Computable<A,V> origin) {
        this.origin = origin;
    }

    @Override
    public V compute(A arg) throws InterruptedException {
        //循环为了实现当接口调用失败或调用中断时,重新开启接口的调用
        while (true) {
            FutureTask<V> f = cache.get(arg);
            if (f == null) {
                //开启异步来处理复杂接口的调用
                Callable<V> task = () -> origin.compute(arg);
                FutureTask<V> futureTask = new FutureTask<>(task);
                //put-if-absent 缺少即加入
                f = cache.putIfAbsent(arg, futureTask);
                if (f == null) {
                    //first put
                    f = futureTask;
                    f.run();
                }
            }
            try {
                //此处会等待任务返回值
                return f.get();
            } catch (CancellationException e) {
                //如果任务取消,则删除后重新开启任务
                cache.remove(arg,f);
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

}

2,测试


@FunctionalInterface
interface Computable<A,V> {
    V compute(A arg) throws InterruptedException;
}

/**
 * 高耗时接口DEMO
 */
class ExpensiveFunction implements Computable<String, Integer> {
    @Override
    public Integer compute(String arg) throws InterruptedException {
        Thread.sleep(2000);
        return new Random().nextInt(100);
    }
}

/**
 * 缓存的实现
 * cache 装饰者模式实现
 */
class Memorizer<A,V> implements Computable<A,V> {

    private final Map<A, FutureTask<V>> cache = new ConcurrentHashMap<>();
    //通过组合实现接口的调用
    private Computable<A,V> origin;

    public Memorizer(Computable<A,V> origin) {
        this.origin = origin;
    }

    @Override
    public V compute(A arg) throws InterruptedException {
        //循环为了实现当接口调用失败或调用中断时,重新开启接口的调用
        while (true) {
            FutureTask<V> f = cache.get(arg);
            if (f == null) {
                //开启异步来处理复杂接口的调用
                Callable<V> task = () -> origin.compute(arg);
                FutureTask<V> futureTask = new FutureTask<>(task);
                //put-if-absent 缺少即加入
                f = cache.putIfAbsent(arg, futureTask);
                if (f == null) {
                    //first put
                    f = futureTask;
                    f.run();
                }
            }
            try {
                //此处会等待任务返回值
                return f.get();
            } catch (CancellationException e) {
                //如果任务取消,则删除后重新开启任务
                cache.remove(arg,f);
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

}


// 测试接口调用
public class SimpleCache {

    public static void main(String[] args) throws InterruptedException {
        Computable<String, Integer> ex = new ExpensiveFunction();
        Computable<String, Integer> m = new Memorizer<>(ex);

        long s = System.currentTimeMillis();
        Integer compute = m.compute("1");
        long e = System.currentTimeMillis();
        System.err.println(compute+": "+(e-s));

        long s1 = System.currentTimeMillis();
        Integer compute1 = m.compute("1");
        long e1 = System.currentTimeMillis();
        System.err.println(compute1+": "+(e1-s1));

    }

}

3,原理

1,通过`ConcurrentHashMap`确保程序的线程安全,使用原子方法`putIfAbsent`,避免`先检查后执行`的操作漏洞(该漏洞可能会造成两次或多次接口调用)

2,使用`FutureTask`提交缓存的高效性,同时也能避免`先检查后执行`的漏洞,如果使用原返回值,那么编码的实现可能如下,这种情况必然会产生资源浪费的情况

...
V result = cache.get(key);
if (result == null) {
    //此处必然会存在多次接口调用的问题,会造成资源的浪费
    result = 原接口.compute(arg);
    //显然,此处使用putIfAbsent的结果也是相同的
    catch.put(arg,result);
}
...

4,幂等性尝试

1,幂等性场景

  • 网络延迟导致多次重复提交。
  • 表单重复提交。

2,实现策略

调用接口时,保证能有唯一编码确定接口调用标识,比如保存订单时订单的ID。将订单作为key值储存缓存并实现异步的接口调用,最终future.get()来等待线程回归。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值