前言
微服務原則上是應該有多個服務提供者的例項的,在通常情況下服務提供者的數量和分佈往往是動態變化的,這樣在傳統的單體應用中的那種硬編碼服務url進行遠端呼叫的方式就不足取。服務註冊中心就是為了解決服務之間的註冊與發現而產生的。
服務註冊中心本質上是為了解耦服務提供者和服務消費者。
服務註冊中心的一般原理
服務註冊中心的一般原理:
- 服務註冊中心啟動
- 服務提供者啟動,並註冊到服務註冊中心
- 服務消費者從註冊中心獲取服務資訊
- 服務消費者遠端呼叫服務提供者
主流服務註冊中心對比
Zookeeper
Zookeeper它是⼀個分散式服務框架,是Apache Hadoop 的⼀個⼦項⽬,它主要是⽤來解決分散式應⽤中經常遇到的⼀些資料管理問題,如:統⼀命名服務、狀態同步服務、叢集管理、分散式應⽤配置項的管理等。
簡單來說zookeeper本質=儲存+監聽通知。
Zookeeper ⽤來做服務註冊中⼼,主要是因為它具有節點(znode)變更通知功能,只要客戶端監聽相關服務節點,服務節點的所有變更,都能及時的通知到監聽客戶端,這樣作為調⽤⽅只要使⽤Zookeeper 的客戶端就能實現服務節點的訂閱和變更通知功能了,⾮常⽅便。另外,Zookeeper可⽤性也可以,因為只要半數以上的選舉節點存活,整個叢集就是可⽤的。
Eureka
由Netflix開源,並被Pivatal整合到SpringCloud體系中,它是基於 RestfulAPI ⻛格開發的服務註冊與發現元件。
Consul
Consul是由HashiCorp基於Go語⾔開發的⽀持多資料中⼼分散式⾼可⽤的服務釋出和註冊服務軟體, 採⽤Raft演算法保證服務的⼀致性,且⽀持健康檢查。
Nacos
Nacos是⼀個更易於構建雲原⽣應⽤的動態服務發現、配置管理和服務管理平臺。簡單來說 Nacos就是 註冊中⼼ + 配置中⼼的組合,幫助我們解決微服務開發必會涉及到的服務註冊與發現,服務配置,服務管理等問題。 Nacos 是 Spring Cloud Alibaba 核⼼元件之⼀,負責服務註冊與發現,還有配置。
CAP原理三者對比:
Eureka基礎架構
Eureka 包含兩個元件:Eureka Server 和 Eureka Client, Eureka Client是⼀個Java客戶端,⽤於簡化與Eureka Server的互動; Eureka Server提供服務發現的能⼒,各個微服務啟動時,會通過Eureka Client向Eureka Server 進⾏註冊⾃⼰的資訊(例如⽹絡資訊),Eureka Server會儲存該服務的資訊;
詳細流程如下:
-
服務提供者向Eureka Server中註冊服務, Eureka Server接受到註冊事件會在叢集和分割槽中進⾏資料同步,Application Client作為消費端(服務消費者)可以從Eureka Server中獲取到服務註冊資訊,進⾏服務調⽤。微服務啟動後,會週期性地向Eureka Server傳送⼼跳(預設週期為30秒)以續約⾃⼰的資訊
-
Eureka Server在⼀定時間內沒有接收到某個微服務節點的⼼跳, Eureka Server將會登出該微服務節點(預設90秒)
-
每個Eureka Server同時也是Eureka Client,多個Eureka Server之間通過複製的⽅式完成服務註冊列表的同步
-
Eureka Client會快取Eureka Server中的資訊。即使所有的Eureka Server節點都宕掉,服務消費者依然可以使⽤快取中的資訊找到服務提供者
下面我們來分別進行Eureka單例項及叢集環境的搭建及使用(僅列出部分關鍵程式碼,詳細程式碼請看文末原始碼):
搭建單例項EurekaServer
新建父工程lagou-parent,在pom.xml中統一規定springcloud的版本:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
新建一個子模組spring-cloud-eureka-server8761,
新增依賴
<!--Eureka server依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
在啟動類上面新增@EnableEurekaServer註解,表明此工程為EurekaServer服務工程。
application.yml:
#eureka server服務埠
server:
port: 8761
spring:
application:
name: lagou-cloud-eureka-server # 應用名稱,應用名稱會在Eureka中作為服務名稱
# eureka 客戶端配置(和Server互動),Eureka Server 其實也是一個Client
eureka:
instance:
hostname: localhost # 當前eureka例項的主機名
client:
service-url:
# 配置客戶端所互動的Eureka Server的地址(Eureka Server叢集中每一個Server其實相對於其它Server來說都是Client)
# 叢集模式下,defaultZone應該指向其它Eureka Server,如果有更多其它Server例項,逗號拼接即可
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
register-with-eureka: false # ⾃⼰就是服務不需要註冊⾃⼰ 叢集模式下可以改成true
fetch-registry: false # ⾃⼰就是服務不需要從Eureka Server獲取服務資訊,預設為true,叢集模式下可以改成true
執行啟動類,瀏覽器訪問http://localhost:8761/,出現以下介面說明啟動成功。
對介面上的資訊進行說明:
至此,單個Eureka server搭建完成。
EurekaServer叢集搭建
在上面單體Eureka server的基礎上搭建Eureka叢集。
為了模擬伺服器叢集的效果,修改本機hosts檔案:C:\Windows\System32\drivers\etc\hosts
127.0.0.1 LagouCloudEurekaServerA
127.0.0.1 LagouCloudEurekaServerB
在原有工程上新建一個Eureka server模組,命名為spring-cloud-eureka-server8762,
spring-cloud-eureka-server8761和spring-cloud-eureka-server8762的application.yml檔案如下:
8761:
#eureka server服務埠
server:
port: 8761
spring:
application:
name: lagou-cloud-eureka-server # 應用名稱,應用名稱會在Eureka中作為服務名稱
# eureka 客戶端配置(和Server互動),Eureka Server 其實也是一個Client
eureka:
instance:
hostname: LagouCloudEurekaServerA # 當前eureka例項的主機名
client:
service-url:
# 配置客戶端所互動的Eureka Server的地址(Eureka Server叢集中每一個Server其實相對於其它Server來說都是Client)
# 叢集模式下,defaultZone應該指向其它Eureka Server,如果有更多其它Server例項,逗號拼接即可
defaultZone: http://LagouCloudEurekaServerB:8762/eureka/
register-with-eureka: true # ⾃⼰就是服務不需要註冊⾃⼰ 叢集模式下可以改成true
fetch-registry: true # ⾃⼰就是服務不需要從Eureka Server獲取服務資訊,預設為true,叢集模式下可以改成true
8762:
#eureka server服務埠
server:
port: 8762
spring:
application:
name: lagou-cloud-eureka-server
eureka:
instance:
hostname: LagouCloudEurekaServerB
client:
service-url:
defaultZone: http://LagouCloudEurekaServerA:8761/eureka/
register-with-eureka: true
fetch-registry: true
瀏覽器中訪問
http://LagouCloudEurekaServerA:8761/eureka/
及
http://LagouCloudEurekaServerB:8762/eureka/
效果如下:
8761:
8762:
註冊服務提供者到EurekaServer
父工程中引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
服務提供者微服務中引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
服務提供者微服務applicaion.yml:
# Eureka配置
eureka:
client:
service-url:
defaultZone: http://LagouCloudEurekaServerA:8761/eureka/,http://LagouCloudEurekaServerB:8762/eureka/ #把 eureka 叢集中的所有 url 都填寫了進來,也可以只寫⼀臺,因為各個eureka server可以同步登錄檔
instance:
prefer-ip-address: true #使⽤ip註冊,否則會使⽤主機名註冊了(此處考慮到對⽼版本的相容,新版本經過實驗都是ip)
#⾃定義例項顯示格式,加上版本號,便於多版本管理,注意是ip-address,早期版本是ipAddress
instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
服務提供者啟動類新增註解 @EnableDiscoveryClient或@EnableEurekaClient。
說明:
1)從Spring Cloud Edgware版本開始, @EnableDiscoveryClient 或 EnableEurekaClient
可省略。只需加上相關依賴,並進⾏相應配置,即可將微服務註冊到服務發現元件上。
2)@EnableDiscoveryClient和@EnableEurekaClient⼆者的功能是⼀樣的。但是如果選⽤的是eureka伺服器,那麼就推薦@EnableEurekaClient,如果是其他的註冊中⼼,那麼推薦使⽤
@EnableDiscoveryClient,考慮到通⽤性,後期我們可以使⽤@EnableDiscoveryClient
啟動測試:
啟動EurekaServer,可以看到服務提供者例項已經成功註冊到EurekaServer上:
註冊服務消費者到EurekaServer
註冊服務消費者和服務提供者類似,pom中新增依賴,在啟動類新增@EnableDiscoveryClient,在application.yml中新增eureka的配置資訊。
可以看到服務消費者資訊成功註冊到EurekaServer上:
服務提供者呼叫服務消費者(通過eureka)
package com.lagou.edu.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class AutoDeliverController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/checkState/{userId}")
public Integer findResumeOpenState(@PathVariable Long userId) {
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("lagou-service-resume");
ServiceInstance serviceInstance = serviceInstanceList.get(0);
String host = serviceInstance.getHost();
int port = serviceInstance.getPort();
String url="http://"+host+":"+port+"/resume/openstate/"+userId;
Integer forObject =restTemplate.getForObject(url,Integer.class);
System.out.println("======>>>從eureka server獲取服務提供者例項:"+url);
return forObject;
}
}
呼叫成功資訊如下:
原始碼地址
原始碼地址:eureka-demo 原始碼
歡迎訪問我的部落格:https://www.liuyj.top