Flink窗口:解鎖流計算的秘密武器

Flink 窗口初識

在大數據的世界里,數據源源不斷地產生,形成了所謂的 “無限數據流”。想象一下,網絡流量監控中,每一秒都有海量的數據包在網絡中穿梭,這些數據構成了一個無始無終的流。對于這樣的無限數據流,直接處理往往是不現實的,就好比讓你一口氣喝完大海里的水,這顯然是不可能的。

Flink 窗口的出現,就像是給這無盡的數據流加上了一個個 “收納盒”。它將無限的數據流按照一定的規則分塊,把大規模的數據劃分成一個個有限的小數據集,然后再對這些小數據集進行處理。這樣一來,原本看似無法處理的海量數據,就變得可管理、可計算了。通過 Flink 窗口,我們可以在每個 “收納盒”(窗口)內進行各種聚合操作,比如計算一段時間內的網站訪問量、統計某段時間內的訂單金額總和等。

接下來,讓我們深入了解 Flink 窗口的各種類型以及它們的使用方法和場景。

Flink 窗口大揭秘

窗口類型大盤點

Flink 提供了多種類型的窗口,每種窗口都有其獨特的特點和適用場景,就像不同形狀和用途的容器,適用于裝不同類型的物品。

  • 滾動窗口(Tumbling Window):滾動窗口是固定大小且不重疊的窗口。比如我們設置一個 5 分鐘的滾動窗口,那么數據就會被按照每 5 分鐘一個窗口進行劃分,前 5 分鐘的數據在一個窗口內處理,接著下一個 5 分鐘的數據在另一個窗口內處理,以此類推。就像超市里的貨物擺放,每一排貨架固定存放一定時間段內上架的商品,不同排貨架之間的商品不會混淆。在電商場景中,統計每小時的訂單數量時,就可以使用滾動窗口,每個小時的訂單數據被劃分到一個單獨的窗口中進行統計。
  • 滑動窗口(Sliding Window):滑動窗口也是固定大小的窗口,但它與滾動窗口的不同之處在于,滑動窗口可以有重疊部分。例如,我們設置一個大小為 10 分鐘,滑動步長為 5 分鐘的滑動窗口。第一個窗口包含第 0 - 10 分鐘的數據,第二個窗口包含第 5 - 15 分鐘的數據,這樣相鄰的兩個窗口就有 5 分鐘的數據是重疊的。想象一下地鐵的檢票口,每 5 分鐘統計一次過去 10 分鐘內的進站人數,這里就可以使用滑動窗口,通過不斷滑動窗口來實時獲取不同時間段內的進站人數統計信息。
  • 會話窗口(Session Window):會話窗口是根據會話來劃分的,會話之間是不重疊的。當一段時間內沒有數據到達時,會話就會結束。比如在用戶行為分析中,一個用戶在網站上的一系列操作構成一個會話,如果這個用戶 10 分鐘內沒有任何操作,那么這個會話就結束了,下一次操作會開啟新的會話。每個會話窗口內的數據包含了該用戶在這個會話期間的所有操作記錄,通過對會話窗口內的數據進行分析,可以了解用戶在一次訪問中的行為模式,比如用戶瀏覽了哪些頁面、停留了多長時間等。
  • 全局窗口(Global Window):全局窗口是一種特殊的窗口,它會將所有具有相同鍵的數據都分配到同一個窗口中,直到手動觸發計算或者滿足某些條件才會進行計算。在實際應用中,如果我們要對所有用戶的行為數據進行匯總分析,不考慮時間或者其他條件的限制,就可以使用全局窗口。但由于全局窗口不會自動觸發計算,所以通常需要結合自定義觸發器來使用,比如設置當數據量達到一定數量時觸發計算,或者每隔一段時間觸發一次計算。
  • 計數窗口(Count Window):計數窗口是基于元素數量來定義窗口大小的。例如,我們設置一個計數窗口大小為 100,那么當窗口內的元素數量達到 100 時,就會觸發對這個窗口內數據的計算。在網絡流量監控中,如果我們要統計每接收 100 個數據包時的流量情況,就可以使用計數窗口,當收到第 100 個數據包時,對這 100 個數據包所產生的流量數據進行統計分析 。

