Flink用于外部數據訪問的異步I/O

本頁闡述了使用Flink的API來進行外部數據存儲的異步I/O,對于不熟悉異步或者事件驅動編程的用戶,一篇關于Future和事件驅動編程可能會很有用。

注意:關于異步I/O的詳細設計和實現可以在異步I/O設計和實現這篇文章找到。

異步I/O操作的需要

當與外部系統進行交互(例如使用存儲在數據庫中的數據豐富流事件)時, 需要注意的是, 與外部系統的通信延遲并不決定流應用程序的總體工作。

原始的訪問外部系統中的數據,例如通過一個MapFunction來訪問,通常意味著同步交互:將一個請求發送到數據庫,MapFunction等待直到接收到響應為止。很多情況下,這種等待會占用很大一部分函數的時間。

與外部數據庫系統進行異步交互意味著一個并行函數實例可以并發地處理多個請求和并發地接收多個響應。那樣的話,等待時間就可以被其他的請求或者響應所覆蓋。至少,等待時間可以被多個請求攤銷,這在很多情況下會導致更高的流吞吐量。


注意:通過擴展MapFunction到一個很高的并發度來提高吞吐量在一定程度上是可行的,但是常常會導致很高的資源消耗:有很多的并行MapFunction實例意味著更多的任務、線程、Flink內部網絡連接、與數據庫之間的網絡連接、緩存以及通常的內部開銷。

前提

如上節所述,實現一個連接數據庫(或者key/value存儲系統)的正確異步I/O需要一個客戶端,數據庫支持通過該客戶端來進行異步請求。許多流行的數據庫都支持這種客戶端。

對于沒有這種客戶端的情況下,用戶可以將異步客戶端換成一個可以通過創建多個客戶端并使用線程池處理同步調用來嘗試將同步客戶端轉換為有限的并發客戶端。然而,這個方法通常比純粹的異步客戶端性能要低一些。

異步I/O API

Flink的Async I/O允許用戶在數據流中使用異步的請求客戶端,這個API會處理與數據流的交互,同時還處理順序、事件時間、容錯等。

假設已經目標數據庫已經有了異步客戶端,要實現一個通過異步I/O來操作數據庫還需要三個步驟:
  1、實現用來分發請求的AsyncFunction
  2、獲取操作結果的callback,并將它提交到AsyncCollector
  3、將異步I/O操作作為轉換操作應用于DataStream

下面代碼展示了基本的模式:

// This example implements the asynchronous request and callback with Futures that have the
// interface of Java 8's futures (which is the same one followed by Flink's Future)

/**
 * An implementation of the 'AsyncFunction' that sends requests and sets the callback.
 */
class AsyncDatabaseRequest extends RichAsyncFunction<String, Tuple2<String, String>> {

    /** The database specific client that can issue concurrent requests with callbacks */
    private transient DatabaseClient client;

    @Override
    public void open(Configuration parameters) throws Exception {
        client = new DatabaseClient(host, post, credentials);
    }

    @Override
    public void close() throws Exception {
        client.close();
    }

    @Override
    public void asyncInvoke(final String str, final AsyncCollector<Tuple2<String, String>> asyncCollector) throws Exception {

        // issue the asynchronous request, receive a future for result
        Future<String> resultFuture = client.query(str);

        // set the callback to be executed once the request by the client is complete
        // the callback simply forwards the result to the collector
        resultFuture.thenAccept( (String result) -> {

            asyncCollector.collect(Collections.singleton(new Tuple2<>(str, result)));
         
        });
    }
}

// create the original stream
DataStream<String> stream = ...;

// apply the async I/O transformation
DataStream<Tuple2<String, String>> resultStream =
    AsyncDataStream.unorderedWait(stream, new AsyncDatabaseRequest(), 1000, TimeUnit.MILLISECONDS, 100);
/**
 * An implementation of the 'AsyncFunction' that sends requests and sets the callback.
 */
class AsyncDatabaseRequest extends AsyncFunction[String, (String, String)] {

    /** The database specific client that can issue concurrent requests with callbacks */
    lazy val client: DatabaseClient = new DatabaseClient(host, post, credentials)

