對於一個互連網企業,後端服務是必不可少的一個組成部分。拋開業務應用來說,往下的基礎服務設施做到哪些才能夠保證業務的穩定可靠、易維護、高可用呢。縱觀整個互連網技術體系再結合公司的目前狀況,個人認為必不可少或者非常關鍵的後端基礎技術/設施如下圖所示:
這裡的後端基礎設施主要指的是應用線上上穩定運行需要依賴的關鍵組件/服務等。開發或者搭建好以上的後端基礎設施,一般情況下是能夠支撐很長一段時間內的業務的。此外,對於一個完整的架構來說,還有很多應用感知不到的系統基礎服務,如負載平衡、自動化部署、系統安全等,並沒有包含在本文的描述範圍內。 Api網關
在移動app的開發過程中,通常後端提供的介面需要以下功能的支援: 負載平衡 api存取權限控制 使用者鑒權
一般的做法,使用nginx做負載平衡,然後在每個業務應用裡做api介面的存取權限控制和使用者鑒權,更最佳化一點的方式則是把後兩者做成公用類庫供所有業務調用。但從總體上來看,這三種特性都屬於業務的公用需求,更可取的方式則是整合到一起作為一個服務,既可以動態地修改許可權控制和鑒權機制,也可以減少每個業務整合這些機制的成本。這種服務就是Api網關(http://blog.csdn.net/pzxwhc/article/details/49873623),可以選擇自己實現,也可以使用開源軟體實現,如Kong。如下圖所示:
但是以上方案的一個問題是由於所有api請求都要經過網關,它很容易成為系統的效能瓶頸。因此,可以採取的方案是:去掉api網關,讓業務應用直接對接統一認證中心,在基礎架構層面保證每個api調用都需要先通過統一認證中心的認證,這裡可以採取緩衝認證結果的方式避免對統一認證中心產生過大的請求壓力。 業務應用和後端基礎架構
業務應用分為:線上業務應用和內部業務應用。 線上業務應用:直接面向互連網使用者的應用、介面等,典型的特點就是:請求量大、高並發、高可用、對故障的容忍度低。 內部業務應用:這個是面向公司內部的應用。比如,內部資料管理平台、廣告投放平台等。相比起線上業務應用,其特點: 資料保密性高、壓力小、並發量小、允許故障的發生。
業務應用基於後端的基礎架構開發,針對Java後端來說,應該有的幾個架構如下: MVC架構:從十年前流行的Struts1、2到現在最為推崇的SpringMVC、Jersey以及國人開發的JFinal、阿里的WebX等等,這些架構尤其是後面流行的這些都是各有千秋的。選型的主要因素是看你的團隊是否有一個對某架構能夠做二次開發、定製的人在。很多時候,針對這些通用的架構,你是需要做一些特定的開發才能滿足特定的需求的。比如,很多團隊傳遞參數使用的都是UnderScore的命名法(底線串連單詞),但是Java中確是使用LowCamel命名的。對於SpringMVC,可以通過註解的alias來指定,但這樣需要對每一個參數都要指定alias有點效率太低,此外ModelAttribute也不支援別名,更好的方式是在架構層面統一對參數做Camel命名的轉換達到目的。 IOC架構:ioc帶來的好處無須多言。目前Java中最為流行的Spring自誕生就天然支援IOC。 ORM架構:MyBatis是目前最為流行的orm架構。此外,Spring ORM中提供的JdbcTemplate也很不錯。當然,對於分庫分表、主從分離這些需求,一般就需要實現自己的ORM架構來支援了,像阿里的tddl、噹噹的sharding-jdbc(從datasource層面解決了分庫分表、讀寫分離的問題,對應用透明、零侵入)。此外,為了在服務層面統一解決分庫分表、主從分離、主備切換、緩衝、故障恢複等問題,很多公司都是有自己的資料庫中介軟體的,比如阿里的Cobar、360的Atlas、網易的DDB,還有官方提供的MySQL Proxy以及開源的MyCat、kingshard和收費的oneproxy。目前,線上有一定規模使用的應該是kingshard,當然如果不缺錢也可以上oneproxy。 緩衝架構:緩衝架構主要指的是對redis、memcached這些快取服務器的操作統一封裝,一般使用Spring的RedisTemplate即可,也可以使用jedis做自己的封裝,支援用戶端分布式方案、主從等。 JavaEE應用效能檢測架構:對於線上的JavaEE應用,需要有一個統一的框架組成到每一個業務中檢測每一個請求、方法調用、jdbc串連、redis串連等的耗時、狀態等。jwebap是一個可以使用的效能偵查工具,但由於其已經很多年沒有更新,有可能的話建議基於此項目做二次開發。
一般來說,以上幾個架構即可以完成一個後端應用的雛形。
對於這些架構來說,最為關鍵的是根據團隊技術構成選擇最合適的,有能力開發自己的架構則更好。此外,這裡需要提供一個後端應用的模板或產生工具(如maven的archetype)給團隊成員使用,可以讓大家在開發新的應用的時候,迅速的產生雛形應用,而無需再做一些架構搭建的重複性勞動。 緩衝、資料庫、搜尋引擎、訊息佇列
緩衝、資料庫、搜尋引擎、訊息佇列這四者都是應用依賴的後端基礎服務,他們的效能直接影響到了應用的整體效能,有時候你代碼寫的再好也許就是因為這些服務導致應用效能無法提升上去。 緩衝
如緩衝五分鐘法則所講:如果一個資料頻繁被訪問,那麼就應該放記憶體中。這裡的緩衝就是一種讀寫效率都非常高的儲存方案,能夠應對高並發的訪問請求,通常情況下也不需要持久化的保證。但相對其他儲存來說,緩衝一般是基於記憶體的,成本比較昂貴,因此不能濫用。
緩衝可以分為:本機快取和分布式緩衝。 本機快取:主要指的是記憶體中的緩衝機制。在Java中,Google Guava中就提供了本機快取的實現機制。當然使用java的ConncurrentHashMap你也可以實現自己的本機快取方案。 分布式緩衝:指的單獨的快取服務。幾年前比較流行的是memcached,但其只是一個KV的儲存,支援的資料結構太少。現在最為流行的就是Redis,能夠支援豐富的資料結構,基於事件驅動的單線程非阻塞IO也能夠應對高並發的情境。叢集方案除了官方的redis cluster, 目前比較流行的還有豌豆莢的codis、twitter的twemproxy。
對於緩衝的使用,需要注意以下幾點: 緩衝的失效機制:當給某一個key設定了有效期間,那麼緩衝何時對此key進行刪除呢。一般來說會有以下幾種方式: 守護進程定時去掃描key,找到已經失效的key,然後刪除 讀取key的時候先去判斷key是否失效,如果失效則刪除並返回空。 緩衝的淘汰機制:是當緩衝記憶體達到上限時如何刪除緩衝中的key。Redis提供了以下資料淘汰策略: volatile-lru:從已設定到期時間的資料集中挑選最近最少使用的資料淘汰 volatile-ttl:從已設定到期時間的資料集中挑選將要到期的資料淘汰 volatile-random:從已設定到期時間的資料集中任意選擇資料淘汰 allkeys-lru:從資料集中挑選最近最少使用的資料淘汰 allkeys-random:從資料集中任意選擇資料淘汰 no-enviction(驅逐):禁止驅逐資料
對於其具體的實現機制,可以參考《Redis設計與實現》一書 緩衝的更新機制: 通常來說有四種方式:Cache aside, Read through, Write through, Write behind caching,具體的可見陳皓大神的這篇總結:緩衝更新的套路。 緩衝的服務過載保護:緩衝的服務過載指的是由於緩衝失效,而引起後端服務的壓力驟增,進一步產生雪崩效應。這個現象和緩衝更新是相關的,採取何種策略在緩衝失效的時候去更新緩衝直接決定了服務過載的保護機制。通常的分為用戶端和服務端的應對方案。前者的方案有:基於逾時的簡單模式、基於逾時的常規模式、基於重新整理的簡單模式、基於重新整理的常規模式、基於重新整理的續約模式。後者的方案則是很常見的流量控制和服務降級。具體的可以看美團技術團隊總結的這篇文章:Cache應用中的服務過載案例研究。 資料庫
資料庫是後端開發中非常常見的一個服務元件。對於資料庫的選型,要根據業務的特點和資料結構的特點來決定。
從儲存介質上,資料庫可以分為: 記憶體資料庫: 資料主要儲存在記憶體中,同時也可以採取措施對資料進行持久化到硬碟中。如Redis、H2DB的記憶體模式。對於這種資料庫,由於記憶體成本昂貴,因此一定要做好儲存的量化分析、容量預估,防止記憶體不足造成服務不可用。 硬碟資料庫:資料存放區在硬碟上的這種資料庫是最為常見的。MySQL、Oracle、Postgresql、HBASE、H2DB、SqlLite等等都是硬碟資料庫。此外,SSDB是基於SSD硬碟的KV資料庫,支援的資料介面很豐富,是Redis的另外一個選擇。
從儲存資料類型、資料模式上,資料庫可以分為: 關係型資料庫:MySQL、Oracle、Postgresql都是關係型資料庫的,是採用關聯式模式(關聯式模式指的就是二維表格模型,而一個關係型資料庫就是由二維表及其之間的聯絡所組成的一個資料群組織)來組織資料的資料庫。 非關係型資料庫:非關係型資料庫是相對關係型資料庫來講的。以索引值對儲存,且結構不固定,每一個元組可以有不一樣的欄位,每個元組可以根據需要增加一些自己的索引值對,這樣就不會局限於固定的結構,可以減少一些時間和空間的開銷。但是,其沒有關係型資料庫那種嚴格的資料模式,並不適合複雜的查詢以及需要強交易管理的業務。非關係型資料庫又可以分為: KV資料庫:主要以(key,value)索引值對儲存資料的資料庫。以Redis、RocksDB(levelDB)、SSDB為代表。 文檔資料庫:總體形式上也是索引值對的形式,但是值裡面又可以有各種資料結構:數組、索引值對、字串等等。以mongodb、couchdb為代表。 列資料庫:也叫作稀疏大資料庫,一般是用來儲存海量資料的。相對於行資料庫,這種資料庫是以列為單位儲存資料在介質上的。以Hbase、Cassendra為代表。
和資料庫相關的一個很重要的就是資料庫的索引。有一種說法是:“掌握了索引就等於掌握了資料庫”。暫且不去評判此說法是否真的準確,但索引的確關係著資料庫的讀寫效能。需要對資料庫的索引原理做到足夠的瞭解才能更好的使用各種資料庫。通常來說,Mysql、Oracle、Mongodb這些都是使用的B樹作為索引,是考慮到傳統硬碟的特點後兼顧了讀寫效能以及範圍尋找需求的選擇,而Hbase用得LSM則是為了提高寫效能對讀效能做了犧牲。 搜尋引擎
搜尋引擎也是後端應用中一個很關鍵的組件,尤其是對內容類別、電商類的應用,通過關鍵詞、關鍵字搜尋內容、商品是一個很常見的使用者情境。比較成熟的開源搜尋引擎有Solr和Elasticsearch,很多中小型互連網公司搜尋引擎都是基於這兩個開源系統搭建的。它們都是基於Lucence來實現的,不同之處主要在於termIndex的儲存、分布式架構的支援等等。
對於搜尋引擎的使用,從系統熟悉、服務搭建、功能定製,需要花費較長時間。在這個過程中,需要注意以下問題: 搜尋引擎與公司現有資料系統的整合。現有的持久化、供搜尋的資料的載體是什麼, 如何讓搜尋引擎在全量和增量建索引過程中無縫整合原來的資料載體,才能發揮搜尋引擎自身的即時性, 水平擴充性(效能與容量和機器數量成正比)等優勢。 和資料庫一樣,對搜尋引擎的索引機制也需要做到深入的瞭解。
更為詳細的對於搜尋引擎的工程化實踐可以參考有贊工程師的這篇文章:有贊搜尋引擎實踐(工程篇)
另外,搜尋引擎還可以用在資料的多維分析上,就是GrowingIO、MixPanel中的可以任意維度查詢資料報表的功能。當然,druid也許是一個更好的實現多維分析的方案,官方也有其與es的比較:http://druid.io/docs/latest/comparisons/druid-vs-elasticsearch.html。 訊息佇列
軟體的組織圖,從開始的面向組件到SOA、SAAS是一個逐漸演變的過程。而到了今天微服務盛行的時代,你都不好意思說自己的系統只是單一的一個系統而沒有解耦成一個個service。當然,小的系統的確沒有拆分的必要性,但一個複雜的系統拆成一個個service做微服務架構確實是不得不做的事情。
那麼問題就來了,service之間的通訊如何來做呢。使用什麼協議。通過什麼方式調用。都是需要考慮的問題。
先拋開協議不談,service之間的調用方式可以分為同步調用以及非同步呼叫。同步調用的方式無需多說,那麼非同步呼叫是怎麼進行的呢。一種很常見的方式就是使用訊息佇列,調用方把請求放到隊列中即可返回,然後等待服務提供者去隊列中去擷取請求進行處理,然後把結果返回給調用方即可(可以通過回調)。
非同步呼叫就是訊息中介軟體一個非常常見的應用情境。此外,訊息佇列的應用情境還有以下: 解耦:一個事務,只關心核心的流程,需要依賴其他系統但不那麼重要的事情,有通知即可,無須等待結果。 最終一致性:指的是兩個系統的狀態保持一致,要麼都成功,要麼都失敗,可以有一定的延遲,只要最終達到一致性即可。 廣播:這是訊息佇列最基本的功能。生產者只需要發布訊息,無須關心有哪些訂閱者來消費訊息。 錯峰與流控:當上下遊系統處理能力不同的時候就需要類似訊息佇列的方式做為緩衝區來隔開兩個系統。
目前主流的訊息佇列軟體,主要有以下幾種: ActiveMQ:Java中最為簡單的訊息佇列,是對JMS的實現,沒有規定訊息的順序、安全、重發等特性。 RabbitMQ:是對AMQP協議的實現,對於訊息的順序性、安全、重發等都做了很好的支援。比較適合不允許資料丟失、有事務需求的業務情境下的訊息傳輸。 Kafka:是基於Log的訊息佇列,底層依賴於檔案的順序讀取,是append-only的。適合對資料丟失不敏感、強調效能的一些海量日誌傳輸情境中。是最近幾年大資料領域很火的一個技術。 ZeroMQ:是一個網路編程的Pattern庫,將常見的網路請求形式(分組管理,連結管理,發布訂閱等)模式化、組件化,簡而言之socket之上、MQ之下。對於MQ來說,網路傳輸只是它的一部分,更多需要處理的是訊息儲存、路由、Broker服務發現和尋找、事務、消費模式(ack、重投等)、叢集服務等。 檔案儲存體
不管是業務應用、依賴的後端服務還是其他的各種服務,最終還是要依賴於底層檔案儲存體的。通常來說,檔案儲存體需要滿足的特性有:可靠性、容災性、穩定性,即要保證儲存的資料不會輕易丟失,即使發生故障也能夠有復原方案,也要保證高可用率。在底層可以採用傳統的RAID作為解決方案,再上一層,目前hadoop的hdfs則是最為普遍的分布式檔案儲存體方案,當然還有NFS、Samba這種共用檔案系統也提供了簡單的分布式儲存的特性。
此外,如果檔案儲存體確實成為了應用的瓶頸或者必須提高檔案儲存體的效能從而提升整個系統的效能時,那麼最為直接和簡單的做法就是拋棄傳統機械硬碟,用SSD硬碟替代。像現在很多公司在解決業務效能問題的時候,最終的關鍵點往往就是SSD。這也是用錢換取時間和人力成本最直接和最有效方式。在資料庫部分描述的SSDB就是對LevelDB封裝之後,利用SSDB的特性的一種高效能KV資料庫。
至於HDFS,如果要使用上面的資料,是需要通過hadoop的。類似xx on yarn的一些技術就是將非hadoop技術跑在hdfs上的解決方案(當然也是為了使用MR)。 統一認證中心
統一認證中心,主要是對app使用者、內部使用者、app等的認證服務,包括 使用者的註冊、登入驗證、token鑒權 內部資訊系統使用者的管理和登入鑒權 App的管理,包括app的secret產生,app資訊的驗證(如驗證介面簽名)等。
之所以需要統一認證中心,就是為了能夠集中對這些所有app都會用到的資訊進行管理,也給所有應用提供統一的認證服務。尤其是在有很多業務需要共用使用者資料的時候,構建一個統一認證中心是非常必要的。此外,通過統一認證中心構建移動app的單點登入也是水到渠成的事情(模仿web的機制,將認證後的資訊加密儲存到本地磁碟中供多個app使用)。 單點登入系統
目前很多大的線上web網站都是有單點登入系統的,通俗的來說就是只需要一次使用者登入,就能夠進入多個業務應用(許可權可以不相同),非常方便使用者的操作。而在移動互連網公司中,內部的各種管理、資訊系統同樣也需要單點登入系統。目前,比較成熟的、用的最多的單點登入系統應該是耶魯大學開源的CAS, 可以基於https://github.com/apereo/cas/tree/master/cas-server-webapp來定製開發的。此外,國人開源的kisso的這個也不錯。基本上,單點登入的原理都類似下圖所示:
統一配置中心
在Java後端應用中,一種讀寫配置比較通用的方式就是將設定檔寫在propeties、yaml、HCON檔案中,修改的時候只需要更新檔案重新部署即可,可以做到不牽扯代碼層面改動的目的。統一配置中心,則是基於這種方式之上的統一對所有業務或者基礎後端服務的相關設定檔進行管理的統一服務, 具有以下特性: 能夠線上動態修改設定檔並生效 設定檔可以區分環境(開發、測試、生產等) 使用方便: 在java中可以通過註解、xml配置的方式引入相關配置
disconf是可以在生產環境使用的一個方案,也可能根據自己的需求開發自己的配置中心(可以選擇zookeeper作為配置儲存)。 服務治理架構
對於外部API調用或者用戶端對後端api的訪問,可以使用http協議或者說restful(當然也可以直接通過最原始的socket來調用)。但對於內部服務間的調用,一般都是通過RPC機制來調用的。目前主流的RPC協議有: RMI Hessian Thrift Dubbo
這些RPC協議各有優劣點,需要針對業務需求做出相應的最好的選擇。
這樣,當你的系統服務在逐漸增多,RPC調用鏈越來越複雜,很多情況下,需要不停的更新文檔來維護這些調用關係。一個對這些服務進行管理的架構可以大大節省因此帶來的繁瑣的人力工作。
傳統的ESB(企業服務匯流排)本質就是一個服務治理方案,但esb作為一種proxy的角色存在於client和server之間,所有請求都需要經過esb,使得esb很容易成為效能瓶頸。因此,基於傳統的esb,更好的一種設計如下圖所示:
如圖,以配置中心為樞紐,調用關係只存在於client和提供服務的server之間,就避免了傳統esb的效能瓶頸問題。對於這種設計,esb應該支援的特性如下: 服務提供者的註冊、管理 服務消費者的註冊、管理 服務的版本管理、負載平衡、流量控制、服務降級等 服務的容錯、熔斷等
阿里開源的dubbo則對以上做了很好的實現,也是目前很多公司都在使用的方案。但由於某些原因,dubbo現已不再維護,推薦大家使用噹噹後來維護的