窗口操作全解析

在 Flink 中,窗口操作主要涉及到KeyedStream和Datastream兩種流的處理,不同的流有不同的窗口操作方式,它們就像是不同的工具,用于處理不同類型的數據流。

  • KeyedStream 上的窗口操作:當我們對KeyedStream進行窗口操作時,通常使用.window()方法來指定窗口分配器。例如:
    DataStream<Tuple2<String, Integer>> stream = env.fromElements(Tuple2.of("a", 1), Tuple2.of("b", 2), Tuple2.of("a", 3), Tuple2.of("b", 4));KeyedStream<Tuple2<String, Integer>, String> keyedStream = stream.keyBy(t -> t.f0);WindowedStream<Tuple2<String, Integer>, String, TimeWindow> windowedStream = keyedStream.window(TumblingProcessingTimeWindows.of(Time.seconds(5)));

首先將DataStream通過keyBy操作轉換為KeyedStream,然后使用window方法指定了一個大小為 5 秒的滾動處理時間窗口。這樣,相同鍵(這里是Tuple2的第一個元素)的數據會被分配到同一個窗口中進行處理。

在KeyedStream的窗口操作中,還可以設置觸發器(Trigger)、退出器(Evictor)以及處理遲到數據等。觸發器用于決定窗口何時被觸發計算,比如可以設置當窗口中的元素數量達到一定值時觸發,或者當時間到達某個點時觸發。退出器則用于在窗口觸發計算之前或之后對窗口中的元素進行處理,比如可以根據某些條件刪除窗口中的部分元素。處理遲到數據時,可以通過allowedLateness方法來指定允許數據遲到的時間,對于遲到的數據,可以通過sideOutputLateData方法將其輸出到側輸出流中進行單獨處理。

  • Datastream 上的窗口操作:對于Datastream,我們使用.windowAll()方法來進行窗口操作。不過需要注意的是,windowAll操作會將所有分區的流都匯集到單個的 Task 中進行處理,這可能會導致性能問題,所以在實際應用中一般不推薦使用,除非數據量較小或者有特殊需求。例如:
    DataStream<Tuple2<String, Integer>> stream = env.fromElements(Tuple2.of("a", 1), Tuple2.of("b", 2), Tuple2.of("a", 3), Tuple2.of("b", 4));AllWindowedStream<Tuple2<String, Integer>, TimeWindow> allWindowedStream = stream.windowAll(TumblingProcessingTimeWindows.of(Time.seconds(5)));

這段代碼將DataStream通過windowAll方法指定了一個 5 秒的滾動處理時間窗口,所有的數據都會被分配到這個全局的窗口中進行處理。同樣,在windowAll操作中也可以設置觸發器、退出器和處理遲到數據,方式與KeyedStream類似。

窗口函數大賞

窗口函數是對窗口內的數據進行計算和轉換的關鍵工具,Flink 提供了多種窗口函數,每種函數都有其獨特的作用和適用場景。

  • ReduceFunction:ReduceFunction是一種簡單的聚合函數,它通過將窗口內的元素兩兩合并,最終得到一個聚合結果。例如,我們要計算每個窗口內訂單金額的總和,可以使用ReduceFunction:
DataStream<Tuple2<String, Double>> orderStream = env.fromElements(Tuple2.of("user1", 100.0), Tuple2.of("user1", 200.0), Tuple2.of("user2", 150.0));KeyedStream<Tuple2<String, Double>, String> keyedOrderStream = orderStream.keyBy(t -> t.f0);SingleOutputStreamOperator<Tuple2<String, Double>> resultStream = keyedOrderStream.window(TumblingProcessingTimeWindows.of(Time.seconds(5))).reduce((acc, value) -> Tuple2.of(acc.f0, acc.f1 + value.f1));

