響應式編程框架Reactor【4】

文章目錄

  • 七、調度與線程模型
    • 7.1 概述
    • 7.2 Scheduler: Reactor 的線程調度器
    • 7.3 兩大核心操作符:subscribeOn vs publishOn
    • 7.4 示例詳解
      • 7.4.1 subscribeOn()的全局影響
      • 7.4.2 publishOn() 的局部切換
      • 7.4.3 多個publishOn切換
      • 7.4.4 線程切換時序圖
    • 7.5 核心調度器
      • 7.5.1 BoundedElastic:IO 密集型任務首選
      • 7.5.2 Parallel:CPU 密集型任務首選
      • 7.5.3 Single:串行任務專用
      • 7.5.4 Schedulers.immediate()
      • 7.5.5 Schedulers.elastic()
      • 7.5.6 Schedulers.fromExecutorService(ExecutorService)
      • 7.5.7 Schedulers.new() 工廠方法
      • 7.5.8 調度器使用最佳實踐
    • 7.6 線程模型實戰: 典型場景
      • 7.6.1 I/O密集型任務
      • 7.6.2 場景 2:CPU 密集型任務
      • 7.6.3 混合任務(I/O + CPU)
    • 7.7 綜合示例
    • 7.8 高級特性
      • 7.8.1 調用器生命周期管理
      • 7.8.2 自定義線程命名
      • 7.8.3 在操作符中使用調度器
    • 7.9 最佳實踐與陷阱

七、調度與線程模型

? 核心作用

  • 線程抽象:將底層線程管理與響應式流解耦,提供統一的 API 控制執行上下文。
  • 異步執行:支持非阻塞操作,避免阻塞主線程,提升系統吞吐量。
  • 并發控制:通過不同類型的調度器,適配不同的并發場景(如 IO 密集型、CPU 密集型)。

🌺 關鍵概念

  • 調度器(Scheduler):負責提供執行任務的線程,是 Reactor 中線程池的抽象。
  • 調度器工作線程(Worker):Scheduler 創建的輕量級工作單元,負責執行具體任務。
  • publishOn () 與 subscribeOn ():用于切換執行上下文的操作符。
    • subscribeOn():指定訂閱操作(包括上游數據生成)的執行線程。
    • publishOn():指定下游操作符鏈的執行線程

7.1 概述

Reactor 與 RxJava 類似,可以被認為是并發無關的 。也就是說,它不強制執行并發模型。相反,它把控制權交給開發者自己。然而,這并不妨礙該庫幫助你處理并發問題。

獲得 FluxMono 并不一定意味著它在專用的 Thread ,大多數操作符會在前一個操作符執行的 Thread 中繼續工作。除非另有說明,最頂層的操作符(源操作符)本身會在調用 subscribe() Thread 中運行。以下示例在新線程中運行 Mono

