本文原載於 SegmentFault 專欄 PHP 學習總結
作者:Jochen
整理編輯:SegmentFault
近些年微服務架構大行其道,趁著最近有時間,來搗鼓搗鼓微服務是怎麼一回事。
微服務架構
微服務的概念由 [Martin Fowler](https://martinfowler.com/) 於 2014 年 3 月提出:
微服務架構是一種架構模式,它提倡將單一應用程式劃分成一組小的服務,服務之間相互協調、互相配合,為用戶提供最終價值。每個服務運行在其獨立的進程中,服務和服務之間採用輕量級的通信機制相互溝通。每個服務都圍繞著具體的業務進行構建,並且能夠被獨立的部署到生產環境、類生產環境等。另外,應儘量避免統一的、集中的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言、工具對其進行構建。
下圖是一個電商系統的微服務架構圖:
微服務架構與單體應用相比,具有以下優點:
每個服務都比較簡單,只關注於一個業務功能;
微服務架構方式是鬆耦合的,每個服務可以獨立測試、部署、升級、發布;
每個微服務可由不同團隊獨立開發,可以各自選擇最佳及最合適的不同的程式語言與工具;
每個服務可以根據需要進行水平擴展,提高系統並發能力。
沒有銀彈,微服務架構在帶來諸多優點的同時,也會有如下缺點:
微服務架構提高了系統的複雜度,增加了運維開銷及成本。如單體應用可能只需部署至一小片應用服務集群,而微服務架構可能變成需要構建/測試/部署/運行數十個獨立的服務,並可能需要支持多種語言和環境;
作為一種分布式系統,微服務架構引入了其他若干問題,例如消息序列化、網絡延遲、異步機制、容錯處理、服務雪崩等;
服務管理的複雜性,如服務的註冊、發現、降級、熔斷等問題;
服務與服務之間存在相互調用的情況,為排查系統故障帶來巨大挑戰。
可以說,正是傳統應用架構的系統變得日益臃腫,面臨難以維護、擴展的問題,同時容器化技術(Docker)的蓬勃發展和 [DevOps](https://zh.wikipedia.org/wiki/DevOps) 思想的日漸成熟,催生了新的架構設計風格 – 微服務架構的出現。
RPC 架構
微服務架構中的各個服務通常不在同一個機器上,甚至不會在同一個網絡環境裡,因此微服務之間如何調用是一個亟待解決的問題,我們通常使用 RPC 協議來解決:
RPC(Remote Procedure Call),即遠程過程調用,是一個計算機通信協議。該協議允許運行於一臺計算機的程序調用另一臺計算機的子程序,而程式設計師無需額外地為這個交互作用編程。——維基百科
實現了 RPC 協議的框架,可以讓服務方和調用方屏蔽各種底層細節,讓調用方像調用本地函數一樣調用遠端的函數(服務)。RPC 框架一般為服務端和客戶端提供了序列化、反序列化、連接池管理、負載均衡、故障轉移、隊列管理、超時管理、異步管理等職能。在網上找到一個說明 RPC 框架工作原理圖:
目前,根據序列化數據時採用的技術的不同,可分為 `JSON-RPC` 和 `gRPC` 兩種:
[JSON-RPC](https://www.jsonrpc.org/) 是一種基於 JSON 格式的輕量級的 RPC 協議標準,可基於 HTTP 協議來傳輸,或直接基於 TCP 協議來傳輸。`JSON-RPC` 優點是易於使用和閱讀。
[gRPC](https://www.grpc.io/) 是一個高性能、通用的開源 RPC 框架,其由 Google 主要面向移動應用開發並基於[ HTTP/2 ](https://http2.github.io/)協議標準而設計,基於[ ProtoBuf ](http://en.wikipedia.org/wiki/Protocol_Buffers)(Protocol Buffers) 序列化協議開發,且支持眾多開發語言。`gRPC` 具有低延遲、高效率、高擴展性、支持分布式等優點。
Consul
現在有了 RPC 框架,我們就可以只考慮服務與服務之間的業務調用而不用考慮底層傳輸細節。此時,如果服務 A 想調用服務 B 時,我們可以在服務 A 中配置服務 B 的 IP 地址和埠,然後剩下的傳輸細節就交給 RPC 框架。
這在微服務規模很小的情況下是沒有問題的,但是在服務規模很大、而且每個服務不止部署一個實例的情況下會面臨巨大挑戰。比如,服務 B 部署了三個實例,這時候服務 A 想調用服務 B 該請求哪個實例的 IP ?
假如服務 B 部署的三個實例有兩個都掛掉了,服務 A 可能會依舊去請求掛掉的實例,服務將不可用。將 IP 地址和埠寫成配置文件顯得很不靈活,微服務架構往往要保證高可用及動態伸縮。
因此,我們需要一個服務註冊與服務發現的工具,能夠動態地變更服務信息,並且找到可用的服務的 IP 地址和埠。
目前市面上服務發現的工具有很多,如:
[Consul](https://www.consul.io/)
[ZooKeeper](https://zookeeper.apache.org/)
[Etcd](https://etcd.io/)
[Doozerd](https://github.com/ha/doozerd)
本文主要以 Consul 軟體為例。
Consul 是一個支持多數據中心、分布式高可用的服務發現和配置共享的服務軟體,由 HashiCorp 公司用 Go 語言開發, 基於 Mozilla Public License 2.0 的協議進行開源。Consul 支持健康檢查,並允許 HTTP 、gRPC 和 DNS 協議調用 API 存儲鍵值對。
下面是引入服務註冊與服務發現工具後的架構圖:
在這個架構中:
首先 S-B 的實例啟動後將自身的服務信息(主要是服務所在的 IP 地址和埠號)註冊到 Consul 中。
Consul 會對所有註冊的服務做健康檢查,以此來確定哪些服務實例可用哪些不可用。
S-A 啟動後就可以通過訪問 Consul 來獲取到所有健康的 S-B 實例的 IP 和埠,並將這些信息放入自己的內存中,S-A 就可用通過這些信息來調用 S-B。
S-A 可以通過監聽 Consul 來更新存入內存中的 S-B 的服務信息。比如 S-B-1 掛了,健康檢查機制就會將其標為不可用,這樣的信息變動就被 S-A 監聽到了,S-A 就更新自己內存中 S-B-1 的服務信息。
可見, Consul 軟體除了服務註冊和服務發現的功能之外,還提供了健康檢查和狀態變更通知的功能。
Hyperf
對於 Java 開發者來說,有技術相當成熟的微服務框架可供選擇:
[Dubbo](https://dubbo.apache.org/zh-cn/) [Spring Cloud](https://www.springcloud.cc/)
作為一名 PHPer,我用 Google 查了一下「PHP + 微服務」,發現有用的相關內容少之又少 ,沒有什麼實質性的參考價值,無限惆悵。。。
幸好,有大神在基於 Swoole 擴展的基礎上,實現了高性能、高靈活性的 PHP 協程框架 [Hyperf](https://www.hyperf.io/) ,並提供了微服務架構的相關組件。
Hyperf 是基於 `Swoole 4.3+` 實現的高性能、高靈活性的 PHP 協程框架,內置協程伺服器及大量常用的組件,性能較傳統基於 `PHP-FPM` 的框架有質的提升,提供超高性能的同時,也保持著極其靈活的可擴展性,標準組件均基於 [PSR 標準](https://www.php-fig.org/psr) 實現,基於強大的依賴注入設計,保證了絕大部分組件或類都是 `可替換` 與 `可復用` 的。
於是,我在學習了微服務架構相關的基礎知識之後,使用 Hyperf 框架構建了一個基於 PHP 的微服務集群,這是項目源碼地址:
https://github.com/Jochen-z/php-microservice-demo。
該項目使用 Dokcer 搭建,`docker-compose.yml` 代碼如下:
version:"3"services: consul-server-leader:image:consul:latestcontainer_name: consul-server-leadercommand:"agent -server -bootstrap -ui -node=consul-server-leader -client=0.0.0.0"environment: - CONSUL_BIND_INTERFACE=eth0ports: - "8500:8500"networks: - microservice microservice-1:build:context: .container_name:"microservice-1"command:"php bin/hyperf.php start"depends_on: - "consul-server-leader"volumes: - ./www/microservice-1:/var/wwwnetworks: - microservicetty:true microservice-2:build:context: .container_name:"microservice-2"command:"php bin/hyperf.php start"depends_on: - "consul-server-leader"volumes: - ./www/microservice-2:/var/wwwnetworks: - microservicetty:trueapp:build:context: .container_name:"app"command:"php bin/hyperf.php start"depends_on: - "microservice-1"volumes: - ./www/web:/var/wwwports: - "9501:9501"networks: - microservicetty:truenetworks:microservice:driver: bridgevolumes:microservice:driver: local
這裡啟動了一個 Consul 容器 `consul-server-leader` 作為服務註冊和服務發現的組件,容器 `microservice-1` 和 `microservice-2` 分別提供了加法運算和除法運算的服務。
容器 `app` 作為服務調用方,配置了 `consul-server-leader` 容器的 URL,通過訪問 `consul-server-leader` 獲取 `microservice-1` 和 `microservice-2` 服務的 IP 地址和埠,然後 `app` 通過 RPC 協議調用加法運算和除法運算的服務獲取結果並返回給用戶。
`app` 容器為 Web 應用,部署了一個 Hyperf 項目並對外提供 HTTP 服務。例如,在 `App\Controller\IndexController` 控制器裡有 `add` 方法:
publicfunctionadd(AdditionService $addition){ $a = (int)$this->request->input('a', 1); # 接受前端用戶參數 $b = (int)$this->request->input('b', 2);return ['a' => $a,'b' => $b,'add' => $addition->add($a, $b) # RPC調用 ];}
在 `App\JsonRpc\AdditionService` 中 `add` 的實現:
classAdditionServiceextendsAbstractServiceClient{/** * 定義對應服務提供者的服務名稱 * @var string */protected $serviceName = 'AdditionService';/** * 定義對應服務提供者的服務協議 * @var string */protected $protocol = 'jsonrpc-http';publicfunctionadd(int $a, int $b): int{return$this->__request(__FUNCTION__, compact('a', 'b')); }}
繼承了 `AbstractServiceClient` 即可創建一個微服務客戶端請求類,Hyperf 在底層幫我們實現了與 Consul 和服務提供者交互的細節,我們只要 `AdditionService` 類裡的 `add` 方法即可遠程調用 `microservice-1` 和 `microservice-2` 提供的服務。
至此,PHP 微服務集群搭建就完成了!
參考文章:1. [一文詳解微服務架構](https://www.cnblogs.com/skabyy/p/11396571.html)2. [離不開的微服務架構,脫不開的RPC細節](https://mp.weixin.qq.com/s/CepNdL2A_QMcESgQVZBn2g)3. [用 Consul 來做服務註冊與服務發現](https://juejin.im/post/5ca1f1acf265da308d50be02)4. [Hyperf文檔-微服務](https://doc.hyperf.io/#/zh/microservice)