新 Uber 司機端是如何克服網絡延遲問題

Carbon: Optimistic Mode article feature image
Carbon: Optimistic Mode article feature image

本文是 Uber 的客戶端工程師團隊講述瞭如何開發最新版本司機端系列文章中的第三篇,該系列代號 Carbon ,是咱們共享出行業務的核心。包括其它功能在內,Uber 司機端使得超過 300 萬名司機能夠查看費用、里程以及收益狀況。2017 年咱們結合司機的反饋開始對司機端進行從新設計,並在 2018 年 9 月份啓動了該項目。git

城市建築和無線數據技術的競爭意味着在城市中存在一些手機沒有信號的黑色區域。這種黑色區域景區更爲常見,致使網絡質量和阻塞程度頻繁的變化。這些問題尤爲影響着那些接送乘客的司機們。github

能夠舉一個合適的例子來講明這種問題。假設一個司機到達了很是擁擠的班加羅爾機場終點。乘客想支付現金,司機須要在應用裏面操做完成訂單來查看最終的金額。把車停在路邊,司機端卻沒法聯網。乘客匆忙趕飛機,不能聯網就意味着司機就不能結束行程並查看最終的金額。司機可能會繼續開下去,增長了額外的時間,也可能增長了行程花費,給司機和乘客都帶來了不便。後端

爲了處理這種網絡覆蓋漏洞和預防這類事件的發生,咱們提出了 —— 樂觀模式。新版本的司機端能夠離線操做,這樣司機就能夠在沒有網絡的狀況下用最後一次服務端的預估數據來結束行程。樂觀模式下司機端能夠任何網絡下正常工做,極大的提升了司機和乘客的體驗。服務器

樂觀模式組件

咱們以前的司機端版本中支持一些離線能力來收集失敗的請求,一旦網絡恢復就會上傳到服務器進行整理。雖然這種功能有助於預防一些顯示錯誤,可是不能智能的更新應用狀態,不能將多個功能堆積在一塊兒,也不能誇會話持久化狀態。咱們爲新版本的司機端開發了下面這個組件來處理這些問題。網絡

樂觀請求

司機端的任何組件均可以經過提交一個樂觀請求來開始流轉。一個樂觀請求可以序列化儲存到磁盤,對於一個普通的網絡請求來講佔用的內存很是小,而且每個樂觀請求都對應一個樂觀轉換。app

樂觀轉換

樂觀模式的核心是轉換,換句話說,操做轉換一個對象從當前狀態到樂觀狀態,也就是,從服務返回的預期結果。轉換還可以堆積,一個對象能夠通過屢次轉換。舉一個例子來理解下轉換:想象一個類Counter有一個屬性count。咱們能夠實現一個轉換來增長count屬性的值。框架

Carbon: Optimistic Mode article figure 1

圖一:在這個簡單的例子中,Counter對象每通過一次增長轉換,count屬性值就會增長一。ide

根據業務需求轉換既能夠是簡單的也能夠是複雜的。每個樂觀請求都關聯一個轉換,轉換會根據樂觀請求返回一個最終的_樂觀狀態_。當數據從服務端返回時用戶是無感知的,這種方式提供了一種平滑的過渡方案。優化

當客戶端提交一個樂觀請求時,關聯在請求上的轉換就會立馬生效,應用進入樂觀狀態,從而完成請求。樂觀狀態會一直被保持直到收到服務端的真實狀態,而後同步應用和服務端。ui

Carbon: Optimistic Mode article figure 2a

圖 2-1: 普通的計數請求失敗

Carbon: Optimistic Mode article figure 2b

圖 2-2: 在無網絡的狀況下樂觀模式使用轉換及時更新數據狀態,未來有網絡的狀況下和服務端進行同步。

樂觀流

咱們整個應用都在使用 RX streams 傳遞數據。應用的每一個功能都會隨着已發佈數據流的狀態改變做出響應。這種機制使咱們可以使用相同的流輕鬆地將樂觀變換應用於對象的最新狀態。爲了得到樂觀狀態,咱們結合了數據最後的狀態和可用的轉換。在將數據發佈迴流並由功能使用以前,數據已經應用了每一個轉換。隨後業務只需簡單的根據數據的樂觀狀態做出響應。

