什么是服務注冊發現?
對於搞微服務的同學來說,服務注冊、服務發現的概念應該不會太陌生。
簡單來說,當服務A需要依賴服務B時,我們就需要告訴服務A,哪里可以調用到服務B,這就是服務注冊發現要解決的問題。
Service B
把自己注冊到Service Registry
叫做 服務注冊Service A
從Service Registry
發現Service B
的節點信息叫做 服務發現
服務注冊
服務注冊是針對服務端的,服務啟動后需要注冊,分為幾個部分:
- 啟動注冊
- 定時續期
- 退出撤銷
啟動注冊
當一個服務節點起來之后,需要把自己注冊到 Service Registry
上,便於其它節點來發現自己。注冊需要在服務啟動完成並可以接受請求時才會去注冊自己,並且會設置有效期,防止進程異常退出后依然被訪問。
定時續期
定時續期相當於 keep alive
,定期告訴 Service Registry
自己還在,能夠繼續服務。
退出撤銷
當進程退出時,我們應該主動去撤銷注冊信息,便於調用方及時將請求分發到別的節點。同時,go-zero 通過自適應的負載均衡來保證即使節點退出沒有主動注銷,也能及時摘除該節點。
服務發現
服務發現是針對調用端的,一般分為兩類問題:
- 存量獲取
- 增量偵聽
還有一個常見的工程問題是
- 應對服務發現故障
當服務發現服務(比如 etcd, consul, nacos等)出現問題的時候,我們不要去修改已經獲取到的 endpoints 列表,從而可以更好的確保 etcd 等宕機后所依賴的服務依然可以正常交互。
存量獲取
當 Service A
啟動時,需要從 Service Registry
獲取 Service B
的已有節點列表:Service B1
, Service B2
, Service B3
,然后根據自己的負載均衡算法來選擇合適的節點發送請求。
增量偵聽
上圖已經有了 Service B1
, Service B2
, Service B3
,如果此時又啟動了 Service B4
,那么我們就需要通知 Service A
有個新增的節點。如圖:
應對服務發現故障
對於服務調用方來說,我們都會在內存里緩存一個可用節點列表。不管是使用 etcd
,consul
或者 nacos
等,我們都可能面臨服務發現集群故障,以 etcd
為例,當遇到 etcd
故障時,我們就需要凍結 Service B
的節點信息而不去變更,此時一定不能去清空節點信息,一旦清空就無法獲取了,而此時 Service B
的節點很可能都是正常的,並且 go-zero
會自動隔離和恢復故障節點。
服務注冊、服務發現的基本原理大致如此,當然實現起來還是比較復雜的,接下來我們一起看看 go-zero
里支持哪些服務發現的方式。
go-zero 之內置服務發現
go-zero
默認支持三種服務發現方式:
- 直連
- 基於 etcd 的服務發現
- 基於 kubernetes endpoints 的服務發現
直連
直連是最簡單的方式,當我們的服務足夠簡單時,比如單機即可承載我們的業務,我們可以直接只用這種方式。
在 rpc
的配置文件里直接指定 endpoints
即可,比如:
Rpc:
Endpoints:
- 192.168.0.111:3456
- 192.168.0.112:3456
zrpc
調用端就會分配負載到這兩個節點上,其中一個節點有問題時 zrpc
會自動摘除,等節點恢復時會再次分配負載。
這個方法的缺點是不能動態增加節點,每次新增節點都需要修改調用方配置並重啟。
基於 etcd 的服務發現
當我們的服務有一定規模之后,因為一個服務可能會被很多個服務依賴,我們就需要能夠動態增減節點,而無需修改很多的調用方配置並重啟。
常見的服務發現方案有 etcd
, consul
, nacos
等。
go-zero內置集成了基於 etcd
的服務發現方案,具體使用方法如下:
Rpc:
Etcd:
Hosts:
- 192.168.0.111:2379
- 192.168.0.112:2379
- 192.168.0.113:2379
Key: user.rpc
Hosts
是etcd
集群地址Key
是服務注冊上去的key
基於 Kubernetes Endpoints 的服務發現
如果我們的服務都是部署在 Kubernetes
集群上的話,Kubernetes
本身是通過自帶的 etcd
管理集群狀態的,所有的服務都會把自己的節點信息注冊到 Endpoints
對象,我們可以直接給 deployment
權限去讀取集群的 Endpoints
對象即可獲得節點信息。
Service B
的每個Pod
啟動時,會將自己注冊到集群的Endpoints
里Service A
的每個Pod
啟動時,可以從集群的Endpoints
里獲取Service B
的節點信息- 當
Service B
的節點發生改變時,Service A
可以通過watch
集群的Endpoints
感知到
在這個機制工作之前,我們需要配置好當前 namespace
內 pod
對集群 Endpoints
訪問權限,這里有三個概念:
- ClusterRole
- 定義集群范圍的權限角色,不受
namespace
控制
- 定義集群范圍的權限角色,不受
- ServiceAccount
- 定義
namespace
范圍內的service account
- 定義
- ClusterRoleBinding
- 將定義好的
ClusterRole
和不同namespace
的ServiceAccount
進行綁定
- 將定義好的
具體的 Kubernetes
配置文件可以參考 這里,其中 namespace
按需修改。
注意:當啟動時報沒有權限獲取 Endpoints
時記得檢查這些配置有沒落實 😃
zrpc
的基於 Kubernetes Endpoints
的服務發現使用方法如下:
Rpc:
Target: k8s://mynamespace/myservice:3456
其中:
mynamespace
:被調用的rpc
服務所在的namespace
myservice
:被調用的rpc
服務的名字3456
:被調用的rpc
服務的端口
在創建 deployment
配置文件時一定要加上 serviceAccountName
來指定使用哪個 ServiceAccount
,示例如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: alpine-deployment
labels:
app: alpine
spec:
replicas: 1
selector:
matchLabels:
app: alpine
template:
metadata:
labels:
app: alpine
spec:
serviceAccountName: endpoints-reader
containers:
- name: alpine
image: alpine
command:
- sleep
- infinity
注意其中 serviceAccountName
指定該 deployment
創建出來的 pod
用哪個 ServiceAccount
。
server
和 client
都部署到 Kubernetes
集群里之后可以通過以下命令滾動重啟所有 server
節點
kubectl rollout restart deploy -n adhoc server-deployment
利用如下命令查看 client
節點日志:
kubectl -n adhoc logs -f deploy/client-deployment --all-containers=true
可以看到我們的服務發現機制完美跟進了 server
節點的變化,並且在服務更新期間沒有出現異常請求。
完整代碼示例見 https://github.com/zeromicro/zero-examples/tree/main/discovery/k8s
下一篇文章我將講解在 go-zero
里如何實現基於 consul
, nacos
等的服務注冊發現,敬請期待!
項目地址
https://github.com/tal-tech/go-zero
歡迎使用 go-zero
並 star 支持我們!
微信交流群
關注『微服務實踐』公眾號並點擊 交流群 獲取社區群二維碼。