在上述代碼中,ReduceFunction將同一個窗口內相同用戶的訂單金額進行累加,最終得到每個用戶在每個窗口內的訂單總金額。

  • AggregateFunction:AggregateFunction是一種更靈活的聚合函數,它允許我們自定義聚合的邏輯,包括初始化累加器、將元素添加到累加器以及從累加器中獲取最終結果。例如,我們要計算每個窗口內訂單的平均金額,可以使用AggregateFunction:
public class AverageAggregate implements AggregateFunction<Tuple2<String, Double>, Tuple2<String, Double, Integer>, Tuple2<String, Double>> {@Overridepublic Tuple2<String, Double, Integer> createAccumulator() {return Tuple2.of("", 0.0, 0);}@Overridepublic Tuple2<String, Double, Integer> add(Tuple2<String, Double> value, Tuple2<String, Double, Integer> accumulator) {return Tuple2.of(value.f0, accumulator.f1 + value.f1, accumulator.f2 + 1);}@Overridepublic Tuple2<String, Double> getResult(Tuple2<String, Double, Integer> accumulator) {if (accumulator.f2 == 0) {return Tuple2.of(accumulator.f0, 0.0);}return Tuple2.of(accumulator.f0, accumulator.f1 / accumulator.f2);}@Overridepublic Tuple2<String, Double, Integer> merge(Tuple2<String, Double, Integer> a, Tuple2<String, Double, Integer> b) {return Tuple2.of(a.f0, a.f1 + b.f1, a.f2 + b.f2);}}使用時:DataStream<Tuple2<String, Double>> orderStream = env.fromElements(Tuple2.of("user1", 100.0), Tuple2.of("user1", 200.0), Tuple2.of("user2", 150.0));KeyedStream<Tuple2<String, Double>, String> keyedOrderStream = orderStream.keyBy(t -> t.f0);SingleOutputStreamOperator<Tuple2<String, Double>> resultStream = keyedOrderStream.window(TumblingProcessingTimeWindows.of(Time.seconds(5))).aggregate(new AverageAggregate());

AverageAggregate實現了AggregateFunction接口,通過自定義的邏輯,在窗口內計算出每個用戶訂單的平均金額。

  • FoldFunction:FoldFunction與ReduceFunction類似,也是用于聚合操作,但它在聚合過程中可以攜帶一個初始值。例如,我們要計算每個窗口內訂單金額的總和,并且設置初始值為 100,可以使用FoldFunction:
DataStream<Tuple2<String, Double>> orderStream = env.fromElements(Tuple2.of("user1", 100.0), Tuple2.of("user1", 200.0), Tuple2.of("user2", 150.0));KeyedStream<Tuple2<String, Double>, String> keyedOrderStream = orderStream.keyBy(t -> t.f0);SingleOutputStreamOperator<Tuple2<String, Double>> resultStream = keyedOrderStream.window(TumblingProcessingTimeWindows.of(Time.seconds(5))).fold(Tuple2.of("", 100.0), (acc, value) -> Tuple2.of(acc.f0, acc.f1 + value.f1));

這里,FoldFunction從初始值 100 開始,將窗口內的訂單金額依次累加到初始值上,最終得到每個用戶在每個窗口內加上初始值后的訂單總金額。

  • ProcessWindowFunction:ProcessWindowFunction是一種更高級的窗口函數,它不僅可以訪問窗口內的所有元素,還可以訪問窗口的元數據,如窗口的開始時間、結束時間等。例如,我們要輸出每個窗口內訂單金額的總和以及窗口的開始和結束時間,可以使用ProcessWindowFunction:
