无法访问netflix服务_Choerodon 的微服务之路(三):服务注册与发现
本文是 Choerodon 的微服務(wù)之路系列推文第三篇。在上一篇《Choerodon的微服務(wù)之路(二):微服務(wù)網(wǎng)關(guān)》中,介紹了Choerodon 在搭建微服務(wù)網(wǎng)關(guān)時(shí)考慮的一些問題以及兩種常見的微服務(wù)網(wǎng)關(guān)模式,并且通過代碼介紹了Choerodon 的網(wǎng)關(guān)是如何實(shí)現(xiàn)的。本篇文章將介紹Choerodon 的注冊中心,通過代碼的形式介紹 Choerodon 微服務(wù)框架中,是如何來實(shí)現(xiàn)服務(wù)注冊和發(fā)現(xiàn)的。
▌文章的主要內(nèi)容包括:
- 服務(wù)注冊/發(fā)現(xiàn)
- 服務(wù)注冊表
- 健康檢查
在上一篇文章的開始,我們提到解決微服務(wù)架構(gòu)中的通信問題,基本只要解決下面三個(gè)問題:
- 服務(wù)網(wǎng)絡(luò)通信能力
- 服務(wù)間的數(shù)據(jù)交互格式
- 服務(wù)間如何相互發(fā)現(xiàn)與調(diào)用
網(wǎng)絡(luò)的互通保證了服務(wù)之間是可以通信的,通過對JSON 的序列化和反序列化來實(shí)現(xiàn)網(wǎng)絡(luò)請求中的數(shù)據(jù)交互。Choerodon 的 API 網(wǎng)關(guān)則統(tǒng)一了所有來自客戶端的請求,并將請求路由到具體的后端服務(wù)上。然而這里就會(huì)有一個(gè)疑問,API 網(wǎng)關(guān)是如何與后端服務(wù)保持通信的,后端服務(wù)之間又是如何來進(jìn)行通信的?當(dāng)然我們能想到最簡單的方式就是通過 URL + 端口的形式直接訪問(例如:http://127.0.0.1:8080/v1/hello)。
在實(shí)際的生產(chǎn)中,我們認(rèn)為這種方式應(yīng)該是被避免的。因?yàn)?Choerodon 的每個(gè)服務(wù)實(shí)例都部署在 K8S 的不同 pod 中,每一個(gè)服務(wù)實(shí)例的 IP 地址和端口都可以改變。同時(shí)服務(wù)間相互調(diào)用的接口地址如何管理,服務(wù)本身集群化后又是如何進(jìn)行負(fù)載均衡。這些都是我們需要考慮的。
為了解決這個(gè)問題,自然就想到了微服務(wù)架構(gòu)中的注冊中心。一個(gè)注冊中心應(yīng)該包含下面幾個(gè)部分:
- 服務(wù)注冊/發(fā)現(xiàn):服務(wù)注冊是微服務(wù)啟動(dòng)時(shí),將自己的信息注冊到注冊中心的過程。服務(wù)發(fā)現(xiàn)是注冊中心監(jiān)聽所有可用微服務(wù),查詢列表及其網(wǎng)絡(luò)地址。
- 服務(wù)注冊表:用來紀(jì)錄各個(gè)微服務(wù)的信息。
- 服務(wù)檢查:注冊中心使用一定的機(jī)制定時(shí)檢測已注冊的服務(wù),如果發(fā)現(xiàn)某實(shí)例長時(shí)間無法訪問,就會(huì)從服務(wù)注冊表中移除該實(shí)例。
Choerodon 中服務(wù)注冊的過程如下圖所示:
服務(wù)注冊/發(fā)現(xiàn)
當(dāng)我們通過接口去調(diào)用其他服務(wù)時(shí),調(diào)用方則需要知道對應(yīng)服務(wù)實(shí)例的 IP 地址和端口。對于傳統(tǒng)的應(yīng)用而言,服務(wù)實(shí)例的網(wǎng)絡(luò)地址是相對不變的,這樣可以通過固定的配置文件來讀取網(wǎng)絡(luò)地址,很容易地使用 HTTP/REST 調(diào)用另一個(gè)服務(wù)的接口。
但是在微服務(wù)架構(gòu)中,服務(wù)實(shí)例的網(wǎng)絡(luò)地址是動(dòng)態(tài)分配的。而且當(dāng)服務(wù)進(jìn)行自動(dòng)擴(kuò)展,更新等操作時(shí),服務(wù)實(shí)例的網(wǎng)絡(luò)地址則會(huì)經(jīng)常變化。這樣我們的客戶端則需要一套精確地服務(wù)發(fā)現(xiàn)機(jī)制。
Eureka 是 Netflix 開源的服務(wù)發(fā)現(xiàn)組件,本身是一個(gè)基于 REST 的服務(wù)。它包含 Server 和 Client 兩部分。
Eureka Server 用作服務(wù)注冊服務(wù)器,提供服務(wù)發(fā)現(xiàn)的能力,當(dāng)一個(gè)服務(wù)實(shí)例被啟動(dòng)時(shí),會(huì)向 Eureka Server 注冊自己的信息(例如IP、端口、微服務(wù)名稱等)。這些信息會(huì)被寫到注冊表上;當(dāng)服務(wù)實(shí)例終止時(shí),再從注冊表中刪除。這個(gè)服務(wù)實(shí)例的注冊表通過心跳機(jī)制動(dòng)態(tài)刷新。這個(gè)過程就是服務(wù)注冊,當(dāng)服務(wù)實(shí)例注冊到注冊中心以后,也就相當(dāng)于注冊中心發(fā)現(xiàn)了服務(wù)實(shí)例,完成了服務(wù)注冊/發(fā)現(xiàn)的過程。
閱讀 Spring Cloud Eureka 的源碼可以看到,在 eureka-client-1.6.2.jar 的包中,com.netflix.discovery。 DiscoveryClient 啟動(dòng)的時(shí)候,會(huì)初始化一個(gè)定時(shí)任務(wù),定時(shí)的把本地的服務(wù)配置信息,即需要注冊到遠(yuǎn)端的服務(wù)信息自動(dòng)刷新到注冊服務(wù)器上。該類包含了 Eureka Client 向 Eureka Server 注冊的相關(guān)方法。
在 DiscoveryClient 類有一個(gè)服務(wù)注冊的方法 register(),該方法是通過 HTTP 請求向 Eureka Server 注冊。其代碼如下:
boolean register() throws Throwable {logger.info(PREFIX + appPathIdentifier + ": registering service...");EurekaHttpResponse<Void> httpResponse;try {httpResponse = eurekaTransport.registrationClient.register(instanceInfo);} catch (Exception e) {logger.warn("{} - registration failed {}", PREFIX + appPathIdentifier, e.getMessage(), e);throw e;}if (logger.isInfoEnabled()) {logger.info("{} - registration status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode());}return httpResponse.getStatusCode() == 204;}?對于 Choerodon 而言,客戶端依舊采用 Eureka Client,而服務(wù)端采用 GoLang 編寫,結(jié)合 K8S,通過主動(dòng)監(jiān)聽 K8S 下 pod 的啟停,發(fā)現(xiàn)服務(wù)實(shí)例上線,Eureka Client 則通過 HTTP 請求獲取注冊表,來實(shí)現(xiàn)服務(wù)注冊/發(fā)現(xiàn)過程。
注冊中心啟動(dòng)時(shí),會(huì)構(gòu)造一個(gè) podController,用來監(jiān)聽pod 的生命周期。代碼如下:
func Run(s *options.ServerRunOptions, stopCh <-chan struct{}) error {... ... podController := controller.NewController(kubeClient, kubeInformerFactory, appRepo)go kubeInformerFactory.Start(stopCh)go podController.Run(instance, stopCh, lockSingle)return registerServer.PrepareRun().Run(appRepo, stopCh) }?在 http://github.com/choerodon/go-register-server/controller/controller.go 中定義了 Controller,提供了 Run() 方法,該方法會(huì)啟動(dòng)兩個(gè)進(jìn)程,用來監(jiān)聽環(huán)境變量 REGISTER_SERVICE_NAMESPACE 中配置的對應(yīng) namespace 中的 pod,然后在 pod 啟動(dòng)時(shí),將 pod 信息轉(zhuǎn)化為自定義的服務(wù)注冊信息,存儲(chǔ)起來。在 pod 下線時(shí),從存儲(chǔ)中刪除服務(wù)信息。其代碼如下:
func (c *Controller) syncHandler(key string, instance chan apps.Instance, lockSingle apps.RefArray) (bool, error) {namespace, name, err := cache.SplitMetaNamespaceKey(key)if err != nil {runtime.HandleError(fmt.Errorf("invalid resource key: %s", key))return true, nil} pod, err := c.podsLister.Pods(namespace).Get(name)if err != nil {if errors.IsNotFound(err) {if ins := c.appRepo.DeleteInstance(key); ins != nil {ins.Status = apps.DOWNif lockSingle[0] > 0 {glog.Info("create down event for ", key)instance <- *ins}}runtime.HandleError(fmt.Errorf("pod '%s' in work queue no longer exists", key))return true, nil} return false, err} _, isContainServiceLabel := pod.Labels[ChoerodonServiceLabel]_, isContainVersionLabel := pod.Labels[ChoerodonVersionLabel]_, isContainPortLabel := pod.Labels[ChoerodonPortLabel] if !isContainServiceLabel || !isContainVersionLabel || !isContainPortLabel {return true, nil} if pod.Status.ContainerStatuses == nil {return true, nil} if container := pod.Status.ContainerStatuses[0]; container.Ready && container.State.Running != nil && len(pod.Spec.Containers) > 0 {if in := convertor.ConvertPod2Instance(pod); c.appRepo.Register(in, key) {ins := *inins.Status = apps.UPif lockSingle[0] > 0 {glog.Info("create up event for ", key)instance <- ins}} } else {if ins := c.appRepo.DeleteInstance(key); ins != nil {ins.Status = apps.DOWNif lockSingle[0] > 0 {glog.Info("create down event for ", key)instance <- *ins}}} return true, nil }http://github.com/choerodon/go-register-server/eureka/repository/repository 中的 ApplicationRepository 提供了 Register() 方法,該方法手動(dòng)將服務(wù)的信息作為注冊表存儲(chǔ)在注冊中心中。
func (appRepo *ApplicationRepository) Register(instance *apps.Instance, key string) bool {if _, ok := appRepo.namespaceStore.Load(key); ok {return false} else {appRepo.namespaceStore.Store(key, instance.InstanceId)}appRepo.instanceStore.Store(instance.InstanceId, instance)return true }通過上面的代碼我們可以了解到Choerodon 注冊中心是如何實(shí)現(xiàn)服務(wù)注冊的。有了注冊中心后,下面我們來介紹下服務(wù)發(fā)現(xiàn)中的服務(wù)注冊表。
服務(wù)注冊表
在微服務(wù)架構(gòu)中,服務(wù)注冊表是一個(gè)很關(guān)鍵的系統(tǒng)組件。當(dāng)服務(wù)向注冊中心的其他服務(wù)發(fā)出請求時(shí),請求調(diào)用方需要獲取注冊中心的服務(wù)實(shí)例,知道所有服務(wù)實(shí)例的請求地址。
Choerodon 沿用 Spring Cloud Eureka 的模式,由注冊中心保存服務(wù)注冊表,同時(shí)客戶端緩存一份服務(wù)注冊表,每經(jīng)過一段時(shí)間去注冊中心拉取最新的注冊表。
在http://github.com/choerodon/go-register-server/eureka/apps/types 中定義了 Instance 對象,聲明了一個(gè)微服務(wù)實(shí)例包含的字段。代碼如下:
type Instance struct {InstanceId string `xml:"instanceId" json:"instanceId"`HostName string `xml:"hostName" json:"hostName"`App string `xml:"app" json:"app"`IPAddr string `xml:"ipAddr" json:"ipAddr"`Status StatusType `xml:"status" json:"status"`OverriddenStatus StatusType `xml:"overriddenstatus" json:"overriddenstatus"`Port Port `xml:"port" json:"port"`SecurePort Port `xml:"securePort" json:"securePort"`CountryId uint64 `xml:"countryId" json:"countryId"`DataCenterInfo DataCenterInfo `xml:"dataCenterInfo" json:"dataCenterInfo"`LeaseInfo LeaseInfo `xml:"leaseInfo" json:"leaseInfo"`Metadata map[string]string `xml:"metadata" json:"metadata"`HomePageUrl string `xml:"homePageUrl" json:"homePageUrl"`StatusPageUrl string `xml:"statusPageUrl" json:"statusPageUrl"`HealthCheckUrl string `xml:"healthCheckUrl" json:"healthCheckUrl"`VipAddress string `xml:"vipAddress" json:"vipAddress"`SecureVipAddress string `xml:"secureVipAddress" json:"secureVipAddress"`IsCoordinatingDiscoveryServer bool `xml:"isCoordinatingDiscoveryServer" json:"isCoordinatingDiscoveryServer"` LastUpdatedTimestamp uint64 `xml:"lastUpdatedTimestamp" json:"lastUpdatedTimestamp"`LastDirtyTimestamp uint64 `xml:"lastDirtyTimestamp" json:"lastDirtyTimestamp"`ActionType string `xml:"actionType" json:"actionType"` }客戶端可以通過訪問注冊中心的/eureka/apps 接口獲取對應(yīng)的注冊表信息。如下所示:
{"name": "iam-service","instance": [{"instanceId": "10.233.73.39:iam-service:8030","hostName": "10.233.73.39","app": "iam-service","ipAddr": "10.233.73.39","status": "UP","overriddenstatus": "UNKNOWN","port": {"@enabled": true,"$": 8030},"securePort": {"@enabled": false,"$": 443},"countryId": 8,"dataCenterInfo": {"name": "MyOwn","@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo"},"leaseInfo": {"renewalIntervalInSecs": 10,"durationInSecs": 90,"registrationTimestamp": 1542002980,"lastRenewalTimestamp": 1542002980,"evictionTimestamp": 0,"serviceUpTimestamp": 1542002980},"metadata": {"VERSION": "2018.11.12-113155-master"},"homePageUrl": "http://10.233.73.39:8030/","statusPageUrl": "http://10.233.73.39:8031/info","healthCheckUrl": "http://10.233.73.39:8031/health","vipAddress": "iam-service","secureVipAddress": "iam-service","isCoordinatingDiscoveryServer": true,"lastUpdatedTimestamp": 1542002980,"lastDirtyTimestamp": 1542002980,"actionType": "ADDED"}] }我們可以在服務(wù)注冊表中獲取到所有服務(wù)的 IP 地址、端口以及服務(wù)的其他信息,通過這些信息,服務(wù)直接就可以通過 HTTP 來進(jìn)行訪問。有了注冊中心和注冊表之后,我們的注冊中心又是如何來確保服務(wù)是健康可用的,則需要通過健康檢查機(jī)制來實(shí)現(xiàn)。
健康檢查
在我們提供了注冊中心以及服務(wù)注冊表之后,我們還需要確保我們的服務(wù)注冊表中的信息,與服務(wù)實(shí)際的運(yùn)行狀態(tài)保持一致,需要提供一種機(jī)制來保證服務(wù)自身是可被訪問的。在Choerodon微服務(wù)架構(gòu)中處理此問題的方法是提供一個(gè)健康檢查的端點(diǎn)。當(dāng)我們通過 HTTP 進(jìn)行訪問時(shí),如果能夠正常訪問,則應(yīng)該回復(fù) HTTP 狀態(tài)碼200,表示健康。
Spring Boot 提供了默認(rèn)的健康檢查端口。需要添加spring-boot-starter-actuator 依賴。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency>?訪問 /health 端點(diǎn)后,則會(huì)返回如下類似的信息表示服務(wù)的狀態(tài)。可以看到 HealthEndPoint 給我們提供默認(rèn)的監(jiān)控結(jié)果,包含磁盤檢測和數(shù)據(jù)庫檢測等其他信息。
{"status": "UP","diskSpace": {"status": "UP","total": 398458875904,"free": 315106918400,"threshold": 10485760},"db": {"status": "UP","database": "MySQL","hello": 1} }?但是因?yàn)?Choerodon 使用的是 K8S 作為運(yùn)行環(huán)境。我們知道 K8S 提供了 liveness probes 來檢查我們的應(yīng)用程序。而對于 Eureka Client 而言,服務(wù)是通過心跳來告知注冊中心自己是 UP 還是 DOWN的。這樣我們的系統(tǒng)中則會(huì)出現(xiàn)兩種檢查機(jī)制,則會(huì)出現(xiàn)如下幾種情況。
- K8S 通過,/health 通過
- K8S 通過,/health 未通過
- K8S 未通過,/health 通過
第一種情況,當(dāng)兩種都通過的話,服務(wù)是可以被訪問的。
第二種情況,K8S 認(rèn)為服務(wù)是正常運(yùn)行的,但注冊中心認(rèn)為服務(wù)是不健康的,注冊表中不會(huì)記錄該服務(wù),這樣其他服務(wù)則不能獲取該服務(wù)的注冊信息,也就不會(huì)通過接口進(jìn)行服務(wù)調(diào)用。則服務(wù)間不能正常訪問,如下圖所示:
第三種情況,服務(wù)通過心跳告知注冊中心自己是可用的,但是可能因?yàn)榫W(wǎng)絡(luò)的原因,K8S 將 pod 標(biāo)識(shí)為不可訪問,這樣當(dāng)其他服務(wù)來請求該服務(wù)時(shí),則不可以訪問。這種情況下服務(wù)間也是不能正常訪問的。如下圖所示:
同時(shí),當(dāng)我們配置了管理端口之后,該端點(diǎn)則需要通過管理端口進(jìn)行訪問。可以再配置文件中添加如下配置來修改管理端口。
management.port: 8081?當(dāng)我們開啟管理端口后,這樣會(huì)使我們的健康檢查變得更加復(fù)雜,健康檢查并不能獲取服務(wù)真正的健康狀態(tài)。
在這種情況下,Choerodon 使用 K8S 來監(jiān)聽服務(wù)的健康端口,同時(shí)需要保證服務(wù)的端口與管理端口都能被正常訪問,才算通過健康檢查。可以在部署的 deploy 文件中添加 readinessProbe 參數(shù)。
apiVersion: v1 kind: Pod spec:containers:readinessProbe:exec:command:- /bin/sh- -c- curl -s localhost:8081/health --fail && nc -z localhost 8080failureThreshold: 3initialDelaySeconds: 60periodSeconds: 10successThreshold: 1timeoutSeconds: 10這樣,當(dāng)我們的服務(wù)啟動(dòng)之后,才會(huì)被注冊中心正常的識(shí)別。當(dāng)服務(wù)狀態(tài)異常時(shí),也可以盡快的從注冊表中移除。
總結(jié)
回顧一下這篇文章,我們介紹了 Choerodon 的注冊中心,通過代碼的形式介紹了 Choerodon 微服務(wù)框架中,是如何來實(shí)現(xiàn)服務(wù)注冊和發(fā)現(xiàn)的,其中 Spring Cloud 的版本為 Dalston.SR4。具體的代碼可以參見我們的 github 地址(https://github.com/choerodon/go-register-server)。
更多關(guān)于微服務(wù)系列的文章,點(diǎn)擊藍(lán)字可閱讀 ▼
Choerodon的微服務(wù)之路(二):微服務(wù)網(wǎng)關(guān)
Choerodon的微服務(wù)之路(一):如何邁出關(guān)鍵的第一步
關(guān)于Choerodon豬齒魚
Choerodon豬齒魚是一個(gè)開源企業(yè)服務(wù)平臺(tái),是基于Kubernetes的容器編排和管理能力,整合DevOps工具鏈、微服務(wù)和移動(dòng)應(yīng)用框架,來幫助企業(yè)實(shí)現(xiàn)敏捷化的應(yīng)用交付和自動(dòng)化的運(yùn)營管理的開源平臺(tái),同時(shí)提供IoT、支付、數(shù)據(jù)、智能洞察、企業(yè)應(yīng)用市場等業(yè)務(wù)組件,致力幫助企業(yè)聚焦于業(yè)務(wù),加速數(shù)字化轉(zhuǎn)型。
大家也可以通過以下社區(qū)途徑了解豬齒魚的最新動(dòng)態(tài)、產(chǎn)品特性,以及參與社區(qū)貢獻(xiàn):
- 豬齒魚官網(wǎng):http://choerodon.io
- 論壇:http://forum.choerodon.io
- Github:https://github.com/choerodon
由Choerodon豬齒魚核心團(tuán)隊(duì)創(chuàng)立的 BuildRun(https://gobuildrun.com),基于豬齒魚帶來了多云架構(gòu)環(huán)境下基于視覺的企業(yè)級(jí)應(yīng)用創(chuàng)建、集成、部署、生命周期管理和分發(fā)的能力。
BuildRun 以云原生的現(xiàn)代化軟件架構(gòu)來幫助企業(yè)提升軟件開發(fā)生產(chǎn)力和業(yè)務(wù)敏捷性,提供多云低代碼應(yīng)用平臺(tái)和多云應(yīng)用生命周期管理平臺(tái),幫助企業(yè)隱藏應(yīng)用開發(fā)和運(yùn)行時(shí)的基礎(chǔ)架構(gòu)復(fù)雜性,讓每個(gè)人專注于業(yè)務(wù)邏輯,促進(jìn)團(tuán)隊(duì)快速、持續(xù)地將想法轉(zhuǎn)化為真正的商業(yè)價(jià)值。
團(tuán)隊(duì)在云原生技術(shù)和架構(gòu)(DevOps、持續(xù)交付、微服務(wù)和容器)應(yīng)用場景,如企業(yè)數(shù)字化轉(zhuǎn)型、企業(yè)中臺(tái)等方面擁有豐富的經(jīng)驗(yàn),公司客戶包括商業(yè)地產(chǎn)、建筑工程、醫(yī)藥、家居、汽車配業(yè)、大型工業(yè)等企業(yè),為您提供最合適的解決方案。
免費(fèi)注冊試用:https://apps.gobuildrun.com/#/base/register-organization
總結(jié)
以上是生活随笔為你收集整理的无法访问netflix服务_Choerodon 的微服务之路(三):服务注册与发现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 陇南看不孕不育比较好的医院推荐
- 下一篇: 地下城与勇士DNF街霸穿什么好?