    /** The context used for the future callbacks */
    implicit lazy val executor: ExecutionContext = ExecutionContext.fromExecutor(Executors.directExecutor())


    override def asyncInvoke(str: String, asyncCollector: AsyncCollector[(String, String)]): Unit = {

        // issue the asynchronous request, receive a future for the result
        val resultFuture: Future[String] = client.query(str)

        // set the callback to be executed once the request by the client is complete
        // the callback simply forwards the result to the collector
        resultFuture.onSuccess {
            case result: String => asyncCollector.collect(Iterable((str, result)));
        }
    }
}

// create the original stream
val stream: DataStream[String] = ...

// apply the async I/O transformation
val resultStream: DataStream[(String, String)] =
    AsyncDataStream.unorderedWait(stream, new AsyncDatabaseRequest(), 1000, TimeUnit.MILLISECONDS, 100)

重要提醒:AsyncCollector在第一次調用AsyncCollector.collect時就完成了,所有后續的collect調用都會被忽略。

下面的兩個參數控制了異步操作:
  ****Timeout****:timeout定義了異步操作過了多長時間后會被丟棄,這個參數是防止了死的或者失敗的請求
  ****Capacity****:這個參數定義了可以同時處理多少個異步請求,雖然異步I/O方法會帶來更好的吞吐量,但是算子任然會成為流應用的瓶頸。限制并發請求的數量確保了算子不會積累不斷增加的積壓的待處理請求,但一旦容量耗盡,它將觸發背壓。

結果順序

由AsyncFunction發出的并發請求經常是以無序的形式完成,取決于哪個請求先完成。為了控制發出請求結果的順序,Flink提供了兩種模式:
  ****Unordered****:結果記錄在異步請求完成后就發出,流中的記錄的順序通過異步I/O操作后會與先前的不一致。當使用處理時間作為時間特性時這種模式具有低延遲、低消耗特點。通過AsyncDataStream.unorderedWait(...)來使用這種模式。
  ****Ordered****:在這種情況下,流的順序是保留的,結果記錄發出的順利與異步請求觸發的順序(算子輸入記錄的順序)一致。為了實現這一點,算子會將結果記錄緩存起來直到所有的處理記錄都被發出(或者超時)為止。這常常會導致一定程度的延遲和checkpoint消耗,因為跟非排序模式相比,記錄或者結果會被長時間保存在checkpoint State中。通過AsyncDataStream.orderedWait(...)來使用這種模式。

事件時間

當使用流程序使用事件時間時,異步I/O操作將正確處理水印,這具體說明了如下兩種模式:
  ****Unordered****:水印不會超過記錄反之亦然,這也就意味著水印建立起了一個秩序邊界。記錄在兩個水印間無序地發出。在一個水印后產生的記錄只能在這個水印發出之后才能發出,同樣水印也只能在所有水印之前的記錄都發出之后才能發出。
  ****Ordered****:保存水印的順序,就如保存記錄之間的順序一樣。與處理時間相比,開銷沒有顯著變化。
請記住,攝入時間是一個特殊的事件時間,會基于源處理時間的自動產生水印。

容錯性保證

異步I/O操作提供了exactly-once容錯性保證,它將異步請求的記錄存儲在checkpoint中,并在從故障中恢復時恢復/重新觸發請求。

實施提示

警告

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,836評論 18 139
  • 國家電網公司企業標準(Q/GDW)- 面向對象的用電信息數據交換協議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,079評論 6 13
  • “失敗是成功之母”,這句話,我們大多數人從小到大都是耳熟能詳的。但是這句話,講了這么多年。真正能做到的能有幾個呢?...
    鹿鹿無畏閱讀 866評論 0 51
  • 花開君遠行, 花落不見人, 相思愈久矣, 奈何阻重深。
    釋迦干屎橛閱讀 217評論 0 0
  • 知道自己漂泊了多久嗎 你的影蹤不難發現 呵,正在窗臺發什么呆呢 還是早已厭倦了無邊流浪路 軀體的停駐只為一顆心的閑...
    樂從心閱讀 185評論 8 17