public class OrderSumWithWindowInfo extends ProcessWindowFunction<Tuple2<String, Double>, Tuple3<String, Double, String>, String, TimeWindow> {@Overridepublic void process(String key, Context context, Iterable<Tuple2<String, Double>> elements, Collector<Tuple3<String, Double, String>> out) throws Exception {double sum = 0;for (Tuple2<String, Double> element : elements) {sum += element.f1;}String windowInfo = "Start: " + context.window().getStart() + ", End: " + context.window().getEnd();out.collect(Tuple3.of(key, sum, windowInfo));}}使用時:DataStream<Tuple2<String, Double>> orderStream = env.fromElements(Tuple2.of("user1", 100.0), Tuple2.of("user1", 200.0), Tuple2.of("user2", 150.0));KeyedStream<Tuple2<String, Double>, String> keyedOrderStream = orderStream.keyBy(t -> t.f0);SingleOutputStreamOperator<Tuple3<String, Double, String>> resultStream = keyedOrderStream.window(TumblingProcessingTimeWindows.of(Time.seconds(5))).process(new OrderSumWithWindowInfo());

OrderSumWithWindowInfo繼承自ProcessWindowFunction,在process方法中,通過遍歷窗口內的元素計算出訂單金額總和,并結合窗口的元數據(開始時間和結束時間)輸出結果。

代碼實戰:Flink 窗口應用

接下來,我們通過一個具體的代碼示例,來深入了解如何在實際應用中使用 Flink 窗口進行數據處理。假設我們有一個電商平臺的用戶行為數據,數據包含用戶 ID、行為類型(如點擊、購買、收藏等)、商品 ID 以及時間戳等信息。我們的目標是使用不同類型的窗口來計算一段時間內的用戶行為統計,比如統計每小時內每個用戶的點擊次數、每 10 分鐘內每個商品的購買次數等 。

首先,我們需要創建一個 Flink 的StreamExecutionEnvironment執行環境,這是 Flink 程序的入口,就像打開一扇通往數據處理世界的大門:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

然后,我們從數據源讀取用戶行為數據,這里假設數據源是一個 Kafka 主題,我們使用 Flink 提供的FlinkKafkaConsumer來讀取數據,并將數據轉換為UserBehavior對象,UserBehavior是一個自定義的 POJO 類,包含用戶行為的相關字段:

Properties props = new Properties();props.setProperty("bootstrap.servers", "localhost:9092");props.setProperty("group.id", "user-behavior-analysis");FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>("user_behavior_topic", new SimpleStringSchema(), props);DataStream<String> dataStream = env.addSource(consumer);DataStream<UserBehavior> userBehaviorStream = dataStream.map(new MapFunction<String, UserBehavior>() {@Overridepublic UserBehavior map(String value) throws Exception {String[] fields = value.split(",");return new UserBehavior(Long.parseLong(fields[0]),Long.parseLong(fields[1]),Integer.parseInt(fields[2]),fields[3],Long.parseLong(fields[4]));}});

滾動窗口應用

使用滾動窗口統計每小時內每個用戶的點擊次數。我們先按用戶 ID 進行分組,然后使用大小為 1 小時的滾動處理時間窗口,再通過ReduceFunction對窗口內的點擊次數進行累加:

KeyedStream<UserBehavior, Long> keyedStream = userBehaviorStream.filter(behavior -> "pv".equals(behavior.getBehavior())).keyBy(UserBehavior::getUserId);SingleOutputStreamOperator<Tuple2<Long, Integer>> tumblingWindowResult = keyedStream.window(TumblingProcessingTimeWindows.of(Time.hours(1))).reduce((acc, value) -> Tuple2.of(acc.f0, acc.f1 + 1));tumblingWindowResult.print("Tumbling Window Result: ");

滑動窗口應用

使用滑動窗口統計每 10 分鐘內每個商品的購買次數,窗口大小為 10 分鐘,滑動步長為 5 分鐘。同樣先按商品 ID 分組,然后應用滑動窗口,并通過AggregateFunction進行聚合計算:

