官术网_书友最值得收藏!

1.3 服務化時期

1.3.1 應用到服務

重復建設在軟件工程思想中向來就是不可取的,工程師們設想將共同的功能組件抽象出來,形成獨立的“應用”,不過這里的關鍵問題就是這些“應用”沒有直觀上的入口,因此用傳統(tǒng)的思想來定義并不妥。為此工程師們想出了“服務(Service)”一詞來描述這種功能性的組件,它們?yōu)閼锰峁┩ㄓ霉δ苄缘闹危胺铡庇诰唧w應用。

如今“服務”這個詞在軟件工程中已經(jīng)泛化,可以說,凡是向其他組件提供“支撐”功能的系統(tǒng)都可以稱為服務,甚至還出現(xiàn)“軟件即服務”(SaaS)這樣的概念。在這種概念下,軟件不再是整體打包一次性賣給消費者,而是消費者在使用時按需獲取,而且服務還會不定期升級,消費者無須感知,并可以按使用時長或者資源的消耗率來計算費用。

“服務”思想其實就是把其他應用當成了消費者,為其提供特定功能。上述應用架構中的“登錄、采集用戶基本信息”等功能可以抽象成一個名為“用戶中心”的服務組件。以此類推,還可以繼續(xù)抽象出“消息中心”“索引搜索”“文件存儲”“緩存系統(tǒng)”等通用服務組件,總的原則就是盡量提取公共的部分以避免重復的開發(fā)工作。

之后,功能的開發(fā)也即服務的開發(fā)了。

1.3.2 遠程調(diào)用

服務拆分后,就像天上的繁星一樣分散到不同網(wǎng)絡的各角落了,那么現(xiàn)在的問題是如何互相訪問呢?前面說過了,服務與應用不同,沒有可視化的頁面可以訪問,而且調(diào)用者是計算機程序而不是人類用戶,工程師們需要一個程序之間的通信協(xié)議,就好像下面的對話一樣明了方便。

“我需要用戶A的記錄,請給我它的結構及數(shù)據(jù)”

“好,這是A的記錄的結果及數(shù)據(jù),請拿好”

“遠程過程調(diào)用(Remote Procedure Call,簡稱RPC)”協(xié)議應運而生,其作用就是讓應用(服務)之間的程序調(diào)用變得像本地一樣簡單,這種技術完全屏蔽了各種網(wǎng)絡拓撲的復雜性,只要知道對方的“地址”就可以發(fā)起調(diào)用。

RPC在Java生態(tài)中,最早出現(xiàn)在1.1版本中的RMI(Remote Method Invocation)中。Oracle后來又提出的EJB那一套,基于JAX-RPC接口——JAX-RPC基于SOAP(簡單對象訪問協(xié)議),調(diào)用地址都是由“類目服務器(Name Server)”來注冊管理的。

