<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      糊涂啊!這個需求居然沒想到用時間輪來解決。

      你好呀,我是歪歪。

      上周不是發布了這篇文章嘛:《也是出息了,業務代碼里面也用上算法了。》

      里面聊到一個場景,A、B、C 三個平臺需要調用下游系統的接口查詢數據。

      當時下游對該查詢接口做了限流,只支持一秒最多一個請求。

      其中 A 平臺要求每個請求間隔 6s 或者以上。

      B,C 平臺可以接受一秒一次請求。

      如何實現時間的最大化利用?

      也就是把 A 平臺中的間隔時間利用起來:

      把中間用其他平臺的數據塞滿:

      我當時靈機一動,想到一個騷操作:加權輪詢負載均衡算法。

      最終也算是實現了這個需求。

      你知道的,在業務代碼里面能用到算法還是一件很稀奇的事情,所以我寫了上面那篇文章。

      文章發布之后,經過讀者提醒,我又打開了新思路。

      找到了比我這個加權輪詢負載均衡算法實現更加優雅的實現方案。

      時間輪

      其實當這個讀者提到“一個平臺一條時間輪”這句話的時候,我的思路一下就打開了。

      時間輪,這個玩意我也會啊。

      但是時間輪這個玩意,我們先按下不表,先分析一下這個讀者的思路。

      他在描述的時候,對時間輪加了引號,等我理解他的意思之后才知道,這個引號加的很巧妙。

      因為他用的并不是真正的時間輪,而是借鑒了時間輪的“時間間隔”思路。

      為了我畫圖方便,我先做一個預設:

      • A 平臺的時間間隔為 5s
      • B 平臺的時間間隔為 3s
      • C 平臺的時間間隔為 1s

      然后我們需要一個初始化的時間,假設為 10 點整,這個 10 點整你可以理解為程序跑起來的時間點。

      那么整個核心思路大概是這樣的。

      首先,在程序剛剛啟動的時候,A、B、C 3 個平臺都是滿足條件的。

      所以理論上任何一個平臺都可以。

      對平臺進行循環,最先循環到的是 A:

      然后,關鍵的地方就來了。

      平臺被選中后,按照專屬的時間間隔更新下次執行時間:

      A 平臺下次執行時間就變成了 10:00:05。

      按照這個邏輯往下推。

      前三秒就是這樣的:

      繼續往下,到第六秒的時候,是這樣的:

      到這里,我必須把 10:00:05 這個時間切片單獨拿出來給你看看,這個點非常關鍵:

      你說,這個時候應該是 A 執行還是 C 執行呢,畢竟它們都滿足“當前時間大于等于下次執行時間”這個判斷條件。

      按理來說,代碼循環的時候,最新取到的肯定是 A,所以 A 先執行,沒毛病。

      但是實際上大概率是 C 執行。

      為啥呢?

      回到 A 的下次執行時間被更新時的這個時間點:

      你仔細分析圖中的這句話:

      更新平臺 A 的下次執行時間: System.currentTimeMillis()+5s

      那平臺 A 的下次執行時間會正正好好是 10:00:05 嗎?

      有 System.currentTimeMillis() 的存在,是不是 10:00:05:123 這樣帶著點毫秒數的時間更加合理點?

      所以,當我把毫秒數帶著,這樣你再看,是不是大概率是 C 執行了:

      那么問題又來了:為什么是大概率 C 執行呢?

      在什么情況下可能會把 A 選出來執行呢?

      在 GC 抖動的情況下,當前時間可能也會往前偏移一點,可能會把 A 選出來執行。

      但是,A、C 誰先誰后,根本不重要,因為在當前的情況下,A、C 誰都可以執行。

      整個思路就是這樣的。

      思路清晰了,代碼不就是“呼大模型”而出了嘛:

      public class PlatformLaneScheduler {
          // 平臺配置類
          static class Platform {
              final String name;
              final long interval; // 執行間隔(毫秒)
              long nextAllowedTime; // 下次允許執行時間
              
              public Platform(String name, long intervalSeconds) {
                  this.name = name;
                  this.interval = intervalSeconds * 1000;
                  this.nextAllowedTime = System.currentTimeMillis(); // 初始可立即執行
              }
          }
          
          // 全局狀態
          private final Map<String, Platform> platforms = new HashMap<>();
          private long lastExecutionTime = 0; // 上次執行時間
          private final ScheduledExecutorService scheduler;
          
          public PlatformLaneScheduler() {
              // 初始化調度器(單線程)
              scheduler = Executors.newSingleThreadScheduledExecutor();
              
              // 添加平臺配置
              addPlatform("A", 5);
              addPlatform("B", 3);
              addPlatform("C", 1);
          }
          
          public void addPlatform(String name, long intervalSeconds) {
              platforms.put(name, new Platform(name, intervalSeconds));
          }
          
          public void start() {
              // 每100ms巡檢一次
              scheduler.scheduleAtFixedRate(this::checkPlatforms, 0, 100, TimeUnit.MILLISECONDS);
          }
          
          public void stop() {
              scheduler.shutdown();
          }
          
          private void checkPlatforms() {
              long now = System.currentTimeMillis();
              // 檢查全局限流:1秒內只能執行一次
              if (now - lastExecutionTime < 1000) {
                  return;
              }
              // 查找最早到期的平臺
              Platform earliestPlatform = null;
              long minNextTime = Long.MAX_VALUE;
              for (Platform platform : platforms.values()) {
                  if (platform.nextAllowedTime <= now && platform.nextAllowedTime < minNextTime) {
                      earliestPlatform = platform;
                      minNextTime = platform.nextAllowedTime;
                  }
              }
              // 執行符合條件的平臺請求
              if (earliestPlatform != null) {
                  executePlatformRequest(earliestPlatform, now);
              }
          }
          
          private void executePlatformRequest(Platform platform, long now) {
              // 執行請求
              System.out.printf("[%tT.%tL] %s平臺執行 | 設定間隔: %ds | 實際間隔: %.3fs%n",
                      now, now, platform.name, platform.interval / 1000,
                      (now - platform.nextAllowedTime + platform.interval) / 1000.0);
              // 更新平臺狀態
              platform.nextAllowedTime = now + platform.interval;
              // 更新全局狀態
              lastExecutionTime = now;
          }
          
          public static void main(String[] args) throws InterruptedException {
              PlatformLaneScheduler laneScheduler = new PlatformLaneScheduler();
              laneScheduler.start();
              // 運行60秒
              TimeUnit.SECONDS.sleep(60);
              laneScheduler.stop();
          }
      }

      從代碼執行結果來看,第 6 秒,它確實選擇了 C 平臺執行:

      老實說,這個解決方案,比我那個劍走偏鋒的加權輪詢負載均衡的方案好多了。

      可以支持任意多個平臺,每個平臺都可以配置個性化的時間間隔。

      而且這個方案的底層邏輯理解起來的成本也非常低。

      但是,你看看這章的小標題,叫做“時間輪”。

      上面這個方案并不是真正意義上的時間輪。

      真正的時間輪

      那么什么是時間輪呢?

      首先時間輪最基本的結構其實就是一個數組,比如下面這個長度為 8 的數組:

      怎么變成一個輪呢?

      首尾相接就可以了:

      假如每個元素代表一秒鐘,那么這個數組一圈能表達的時間就是 8 秒,就是這樣的:

      注意我前面強調的是一圈,為 8 秒。

      那么 2 圈就是 16 秒, 3 圈就是 24 秒,100 圈就是 800 秒。

      這個能理解吧?

      我再給你配個圖:

      雖然數組長度只有 8,但是它可以在上疊加一圈又一圈,那么能表示的數據就多了。

      比如我把上面的圖的前三圈改成這樣畫:

      希望你能看明白,如果你看不明白,不要懷疑自己,肯定是垃圾作者畫得不行,和你自己沒關系。

      記住,全文重點:與其反思自己,不如指責別人。

      我畫上面的圖主要是要你知道這里面有一個“第幾圈”的概念。

      好了,我現在把前面的這個數組美化一下,從視覺上也把它變成一個輪子。

      輪子怎么說?

      輪子的英文是 wheel,所以我們現在有了一個叫做 wheel 的數組:

      然后,把前面的數據給填進去大概是長這樣的。

      為了方便示意,我只填了下標為 0 和 3 的位置,其他地方也是一個意思:

      那么問題就來了。假設這個時候我有一個需要在 800 秒之后執行的任務,應該是怎么樣的呢?

      800 mod 8 =0,說明應該掛在下標為 0 的地方:

      假設又來一個 400 秒之后需要執行的任務呢?

      同樣的道理,繼續往后追加即可:

      不要誤以為下標對應的鏈表中的圈數必須按照從小到大的順序來,這個是沒有必要的。

      好,現在又來一個 403 秒后需要執行的任務,應該掛在哪兒?

      403 mod 8 = 3,那么就是這樣的:

      我為什么要不厭其煩的給你說怎么計算,怎么掛到對應的下標中去呢?

      因為我還需要引出一個東西:待分配任務的隊列。

      上面畫 800 秒、 400 秒和 403 秒的任務的時候,我還省略了一步。

      其實應該是這樣的:

      你看這個玩意,是不是就和我們討論的場景很像了。

      還是這一套老參數:

      • A 平臺的時間間隔為 5s
      • B 平臺的時間間隔為 3s
      • C 平臺的時間間隔為 1s

      假設我們的時間輪一圈是 8s,這個時間你可以自定義,你要是喜歡 10s 也不是不可以。

      那么第一個八秒,即第一圈,應該是這樣的:

      • 第 1 秒,A 平臺執行。放在第 1 圈,第 1 個位置
      • 第 2 秒,B 平臺執行。放在第 1 圈,第 2 個位置
      • 第 3 秒,C 平臺執行。放在第 1 圈,第 3 個位置
      • 第 4 秒,C 平臺執行。放在第 1 圈,第 4 個位置
      • 第 5 秒,B 平臺執行。放在第 1 圈,第 5 個位置
      • 第 6 秒,C 平臺執行。放在第 1 圈,第 6 個位置
      • 第 7 秒,A 平臺執行。放在第 1 圈,第 7 個位置
      • 第 8 秒,C 平臺執行。放在第 2 圈,第 0 個位置

      第二個八秒,即第二圈,是這樣的,我用不同的顏色來表示:

      • 第 9 秒,B 平臺執行。放在第 2 圈,第 1 個位置
      • 第 10 秒,C 平臺執行。放在第 2 圈,第 2 個位置
      • 第 11 秒,C 平臺執行。放在第 2 圈,第 3 個位置
      • 第 12 秒,A 平臺執行。放在第 2 圈,第 4 個位置
      • 第 13 秒,B 平臺執行。放在第 2 圈,第 5 個位置
      • 第 14 秒,C 平臺執行。放在第 2 圈,第 6 個位置
      • 第 15 秒,C 平臺執行。放在第 2 圈,第 7 個位置
      • 第 16 秒,B 平臺執行。放在第 3 圈,第 0 個位置

      這玩意,才叫真正的時間輪。

      以時間間隔為桶,根本不關心桶里面裝的是哪個平臺的數據。

      反正時間一到,就把桶里面的數據往外扔就完事了。

      你理解了真正的時間輪,你就知道為什么我說前面讀者給出來的方案中的“時間輪”這個引號加的很巧妙。

      他只是借用了時間輪中"定期檢查"的思想,但從數據結構、調度機制和實現方式上,都與真正的時間輪有本質區別。

      有點那種就是:我也不想解釋我這個方案不是真正的時間輪,我只是聯想到了真正的時間輪,借鑒了里面的一些思路。但是我又懶得給你解釋這么多,我就加個引號在這里,你自己去悟。能不能悟出來,就看個人道行深淺了。

      扔掉時間輪

      所以,你以為到這里就結束了嗎?

      這篇文章下面還有一個評論。

      這個評論寥寥數語,就給出了一個我認為是最佳方案的方案:

      為什么不像上面說的用時間輪?

      因為平臺數比包數小很多,不需要時間輪這種復雜結構。

      所以,還有高手?

      前面我們說了,如果用真正的時間輪的話,里面放的是什么?

      一圈又一圈的,放的是每個平臺的數據。

      但是換個視角,如果我們只關注平臺呢?

      把所有的平臺都放到二叉堆里面去,利用二叉堆這個數據結構,幫我們實現平臺維度的排序。

      數據結構一換,整個解題思路又不一樣了。

      而這位讀者的思路,和 DeepSeek 的思路是一致的。

      直接給出了可運行的代碼。

      核心是基于 Java 的 PriorityQueue 實現:

      不一樣的是 DeepSeek 還給出了“錦上添花”的部分:

      DeepSeek 怎么說

      我還追問了 DeepSeek,讓其用時間輪來解題。

      它也解了:

      隨便給我對比了一下兩個方案的優劣勢:

      然后給出了推薦的方案:

      所以,這里也是在回答前面那篇文章中的這個留言:

      從實踐來看,AI 不會給出我最開始想到的“加權負載均衡算法”。

      它會直接給出“基于優先級隊列”這個最符合實際場景,也是最簡單有效的實現方案。

      寫在最后

      所以,你以為到這里就結束了嗎?

      要我說,其實前面的這些東西其實都不重要。

      你看懂了,更好。看不懂,就算求了。

      通過這個事情,我想要表達的還是我寫文章以來一直堅持的一個觀點:鼓勵分享。

      誰說寫文章給出的方案就一定是要十全十美的?

      我前面寫的這篇文章,里面就帶著“技術債”。

      在分享上一篇文章后,通過和讀者的思維碰撞,我得到了兩種更有效、更優雅的解決方案。

      這就是分享后的獎勵。

      它不只是單向輸出,而是一個相互學習的過程。

      別怕寫得不夠好。

      寫的不好,最壞的結果是你默默修正了認知。

      而最好的結果是什么?

      你收獲的可能不只是兩個更好的方案,而是更多人的智慧增量,以及下次能寫出更“抗打”文章的底氣。

      甚至,進一步來說,拋開技術層面,大膽寫下你的任何思考,哪怕它帶著毛刺和縫隙。

      因為正是這些縫隙,讓光得以照進,讓其他思想得以注入。

      一個人類的思想與另外一個或者一群人的思想進行碰撞,才會產生智慧的花火。

      posted @ 2025-07-21 21:35  why技術  閱讀(3154)  評論(7)    收藏  舉報
      主站蜘蛛池模板: 国产精品免费中文字幕| 秋霞电影网| XXXXXHD亚洲日本HD| 无码av中文字幕免费放| 在线中文字幕国产一区| 亚洲成人av一区免费看| 国产老妇伦国产熟女老妇高清| 国产欧美日韩综合精品二区| 大胸美女被吃奶爽死视频| 免费无码高潮流白浆视频| 亚洲 成人 无码 在线观看| 国产亚洲一区二区三区成人| 亚洲精品日本久久久中文字幕| 国产仑乱无码内谢| 国产成人精品永久免费视频| 东京热人妻丝袜无码AV一二三区观| 亚洲欧洲一区二区精品| 日韩丝袜人妻中文字幕| 国产精品视频中文字幕| 婷婷四房综合激情五月在线| 色老99久久九九爱精品| 欧美激情内射喷水高潮| 丁香花成人电影| 精品久久久久久亚洲综合网 | 加勒比无码av中文字幕| 国产一区二区在线有码| 国产精品普通话国语对白露脸| 国产精品免费久久久免费| 国产精品制服丝袜无码| 亚洲精品一区二区三区蜜| 妇女性内射冈站hdwww000| 久久精品国产88精品久久| 性欧美vr高清极品| 国产精品国产三级国快看| 中文字幕va一区二区三区| 欧美国产精品不卡在线观看| 亚洲欧美综合一区二区三区| 精品国产一区二区三区四区阿崩| 日韩有码中文在线观看| 成人福利一区二区视频在线| 久久久亚洲欧洲日产国码606|