KeyedStream<UserBehavior, Long> itemKeyedStream = userBehaviorStream.filter(behavior -> "buy".equals(behavior.getBehavior())).keyBy(UserBehavior::getItemId);public class PurchaseCountAggregate implements AggregateFunction<UserBehavior, Integer, Integer> {@Overridepublic Integer createAccumulator() {return 0;}@Overridepublic Integer add(UserBehavior value, Integer accumulator) {return accumulator + 1;}@Overridepublic Integer getResult(Integer accumulator) {return accumulator;}@Overridepublic Integer merge(Integer a, Integer b) {return a + b;}}SingleOutputStreamOperator<Tuple2<Long, Integer>> slidingWindowResult = itemKeyedStream.window(SlidingProcessingTimeWindows.of(Time.minutes(10), Time.minutes(5))).aggregate(new PurchaseCountAggregate());slidingWindowResult.print("Sliding Window Result: ");

會話窗口應用

使用會話窗口統計每個用戶每次會話內的操作次數,假設會話間隙時間為 15 分鐘。先按用戶 ID 分組,然后應用會話窗口,通過ProcessWindowFunction獲取窗口內的所有元素并計算操作次數:

KeyedStream<UserBehavior, Long> sessionKeyedStream = userBehaviorStream.keyBy(UserBehavior::getUserId);public class SessionOperationCount extends ProcessWindowFunction<UserBehavior, Tuple2<Long, Integer>, Long, TimeWindow> {@Overridepublic void process(Long key, Context context, Iterable<UserBehavior> elements, Collector<Tuple2<Long, Integer>> out) throws Exception {int count = 0;for (UserBehavior element : elements) {count++;}out.collect(Tuple2.of(key, count));}}SingleOutputStreamOperator<Tuple2<Long, Integer>> sessionWindowResult = sessionKeyedStream.window(ProcessingTimeSessionWindows.withGap(Time.minutes(15))).process(new SessionOperationCount());sessionWindowResult.print("Session Window Result: ");

最后,我們執行 Flink 作業,讓數據在這些窗口操作中流動并得到處理:

env.execute("User Behavior Analysis with Flink Windows");

通過以上代碼示例,我們展示了如何在實際應用中使用 Flink 的滾動窗口、滑動窗口和會話窗口進行用戶行為統計分析。在實際場景中,根據不同的業務需求選擇合適的窗口類型和函數,能夠有效地對實時數據流進行處理和分析,為企業決策提供有力的數據支持。

避坑指南:Flink 窗口使用注意事項

在使用 Flink 窗口時,雖然它為我們處理數據流提供了強大的功能,但也容易遇到一些問題,以下是一些常見的問題及解決方案。

窗口大小設置陷阱

  • 問題描述:窗口大小設置不當可能導致數據處理不準確或性能問題。如果窗口設置過大,可能會包含過多的數據,導致內存占用過高,處理時間過長;如果窗口設置過小,可能無法滿足業務需求,例如統計每小時的訂單金額總和,若窗口設置為 1 分鐘,就無法直接得到每小時的結果 。
  • 解決方案:在設置窗口大小時,需要充分考慮業務需求和數據量。對于數據量較大且對實時性要求不高的場景,可以適當增大窗口大小,減少窗口數量,提高處理效率;對于對實時性要求較高的場景,如實時監控網絡流量,窗口大小應根據實際情況合理設置,確保能夠及時反映流量變化。同時,可以通過性能測試來確定最佳的窗口大小,例如在測試環境中,使用不同大小的窗口對相同的數據集進行處理,觀察內存使用、處理時間等指標,選擇最適合的窗口大小。

數據亂序挑戰

  • 問題描述:在實際應用中,由于網絡延遲、分布式系統等原因,數據可能會亂序到達。這對于基于事件時間的窗口處理會產生很大影響,可能導致窗口計算結果不準確,例如在統計用戶行為事件時,由于事件亂序,可能會將本應屬于上一個窗口的事件計算到當前窗口中。
  • 解決方案:Flink 提供了水印(Watermark)機制來處理數據亂序問題。水印是一種特殊的時間戳,用于表示時間進度,它可以告訴 Flink 哪些數據已經全部到達。通過設置合適的水印生成策略,如WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(5))表示允許數據最多亂序 5 秒,Flink 可以在水印到達窗口結束時間時,觸發窗口計算,從而保證窗口計算結果的準確性。此外,還可以結合allowedLateness方法來指定窗口允許數據遲到的時間,對于遲到的數據,可以通過sideOutputLateData方法將其輸出到側輸出流中進行單獨處理 。

