在分佈式系統領域有個著名的CAP定理:css
C——數據一致性;html
A——服務可用性;java
P——服務對網絡分區故障的容錯性。git
這三個特性在任何分佈式系統中不能同時知足,最多同時知足兩個。github
Zookeeper是著名Hadoop的一個子項目,不少場景下Zookeeper也做爲Service發現服務解決方案。Zookeeper保證的是CP,即任什麼時候刻對Zookeeper的訪問請求能獲得一致的數據結果,同時系統對網絡分割具有容錯性,可是它不能保證每次服務請求的可用性。web
而Spring Cloud Netflix在設計Eureka時遵照的就是AP原則。由於對於服務消費者來講,能消費纔是最重要的——拿到可能不正確的服務實例信息後嘗試消費一下,也好過由於沒法獲取實例信息而不去消費。因此,對於服務發現而言,可用性比數據一致性更加劇要——AP賽過CP。spring
服務治理是微服務架構中最爲核心和基礎的模塊,主要爲各個微服務實例提供自動化註冊與發現功能。apache
服務註冊:服務治理框架會構建一個註冊中心,每一個服務向註冊中心註冊登記本身提供的服務,將服務名、主機、端口號、版本號、通訊協議等告知註冊中心,註冊中心按服務名分類組織成一個服務清單。網絡
例如:架構
有三個提供訂單服務的實例分別運行在 192.168.0.100:808一、192.168.0.101:808二、192.168.0.102:8083;
還有四個提供用戶服務的實例分別運行在 192.168.1.100:809一、192.168.1.101:809二、192.168.1.102:809三、192.168.1.103:8094;
當這些服務所有啓動併成功註冊到註冊中心上後,註冊中心會維護一份相似下面的一個服務清單:
服務名 | 服務地址 |
訂單服務 | 192.168.0.100:808一、192.168.0.101:808二、192.168.0.102:8083 |
用戶服務 | 192.168.1.100:809一、192.168.1.101:809二、192.168.1.102:809三、192.168.1.103:8094 |
服務發現:在服務治理框架下,服務之間再也不經過具體實例地址來相互調用,而是經過向註冊中心諮詢並獲取服務清單,以實現(經過服務名)對具體服務的調用。
例如:前臺用戶下單須要調用訂單服務,首先向註冊中心發起諮詢訂單服務的請求,註冊中心返回訂單服務位置清單,而後調用方經過負載策略選擇其中某一個位置進行服務調用便可。
建立一個空項目,pom.xml以下(後面都基於這個pom):
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.springcloud.demo</groupId> <artifactId>springcloud-demo</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>eureka-server</module> <module>eureka-service-provider</module> </modules> <name>Maven</name> <!-- FIXME change it to the project's website --> <url>http://maven.apache.org/</url> <inceptionYear>2001</inceptionYear> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.8.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <failIfNoTests>true</failIfNoTests> </configuration> </plugin> </plugins> </build> </project>
首先搭建一個註冊中心,命名爲eureka-server,引入eureka依賴包
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
在 Spring Boot 啓動入口文件上加註解 @EnableEurekaServer
@SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run( EurekaServerApplication.class, args ); } }
而後配置application.yml:
server: port: 8081 eureka: instance: hostname: eurekaserver client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ spring: application: name: eurka-server
其中:
eureka.client.registerWithEureka = false 設置爲false表示不向註冊中心註冊本身,默認爲true
eureka.client.fetchRegistry = false 設置爲false表示不須要去檢索服務,默認爲true
啓動應用並訪問 http://localhost:8081/ ,以下圖所示:
能夠看到註冊中心尚未註冊任何服務,至此註冊中心已經搭建完畢。
搭建好註冊中心後,咱們再來建立一個服務提供者,而後將其註冊到註冊中心。新建一個項目eureka-service-provider,添加eureka依賴,在 Spring Boot 啓動入口文件上加註解 @EnableEurekaClient
@SpringBootApplication @EnableEurekaClient public class EurekaServiceApplication { public static void main(String[] args) { SpringApplication.run( EurekaServiceApplication.class, args ); } }
配置application.yml:
server: port: 8082 eureka: instance: hostname: service-provider client: serviceUrl: defaultZone: http://localhost:8081/eureka/ spring: application: name: service-1
由於須要向註冊中心註冊,因此 eureka.client.registerWithEureka、eureka.client.fetchRegistry 不要配置爲 false
啓動eureka-service-provider,刷新註冊中心,能夠看到服務已經註冊成功了:
同時eureka-server控制檯會打印一行日誌:Registered instance SERVICE-1/xxxx:service-1:8082 with status UP (replication=false)
上面只是一個單點註冊中心,微服務場景下應用經過註冊中心來訪問真實服務,若是註冊中心掛了會形成服務沒法調用,因此註冊中心也得要保持高可用,Eureka 不可能沒有想到這一點,其實在Eureka裏,全部結點既是服務提供方,又是服務消費方,註冊中心也不例外。
你們還記得在單節點配置裏講到的這兩個配置嗎:
eureka.client.registerWithEureka = false
eureka.client.fetchRegistry = false
實際上 Eureka 高可用方案就是將本身做爲服務向其餘服務註冊中心註冊本身,這樣就能夠造成一組相互註冊的服務註冊中心,在這組服務註冊中心內部相互同步註冊清單,造成一個高可用集羣
建立如下三個Eureka Server:peer一、peer二、peer3
peer1的配置以下(peer二、peer3配置相似):
server: port: 8081 eureka: instance: hostname: peer1 client: serviceUrl: defaultZone: http://peer2:8082/eureka/,http://peer3:8083/eureka/ spring: application: name: eurka-server
分別啓動peer一、peer二、peer3,訪問peer1:8081
這時候若是peer2忽然掛了,訪問peer1和peer3仍然能夠看到peer2,這是由於Eureka默認開啓了自我保護機制(peer2掛了沒有剔除),能夠經過設置 eureka.server.enable-self-preservation=false 來禁用保護
peer1的配置文件裏新增eureka.server.enable-self-preservation=false設置,而後模擬peer3掛了,peer1註冊中心以下:
大概兩分鐘左右,從peer1註冊中心能夠看到 registered-replicas 有3個,unavailable-replicas 裏有 peer3,available-replicas 裏只有 peer1 和 peer2 可用,
而從 peer2 裏看 peer3 仍然可用(過比較長的時間,peer2 仍然會將 peer3 從 available-replicas 裏剔除)
將serverce-1註冊到註冊中心peer1,而後在peer1 和 peer2裏均可以看到service-1。
peer3 通過運維處理恢復了,從新啓動peer3自動加入了peer1和peer2集羣,可用看到service-1也自動註冊到peer3上了:
服務註冊相關配置:eureka.instance 爲前綴對應的Class
org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean
服務實例相關配置:eureka.client 爲前綴對應的Class
org.springframework.cloud.netflix.eureka.EurekaClientConfigBean
服務配置屬性以eureka.server爲前綴對應的Class
org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean
# 啓用Eureka客戶端
eureka.client.enabled=true
# 從Eureka服務端獲取註冊信息的間隔時間(單位:秒)
eureka.client.registry-fetch-interval-seconds=30
# 是否從Eureka服務端獲取註冊信息
eureka.client.fetch-registry=true
# 是否要將自身的實例信息註冊到Eureka服務端
eureka.client.register-with-eureka=true
# 初始化實例信息的變化到Eureka服務端的間隔時間(單位:秒)
eureka.client.instance-info-replication-interval-seconds=40
# 從Eureka客戶端到Eureka服務端的鏈接總數
eureka.client.eureka-server-total-connections=200
# 獲取實例時是否過濾,僅保留UP狀態的實例
eureka.client.filter-only-up-instances=true
#設爲false,關閉自我保護
eureka.server.enable-self-preservation=false
#表示是否將本身註冊到Eureka Server,默認爲true
eureka.client.register-with-eureka=false
#表示是否從Eureka Server獲取註冊信息,默認爲true
eureka.client.fetch-registry=false
# 掃描失效服務的間隔時間(單位毫秒,默認是60*1000)即60秒
eureka.server.eviction-interval-timer-in-ms=5000
#設置 eureka server同步失敗的等待時間 默認 5分
#在這期間,它不向客戶端提供服務註冊信息
eureka.server.wait-time-in-ms-when-sync-empty=5
#設置 eureka server同步失敗的重試次數 默認爲 5 次
eureka.server.number-of-replication-retries=5
#自我保護係數(默認0.85)
eureka.server.renewal-percent-threshold=0.85
#是否優先使用IP地址做爲主機名的標識,若是不配置就是機器的主機名
eureka.instance.prefer-ip-address=false
#Eureka客戶端向服務的發送心跳的時間間隔(單位:秒)
eureka.instance.lease-renewal-interval-in-seconds=30
#Eureka服務的收到最後一次心跳以後等待的時間上限(單位:秒),
#超過這個時間以後會將該服務實例從註冊中心清單裏剔除
eureka.instance.lease-expiration-duration-in-seconds=90