奶奶從路邊撿來一只小雞。它總是喳喳的叫,但是周末我坐在那里的時候,它就會蹲在我腳上,很安詳的樣子。然后小鮮肉就過來說:麻麻,我好想嚇唬它。小雞被嚇的到處亂串。我說了挺多話沒法讓小鮮肉停下來。我就嘆了口氣在那里看着。一個5歲的孩子本來就不具備換位思考的能力。還有一個問題,我其實根本不知道小雞是怎樣想的。只是覺得它趴在我腳上的時候很安心很安靜。晚上下班我會把手伸到籠子里陪它待一會兒,因為我想它是很想自己的媽媽的。
本文首發於靜兒1986的博客,原文地址是http://www.cnblogs.com/xiexj/p/6874654.html。
先說說我們部門的業務。我們部門掌管樂視網所有的視頻音頻等最核心數據的信息。所有子業務都圍繞着這個核心數據。最籠統的分可以分為兩塊:讀和寫。
讀數據除了直接依賴DB的內部操作之外,就是直接通過http請求的RPC調用。因為調用這個接口的業務方非常多,他們用的語言和技術非常雜,http有更好的通用性和便於運營人員等不懂技術的人排查問題,只能內網訪問,也很安全。這是部門並發量最大的服務。單台機器QPS一般在2k多,低谷時在1k多,高峰時在3k多。線上11台接口服務器同時工作。但是讀數據為了提高並發量,將全量的視頻和專輯數據存在了memcached緩存里。所以一旦其他業務方更新了數據會直接發送MQ消息給讀數據服務,讀數據服務更新了緩存后要給業務方回復消息。
讀數據還有一種被叫做離線數據推送。這是由於像搜索部門這樣的,需要所有視頻相關的全量數據,不合適通過接口訪問。之前用的是他們直接依賴我們的數據庫鏡像。但是這樣我們這邊要進行數據庫改造,分庫分表或者其他的表結構修改,就要通知各個依賴業務方。所以我們就改用定時全量數據推送給他們和MQ實時數據發消息的方式進行解耦。
還有一個面向所有部門同事的服務,就是統一異常平台。部門的所有子業務的異常都會異步發送到統一的Redis中。可以在一個公共的平台統一查看。對於特定業務的,會給特定的同事發出告警郵件。
寫數據的業務方非常多,我們部門也有。其他部門也有。寫數據就是創建,修改和刪除。寫數據QPS很低。
創建時不僅要走我們這邊,還要調用雲存儲,雲轉碼等其他部門的接口。具體流程可參考之前的一篇文章:《一個請求過來都經過了什么》。
修改是走我們部門內部系統或者調用我們的API。
刪除只能走我們部門內部系統,刪除接口目前不向其他業務部門開放。
我自己目前主要做的就是讀數據的部分。離線數據算是我開發的,其他人是拷貝的我的。目前正在進行RPC接口的改造。寫我也做過。PGC項目是專業用戶進行視頻上傳的,當年是我做的。這是一個平台。我做過數據接入。就是我們購買一些版權的公司將他們的視頻的介質和媒體信息直接ftp放到我們服務器上,或者給我們一種調用方式我們自己去取,這種屬於后台服務。我也維護過我們的視頻后台系統。是我去美國做數據接入的時候,美國運營同事總是過來找我說我們系統的問題,由於時差,我只能自己動手改了。
后台服務可以自己想怎么寫怎么寫。其他的,不管是讀接口,PGC還是后台系統都是采用的SOA架構將業務垂直分解為前端(對於平台來說就是和界面打交道的,對於讀接口來說就是業務方調用的),使用Dubbo來調用數據服務。基於這種設計,一個子業務的代碼基本模塊都分為API模塊,WEB模塊(讀接口無此模塊),common模塊(一些公用工具或者公用POJO), client模塊(dubbo服務的接口),service服務(dubbo服務的實現,即服務提供者)。
簡單介紹一下Dubbo:Dubbo是一種透明化的遠程方法調用,就像調用本地方法一樣調用遠程方法,只需簡單配置,沒有任何API侵入。實現了軟負載均衡及容錯機制,服務自動注冊與發現,能夠平滑添加或刪除服務提供者。
下面是今天的重點,吐槽一下我們目前讀接口的架構不合理(當然不合理是很正常的,這是一個為期三年的古董),我們新方案已經設計好了。
讀接口業務簡單,並發支持需求大。如果說采用Dubbo層是為了與業務分離,提高服務的復用率,提高數據的統一性。目前一個子業務就自己弄一套Dubbo。有API和WEB兩個對前端業務的也將就起到作用了。讀接口就是一個api調服務,有啥復用的啊。Dubbo的讀接口采用默認的阻塞模式,高並發的情況下Dubbo就會成為性能瓶頸。
業務方可以進行數據的更新,他們更新DB后還要維護我們的緩存。我們的緩存采用的樂視統一的CouchBase集群。CouchBase是Memcached的升級版。既然是走Memcached協議,那么就可以使用Moxi代理來提高性能。
簡單介紹一下Moxi:它基於memcached開發,對於並發的gets請求,做合並來減少和memcahed server的交互。對熱點訪問的cache會在本地緩存,減少network hops。network hops就是網絡路由跳數,距離目的網絡所經過的路由器數目。對於某種類型的key,可以異步set。可配置超時時間,也有錯誤重試機制。
各個業務線反饋說Memcached不好用,有性能問題。好多業務線自己換成了Redis,解決了好多神奇問題。撇開這些數據不談,給你一個用Redis而不用Memcached理由:Memcached的各種強化和代理Memcached自己都可以做,但是沒有做。Redis卻在不斷的更新和優化。
還有一個專門維護這個緩存數據一致性的服務。我個人覺得我們系統過分的強調了一致性,卻犧牲了性能。下面是一些基本的理論:
分布式領域CAP理論:
- Consistency(一致性),數據一致更新,所有數據變動都是同步的。
- Availability(可用性),好的響應性能
- Partition tolerance(分區容錯性)可靠性。
定理:任何分布式系統只可同時滿足兩點,沒法三者兼容。
忠告:架構師不要將精力浪費在如何設計能滿足三者的完美分布式系統,而是應該進行取舍。
一致性模型:
- 強一致性
- 單調一致性
- 會話一致性
- 弱一致性
- 最終一致性
BASE理論(CAP理論的延伸):
- 基本可用(Basically Available):允許損失部分可用性,保證核心可用
- 軟狀態(Soft State):分布式存儲中一般一份數據至少會有三個副本,允許不同節點間副本同步的延時就是軟狀態的提現。mysql replication的異步賦值也是一種體現。
- 最終一致性(Eventual Consistency):
核心思想:即使無法做到強一致性,但是應用可以采用合適的方式達到最終一致性。
像我們常用的分布式存儲:mysql的主從讀寫分離,redis緩存的master-slave形式的主從復制都是基於操作記錄的,都會有時延,也就是保證最終一致性而已。
題外話:基本的理論和名詞概念是很實用的。比如在之前公司做搜索引擎的時候,搜索引擎需要分詞,分詞的分詞組件叫Tokenizer。這個不是搜索引擎特有的概念。Java的rt.jar這個最基礎類庫里有StringTokenizer和StreamTokenizer。如果你了解了這個,就很容易理解理解搜索引擎的分詞是干什么用的。
再說我們項目中過分強調數據一致性而損失性能和高可用的事情。完全可以不對一致性不那么精益求精。這並不是說不追求完美。追求完美可以提現在哪些方面呢?
之前做的一個信用名片的項目,有的用戶在某星手機的某些版本中上傳頭像,上傳之后頭是倒着的。經調查發現我們拍的照片在手機上可以各個角度拍攝,憑借着EXIF信息,這是一種專門為數碼相機設置的格式。里面有拍攝時翻轉手機的信息,可以有8種角度。很多操作系統和圖片查看軟件都會自動按照信息里的翻轉角度,將圖片翻轉回來。但是某星的某些版本沒有這個功能。針對這個問題,開始的時候,我用了一個國外的元數據萃取的工具包將這個EXIF信息的翻轉信息提取出來。效果已經達到了。但是作為代碼手藝人,覺得只是這么個小需要就添加了一個1MB多的jar包,而且實際上它的實現是解析構建DOM樹的方式,意味着為了提取翻轉信息,要解析構造整個文件對象。所以我自己又采用了直接讀取圖片二進制流的方式,取圖片前面如果以0xFFD8開頭的就是包含EXIF信息的,否則不予處理,讀取文件結束。以0xFFD8開頭的要判斷是否包含旋轉信息,包含旋轉信息的判斷是Intel標准還是Motorola標准,因為這兩個標准高字節和低字節代表的含義正好相反。就這樣一直到讀到旋轉信息或者判斷出不存在旋轉信息,文件關閉。所以最后讀入的文件數據很少,效率大大提高。
在之前公司用Solr搜索引擎的時候,有個需求是過濾輸入的html標簽。但是在Solr中對索引讀入后的第一個操作就是分詞,使用Solr自帶的或者外部的分詞器。然后再對分好的詞進行更細節的過濾或者近義詞之類的。但是這第一步就直接破壞了文檔的結構,變成了一個個單詞,而不是html文檔形式。再去除,可以去除,一個個過濾符號和單詞是否是html標簽,判斷前后都是啥。能做,第一,麻煩,最重要的是效率低。所以我當時的做法是直接修改了自己用IK分詞器的源碼,讀入數據第一個操作先過濾標簽。這就很好辦了,apache有現成的工具類。這樣避免了讀入讀出帶來的性能損耗。
說了這么多,架構到底是一個什么概念。我個人理解,在一個組織和系統內可以有很多維度的架構。
比如業務架構是我們老大要考慮的事情。他心目中有一個自己的業務架構定位,外面提了一個需求,他有自己的規划,這個是不是應該納入我們的業務體系。
比如系統架構是管理者和架構師一起進行考慮的事情。業務定好了,那么用怎樣的模塊去划分這個業務更利於維護,更高效。
比如技術架構是架構師的最重要責任。真正從技術角度去展示一個系統,包括硬件的,軟件的,抽象的,具體的。它包括網絡,服務器,第三方產品使用和調用,軟件架構和數據架構。
比如軟件架構是開發人員可以接觸到的軟件開始的部分。比如一個web系統從nginx反向代理開始到一個SOA架構的系統:包括resin服務,resin服務上有一個mvc架構的程序,調用另一個服務的系統,系統又和緩存,數據庫,消息隊列,第三方調用打交道。
比如數據架構,這個之所以單提出來是因為數據是服務的核心。比如對一個WEB應用來說,有一些數據是直接可以放到客戶端的,如JS,靜態頁面。還有一些數據是要放到服務端的。服務端的數據又要考慮是放在緩存還是數據庫還是文件等等。放在什么地方要綜合考慮效率,帶寬,安全,數據有效性和可靠性。