2015年12月19日 星期六

車聯網的即時服務架構-以Uber為例

大數據的架構是很有趣的,因為牽涉既有傳統的資訊設施,也需融合新擁有的數據儲存與分析設施,而且在不同的使用情境下,架構也不相同。

因此想切入大數據的公司,常常面臨到的問題除了「不知要如何開始」之外,就是「要如何建構可以容易擴展的平台」,以及在最節省資源的情況下,如何給予使用者最佳的服務。而筆者所協助的公司,也常常面臨到技術太多無從選擇,導致不知道如何將新技術配置到架構上。因此大部分的大數據專案常常因為「想做的題目太多,不知如何做起」、「技術太多種,不知從何用起」導致上層也無法承擔與拿定主意,最後不是中途喊卡,就是專案失敗。


近幾年車聯網(Internet of Vehicles)的議題開始被討論,而筆者也正在觀察這種不同聯網的技術架構,因此就以最近由優步(Uber)的首席系統架構師Matt Ranney發表的文章中,它們透過開放原始碼建構的即時服務平台Disco,從中整理一些車聯網建構即時服務系統的方式。

首先我們可以從Uber的現有狀態來觀察,甚麼樣的需求,會需要建置這樣的服務平台? 從Uber的全球服務來看,他們每秒必須面臨一百萬筆Uber車子的Geospatial的索引,以及數千個節點的連接。

Uber整理了幾個他們從2009年開始至今約6年間採用的技術包含「Node.jsPythonJavaGoNative applications on iOS and AndroidRedisPostgresMySQLRiakTwitter's Twemproxy for RedisGoogle's S2 Geometry LibraryringpopTChannelThrift筆者針對這些技術整理技術概要介紹如下

  1. Node.js
    Node.js從2009年誕生之今,也是約6年期間。主要流行的原因是將Java Script帶到後端(Back end)應用,以前專門用JS寫前端的人,只要學Node.js就可以開始寫後端(Back end),所以這些人就變成所謂的全端(Full Stack)。

    Node.js的底層是採用Google V8的引擎,以C++撰寫而成,特性在於撰寫即時的網站服務(Real-Time Web Service),Node.js有所謂的異步I/O模型,一般服務器處理的方式採用同步I/O的方式,如:我們丟一個檔案,服務器就把他印出來,是一個直觀的做法。但在互聯網的環境,大量的請求就會把CPU資源吃光。而異步I/O的方式就是遇到I/O請求就先往後台丟,讓CPU能先處理任務,這樣單一CPU就可以處理多個問題,所以Node.js的異步I/O模型又稱非阻塞I/O。Uber採用Node.js來處理API、訂單的各種即時服務。
  2. Python
    正如其名蟒蛇(Python)一樣,有龐大又好用的套件支持,對於一些想要快速進行服務雛形,都可以透過Python很快的去實作。Python從1991年誕生之今,以優雅、明確、簡單的概念讓開發者可以很快的把各種服務想法快速的實現,因此Uber就是利用Python這種方便「整合」的特性,各種服務機制透過Python來撰寫。
  3. Java
    這個語言資訊相關科系的同學是再熟悉不過了,Java從1995年誕生後至今都仍然很活躍的語言,近年最知名的應用就是Android作業系統,因為Java的語法嚴謹、普遍,因此有些開源軟體會採用Java作為主要開發語言,則虛擬機採用C/C++來做撰寫。Uber則是透過Java與前面的Python整合一些地理資訊上的應用。
  4. Go
    這個語言與Node.js都是在2009年出現,這個語言非常有野心,據說要取代C/C++,當然筆者認為短時間不太可能出現這樣的情形。不過Go因為有了Google的支持,使得在語言發展上較其他語言有更大的優勢。不過筆者觀察的特色在於Go處理需要擴展性(Scalability)與平行性(Concurrency),有很大的優勢,因此Uber把原本要給C/C++要做的事情,部分交給了Go來做處理,藉此提高系統的運作效率。
  5. Native applications on iOS and Android
    因為Uber的服務講究的是好的性能與效率,因此開發了原生的iOS與Android APP。誠如前面提到的Android是採用Java撰寫,而iOS早期為Objective-C所編輯,從2014年開始改Swift語言做開發。
  6. Redis
    這個資料庫也是在2009年出現,屬於NoSQL的一種,採用鍵值(Key–value)的方式儲存資料,它最大的特性在於資料都是儲存於記憶體上,便於使用與高吞吐的情境上,屬於CAP理論中的CP,也就是一致性(Consistent)與分隔容忍性(Partition Tolerance)。Uber採用它做暫存的動作,讓服務能處在低延遲高效率的狀態上。Uber有不少的應用都是採用基於Twemproxy的Redis,使服務能保持不間斷。
  7. PostgreSQLMySQL
    PostgreSQL與MySQL一樣都是傳統關聯式資料庫(RDBMS),跟Java一樣兩個都是1995年誕生,目前MySQL一直在市場上有一定程度的擁護者,不過近幾年因為MySQL商業化後,當初開發MySQL的開發者轉至MariaDB,因此稍微式微一些。反觀PostgreSQL的使用者仍保持開源的方式持續成長,筆者認為它與MySQL最大的不同在於對於地理資訊的處理有強大的功能支持,因此除了日常服務,PostgreSQL更能處理有關地理資訊的資料儲存。不過Uber看起來是把PostgreSQL作為服務一開始的資料庫,筆者認為應該就是看到PostgreSQL的特性,而MySQL則是來當作一般日常服務的資料儲存。
  8. Riak
    幾年前看到Riak的時候,是因為正在了解Hadoop的架構,Riak較Hadoop更為年輕,是在 2009年誕生。Riak是採用鍵值(Key–value)的方式儲存資料,在CAP理論中屬於AP,也就是可用性(Availability)與分隔容忍性(Partition Tolerance),也就特性是以確保寫入與讀出為主。不過Riak跟Cassandra一樣沒有Master/Slave,因此資料在吞吐上主機的壓力較小。Uber將Riak應用在調度系統上,看起來相當合理,因為即時的調度在資料輸入與輸出上需要不斷的吞吐。
  9. Twitter's Twemproxy for Redis
    Twemproxy是Twitter所推出的開放原始碼,是Twitter貢獻前面提到的Redis的架構,以提供更高效能的服務。Twitter採用龐大的Redis集群提供時間軸的訊息顯示,而Twemproxy最大的特色在於當一個Node失效的時候,自動卸載與自動重新連接,因此Uber採用它來做低延遲的服務應用。
  10. Google's S2 Geometry Library
    過去的Uber只能看到Uber的供應量,但其實無法完全掌握汽車與客戶間的距離作最佳化的處理,因此為了強化匹配的效率,Uber要將每個區塊變成一個id,Uber透過S2切成一個個細胞(cell),讓所有的乘客可以因此降低等待時間,因此透過S2的覆蓋函數,Uber不再只是尋找可以載乘客的司機,更能找到可以「順路」載乘客的司機。
  11. Ringpop
    Ringpop是Uber自己開發的模組,是給Node.js使用。它是一個採用Gossip協定,降低服務的出錯機率,在Gossip協定中一旦節點都獲得認證, 它們就可以快速地進行查詢與散播訊息,Gossip以前最常出現在P2P的服務當中。Uber就是把Gossip結合可擴充可傳導的弱一致性行程群組成員協定(SWIM)放到Node.js當中。
  12. TChannel
    TChannel透過Ringpop的實現,Uber就建立了屬於自己遠端調用程式(PRC)機制,它們稱為TChannel,使得每一個請求與回應都能獨立與安全的運作,因此TChannel在搭配Redis下,通訊效率比HTTP快,Uber正在改變早期採用HTTP與Json通信模式,往TChannel的Thrift通訊方式作發展。
  13. Thrift
    Thrift最早在2007年前,是被Facebook開發,且被用來處理系統間的RPC服務,最大的特色在於支援相當多的語言,快速實現RPC的服務。

