GitHub代碼
https://github.com/SmallScorpion/flink-tutorial.git
狀態編程和容錯機制
流式計算分爲無狀態和有狀態兩種情況。無狀態的計算觀察每個獨立事件,並根據最後一個事件輸出結果。例如,流處理應用程序從傳感器接收溫度讀數,並在溫度超過90度時發出警告。有狀態的計算則會基於多個事件輸出結果。以下是一些例子。
- 所有類型的窗口。例如,計算過去一小時的平均溫度,就是有狀態的計算。
- 所有用於複雜事件處理的狀態機。例如,若在一分鐘內收到兩個相差20度以上的溫度讀數,則發出警告,這是有狀態的計算。
- 流與流之間的所有關聯操作,以及流與靜態表或動態表之間的關聯操作,都是有狀態的計算。
下圖展示了無狀態流處理和有狀態流處理的主要區別。無狀態流處理分別接收每條數據記錄(圖中的黑條),然後根據最新輸入的數據生成輸出數據(白條)。有狀態流處理會維護狀態(根據每條輸入記錄進行更新),並基於最新輸入的記錄和當前的狀態值生成輸出記錄(灰條)。
Flink中的狀態
- 由一個任務維護,並且用來計算某個結果的所有數據,都屬於這個任務的狀態
- 可以認爲狀態就是一個本地變量,可以被任務的業務邏輯訪問
- Flink 會進行狀態管理,包括狀態一致性、故障處理以及高效存儲和訪問,以便開發人員可以專注於應用程序的邏輯
- 在 Flink 中,狀態始終與特定算子相關聯
- 爲了使運行時的 Flink 瞭解算子的狀態,算子需要預先註冊其狀態
算子狀態(Operator State)
- 算子狀態的作用範圍限定爲算子任務,由同一並行任務所處理的所有數據都可以訪問到相同的狀態
- 狀態對於同一子任務而言是共享的
- 算子狀態不能由相同或不同算子的另一個子任務訪問
算子狀態數據結構
鍵控狀態(Keyed State)
- 鍵控狀態是根據輸入數據流中定義的鍵(key)來維護和訪問的
- Flink 爲每個 key 維護一個狀態實例,並將具有相同鍵的所有數據,都分區到同一個算子任務中,這個任務會維護和處理這個 key 對應的狀態
- 當任務處理一條數據時,它會自動將狀態的訪問範圍限定爲當前數據的 key
- Keyed State很類似於一個分佈式的key-value map數據結構,只能用於KeyedStream(keyBy算子處理之後)
鍵控狀態數據結構
狀態定義及使用
import java.lang
import com.atguigu.bean.SensorReading
import com.atguigu.window.MyReduceFunc
import org.apache.flink.api.common.functions.RichReduceFunction
import org.apache.flink.api.common.state.{ListState, ListStateDescriptor, MapState, MapStateDescriptor, ReducingState, ReducingStateDescriptor, ValueState, ValueStateDescriptor}
import org.apache.flink.api.java.tuple.Tuple
import org.apache.flink.streaming.api.scala._
object ValueStateTest {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
val inputDStream: DataStream[String] = env.readTextFile("D:\\MyWork\\WorkSpaceIDEA\\flink-tutorial\\src\\main\\resources\\SensorReading.txt")
val dataDstream: DataStream[SensorReading] = inputDStream.map(
data => {
val dataArray: Array[String] = data.split(",")
SensorReading(dataArray(0), dataArray(1).toLong, dataArray(2).toDouble)
})
val resultDStrem: DataStream[SensorReading] = dataDstream
.keyBy("id")
.reduce( MyStateTestFunc() )
dataDstream.print("data")
env.execute("state test job")
}
}
case class MyStateTestFunc() extends RichReduceFunction[SensorReading]{
// state 定義
lazy val myValueState: ValueState[Double] = getRuntimeContext
.getState( new ValueStateDescriptor[Double]("myValue", classOf[Double]))
lazy val myListState: ListState[String] = getRuntimeContext
.getListState( new ListStateDescriptor[String]("myList", classOf[String]) )
lazy val myMapState: MapState[String, Double] = getRuntimeContext
.getMapState( new MapStateDescriptor[String, Double]("myMap", classOf[String], classOf[Double]))
lazy val myReducingState: ReducingState[SensorReading] = getRuntimeContext
.getReducingState( new ReducingStateDescriptor[SensorReading]("myReduce", MyReduceFunc(), classOf[SensorReading]) )
override def reduce(t: SensorReading, t1: SensorReading): SensorReading = {
// 獲取狀態
val myValue: Double = myValueState.value()
val myList: lang.Iterable[String] = myListState.get()
val myMap: Double = myMapState.get("sensor_1")
val myReducing: SensorReading = myReducingState.get()
// 狀態寫入
myValueState.update( 0.0 )
myListState.add( "hello flink" )
myMapState.put( "sensor_1", 1.0)
myReducingState.add( t1 )
t1
}
}