public static void main(String[] args) throws InterruptedException {final Mono<String> mono = Mono.just("hello "); // 🥇 Mono<String> 在線程 main 中組裝。Thread t = new Thread(() -> mono.map(msg -> msg + "thread ").subscribe(v -> // 🥈 它是在線程 Thread-0 中訂閱的。System.out.println(v + Thread.currentThread().getName()) // map 和 onNext 回調實際上都在 Thread-0 中運行));t.start();t.join();}

7.2 Scheduler: Reactor 的線程調度器

Scheduler 是 Reactor 的線程抽象,類似于 Java 的 ExecutorService,但專為響應式流設計。

? 核心作用:控制 Publisher 在哪個線程上執行。

Reactor 提供了多種內置 Scheduler

Scheduler用途線程模型
Schedulers.immediate()當前線程執行? 不推薦用于生產
Schedulers.single()共享的單線程1 個線程,復用
Schedulers.parallel()CPU 密集型任務固定線程數(CPU 核數)
Schedulers.boundedElastic()I/O 阻塞任務彈性線程池(默認 10萬線程上限)
Schedulers.newXXX()自定義線程池newParallel()

7.3 兩大核心操作符:subscribeOn vs publishOn

這是理解 Reactor 線程模型的重中之重

🔑 核心區別

操作符作用影響范圍
subscribeOn()指定 Publisher 的創建和上游執行線程影響整個鏈的上游(從源頭到當前位置)
publishOn()指定下游操作的執行線程只影響其后的下游操作(當前位置到 subscribe

🎯 記憶口訣:

  • subscribeOn:從哪里開始(影響源頭)
  • publishOn:從哪里切換(影響后續)

7.4 示例詳解

7.4.1 subscribeOn()的全局影響

package cn.tcmeta.scheduler;import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;import java.util.concurrent.TimeUnit;/*** @author: laoren* @description: subscribeOn的全局影響* @version: 1.0.0*/
public class SubscribeOnExample {public static void main(String[] args) {Flux.just("A", "B", "C").map(data -> {System.out.println("1?? Map1 線程: " + Thread.currentThread().getName());return data + "-1";}).subscribeOn(Schedulers.parallel()).map(data -> {System.out.println("2?? Map2 線程: " + Thread.currentThread().getName());return data + "-2";}).subscribe(data -> {System.out.println("📩 訂閱線程: " + Thread.currentThread().getName() + ", 數據: " + data);});try {TimeUnit.MILLISECONDS.sleep(3000);}catch (InterruptedException e){e.printStackTrace();}}
}
1?? Map1 線程: parallel-1
2?? Map2 線程: parallel-1
📩 訂閱線程: parallel-1, 數據: A-1-2
1?? Map1 線程: parallel-1
2?? Map2 線程: parallel-1
📩 訂閱線程: parallel-1, 數據: B-1-2
1?? Map1 線程: parallel-1
2?? Map2 線程: parallel-1
📩 訂閱線程: parallel-1, 數據: C-1-2

? 結論subscribeOn(Schedulers.parallel()) 即使放在中間,也使 just() 和兩個 map() 都在 parallel 線程執行。

subscribeOn影響范圍:

subscribeOn 影響區域
Flux.just
map1
map2
subscribeOn(parallel)
subscribe

7.4.2 publishOn() 的局部切換

package cn.tcmeta.scheduler;import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;/*** @author: laoren* @description: publishOn()局部切換* @version: 1.0.0*/
public class PublishOnExample {public static void main(String[] args) throws InterruptedException {Flux.just("A", "B").map(data -> {System.out.println("📍 上游 Map 線程: " + Thread.currentThread().getName());return data + "-up";})// ? publishOn 切換下游線程.publishOn(Schedulers.boundedElastic()).map(data -> {System.out.println("📍 下游 Map 線程: " + Thread.currentThread().getName());return data + "-down";}).subscribe(data ->System.out.println("📩 訂閱線程: " + Thread.currentThread().getName() + ", 數據: " + data));Thread.sleep(1000);}
}
📍 上游 Map 線程: main
📍 上游 Map 線程: main
📍 下游 Map 線程: boundedElastic-1
📩 訂閱線程: boundedElastic-1, 數據: A-up-down
📍 下游 Map 線程: boundedElastic-1
📩 訂閱線程: boundedElastic-1, 數據: B-up-down

? 結論publishOn 之后的所有操作(包括 subscribe)都在 boundedElastic 線程執行。

publishOn() 影響范圍:

publishOn 影響區域
map2
subscribe
Flux.just
map1
publishOn(elastic)

🔴 紅色部分(下游)在 elastic 線程執行,justmap1 在主線程。

7.4.3 多個publishOn切換

package cn.tcmeta.scheduler;import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;public class MultiPublishOnExample {public static void main(String[] args) throws InterruptedException {Flux.just("Hello").publishOn(Schedulers.parallel())           // 切到 parallel.map(s -> {System.out.println("ParallelGroup: " + Thread.currentThread().getName());return s + "-1";}).publishOn(Schedulers.boundedElastic())    // 再   切到 boundedElastic.map(s -> {System.out.println("ElasticGroup: " + Thread.currentThread().getName());return s + "-2";}).subscribe(data ->System.out.println("Final: " + Thread.currentThread().getName() + " => " + data));Thread.sleep(1000);}
}

? 每個 publishOn 都會切換其后操作的執行線程。

7.4.4 線程切換時序圖

MainParallelElasticsubscribeOn(parallel) 后,上游執行publishOn(elastic) 切換下游操作和 subscribe 執行MainParallelElastic

7.5 核心調度器

7.5.1 BoundedElastic:IO 密集型任務首選

  • 設計背景:替代已過時的ElasticScheduler(無界線程池,可能導致 OOM),通過有界緩沖隊列動態線程數(空閑線程會回收)避免資源耗盡。
  • 適用場景:數據庫查詢、HTTP 請求、文件 IO 等阻塞且耗時的操作(允許線程阻塞,通過動態擴縮容應對并發)

7.5.2 Parallel:CPU 密集型任務首選

  • 線程特性:線程數固定為 CPU 核心數(Runtime.getRuntime().availableProcessors()),無空閑線程回收(保持計算能力)。
  • 適用場景:數據計算、序列化 / 反序列化、復雜集合處理等非阻塞但耗 CPU的操作(充分利用多核性能)。

7.5.3 Single:串行任務專用

  • 線程特性:全局唯一單線程(所有Schedulers.single()調用共享),任務按提交順序執行。
  • 注意:若需多個獨立串行線程,使用Schedulers.newSingle()創建私有單線程調度器。

7.5.4 Schedulers.immediate()

  • 特性:在當前線程直接執行,不開啟新線程。
  • 適用場景:測試或不需要異步執行的場景。

7.5.5 Schedulers.elastic()

  • 特性:彈性線程池,按需創建線程,空閑線程會在 60s 后回收。
  • 適用場景:IO 密集型任務(如網絡調用、文件操作)。
  • 注意:已被棄用,推薦使用 boundedElastic

7.5.6 Schedulers.fromExecutorService(ExecutorService)

  • 特性:適配自定義的 ExecutorService,靈活集成現有線程池。

7.5.7 Schedulers.new() 工廠方法

  • 特性:創建獨立的新調度器實例(如 newSingle()newParallel()),避免共享資源。

7.5.8 調度器使用最佳實踐

按任務類型選擇調度器

  • IO 密集型(數據庫、網絡、文件)→ boundedElastic(允許阻塞,動態擴縮容);
  • CPU 密集型(計算、排序、序列化)→ parallel(固定線程數,避免線程切換開銷);
  • 串行任務(狀態依賴操作)→ singlenewSingle()(保證順序執行);
  • 同步操作(無阻塞)→ immediate(無需線程切換,減少開銷)。

避免線程阻塞濫用

  • 禁止在parallel線程中執行阻塞操作(會浪費 CPU 核心,降低計算效率);
  • 阻塞操作必須放在boundedElastic線程(其線程設計允許阻塞);
// 錯誤:在parallel線程執行阻塞操作
Flux.range(1, 10).publishOn(Schedulers.parallel()).doOnNext(num -> {Thread.sleep(1000); // 阻塞CPU線程,浪費計算資源});// 正確:阻塞操作放在boundedElastic
Flux.range(1, 10).publishOn(Schedulers.boundedElastic()).doOnNext(num -> Thread.sleep(1000)); // 安全

控制boundedElastic的資源上限

默認配置可能不適合高并發場景,可通過系統屬性調整:

// JVM啟動參數:調整boundedElastic的線程和隊列上限
-Dreactor.schedulers.boundedElastic.maxThreads=100 
-Dreactor.schedulers.boundedElastic.queuesize=1024

減少不必要的線程切換

// 優化前:多次不必要的線程切換
flux.publishOn(A).map(...).publishOn(B).filter(...).publishOn(C)// 優化后:合并操作,減少切換
flux.map(...).filter(...).publishOn(C); // 一次切換即可

7.6 線程模型實戰: 典型場景

7.6.1 I/O密集型任務

如數據庫、HTTP 調用

// 假設這是調用外部 HTTP 服務
Mono<String> callExternalApi() {return Mono.fromCallable(() -> {// 模擬阻塞調用Thread.sleep(1000);return "API Result";}).subscribeOn(Schedulers.boundedElastic()); // ? 使用彈性線程池
}// 使用
callExternalApi().map(result -> processResult(result)) // 可在主線程或其他線程處理.subscribe(System.out::println);

? 原則:I/O 操作必須用 boundedElastic(),防止阻塞 CPU線程。

7.6.2 場景 2:CPU 密集型任務

Flux.range(1, 1000).publishOn(Schedulers.parallel()) // ? 切到并行線程池.map(i -> heavyComputation(i))    // 耗時計算.subscribe(System.out::println);

? 原則:CPU 密集型用 parallel(),避免創建過多線程。

7.6.3 混合任務(I/O + CPU)

externalServiceCall()           // I/O: boundedElastic.publishOn(Schedulers.parallel()) // 切到 CPU 線程池.map(data -> compute(data)) // CPU 密集型計算.publishOn(Schedulers.boundedElastic()) // 再切回 I/O 線程.flatMap(result -> saveToDB(result)) // 再次 I/O 操作.subscribe();

? 原則:根據操作類型動態切換線程池。

7.7 綜合示例

package cn.tcmeta.scheduler;import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;import java.time.Duration;
import java.util.concurrent.TimeUnit;public class SchedulerExamples {public static void main(String[] args) {SchedulerExamples examples = new SchedulerExamples();examples.schedulerTypes();System.out.println("-------------------------------------");examples.publishOnVsSubscribeOn();System.out.println("-------------------------------------");examples.parallelProcessing();System.out.println("-------------------------------------");examples.timeoutWithScheduler();}public void schedulerTypes() {// 1. 立即調度 (當前線程)Flux.just("A", "B", "C").subscribeOn(Schedulers.immediate()).subscribe(System.out::println);// 2. 單一線程調度Flux.range(1, 3).subscribeOn(Schedulers.single()).subscribe(i -> System.out.println(Thread.currentThread().getName() + ": " + i));// 3. 彈性線程池 (適合IO密集型任務)Flux.range(1, 3).subscribeOn(Schedulers.boundedElastic()).subscribe(i -> System.out.println(Thread.currentThread().getName() + ": " + i));// 4. 并行調度 (適合CPU密集型任務)Flux.range(1, 3).subscribeOn(Schedulers.parallel()).subscribe(i -> System.out.println(Thread.currentThread().getName() + ": " + i));}public void publishOnVsSubscribeOn() {// subscribeOn - 影響整個鏈的訂閱上下文Mono.fromCallable(() -> {System.out.println("Callable on: " + Thread.currentThread().getName());return "Result";}).subscribeOn(Schedulers.boundedElastic()).subscribe(result ->System.out.println("Subscribe on: " + Thread.currentThread().getName()));// publishOn - 影響后續操作的執行上下文Flux.range(1, 3).map(i -> {System.out.println("Map1 on: " + Thread.currentThread().getName());return i * 2;}).publishOn(Schedulers.parallel()).map(i -> {System.out.println("Map2 on: " + Thread.currentThread().getName());return i + 1;}).subscribe();}public void parallelProcessing() {// 并行處理流Flux.range(1, 10).parallel(4) // 分成4個并行流.runOn(Schedulers.parallel()).map(i -> i * i).sequential() // 合并回順序流.subscribe(System.out::println);}public void timeoutWithScheduler() {// 使用調度器實現超時Mono.delay(Duration.ofSeconds(3)).timeout(Duration.ofSeconds(1), Schedulers.parallel()).subscribe(System.out::println,error -> System.out.println("Timeout: " + error));try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}

7.8 高級特性

7.8.1 調用器生命周期管理

// 創建獨立的調度器實例
Scheduler customScheduler = Schedulers.newBoundedElastic(10, 100, "custom");// 使用自定義調度器
Flux.just(1, 2, 3).subscribeOn(customScheduler).subscribe();// 使用完畢后關閉調度器(重要!避免資源泄漏)
customScheduler.dispose();

7.8.2 自定義線程命名

Scheduler namedScheduler = Schedulers.newParallel("my-thread", 4);
Flux.just("A", "B").subscribeOn(namedScheduler).subscribe(value -> {System.out.println("Running on: " + Thread.currentThread().getName());});// 輸出:Running on: my-thread-1

7.8.3 在操作符中使用調度器

// 使用 subscribeOn 在 flatMap 中為每個內部流指定調度器
Flux.just(1, 2, 3).flatMap(num -> Mono.just(num * 2).subscribeOn(Schedulers.parallel()) // 為每個元素創建獨立的執行上下文).subscribe();

7.9 最佳實踐與陷阱

? 最佳實踐

  1. I/O 操作Schedulers.boundedElastic()
  2. CPU 計算Schedulers.parallel()
  3. 避免在 map() 中阻塞
  4. 合理使用 publishOn 切換線程
  5. subscribeOn 通常放在鏈的開頭或中間,效果相同

? 常見陷阱

// ? 錯誤:在 parallel 線程中執行阻塞 I/O
Flux.range(1, 10).publishOn(Schedulers.parallel()).map(i -> blockingIoCall(i)) // 阻塞調用!會耗盡 parallel 線程池.subscribe();// ? 正確:使用 boundedElastic
Flux.range(1, 10).flatMap(i -> Mono.fromCallable(() -> blockingIoCall(i)).subscribeOn(Schedulers.boundedElastic())).subscribe();
概念關鍵點
Scheduler線程執行的“容器”,選擇合適的類型至關重要
subscribeOn()影響上游,決定 Publisher 在哪個線程啟動
publishOn()影響下游,用于在鏈中切換執行線程
線程選擇I/O → boundedElastic,CPU → parallel
背壓與線程背壓控制數據流,線程控制執行位置,二者協同工作

🚀 掌握調度,就掌握了 Reactor 的“方向盤”。合理使用 subscribeOnpublishOn,結合正確的 Scheduler,你就能構建出高效、穩定、可擴展的響應式系統。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/95141.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/95141.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/95141.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第21節:環境貼圖與PBR材質升級——構建電影級真實感渲染

第21節&#xff1a;環境貼圖與PBR材質升級——構建電影級真實感渲染 概述 基于物理的渲染&#xff08;Physically Based Rendering, PBR&#xff09;是當代計算機圖形學中最重要的技術進步之一&#xff0c;它徹底改變了實時渲染的質量標準。在本節中&#xff0c;我們將深入探索…

【ROS2】ROS2 基礎學習教程 、movelt學習

主要博主 參考資料&#xff1a; ROS系列&#xff1a; b站荔枝橙 b戰哈薩克斯坦x 《ROS 2機器人開發從入門到實踐》6.2.2 在RViz中顯示機器人_嗶哩嗶哩_bilibili 動手學ROS2–魚香肉絲 ??????? 古月居ros2教程 北京華清智能科技 ros教程 moveit系列&#xff1a; 愛喝青…

Java類加載與JVM詳解:從基礎到雙親委托機制

在Java開發中&#xff0c;理解JVM&#xff08;Java虛擬機&#xff09;和類加載機制是掌握高級特性的關鍵。本文將從JDK、JRE、JVM的關系入手&#xff0c;深入講解JVM的內存結構&#xff0c;并詳細剖析類加載的全過程&#xff0c;包括加載時機、流程以及核心機制——雙親委托模型…

準備機試--圖【y總版】[重要]【最短路】

常用代碼模板3——搜索與圖論 - AcWing 一般&#xff0c;稀疏圖&#xff08;m約等于n&#xff09;:堆優化版本的dj&#xff1b;稠密圖&#xff08;mn^2&#xff09;&#xff1a;樸素dj 最短路的難點在于建圖【抽象出點和邊】 樸素dj

Python API接口實戰指南:從入門到精通

&#x1f31f; Hello&#xff0c;我是蔣星熠Jaxonic&#xff01; &#x1f308; 在浩瀚無垠的技術宇宙中&#xff0c;我是一名執著的星際旅人&#xff0c;用代碼繪制探索的軌跡。 &#x1f680; 每一個算法都是我點燃的推進器&#xff0c;每一行代碼都是我航行的星圖。 &#x…

Spring和mybatis整合后事務攔截器TransactionInterceptor開啟提交事務流程

目錄一、說明二、TransactionInterceptor開啟事務&#xff08;1&#xff09;、攔截方法&#xff08;2&#xff09;、開啟事務綁定數據庫連接&#xff08;3&#xff09;、mybatis中sql執行數據庫連接獲取&#xff08;4&#xff09;、事務提交和當前線程ThreadLocal清理&#xff…

05.《ARP協議基礎知識探秘》

ARP協議基本介紹與實踐 文章目錄**ARP協議基本介紹與實踐**ARP概述ARP報文類型ARP工作過程解析ARP工作原理示意圖無故ARP/免費ARP實驗案例**實驗目標**實驗環境實驗步驟ARP概述 作用&#xff1a;ARP&#xff08;Address Resolution Protocol&#xff0c;地址解析協議&#xff…

互聯網大廠面試:大模型應用開發崗位核心技術點解析

互聯網大廠面試&#xff1a;大模型應用開發崗位核心技術點解析 第一輪&#xff1a;大模型基礎與上下文工程 問題 1&#xff1a;你能簡單介紹 Transformer 架構的工作原理嗎&#xff1f; 小C&#xff1a;嗯&#xff0c;我理解是 Transformer 主要依賴自注意力機制&#xff08;Se…

【深度學習新浪潮】有沒有什么方法可以將照片變成線描稿,比如日式漫畫的那種?

一、技術原理與研究進展 1. 線描生成的核心技術路徑 傳統方法:基于邊緣檢測(如Canny算子)和形態學操作,但難以處理復雜紋理和藝術風格。 深度學習方法: 端到端生成:使用U-Net架構(如ArtLine項目)直接學習照片到線描的映射,結合自注意力機制和感知損失提升細節保留能力…

NV032NV037美光固態閃存NV043NV045

NV032NV037美光固態閃存NV043NV045在數字化浪潮席卷全球的當下&#xff0c;存儲技術的每一次突破都深刻影響著從個人消費到企業級應用的各個領域。美光科技作為行業領軍者&#xff0c;其NV系列固態閃存產品始終以技術創新為核心驅動力。本文將聚焦NV032、NV037、NV043、NV045四…

天碩G40工業固態硬盤破解軌道存儲難題

在高鐵與軌道交通高速發展的今天&#xff0c;軌道檢測探傷是保障列車安全運行的核心環節。據統計&#xff0c;我國鐵路總里程已突破16萬公里&#xff0c;日均檢測數據量超10TB。加固平板一體機作為軌道探傷領域的“移動工作站”&#xff0c;需要在跨越大江南北的極端環境中實時…

基于Velero + 阿里云 OSS的Kubernetes 集群的備份與恢復

在 Kubernetes&#xff08;K8s&#xff09;中&#xff0c;備份和恢復是保障數據安全與業務連續性的關鍵環節&#xff0c;主要方式包括 ETCD 備份恢復 和 Velero 備份恢復&#xff0c;兩者在備份粒度、恢復影響范圍、存儲位置等方面存在以下差異&#xff1a; 1、ETCD 備份恢復&…

解構與重構:“真人不露相,露相非真人” 的存在論新解 —— 論 “真在” 的行為表達本質

解構與重構&#xff1a;“真人不露相&#xff0c;露相非真人” 的存在論新解 —— 論 “真在” 的行為表達本質緒論&#xff1a;傳統解釋的突圍 —— 從 “藏才” 到 “存真”“真人不露相&#xff0c;露相非真人” 這句諺語&#xff0c;自明代《西游記》以降&#xff0c;便長期…

數據結構:哈希表、排序和查找

一、哈希算法1.將數據通過哈希算法映射成一個健值&#xff0c;存取都在同一個位置&#xff0c;實現數據的高效存儲和查找&#xff0c;時間復雜度由O(n)->O(1)2.哈希碰撞&#xff1a;多個數據通過哈希算法得到的鍵值相同二、哈希表1.構建哈希表存放0-100之間的數據2.哈希算法…

【Java基礎】Java I/O模型解析:BIO、NIO、AIO的區別與聯系(Netty入門必備基礎)

Java I/O模型深度解析&#xff1a;BIO、NIO、AIO的區別與聯系 引言 在Java的網絡編程與文件操作中&#xff0c;I/O&#xff08;輸入/輸出&#xff09;模型是繞不開的核心話題。從早期的BIO&#xff08;Blocking I/O&#xff09;到Java 1.4引入的NIO&#xff08;Non-blocking I/…

windows PowerToys之無界鼠標:一套鍵鼠控制多臺設備

&#x1f4bb;簡介 在多設備協作的工作場景中&#xff0c;如何實現一套鍵鼠控制多臺設備了&#xff1f;微軟推出的 PowerToys 工具集中的 Mouse Without Borders&#xff08;無界鼠標&#xff09;&#xff0c;通過軟件層實現跨設備的鍵鼠共享與數據同步功能&#xff0c;為多臺…

一道比較難的sql題,篩選出重復字段的行數

select * from 導入數據表; id city_column 1 北京,上海,廣州 2 上海,上海,深圳 3 北京,杭州,北京 4 上海,廣州,深圳select substring_index(khmc,,,1), * from 導入數據表 truncate table 導入數據表 select count(distinct khmc) from 導入數據表; …

【K8s】整體認識K8s之與集群外部訪問--service

這一篇文章主要是對service發現新的理解 為什么要使用service服務發現&#xff1f; 首先pod的IP&#xff0c;是動態的&#xff0c;當我們重啟一個pod的時候&#xff0c;它會給它分配一個新的IP&#xff0c;但是如果微服務a想要去調用微服務b&#xff0c;他是需要知道微服務b所有…

k8s(自寫)

kubernetes k8s是什么&#xff1f;Kubernetes是什么&#xff1f;架構是怎么樣的&#xff1f;6分鐘快速入門_嗶哩嗶哩_bilibili kubernetes是google開源神器&#xff0c;介于應用服務和服務器之間&#xff0c;能夠通過策略協調和管理多個應用服務&#xff0c;只需要一個yaml文…

實現微信小程序的UniApp相機組件:拍照、錄像與雙指縮放

在微信小程序開發中&#xff0c;相機功能已成為許多應用的核心組成部分。本文將介紹如何使用UniApp框架實現一個功能豐富的相機組件&#xff0c;支持拍照、錄像、前后攝像頭切換以及雙指縮放等功能。功能概述這個相機組件具備以下核心功能&#xff1a;拍照功能&#xff1a;支持…