狀態管理難題

  • 問題描述:Flink 窗口是有狀態的,窗口狀態管理不當可能導致內存溢出或狀態丟失等問題。特別是在處理大規模數據和長時間窗口時,窗口狀態可能會占用大量內存,如果不及時清理,可能會導致內存耗盡,影響任務的正常運行。
  • 解決方案:合理使用 Flink 提供的狀態清理機制,例如對于滾動窗口,可以在窗口關閉后及時清理窗口狀態;對于滑動窗口,由于一個元素可能會被多個窗口共享,需要謹慎管理狀態。可以使用Evictor來在窗口觸發計算之前或之后對窗口中的元素進行處理,例如根據某些條件刪除窗口中的部分元素,從而減少狀態占用。同時,要注意選擇合適的狀態后端,Flink 提供了多種狀態后端,如內存狀態后端、文件系統狀態后端等,根據數據量和性能要求選擇合適的狀態后端,對于大規模數據,建議使用文件系統狀態后端,以避免內存溢出問題 。

觸發器設置誤區

  • 問題描述:觸發器用于決定窗口何時被觸發計算,如果觸發器設置不合理,可能會導致窗口計算不及時或頻繁觸發,影響系統性能和數據處理的準確性。例如,將觸發器設置為每收到一個元素就觸發計算,這在數據量較大時會導致系統負載過高;而設置的觸發條件過于苛刻,可能會導致窗口長時間不觸發計算,數據積壓。
  • 解決方案:根據業務需求選擇合適的觸發器。對于大多數時間窗口,Flink 提供的默認觸發器(如ProcessingTimeTrigger和EventTimeTrigger)通常能夠滿足需求,但在某些特殊場景下,可能需要自定義觸發器。例如,在需要根據窗口內元素數量觸發計算時,可以自定義一個基于元素數量的觸發器。在自定義觸發器時,要仔細考慮觸發條件和邏輯,確保窗口能夠在合適的時機被觸發計算,同時避免不必要的性能開銷。

性能優化盲點

  • 問題描述:在使用 Flink 窗口處理大規模數據時,如果不進行性能優化,可能會導致任務執行效率低下,資源利用率不高。例如,窗口操作中的聚合函數如果實現不當,可能會導致計算量過大,影響處理速度;并行度設置不合理,可能會導致任務執行不均衡,部分節點負載過高,而部分節點閑置。
  • 解決方案:優化窗口操作中的聚合函數,盡量使用高效的算法和數據結構,減少計算量。對于ReduceFunction和AggregateFunction,要確保其實現簡潔高效,避免不必要的計算和內存分配。合理設置并行度,根據數據量和集群資源情況,通過性能測試來確定最佳的并行度。可以使用 Flink 提供的setParallelism方法來設置算子的并行度,同時要注意避免數據傾斜問題,對于可能出現數據傾斜的場景,可以使用rebalance、shuffle等算子對數據進行重新分區,使數據均勻分布到各個并行任務中,提高資源利用率和處理效率。

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

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

相關文章

Java排序算法之<希爾排序>

目錄 1、希爾排序介紹 1.1、定義 1.2、核心思想 2、希爾排序的流程 第 1 輪&#xff1a;gap 4 第 2 輪&#xff1a;gap 2 第 3 輪&#xff1a;gap 1 3、希爾排序的實現 4、時間復雜度分析 5、希爾排序的優缺點 6、適用場景 前言 希爾排序&#xff08;Shell Sort&…

c++加載qml文件

