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

1.3 服務化時期

1.3.1 應用到服務

重復建設在軟件工程思想中向來就是不可取的,工程師們設想將共同的功能組件抽象出來,形成獨立的“應用”,不過這里的關鍵問題就是這些“應用”沒有直觀上的入口,因此用傳統的思想來定義并不妥。為此工程師們想出了“服務(Service)”一詞來描述這種功能性的組件,它們為應用提供通用功能性的支撐,“服務”于具體應用。

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

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

之后,功能的開發也即服務的開發了。

1.3.2 遠程調用

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

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

“好,這是A的記錄的結果及數據,請拿好”

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

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

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

    1:    public  class  HelloClient  {
    2:         public  static  void  main(String  args[]){
    3:              try  {
    4:               //在RMI服務注冊表中查找名稱為RHello的對象,并調用其上的方法
    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,這就是該服務的地址,消費端通過它可以發起訪問以獲得數據。

1.3.3 虛擬IP地址

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

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

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

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

1.3.4 復雜的調用關系

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

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

圖1.6 “某公司的服務調用關系”

1.3.5 服務治理

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

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

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

圖1.7 “Dubbo在ZooKeeper中的數據存儲結構”

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

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

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

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

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

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

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

1.3.6 旁路負載

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

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

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

最終系統架構便變成了圖1.8的樣子:

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

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

主站蜘蛛池模板: 天长市| 五河县| 湾仔区| 清河县| 成安县| 天长市| 宣威市| 长春市| 开鲁县| 信阳市| 吉木乃县| 田东县| 高平市| 甘德县| 石首市| 兰州市| 芮城县| 翁牛特旗| 宁城县| 东港市| 永嘉县| 缙云县| 龙南县| 乃东县| 连云港市| 衡阳市| 长寿区| 资中县| 泽库县| 茶陵县| 塔河县| 苏州市| 赤壁市| 罗源县| 牙克石市| 榕江县| 大足县| 洪洞县| 宁明县| 乐业县| 阿拉善左旗|