欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java限流實(shí)現(xiàn)的幾種方法詳解

 更新時(shí)間:2022年12月03日 08:32:04   作者:tcoding  
這篇文章主要介紹了Java限流實(shí)現(xiàn)的幾種方法,通俗的說(shuō),限流就是 限制一段時(shí)間內(nèi),用戶訪問(wèn)資源的次數(shù),減輕服務(wù)器壓力,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

計(jì)數(shù)器

計(jì)數(shù)器限流方式比較粗暴,一次訪問(wèn)就增加一次計(jì)數(shù),在系統(tǒng)內(nèi)設(shè)置每 N 秒的訪問(wèn)量,超過(guò)訪問(wèn)量的訪問(wèn)直接丟棄,從而實(shí)現(xiàn)限流訪問(wèn)。

具體大概是以下步驟:

  • 將時(shí)間劃分為固定的窗口大小,例如 1 s;
  • 在窗口時(shí)間段內(nèi),每來(lái)一個(gè)請(qǐng)求,對(duì)計(jì)數(shù)器加 1;
  • 當(dāng)計(jì)數(shù)器達(dá)到設(shè)定限制后,該窗口時(shí)間內(nèi)的后續(xù)請(qǐng)求都將被丟棄;
  • 該窗口時(shí)間結(jié)束后,計(jì)數(shù)器清零,從新開(kāi)始計(jì)數(shù)。

這種算法的弊端

在開(kāi)始的時(shí)間,訪問(wèn)量被使用完后,1 s 內(nèi)會(huì)有很長(zhǎng)時(shí)間的真空期是處于接口不可用的狀態(tài)的,同時(shí)也有可能在一秒內(nèi)出現(xiàn)兩倍的訪問(wèn)量。

T窗口的前1/2時(shí)間 無(wú)流量進(jìn)入,后1/2時(shí)間通過(guò)5個(gè)請(qǐng)求;

  • T+1窗口的前 1/2時(shí)間 通過(guò)5個(gè)請(qǐng)求,后1/2時(shí)間因達(dá)到限制丟棄請(qǐng)求。
  • 因此在 T的后1/2和(T+1)的前1/2時(shí)間組成的完整窗口內(nèi),通過(guò)了10個(gè)請(qǐng)求。