Uber服務平台處理的工作,就是協助每一位駕駛與消費者的能夠緊密連接,這件事情的挑戰在於如何在即時的狀態下去做資訊動態的連接,像是一位消費者正準備要找車搭,在自己的手機APP上,系統會顯示附近的車輛,這些過程都是即時呈現,所以服務平台必須能夠快速捕捉駕駛的位置,並回傳到手機APP上,提供準備找車的消費者查詢。

這在日常的時段,做匹配(Matching)的工作還可以應付,當在過新年的時候,才是Uber面臨的巨大挑戰,因為跨年後想要搭車的消費者是瞬間暴增,就像是以前SMS時代,大家跨年之後,想傳簡訊給身邊的朋友祝福,卻發現簡訊發不出去,發生系統滿載的問題,Uber服務平台的挑戰亦同。

大家都知道Uber的服務平台,主要以iOS與Android為主,而真正在調度的機制,幾乎是採用Node.js完成,因為Node.js擁有Java script的特性,資料吞吐上能保有較佳的彈性,因此除了iOS與Android之外,資料介接則是採用Node.js語言建構。以下是其他包含的機制技術:

地圖(Maps/ETA): 最短抵達時間(Estimated Time of Arrival簡稱ETA)該機制被使用來估計抵達的時間,除了可以顯示街道地圖,也可以呈現估計時間,採用了Python、C++與Java語言,採用Python是為了要整合機制,採用C++與Java則考慮提升執行效率。

服務(Service):該服務主要用於提供商業物流服務,大部分是採用Python進行撰寫,因為Python具備開發彈性,並同時採用Microservice的方法。

資料庫(Database):早期採用PostgresSQL,後來採用Redis主要是與Twemproxy結合。也有採用MySQL於線上服務網站。另外還有一些調度服務是採用Riak進行儲存。

配套服務:配套的系統服務,包含收集排名、信件通知、更新資料庫、付款時程......等,都是採用Python進行撰寫,當然還是考量Python的語言可讀性高,且具備開發彈性。

金流:Uber的金流介接都與目前的金流公司合作。


面對Uber接下來的急速增長的客戶數量挑戰,Uber提出新系統的建構想法,像是他們增加了更詳細的資訊交換內容,包含供給端與消費端,供給端提供座位數、車型、小孩座位,消費端則提供需求座位數,結合地理資訊(Geo),做這兩端的調度最佳化(Dispatch Optimization)。

筆者整理Uber的幾個服務項目的新建構思維,如下:
  1. 供給與需求雙方的資訊媒合,消費端的需求可以被快速媒合。
  2. 當消費端出現,服務平台能夠能快速找出最靠近的候選車輛。
  3. 透過較新的技術達到服務的可用性,確保服務不斷線
  4. 每一個服務都有具備重複詢問的功能,確保每一個訂單都能運作。
  5. 從一個可以提供載客的服務,走到可以提供順路載客的服務。
  6. 利用新技術達到減少空載、減少等待、整體到達時間(ETA)最短的服務


基本上現在許多資訊服務都是各種技術混用,因此筆者常需要涉獵各種不同的大數據運行架構,本篇主要針對Uber的技術做了一些整理,希望能提供給需要了解各種技術架構整合的讀者有初步的了解,如果讀者有興趣,也歡迎自行到下面參考資料中作進一步的閱讀。


其他資料參考: