2015/10/16 by Dan Schafer 和 Laney Kuenzel
今年我們宣布並開放原始碼 GraphQL 和 Relay 時,我們說明了如何使用它們來執行查詢讀取和使用突變執行寫入。然而,客戶通常希望在他們關心的資料變更時從伺服器取得推播更新。為了支援這一點,我們在 GraphQL 規格中引入了第三個操作:訂閱。
我們採用與突變相同的方式來處理訂閱;就像伺服器支援的突變清單描述了客戶可以執行的所有動作一樣,伺服器支援的訂閱清單描述了它可以訂閱的所有事件。就像客戶可以在執行 GraphQL 選取後告訴伺服器要重新擷取哪些資料一樣,客戶可以使用 GraphQL 選取告訴伺服器它希望透過訂閱推播哪些資料。
例如,在 Facebook 架構中,我們有一個名為 storyLike
的變異欄位,客戶端可以使用它來按讚貼文。客戶端可能想要重新擷取按讚數,以及按讚句子(「Dan 和其他 3 人按讚」。我們在伺服器上進行此翻譯,因為該翻譯在各種語言中都很複雜)。為此,他們會發出以下變異
mutation StoryLikeMutation($input: StoryLikeInput) { storyLike(input: $input) { story { likers { count } likeSentence { text } } }}
但是當您查看貼文時,您也希望在其他人按讚貼文時收到推播更新!這就是訂閱的用武之地;Facebook 架構有一個名為 storyLikeSubscribe
的訂閱欄位,允許客戶端在有人按讚或取消按讚該故事時收到推播資料!客戶端會建立像這樣的訂閱
subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) { storyLikeSubscribe(input: $input) { story { likers { count } likeSentence { text } } }}
然後客戶端會將此訂閱連同 $input
變數的值傳送至伺服器,其中會包含我們訂閱的故事 ID 等資訊
input StoryLikeSubscribeInput { storyId: string clientSubscriptionId: string}
在 Facebook,我們在建置時將此查詢傳送至伺服器以產生其唯一 ID,然後訂閱其中包含訂閱 ID 的特殊 MQTT 主題,但這裡可以使用許多不同的訂閱機制。
然後,每當有人按讚貼文時,我們就會在伺服器上觸發此訂閱。如果我們的所有客戶端都使用 GraphQL,我們可以將此掛鉤放入 GraphQL 變異中;由於我們也有非 GraphQL 客戶端,因此我們將掛鉤放入 GraphQL 變異下方的層級中以確保它始終觸發。
值得注意的是,此方法要求客戶端訂閱它關心的事件。另一種方法是讓客戶端訂閱查詢,並在該查詢的結果每次變更時要求更新。為什麼我們不採用這種方法?
讓我們回顧一下我們想要重新擷取的資料
fragment StoryLikeData on Story { story { likers { count } likeSentence { text } }}
哪些事件會觸發該片段中擷取的資料變更?
而這僅是事件的冰山一角;當有數千人訂閱,以及數百萬人按讚貼文時,這些事件中的每一個事件都會變得棘手。實作此資料集的即時查詢證明非常複雜。
在建立基於事件的訂閱時,由於事件明確定義了觸發事件的條件,因此很容易就能找出應觸發什麼事件。實作在現有的訊息佇列系統之上也證明相當容易。不過,對於即時查詢而言,這似乎困難得多。我們欄位的數值是由其解析函式的結果決定的,而找出所有可能改變該函式結果的事物很困難。理論上,我們可以在伺服器上進行輪詢來實作這一點,但這有效率和及時性的問題。基於此,我們決定投資於基於事件的訂閱方法。
我們積極建置上述的事件式訂閱方法。我們使用該方法在 iOS 和 Android 應用程式上建置即時按讚和留言功能,並持續擴充其功能和 API。雖然它目前在 Facebook 的實作與 Facebook 的基礎架構相結合,但我們當然期待盡快公開我們的進度。
由於我們的後端和架構不支援即時查詢,我們沒有計畫在 Facebook 開發它們。同時,很明顯地,有些後端和架構可行即時查詢,而且它們在那些情況下提供了很多價值。社群中對此主題的討論非常棒,我們很興奮能看到從中產生什麼樣的即時查詢提案!
訂閱為建立真正動態的應用程式創造了許多可能性。我們很興奮能持續在社群的協助下開發 GraphQL 和 Relay,以實現這些可能性。