Google Cloud - instance 間通訊(pubsub + memcache 實現實例間通訊和保證一致性)

GCP - appengine 經過 version 管理應用,你能夠在 appengine 上部署多個 version(dev、qa等),而每一個 version 能夠有多個 instance,一個 instance 可簡單理解爲一個基於 Spring Boot 實現的微服務,當有請求到達時 appengine 會根據必定策略選擇由哪個 instance 處理該請求,若是現有的 instance 處理的流量已經不少,那麼 appengine 會啓動新的 instance 來處理這個請求,這個行爲主要由 instance 的三種擴縮策略決定:手動、自動和基礎。java

背景

應用數據保存在 GCP 的數據存儲組件 datastore 中,datastore 是一個 NoSQL 數據庫。假設咱們的應用對其中一部分數據 M 的操做的 QPS 要求很高,若是每次都從 datastore 查詢則不能知足需求。python

// 更新 M 數據
POST /m

// 查詢 M 數據
GET /m
複製代碼

解決方案

爲了加快請求的處理速度,咱們在應用啓動時(即 instance 啓動時)先將這部分數據所有加載到內存,以後直接從內存中讀取,而不是每次都從 datastore 中查詢。這種方式有幾個問題須要解決:數據庫

  1. 每次請求到達時不肯定 appengine 會將請求路由給哪個 instance,因此當這部分數據有更新(POST /m)時須要通知該 version 的全部 instance 進行數據同步
  2. POST /m 請求中要確保全部 instance 都成功同步了數據(全部 instance 中 M 數據保持一致),才能以請求處理成功的狀態返回。

數據同步 - pubsub

instance 間通訊是一個棘手的問題,由於 appengine 沒有直接提供 API 或外部組件來完成這件事情,甚至你想將請求發給指定的 instance 都須要犧牲不少靈活性才能實現。固然大多數狀況下請求的路由應該由 appengine 控制,只有在實現某些特殊需求時才能夠考慮一些特殊作法。緩存

數據同步的基本思路是在 POST /m 時先在 datastore transactions 中更新 M 數據,事務成功提交後再經過 pubsub 通知全部 instance 從 datastore 更新最新數據,全部 instance 都確認成功更新數據後,請求成功。bash

經過查閱 appengine 請求路由說明能夠知道經過如下格式的地址能夠將請求路由給指定的 instance。 https://[INSTANCE_ID]-dot-[VERSION_ID]-dot-[SERVICE_ID]-dot-[MY_PROJECT_ID].appspot.com 但這種方式須要將擴縮方式設置爲手動擴縮,並且 INSTANCE_ID並非 instance 的惟一 id,而是一個下標索引,好比 version test 有 3 個 instance,分別爲 A、B、C,那麼能夠保證的是經過 test[0]、test[1]、test[2] 能夠成功訪問(遍歷)三個 instance,但你沒法知道 test[0] 到底是指向 A、B 仍是 C。app

pubsub 是 GCP 的消息組件,消息發送後消費者有兩種方式消費消息:pullpush分佈式

  1. pull: 經過拉取的方式消費消息,咱們的目的是將「數據同步」這個消息馬上通知到每個 instance,pull 的方式須要每個 instance 以輪詢的方式檢查並拉取消息,這樣在資源佔用和響應速度(POST /m)上都不能知足要求。
  2. push: 這種方式在消息發到 pubsub 的指定 topic 後,pubsub 會馬上把這個消息 push 到訂閱了這個 topic 的全部 subscriber (subscriber 指定的 endpoint 處,這裏咱們的域須要設置爲:https://[INSTANCE_ID]-dot-[VERSION_ID]-dot-[SERVICE_ID]-dot-[MY_PROJECT_ID].appspot.com)。

須要注意的是 pubsub 的 tpoic 和 subscriber 的建立只須要執行一次,能夠在 version 啓動後經過 appengine-taskqueue 建立 n 個 subscriber,n 爲這個 version 的實例數。這意味着實例數量 n 是個"常數"(只有手動擴縮模式才能確保 n 爲「常數」)。微服務

到這裏,在 POST /m 裏通知全部 instance 進行 「數據同步」這個消息能夠正確發送並最終通知到全部 instance 了。使用 appengine 預留的 /_ah/push-handlers/.* 路徑能夠簡化 endpoint 的認證和受權,最終的 endpoint 是下面的形式: https://[INSTANCE_ID]-dot-[VERSION_ID]-dot-[SERVICE_ID]-dot-[MY_PROJECT_ID].appspot.com/_ah/push-handlers/your-topic-name pubsub 進行 push 時每一個 instance 的 POST /_ah/push-handlers/your-topic-name controller/handler 就會收到請求,並攜帶着消息內容。在這個請求中,咱們能夠從消息中知道須要怎樣更新數據,進而完成數據同步的任務。google

一致性 - memcahce

上面介紹了數據同步的具體流程,在這個過程當中一致性的保證是很重要的,主要體如今數據同步時須要確保全部的 instance 都成功消費「數據同步」消息,POST /m 才能以請求成功處理的狀態返回。spa

在這裏 version 的 instance 數量 n 是「常量」,那麼咱們只需在一個公共的地方維護一個標識 A,標識當前已經成功同步的 instance 數量,當這個 A == n 時也就意味着全部 instance 都成功同步了數據。

appengine-memcache 是主要應用於 appengine 上的分佈式緩存服務,咱們能夠在上面存儲這個惟一標識,POST /m 請求中成功發送「數據同步」消息後,就以輪詢的方式從 memcache 中查詢 A 的值,同時在 POST /_ah/push-handlers/your-topic-name 中要遞增 A 的值,A == n 時就代表同步完成。

相關文章
相關標籤/搜索