這里展示了c加載qml文件的三種方式以及qml文件中根節點的訪問準備在創建工程的初期&#xff0c;遇到了一個問題&#xff0c;cmake文件以前都是系統自動生成的&#xff0c;不需要我做過多的操作修改&#xff0c;但是&#xff0c;加載qml的程序主函數是需要用到QGuiApplication&a…

007TG洞察:GPT-5前瞻與AI時代競爭力構建:技術挑戰與落地路徑

最近&#xff0c;GPT-5 即將發布的消息刷爆了科技圈&#xff0c;更讓人期待的是&#xff0c;GPT-6 已經悄悄啟動訓練了&#xff0c;OpenAI 的奧特曼表示對未來1-2年的模型充滿信心&#xff0c;預測AI將進化為能夠發現新知識的“AI科學家”。面對日益強大的通用AI&#xff0c;企…

Windows下編譯OpenVDB

本文記錄在Windows下編譯OpenVDB的流程。 零、環境 操作系統Windows 11VS Code1.92.1Git2.34.1MSYS2msys2-x86_64-20240507Visual StudioVisual Studio Community 2022CMake3.22.1 一、編譯 1.1 下載 git clone https://github.com/AcademySoftwareFoundation/openvdb.git …

react 內置hooks 詳細使用場景,使用案例

useState場景&#xff1a;組件中管理局部狀態&#xff0c;如表單值、開關、計數器等。const [count, setCount] useState(0); return <button onClick{() > setCount(count 1)}>Click {count}</button>;useEffect 場景&#xff1a;組件掛載時執行副作用&#…

從0到1學Pandas(九):Pandas 高級數據結構與操作

目錄一、探秘多級索引1.1 創建多級索引1.2 多級索引操作1.3 索引轉換二、探索 Panel 與 xarray2.1 Panel 數據結構2.2 xarray 庫2.3 高維數據操作三、時間序列高級應用3.1 時區處理3.2 時間序列重采樣與頻率轉換3.3 時間序列分解與預測四、數據透視與重塑高級技巧4.1 復雜透視表…

C# 圖像轉換實戰:Bitmap 轉 BitmapSource 的 2 種方法

C# 圖像轉換實戰:Bitmap 轉 BitmapSource 的 2 種方法 引言 兩種轉換方法的完整實現 1. 基于GDI句柄的直接轉換 (ToBitmapSourceFast) 2. 基于內存流的編碼轉換 (ToBitmapSourceSafe) 方法對比與選型指南 避坑指南 GDI句柄泄漏問題 圖像顯示不完整 多線程訪問圖像引發異常 不同…

Spring Boot 整合 Spring MVC:自動配置與擴展實踐

Spring MVC 作為 Java Web 開發的核心框架&#xff0c;在傳統 SSM 項目中需要大量 XML 配置&#xff08;如 DispatcherServlet、視圖解析器等&#xff09;。而 Spring Boot 通過 "自動配置" 特性&#xff0c;簡化了 Spring MVC 的整合過程&#xff0c;同時保留了靈活…