依賴請求

同時也存在一些請求依賴於樂觀請求的完成。例如,甚至在後端不知道行程已經開始的狀況下發送一個結束行程的請求是不合理的。當咱們在等待樂觀請求完成的時候,這樣的依賴請求將會被放入隊列一段時間。若是週期過長,咱們會結束這個請求,通知用戶網絡錯誤。

設計挑戰

咱們在這個設計中遇到了一些挑戰。咱們想要支持多個堆疊的樂觀請求,容許在沒有網絡的狀況下完成多個步驟。因爲和服務器不一樣步,咱們還須要處理錯誤地進入樂觀狀態而且必須回滾到先前狀態的狀況。確保咱們可靠地向司機展現最準確的狀態須要進行屢次迭代,並持續優化。

兜底轉換

樂觀模式開啓的狀況下,應用程序可能會在樂觀請求完成以前收到其餘的網絡數據。

Carbon: Optimistic Mode article figure 3

圖 3: 在這個場景中,咱們在收到服務器最新的狀態以後又進行了樂觀轉換。

咱們繼續拿上面用到的計數器的例子來講。應用程序使用增長變換把最終的值變成了 2。然而,這個值尚未和服務端同步。在這期間,收到的其餘的網絡響應可能仍是舊的值 1。樂觀模式使用轉換更新了這個舊值而且維護這個樂觀狀態。這就確保了應用程序不會在兩種狀態以前來回切換,避免給用戶產生混亂的體驗。

應用重啓時如何存活

全部的樂觀請求和最新的樂觀狀態一塊兒被保存在磁盤裏,因此它們可以在應用重啓的時候得以保留。考慮這麼一種狀況,一些請求正在排隊和服務器同步,可是用戶卻殺死了應用。在從新啓動的時候,樂觀請求和狀態會從磁盤中加載。這容許用戶在從新啓動應用時處於相同的狀態。樂觀請求排隊和服務器同步。

顯示錯誤

咱們遇到的這個新功能的一個特殊問題是它如何顯示錯誤。樂觀模式的請求只應該因爲後端中斷而失敗,而且結果應該是可預測的便於模擬。然而,實踐中會出現錯誤。因爲咱們使用樂觀的流程服務用戶,因此一個小錯就可能帶來很很差的體驗。首先,應用程序的狀態回滾到以前的樂觀狀態,不是用戶所指望的狀態,下個動做可能不太明顯。其次,即便以前的狀態可能已經無效了,咱們也須要用它來接收錯誤緣由來展現。爲了處理這些問題,咱們在司機端里加入了一個全局處理錯誤信息的框架,它能夠調用內部彈窗框架。

請求出錯的狀況老是不多見的。對於常常發生的錯誤,好比行程過短,咱們在手機端上實現了檢查,以便更好的處理。

節省時間

對司機來講,咱們在開始和結束行程上利用樂觀模式節省了大量的時間。咱們常常能夠看到在真實的網絡請求完成以前行程已經開始幾分鐘的狀況。截至 2018 年 11 月,咱們注意到平均每一個樂觀操做節省大約 13.5 秒的時間。即便在新司機端的早期階段,咱們天天累計節省司機的時間也超過了一年。

樂觀模式的發展

在無網絡的狀況下可以正常運行的能力在 Ubers 的其餘應用程序上面使用的也很是好。設計之初是爲了加速開始和結束行程的速度,它還被整合到 Uber Eats 中功能中,當使用現金結算時,能夠更快的結束。它還能用到相似於這種能夠快速響應後續同步到服務端的業務中,好比對乘客或司機的評價,標記消息已讀,和收集交付的指紋。

Uber 司機端系列文章索引

  1. 爲何咱們決定重構 Uber 司機端
  2. 使用RIBs重構Uber司機端
  3. 新 Uber 司機端是如何克服網絡延遲問題的
  4. Scaling Cash Payments in Uber Eats
  5. How to Ship an App Rewrite Without Risking Your Entire Business
  6. Building a Scalable and Reliable Map Interface for Drivers
  7. Engineering Uber Beacon: Matching Riders and Drivers in 24-bit RGB Colors
相關文章
相關標籤/搜索