在這種方案下,訪問某個遠程服務需通過以下類似代碼:

    1:    public  class  HelloClient  {
    2:         public  static  void  main(String  args[]){
    3:              try  {
    4:               //在RMI服務注冊表中查找名稱為RHello的對象,并調(diào)用其上的方法
    5:                   IHello  rhello  =(IHello)Naming.lookup(〝rmi://localhost:8888/RHello〝);
    6:                   System.out.println(rhello.helloWorld());
    7:               System.out.println(rhello.sayHelloToSomeBody(〝熔巖〝));
    8:              }  catch(NotBoundException  e){
    9:                   e.printStackTrace();
    10:             }  catch(MalformedURLException  e){
    11:                  e.printStackTrace();
    12:             }  catch(RemoteException  e){
    13:                  e.printStackTrace();
    14:             }
    15:       }
    16:  }

可以看到上述代碼中有一個URI,這就是該服務的地址,消費端通過它可以發(fā)起訪問以獲得數(shù)據(jù)。

1.3.3 虛擬IP地址

可不要認為RPC就只是調(diào)用。這里有個前提,就是服務端得把自己的地址注冊上去,但是在集群環(huán)境下有多臺機器,如何處理呢?這些地址URI又怎么告訴消費方呢?另外,當服務不可用時,誰又去把這些注冊信息拿掉呢?

這里工程師們想到的解決方案就是前面提到的負載均衡設備概念中的“虛擬IP地址(簡稱VIP)”,只需要在注冊的時候填寫VIP,那么在調(diào)用的時候,請求經(jīng)過負載均衡設備,其就會自動地將流量均分到下屬已經(jīng)注冊的機器中,并且在應用上下線的時候,會自動對下發(fā)的機器列表進行調(diào)整。

在增加了RPC功能后,工程師們終于可以將公共的服務單獨部署,現(xiàn)在整個架構如圖1.5所示。

圖1.5 “服務化后的部署架構”

1.3.4 復雜的調(diào)用關系

歷史的車輪繼續(xù)向前,業(yè)務量再一次發(fā)生了巨大的增長,請求不斷涌進來,新的應用不斷增加,這使得公共服務也變得越來越多,整個調(diào)用網(wǎng)由最開始的十幾條線變成成百上千條甚至上萬條,甚至到了后期,已經(jīng)沒人能弄清楚到底有多少條調(diào)用關系了。

圖1.6就是某企業(yè)的全局調(diào)用關系,可以看到,這里面的調(diào)用鏈錯綜復雜。

圖1.6 “某公司的服務調(diào)用關系”

1.3.5 服務治理

復雜的服務訂閱關系必須要很好地維護起來,否則在出現(xiàn)問題的時候可能完全不知從何入手,所以這個時候,一種叫“服務治理(Service Governance)”的軟負載產(chǎn)品出現(xiàn)了。它的基礎工作跟注冊中心一樣是維護一個訂閱關系,不過在訂閱關系的數(shù)據(jù)之上,還提供了更多高級功能,比如:根據(jù)負載情況選擇最優(yōu)服務節(jié)點、多版本鏈路支持多版本鏈路指同一個服務可以有多個版本存在,不同版本之間調(diào)用不互串,可以共存,而版本的選擇通常是根據(jù)請求中附帶的版本信息決定的。、訂閱關系鑒權限流降級以及灰度發(fā)布等。

經(jīng)典的RPC框架Dubbo就有一個專門的服務治理子系統(tǒng),其底層采用ZooKeeper作為數(shù)據(jù)存儲,如今經(jīng)常提到的Spring Cloud則是采用名為Eureka的自研產(chǎn)品。

Dubbo的“服務治理”采用的是ZooKeeper,由于其自身數(shù)據(jù)結構的設計,其服務關系存儲采用的是樹狀結構,如圖1.7所示:

圖1.7 “Dubbo在ZooKeeper中的數(shù)據(jù)存儲結構”

這種存儲結構的優(yōu)點在于可以很容易地監(jiān)聽數(shù)據(jù)變化,例如需要監(jiān)聽“服務A”對應的“提供者”變化情況時,只需要將監(jiān)聽點掛載到服務A的“提供者”父節(jié)點即可。同理,如果需要同時監(jiān)聽“提供者”與“消費者”,只需要將監(jiān)聽掛載到“服務”節(jié)點即可。然而不足也是很明顯的,例如需要進行關聯(lián)查詢時,樹狀的層次結構就不那么方便了。比如需要查詢機器M1訂閱的所有服務,這種情況下只能逐一遍歷,其時間復雜度為On),其中n為服務數(shù)量,并不高效,再加上ZooKeeper強一致ZooKeeper采用的一致性協(xié)議ZAB源于Paxos,其要求多數(shù)寫入成功后,寫入請求才算成功,并且只能由主節(jié)點寫入。的設計,從性能與功能上來講,我個人認為都不適合作為“服務發(fā)現(xiàn)”來使用。

現(xiàn)今,工程師們普遍傾向使用etcd來實現(xiàn)“注冊中心”,其特點是采用KV存儲,對于結構性查詢,更加輕量、易運維,優(yōu)勢更佳。

訂閱關系可不僅在RPC中,任何鏈路,只要存在服務一說,都會涉及這個概念,比如著名的開源消息框架RocketMQ,也會使用一個目錄服務器(官方名稱是nameserver)來維護Topic與Broker之間的映射關系。

服務治理維護的數(shù)據(jù),從根本上說就是“提供者”與“消費者”之前的映射關系,例如有3臺機器在提供服務A,則標記為Aprovider= {M1, M2, M3},同時有2個消費者在調(diào)用A的服務,則記為Asubscriber= {N1, N2},可以看見這兩條數(shù)據(jù)均為矢量數(shù)據(jù),因此如何較好地結構化存儲,是鏈路關系維護的關鍵。

當然可以嘗試將其存儲在數(shù)據(jù)庫中,但這里的問題是,訂閱關系通常是實時變化的,而且需要通知功能。比如提供A服務的M1這臺機器宕機,消費者Asubscriber需要監(jiān)聽到這種變化,否則會將請求路由到一個已經(jīng)宕機或者下線的服務上,導致錯誤的請求。更復雜的情況是,可能一個關系變化會連鎖式地影響到其他鏈路。

例如B服務其實就是A服務的一個消費者N3,而B服務本身又有消費者,那么當M1出現(xiàn)故障時,Aprovider變成{M2, M3},這時B的流量入口從之前的三臺下降到了兩臺,B服務的鏈路能力是有下降的,即整條鏈路的吞吐量都受到了影響。如何及時、直觀地反映問題,到如今也是個棘手的問題,這點更是彈性調(diào)度不可或缺的基礎。

可以說軟負載中的“服務治理”發(fā)展到今天已經(jīng)遠不只“服務注冊”那么簡單了。

1.3.6 旁路負載

由于“服務治理”系統(tǒng)的存在,工程師們再也不用為應用之間復雜的調(diào)用關系而擔驚受怕了,分布式得以空前發(fā)展。

因為“服務治理”通常都提供健康檢測功能,已經(jīng)能夠很好地維護服務對應的機器列表,所以之前講到的VIP(虛擬IP地址)便變得不那么必需了。這個時候工程師們想出了一種“旁路負載”(亦稱透明負載)的軟負載架構,它的特點是并不直接通過代理流量來分發(fā)負載流量,而是在RPC客戶端中直接埋入負載及其他鏈路邏輯,這樣就省下了網(wǎng)關代理這層,但是由于對外需要統(tǒng)一暴露接口,因此對外的網(wǎng)關仍然需要保留。

當然,對外的網(wǎng)關已經(jīng)不是簡單的流量負載均衡,像“接口版本管理、攻擊防御、請求重定向”等功能也被加入了,這個時候它有了一個更貼切的名字,叫“API網(wǎng)關”。像讀者熟知的Spring Cloud框架,其Zuul組件充當?shù)谋闶恰癆PI網(wǎng)關”的角色。

最終系統(tǒng)架構便變成了圖1.8的樣子:

圖1.8 “采取旁路負載后的架構”

如此一來,系統(tǒng)內(nèi)部的訪問阻礙已經(jīng)減少到很少了,同時隨著Docker容器化及DevOpsDevOps講求的是開發(fā)運維一體化,通過輔助工具及自動化工程,簡化運維上的開銷,讓開發(fā)人員都有能力安全地運維自己開發(fā)的應用。思想的迅速崛起,服務的粒度可以拆分得比以前更細,讓需求開發(fā)更加獨立、互不影響,這就是之后出現(xiàn)的微服務一說。

主站蜘蛛池模板: 唐河县| 汾阳市| 卓资县| 垣曲县| 县级市| 常德市| 荆门市| 淮安市| 临汾市| 新乐市| 天等县| 南宫市| 衡阳市| 黑山县| 泰顺县| 伽师县| 平邑县| 凤翔县| 巢湖市| 双桥区| 紫云| 新宁县| 津市市| 芦山县| 丁青县| SHOW| 栾城县| 繁峙县| 阿图什市| 留坝县| 高雄县| 武强县| 黔江区| 三明市| 宿州市| 彩票| 凤城市| 永寿县| 溆浦县| 密山市| 虞城县|