print(“\033[31m紅\033[32m綠\033[34m藍\033[0m默認色“)

可以讓python的終端字體有著不一樣的顏色。代碼&#xff1a;print("\033[31m紅\033[32m綠\033[34m藍\033[0m默認色")效果&#xff1a;

LNMP-zblog分布式部署

一、準備3臺主機&#xff08;rocky8&#xff09;下載相應服務[rootnginx ~]# yum install -y nginx nfs-utils[rootphp ~]# yum install -y nfs-utils php-mysqlnd php php-fpm[rootmysql ~]# yum install -y mysql-server二、掛載php端[rootphp ~]# vim /etc/exports [rootphp…

常見代碼八股

1. 利用梯度下降法&#xff0c;計算二次函數yx^2x4的最小值 def target_function(x):return x ** 2 x 4def gradient(x):return 2*x 1x_init 10 x x_init steps 100 lr 0.1 for i in range(100):x x - lr*gradient(x)print(f"最小值 f(x) {target_function(x):.4f…

【深入底層】C++開發簡歷4+4技能描述6

簡歷書寫 熟悉C的封裝、繼承、多態&#xff0c;STL常用容器&#xff0c;熟悉C11的Lambda表達式、智能指針等&#xff0c;熟悉C20協程語法&#xff0c;具有良好的編碼習慣與文檔能力。 回答思路 這里是基本上就是要全會&#xff0c;考察的問題也很固定&#xff0c;stl這塊可以定…

forest遠程調用注意事項

1、如果在項目中&#xff0c;同時依賴了其中多個框架&#xff0c;那么按 Fastjson2 > Fastjson > Jackson > Gson 這樣的優先級來判斷&#xff0c;Forest 會以優先級最高的框架作為 JSON 轉換器。2、Forest 支持哪幾種 JSON 框架&#xff1f;A: 支持 Jackson、Gson、F…

網絡資源模板--基于Android Studio 實現的新聞App

目錄 一、測試環境說明 二、項目簡介 三、項目演示 四、部設計詳情&#xff08;部分) 登錄頁 首頁 五、項目源碼 一、測試環境說明 電腦環境 Windows 11 編寫語言 JAVA 開發軟件 Android Studio (2020) 開發軟件只要大于等于測試版本即可(近幾年官網直接下載也可…

通過Location API精準獲取位置信息并優化定位精度!

&#x1f44b; 你好&#xff0c;歡迎來到我的博客&#xff01;我是【菜鳥不學編程】 ?? 我是一個正在奮斗中的職場碼農&#xff0c;步入職場多年&#xff0c;正在從“小碼農”慢慢成長為有深度、有思考的技術人。在這條不斷進階的路上&#xff0c;我決定記錄下自己的學習與成…

構建可擴展的狀態系統:基于 ArkTS 的模塊化狀態管理設計與實現

摘要 在 HarmonyOS 的日常開發中&#xff0c;很多人都會遇到一個問題&#xff1a;多個頁面之間的數據狀態如何共享&#xff1f;尤其是在組件結構越來越復雜的場景下&#xff0c;如果還用傳統方式來傳值&#xff0c;不僅代碼混亂&#xff0c;維護也很吃力。 為了解決這個問題&am…

重生之我在暑假學習微服務第二天《MybatisPlus-下篇》

本系列參考黑馬程序員微服務課程&#xff0c;有興趣的可以去查看相關視頻&#xff0c;本系列內容采用漸進式方式講解微服務核心概念與實踐方法&#xff0c;每日更新確保知識點的連貫性。通過系統化學習路徑幫助開發者掌握分布式系統構建的關鍵技術。讀者可通過平臺訂閱功能獲取…

系統整理Python的條件語句和常用方法

Python 的條件語句&#xff08;if 語句&#xff09;是控制程序流程的基礎之一&#xff0c;結構清晰、語法簡潔&#xff0c;非常適合初學者掌握。一、基本語法結構if 條件:執行代碼塊1 elif 條件2:執行代碼塊2 else:執行代碼塊3示例&#xff1a;score 85if score > 90:print…

記錄個IAR程序下載后硬件復位不運行,必須斷電復位才運行的問題

【問題測試】有個F407的跑馬燈的例子&#xff0c;是MDK和IAR兩個版本&#xff0c;MDK版本的例子下載并復位后可以正常看到LED閃爍&#xff0c;而IAR的例子下進去后&#xff0c;不會閃爍。使用TOOL的上位機內核寄存器監測工具測試發現&#xff0c;硬件復位后竟然還在調試狀態&am…

觀察者模式(Observer Pattern)和 發布-訂閱模式(Publisher-Subscriber Pattern)

你對 觀察者模式&#xff08;Observer Pattern&#xff09;和 發布-訂閱模式&#xff08;Publisher-Subscriber Pattern&#xff09;的描述是非常準確的&#xff0c;并且闡明了它們的核心區別。為了幫助你更好地理解這兩者的細微差異&#xff0c;下面是一個更詳細的對比分析&am…