zoukankan      html  css  js  c++  java
  • Guava并发使用学习

    Guava并发使用学习

    前言:

    在开发高并发系统,有三把利器用来保护系统:缓存,降级,限流。

    在开放api接口时候,有时也需要用限流来控制,防止并发过高,系统奔溃。

    1.Maven配置:

    <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
       <version>27.0.1-jre</version>
    </dependency>

    2.基本类方法

    //0.5代表一秒最多多少个
    
    RateLimiter rateLimiter = RateLimiter.create(0.5);
    
    //rateLimiter.acquire()该方法会阻塞线程,直到令牌桶中能取到令牌为止才继续向下执行,并返回等待的时间。
    
    rateLimiter.acquire()
    
    //从RateLimiter 获取许可如果该许可可以在不超过timeout的时间内获取得到的话,
    //或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待)
    
    tryAcquire(long timeout, TimeUnit unit)

    3. 限流

    //5代表一秒最多5个
    
    RateLimiter rateLimiter = RateLimiter.create(5);
    
     
    
    @RequestMapping(value = "/miaosha")
    @ResponseBody
    public String miaosha(int count, String code) {
    
    
        System.out.println("等待时间:" + rateLimiter.acquire());
        if (update(code, count) > 0) {
            return "购买成功";
        }
        return "购买失败";
    }

    请求过来时,调用RateLimiter.acquire,如果每秒超过了5个请求,就阻塞等待

    jmeter演示:

    等待时间:0.0
    等待时间:0.0
    等待时间:0.0

    等待时间:0.0

    等待时间:0.0
    等待时间:0.175592
    等待时间:0.375583
    等待时间:0.575578
    等待时间:0.775507
    等待时间:0.975484
    等待时间:1.175457
    等待时间:1.375442
    等待时间:1.575435
    等待时间:1.775429
    等待时间:1.975424

    结论:5个请求无需等待直接成功,后面的开始被1秒5次限流了,基本上每0.2秒放行一个。

     

    4. 降级

    RateLimiter rateLimiter = RateLimiter.create(10);
    
     
    
    @RequestMapping("/buy")
    @ResponseBody
    public String buy(int count, String code) {
        //判断能否在1秒内得到令牌,如果不能则立即返回false,不会阻塞程序
        if (!rateLimiter.tryAcquire(100, TimeUnit.MILLISECONDS)) {
            System.out.println("短期无法获取令牌,真不幸,排队也瞎排");
            return "失败";
        }
        if (update(code, count) > 0) {
            System.out.println("购买成功");
            return "成功";
        }
        System.out.println("数据不足,失败");
        return "失败";
    }

     tryAcquire(long timeout, TimeUnit unit)
    * 从RateLimiter 获取许可如果该许可可以在不超过timeout的时间内获取得到的话,
    * 或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待)

    jmeter演示(100个请求):

    购买成功
    * 购买成功
    * 购买成功
    * 购买成功
    * 购买成功
    * 购买成功
    * 购买成功
    * 购买成功
    * 购买成功
    * 购买成功
    * 购买成功
    * 购买成功
    * 短期无法获取令牌,真不幸,排队也瞎排
    * 短期无法获取令牌,真不幸,排队也瞎排
    * 短期无法获取令牌,真不幸,排队也瞎排
    * 短期无法获取令牌,真不幸,排队也瞎排
    * 短期无法获取令牌,真不幸,排队也瞎排
    * 短期无法获取令牌,真不幸,排队也瞎排
    * 短期无法获取令牌,真不幸,排队也瞎排
    * 购买成功
    * 短期无法获取令牌,真不幸,排队也瞎排
    * ...

    基本上就是前10个成功,后面的就开始按照固定的速率而成功了。

    这种场景更符合实际的应用场景,按照固定的单位时间进行分割,每个单位时间产生一个令牌,可供购买。

    5. guava缓存

    创建:

    public static com.google.common.cache.CacheLoader<String, String> createCacheLoader() {
        return new com.google.common.cache.CacheLoader<String, String>() {
            @Override
            public String load(String key) throws Exception {
                System.out.println( "加载创建key:" + key);
                return key+"+value";
            }
        };
    }

     

    //CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
    LoadingCache<String, String> cache = CacheBuilder.newBuilder()
            //设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
            .maximumSize(1000)
            //设置写缓存后30L过期
            .expireAfterAccess(30L, TimeUnit.MILLISECONDS)
            //build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
            .build(createCacheLoader());

     

    //放入缓存 cache.put(key,value);
    // 移除缓存 cache.invalidate(key);
    //批量删除缓存 cache.invalidateAll(keys);  List<String> keys
    //会重新加载创建cache   cache.getUnchecked(key);
    //不会重新加载创建cache   cache.getIfPresent(key);
    //获取,会抛出异常   cache.get(key);
    //任何时候,你都可以显式地清除缓存项,而不是等到它被回收:
    // 个别清除:Cache.invalidate(key)
    //批量清除:Cache.invalidateAll(keys)
    // 清除所有缓存项:Cache.invalidateAll()
    //正如LoadingCache.refresh(K)所声明,刷新表示为键加载新值,这个过程可以是异步的。在刷新操作进行时,缓存仍然可以向其他线程返回旧值,而不像回收操作,读缓存的线程必须等待新值加载完成。
    //expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。
    //expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回 收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。

     

  • 相关阅读:
    python字典及其内置函数详解
    python函数语法学习
    python切片、迭代、生成器、列表生成式等高级特性学习
    Python入门及安装
    node中的加密模块 crypto
    Nodejs+MongoDB+Bootstrap+esj搭建的个人简易博客
    JavaScript的深拷贝和浅拷贝总结
    redux 学习总结
    简述redux(1)
    通信数据转发程序:代理、网关、隧道
  • 原文地址:https://www.cnblogs.com/ltian123/p/10457702.html
Copyright © 2011-2022 走看看