什么是服務(wù)注冊發(fā)現(xiàn)中贝?
對于搞微服務(wù)的同學(xué)來說啃奴,服務(wù)注冊、服務(wù)發(fā)現(xiàn)的概念應(yīng)該不會太陌生雄妥。
簡單來說最蕾,當(dāng)服務(wù)A需要依賴服務(wù)B時,我們就需要告訴服務(wù)A老厌,哪里可以調(diào)用到服務(wù)B瘟则,這就是服務(wù)注冊發(fā)現(xiàn)要解決的問題。
-
Service B
把自己注冊到Service Registry
叫做 服務(wù)注冊 -
Service A
從Service Registry
發(fā)現(xiàn)Service B
的節(jié)點信息叫做 服務(wù)發(fā)現(xiàn)
服務(wù)注冊
服務(wù)注冊是針對服務(wù)端的枝秤,服務(wù)啟動后需要注冊醋拧,分為幾個部分:
- 啟動注冊
- 定時續(xù)期
- 退出撤銷
啟動注冊
當(dāng)一個服務(wù)節(jié)點起來之后,需要把自己注冊到 Service Registry
上淀弹,便于其它節(jié)點來發(fā)現(xiàn)自己丹壕。注冊需要在服務(wù)啟動完成并可以接受請求時才會去注冊自己,并且會設(shè)置有效期薇溃,防止進(jìn)程異常退出后依然被訪問菌赖。
定時續(xù)期
定時續(xù)期相當(dāng)于 keep alive
,定期告訴 Service Registry
自己還在沐序,能夠繼續(xù)服務(wù)琉用。
退出撤銷
當(dāng)進(jìn)程退出時堕绩,我們應(yīng)該主動去撤銷注冊信息,便于調(diào)用方及時將請求分發(fā)到別的節(jié)點邑时。同時奴紧,go-zero 通過自適應(yīng)的負(fù)載均衡來保證即使節(jié)點退出沒有主動注銷,也能及時摘除該節(jié)點晶丘。
服務(wù)發(fā)現(xiàn)
服務(wù)發(fā)現(xiàn)是針對調(diào)用端的黍氮,一般分為兩類問題:
- 存量獲取
- 增量偵聽
還有一個常見的工程問題是
- 應(yīng)對服務(wù)發(fā)現(xiàn)故障
當(dāng)服務(wù)發(fā)現(xiàn)服務(wù)(比如 etcd, consul, nacos等)出現(xiàn)問題的時候,我們不要去修改已經(jīng)獲取到的 endpoints 列表浅浮,從而可以更好的確保 etcd 等宕機后所依賴的服務(wù)依然可以正常交互滤钱。
存量獲取
當(dāng) Service A
啟動時,需要從 Service Registry
獲取 Service B
的已有節(jié)點列表:Service B1
, Service B2
, Service B3
脑题,然后根據(jù)自己的負(fù)載均衡算法來選擇合適的節(jié)點發(fā)送請求。
增量偵聽
上圖已經(jīng)有了 Service B1
, Service B2
, Service B3
铜靶,如果此時又啟動了 Service B4
叔遂,那么我們就需要通知 Service A
有個新增的節(jié)點。如圖:
應(yīng)對服務(wù)發(fā)現(xiàn)故障
對于服務(wù)調(diào)用方來說争剿,我們都會在內(nèi)存里緩存一個可用節(jié)點列表已艰。不管是使用 etcd
,consul
或者 nacos
等蚕苇,我們都可能面臨服務(wù)發(fā)現(xiàn)集群故障哩掺,以 etcd
為例,當(dāng)遇到 etcd
故障時涩笤,我們就需要凍結(jié) Service B
的節(jié)點信息而不去變更嚼吞,此時一定不能去清空節(jié)點信息,一旦清空就無法獲取了蹬碧,而此時 Service B
的節(jié)點很可能都是正常的舱禽,并且 go-zero
會自動隔離和恢復(fù)故障節(jié)點。
服務(wù)注冊恩沽、服務(wù)發(fā)現(xiàn)的基本原理大致如此誊稚,當(dāng)然實現(xiàn)起來還是比較復(fù)雜的,接下來我們一起看看 go-zero
里支持哪些服務(wù)發(fā)現(xiàn)的方式罗心。
go-zero 之內(nèi)置服務(wù)發(fā)現(xiàn)
go-zero
默認(rèn)支持三種服務(wù)發(fā)現(xiàn)方式:
- 直連
- 基于 etcd 的服務(wù)發(fā)現(xiàn)
- 基于 kubernetes endpoints 的服務(wù)發(fā)現(xiàn)
直連
直連是最簡單的方式里伯,當(dāng)我們的服務(wù)足夠簡單時,比如單機即可承載我們的業(yè)務(wù)渤闷,我們可以直接只用這種方式疾瓮。
在 rpc
的配置文件里直接指定 endpoints
即可,比如:
Rpc:
Endpoints:
- 192.168.0.111:3456
- 192.168.0.112:3456
zrpc
調(diào)用端就會分配負(fù)載到這兩個節(jié)點上飒箭,其中一個節(jié)點有問題時 zrpc
會自動摘除爷贫,等節(jié)點恢復(fù)時會再次分配負(fù)載认然。
這個方法的缺點是不能動態(tài)增加節(jié)點,每次新增節(jié)點都需要修改調(diào)用方配置并重啟漫萄。
基于 etcd 的服務(wù)發(fā)現(xiàn)
當(dāng)我們的服務(wù)有一定規(guī)模之后卷员,因為一個服務(wù)可能會被很多個服務(wù)依賴,我們就需要能夠動態(tài)增減節(jié)點腾务,而無需修改很多的調(diào)用方配置并重啟毕骡。
常見的服務(wù)發(fā)現(xiàn)方案有 etcd
, consul
, nacos
等。
go-zero內(nèi)置集成了基于 etcd
的服務(wù)發(fā)現(xiàn)方案岩瘦,具體使用方法如下:
Rpc:
Etcd:
Hosts:
- 192.168.0.111:2379
- 192.168.0.112:2379
- 192.168.0.113:2379
Key: user.rpc
-
Hosts
是etcd
集群地址 -
Key
是服務(wù)注冊上去的key
基于 Kubernetes Endpoints 的服務(wù)發(fā)現(xiàn)
如果我們的服務(wù)都是部署在 Kubernetes
集群上的話未巫,Kubernetes
本身是通過自帶的 etcd
管理集群狀態(tài)的,所有的服務(wù)都會把自己的節(jié)點信息注冊到 Endpoints
對象启昧,我們可以直接給 deployment
權(quán)限去讀取集群的 Endpoints
對象即可獲得節(jié)點信息叙凡。
-
Service B
的每個Pod
啟動時,會將自己注冊到集群的Endpoints
里 -
Service A
的每個Pod
啟動時密末,可以從集群的Endpoints
里獲取Service B
的節(jié)點信息 - 當(dāng)
Service B
的節(jié)點發(fā)生改變時握爷,Service A
可以通過watch
集群的Endpoints
感知到
在這個機制工作之前,我們需要配置好當(dāng)前 namespace
內(nèi) pod
對集群 Endpoints
訪問權(quán)限严里,這里有三個概念:
- ClusterRole
- 定義集群范圍的權(quán)限角色新啼,不受
namespace
控制
- 定義集群范圍的權(quán)限角色新啼,不受
- ServiceAccount
- 定義
namespace
范圍內(nèi)的service account
- 定義
- ClusterRoleBinding
- 將定義好的
ClusterRole
和不同namespace
的ServiceAccount
進(jìn)行綁定
- 將定義好的
具體的 Kubernetes
配置文件可以參考 這里,其中 namespace
按需修改刹碾。
注意:當(dāng)啟動時報沒有權(quán)限獲取 Endpoints
時記得檢查這些配置有沒落實 :)
zrpc
的基于 Kubernetes Endpoints
的服務(wù)發(fā)現(xiàn)使用方法如下:
Rpc:
Target: k8s://mynamespace/myservice:3456
其中:
-
mynamespace
:被調(diào)用的rpc
服務(wù)所在的namespace
-
myservice
:被調(diào)用的rpc
服務(wù)的名字 -
3456
:被調(diào)用的rpc
服務(wù)的端口
在創(chuàng)建 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
創(chuàng)建出來的 pod
用哪個 ServiceAccount
。
server
和 client
都部署到 Kubernetes
集群里之后可以通過以下命令滾動重啟所有 server
節(jié)點
kubectl rollout restart deploy -n adhoc server-deployment
利用如下命令查看 client
節(jié)點日志:
kubectl -n adhoc logs -f deploy/client-deployment --all-containers=true
可以看到我們的服務(wù)發(fā)現(xiàn)機制完美跟進(jìn)了 server
節(jié)點的變化迷帜,并且在服務(wù)更新期間沒有出現(xiàn)異常請求物舒。
完整代碼示例見 https://github.com/zeromicro/zero-examples/tree/main/discovery/k8s
下一篇文章我將講解在 go-zero
里如何實現(xiàn)基于 consul
, nacos
等的服務(wù)注冊發(fā)現(xiàn),敬請期待戏锹!
項目地址
https://github.com/tal-tech/go-zero
歡迎使用 go-zero
并 star 支持我們茶鉴!
微信交流群
關(guān)注『微服務(wù)實踐』公眾號并點擊 交流群 獲取社區(qū)群二維碼。