代碼實(shí)現(xiàn)

 private final Semaphore count = new Semaphore(5);
 @PostConstruct
    public void init() {
        //初始化定時(shí)任務(wù)線程池
        ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2, t -> {
            Thread thread = new Thread(t);
            thread.setName("limit");
            return thread;
        });
        // 每10s執(zhí)行5次
        service.scheduleAtFixedRate(() -> count.release(5), 10, 10, TimeUnit.SECONDS);
  }
 	/**
     * 計(jì)數(shù)器限流
     */
    public void count() {
        try {
            count.acquire();
            System.out.println("count");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

信號(hào)量

控制并發(fā)訪問(wèn)量

具體大概是以下步驟:

  • 初始化信號(hào)量
  • 每個(gè)請(qǐng)求獲取信號(hào)量,請(qǐng)求完釋放

代碼實(shí)現(xiàn)

	private final Semaphore flag = new Semaphore(5);
	/**
     * 信號(hào)量限流
     */
    public void flag() {
        try {
            flag.acquire();
            System.out.println("flag");
            int i = new Random().nextInt(10);
            TimeUnit.SECONDS.sleep(i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            flag.release();
        }
    }

滑動(dòng)窗口

具體大概是以下步驟:

  • 將時(shí)間劃分為細(xì)粒度的區(qū)間每個(gè)區(qū)間
  • 維持一個(gè)計(jì)數(shù)器,每進(jìn)入一個(gè)請(qǐng)求則將計(jì)數(shù)器加一;
  • 多個(gè)區(qū)間組成一個(gè)時(shí)間窗口,每流逝一個(gè)區(qū)間時(shí)間后,則拋棄最老的一個(gè)區(qū)間,納入新區(qū)間。如圖中示例的窗口 T1 變?yōu)榇翱?T2;
  • 若當(dāng)前窗口的區(qū)間計(jì)數(shù)器總和超過(guò)設(shè)定的限制數(shù)量,則本窗口內(nèi)的后續(xù)請(qǐng)求都被丟棄。

代碼實(shí)現(xiàn)

  private final AtomicInteger[] window = new AtomicInteger[10];
 @PostConstruct
    public void init() {
        //初始化定時(shí)任務(wù)線程池
        ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2, t -> {
            Thread thread = new Thread(t);
            thread.setName("limit");
            return thread;
        });
        // 10個(gè)窗口,每次滑動(dòng)1s
        Arrays.fill(window, new AtomicInteger(0));
        service.scheduleAtFixedRate(() -> {
            int index = (int) (System.currentTimeMillis() / 1000 % 10);
            window[index] = new AtomicInteger(0);
        }, 1, 1, TimeUnit.SECONDS);
}
 	/**
     * 滑動(dòng)窗口
     */
    public void window() {
        int sum = 0;
        for (int i = 0; i < window.length; i++) {
            sum += window[i].get();
        }
        if (sum > 10) {
            return;
        }
        System.out.println("window");
        int index = (int) (System.currentTimeMillis() / 1000 % 10);
        window[index].getAndAdd(1);
    }

漏桶

具體大概是以下步驟:

  • 初始化一個(gè)隊(duì)列,做桶
  • 每個(gè)請(qǐng)求入隊(duì)列,隊(duì)列滿則阻塞
  • 啟動(dòng)定時(shí)任務(wù),以固定的速率執(zhí)行,執(zhí)行時(shí)判讀一下入隊(duì)時(shí)間,如果延遲太久,直接丟棄(有可能客戶端已經(jīng)超時(shí),服務(wù)端還沒(méi)有處理)

代碼實(shí)現(xiàn)

 private final BlockingQueue<Long> queue = new LinkedBlockingDeque<>(5);
  @PostConstruct
    public void init() {
        //初始化定時(shí)任務(wù)線程池
        ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2, t -> {
            Thread thread = new Thread(t);
            thread.setName("limit");
            return thread;
        });
        // 一恒定的速率執(zhí)行
        service.scheduleAtFixedRate(() -> {
            try {
                if (System.currentTimeMillis() - queue.take() > 1000L) {
                    process();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 100, 100, TimeUnit.MILLISECONDS);
}
	/**
     * 漏桶限流
     */
    public void bucket() {
        try {
            queue.put(System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  private void process() {
        System.out.println("process");
    }

令牌桶

令牌桶算法是漏斗算法的改進(jìn)版,為了處理短時(shí)間的突發(fā)流量而做了優(yōu)化,令牌桶算法主要由三部分組成:令牌流、數(shù)據(jù)流、令牌桶。

名詞釋義:

  • 令牌桶:流通令牌的管道,用于生成的令牌的流通,放入令牌桶中。
  • 數(shù)據(jù)流:進(jìn)入系統(tǒng)的數(shù)據(jù)流量。
  • 令牌桶:保存令牌的區(qū)域,可以理解為一個(gè)緩沖區(qū),令牌保存在這里用于使用。

具體大概是以下步驟:

  • 初始化一個(gè)隊(duì)列做桶,大小為通的大小
  • 啟動(dòng)定時(shí)任務(wù),以一定的速率往隊(duì)列中放入令牌
  • 每個(gè)請(qǐng)求來(lái)臨,去隊(duì)列中獲取令牌,獲取成功正執(zhí)行,否則阻塞

代碼實(shí)現(xiàn)

private final BlockingQueue<Integer> token = new LinkedBlockingDeque<>(5);
  @PostConstruct
    public void init() {
        //初始化定時(shí)任務(wù)線程池
        ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2, t -> {
            Thread thread = new Thread(t);
            thread.setName("limit");
            return thread;
        });
        // 以恒定的速率放入令牌
        service.scheduleAtFixedRate(() -> {
            try {
                token.put(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 1, 1, TimeUnit.SECONDS);
    }
    public void token() {
        try {
            token.take();
            System.out.println("token");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

測(cè)試

  @Resource
    private LimitDemo demo;
    @Test
    public void count() throws InterruptedException {
        process(() -> demo.count());
    }
    @Test
    public void flag() throws InterruptedException {
        process(() -> demo.flag());
    }
    @Test
    public void window() throws InterruptedException {
        process(() -> demo.window());
    }
    @Test
    public void bucket() throws InterruptedException {
        process(() -> demo.bucket());
    }
    @Test
    public void token() throws InterruptedException {
        process(() -> demo.token());
    }
    private void process(Process process) throws InterruptedException {
        CompletableFuture<?>[] objects = IntStream.range(0, 10).mapToObj(i -> CompletableFuture.runAsync(() -> {
            while (true) {
                process.execute();
            }
        })).collect(Collectors.toList()).toArray(new CompletableFuture<?>[] {});
        CompletableFuture.allOf(objects);
        new CountDownLatch(1).await();
    }
    @FunctionalInterface
    public interface Process {
        void execute();
    }

示例代碼

源碼地址 https://github.com/googalAmbition/googol/tree/master/limit

到此這篇關(guān)于Java限流實(shí)現(xiàn)的幾種方法詳解的文章就介紹到這了,更多相關(guān)Java限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java CAS原子操作詳解

    Java CAS原子操作詳解

    在synchronized的優(yōu)化過(guò)程中我們看到大量使用了CAS操作,CAS全稱Compare And Set(或Compare And Swap),簡(jiǎn)單來(lái)說(shuō)CAS操作就是一個(gè)虛擬機(jī)實(shí)現(xiàn)的原子操作
    2023-02-02
  • spring boot項(xiàng)目同時(shí)傳遞參數(shù)和文件的多種方式代碼演示

    spring boot項(xiàng)目同時(shí)傳遞參數(shù)和文件的多種方式代碼演示

    這篇文章主要介紹了spring boot項(xiàng)目同時(shí)傳遞參數(shù)和文件的多種方式,在開(kāi)發(fā)接口中,遇到了需要同時(shí)接收參數(shù)和文件的情況,可以有多種方式實(shí)現(xiàn)文件+參數(shù)的接收,這里基于spring boot 3 + vue 3 + axios,做一個(gè)簡(jiǎn)單的代碼演示,需要的朋友可以參考下
    2023-06-06
  • 詳解設(shè)計(jì)模式中的proxy代理模式及在Java程序中的實(shí)現(xiàn)

    詳解設(shè)計(jì)模式中的proxy代理模式及在Java程序中的實(shí)現(xiàn)

    代理模式主要分為靜態(tài)代理和動(dòng)態(tài)代理,使客戶端方面的使用者通過(guò)設(shè)置的代理來(lái)操作對(duì)象,下面來(lái)詳解設(shè)計(jì)模式中的proxy代理模式及在Java程序中的實(shí)現(xiàn)
    2016-05-05
  • 使用java采集京東商城區(qū)劃數(shù)據(jù)示例

    使用java采集京東商城區(qū)劃數(shù)據(jù)示例

    這篇文章主要介紹了java采集京東的全國(guó)區(qū)劃數(shù)據(jù)示例,保存成json形式,如想轉(zhuǎn)換到數(shù)據(jù)庫(kù)只需反序列化為對(duì)象保存到數(shù)據(jù)庫(kù)即可
    2014-03-03
  • 如何在SpringBoot 中使用 Druid 數(shù)據(jù)庫(kù)連接池

    如何在SpringBoot 中使用 Druid 數(shù)據(jù)庫(kù)連接池

    這篇文章主要介紹了SpringBoot 中使用 Druid 數(shù)據(jù)庫(kù)連接池的實(shí)現(xiàn)步驟,幫助大家更好的理解和學(xué)習(xí)使用SpringBoot,感興趣的朋友可以了解下
    2021-03-03
  • Java原子變量類常見(jiàn)問(wèn)題解決

    Java原子變量類常見(jiàn)問(wèn)題解決

    這篇文章主要介紹了Java原子變量類常見(jiàn)問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • SpringFramework應(yīng)用接入Apollo配置中心過(guò)程解析

    SpringFramework應(yīng)用接入Apollo配置中心過(guò)程解析

    這篇文章主要介紹了SpringFramework應(yīng)用接入Apollo配置中心過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 淺談Java設(shè)計(jì)模式系列-裝飾器模式

    淺談Java設(shè)計(jì)模式系列-裝飾器模式

    這篇文章主要介紹了Java設(shè)計(jì)模式系列-裝飾器模式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 淺談java中BigDecimal的equals與compareTo的區(qū)別

    淺談java中BigDecimal的equals與compareTo的區(qū)別

    下面小編就為大家?guī)?lái)一篇淺談java中BigDecimal的equals與compareTo的區(qū)別。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-11-11
  • Java 正則表達(dá)式 解釋說(shuō)明

    Java 正則表達(dá)式 解釋說(shuō)明

    java正則知識(shí)小結(jié),一些常見(jiàn)的正則都包括在里面,推薦收藏。
    2009-06-06

最新評(píng)論