在上一篇的初識SpringCloud微服務中,咱們簡單講解到服務的提供者與消費者,當服務多了以後,會存在依賴與管理之間混亂的問題,以及須要對外暴露本身的地址,爲了解決此等問題,咱們學習Eureka註冊中心。html
java
首先咱們來解決第一問題,服務的管理。spring
問題分析apache
在剛纔的案例中,user-service對外提供服務,須要對外暴露本身的地址。而consumer(調用者)須要記錄服務提供者的地址。未來地址出現變動,還須要及時更新。這在服務較少的時候並不以爲有什麼,可是在如今日益複雜的互聯網環境,一個項目確定會拆分出十幾,甚至數十個微服務。此時若是還人爲管理地址,不只開發困難,未來測試、發佈上線都會很是麻煩,這與DevOps的思想是背道而馳的。緩存
網約車網絡
這就比如是 網約車出現之前,人們出門叫車只能叫出租車。一些私家車想作出租卻沒有資格,被稱爲黑車。而不少人想要約車,可是無奈出租車太少,不方便。私家車不少卻不敢攔,並且滿大街的車,誰知道哪一個纔是願意載人的。一個想要,一個願意給,就是缺乏引子,缺少管理啊。架構
此時滴滴這樣的網約車平臺出現了,全部想載客的私家車所有到滴滴注冊,記錄你的車型(服務類型),身份信息(聯繫方式)。這樣提供服務的私家車,在滴滴那裏都能找到,一目瞭然。app
此時要叫車的人,只須要打開APP,輸入你的目的地,選擇車型(服務類型),滴滴自動安排一個符合需求的車到你面前,爲你服務,完美!maven
Eureka作什麼?ide
Eureka就比如是滴滴,負責管理、記錄服務提供者的信息。服務調用者無需本身尋找服務,而是把本身的需求告訴Eureka,而後Eureka會把符合你需求的服務告訴你。
同時,服務提供方與Eureka之間經過「心跳」
機制進行監控,當某個服務提供方出現問題,Eureka天然會把它從服務列表中剔除。
這就實現了服務的自動註冊、發現、狀態監控。
基本架構:
提供者:啓動後向Eureka註冊本身信息(地址,提供什麼服務)
消費者:向Eureka訂閱服務,Eureka會將對應服務的全部提供者地址列表發送給消費者,而且按期更新
心跳(續約):提供者按期經過http方式向Eureka刷新本身的狀態
接下來咱們建立一個項目,啓動一個EurekaServer:
依然使用spring提供的快速搭建工具:
選擇依賴:
完整的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.leyou.demo</groupId>
<artifactId>eureka-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>eureka-demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!-- SpringCloud版本,是最新的F系列 -->
<spring-cloud.version>Finchley.RC1</spring-cloud.version>
</properties>
<dependencies>
<!-- Eureka服務端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- SpringCloud依賴,必定要放到dependencyManagement中,起到管理版本的做用便可 -->
<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>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
編寫啓動類:
@SpringBootApplication
@EnableEurekaServer // 聲明這個應用是一個EurekaServer
public class EurekaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaDemoApplication.class, args);
}
}
編寫配置:
server:
port: 10086 # 端口
spring:
application:
name: eureka-server # 應用名稱,會在Eureka中顯示
eureka:
client:
register-with-eureka: false # 是否註冊本身的信息到EurekaServer,默認是true
fetch-registry: false # 是否拉取其它服務的信息,默認是true
service-url: # EurekaServer的地址,如今是本身的地址,若是是集羣,須要加上其它Server的地址。
defaultZone: http://127.0.0.1:${server.port}/eureka
註冊服務,就是在服務上添加Eureka的客戶端依賴,客戶端代碼會自動把服務註冊到EurekaServer中。
咱們在user-service-demo中添加Eureka客戶端依賴:
先添加SpringCloud依賴:
在啓動類上開啓Eureka客戶端功能
@SpringBootApplication
@EnableDiscoveryClient // 開啓EurekaClient功能
public class UserServiceDemoApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceDemoApplication.class, args);
}
}
編寫配置
注意:
這裏咱們添加了spring.application.name屬性來指定應用名稱,未來會做爲應用的id使用。
不用指定register-with-eureka和fetch-registry,由於默認是true
咱們發現user-service服務已經註冊成功了
接下來咱們修改consumer-demo,嘗試從EurekaServer獲取服務。
方法與消費者相似,只須要在項目中添加EurekaClient依賴,就能夠經過服務名稱來獲取信息了!
1)添加依賴:
先添加SpringCloud依賴:
而後是Eureka客戶端:
<!-- Eureka客戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2)在啓動類開啓Eureka客戶端
@SpringBootApplication
@EnableDiscoveryClient // 開啓Eureka客戶端
public class UserConsumerDemoApplication {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
public static void main(String[] args) {
SpringApplication.run(UserConsumerDemoApplication.class, args);
}
}
3)修改配置:
4)修改代碼,用DiscoveryClient類的方法,根據服務名稱,獲取服務實例:
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;// Eureka客戶端,能夠獲取到服務實例信息
public List<User> queryUserByIds(List<Long> ids) {
List<User> users = new ArrayList<>();
// String baseUrl = "http://localhost:8081/user/";
// 根據服務名稱,獲取服務實例
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
// 由於只有一個UserService,所以咱們直接get(0)獲取
ServiceInstance instance = instances.get(0);
// 獲取ip和端口信息
String baseUrl = "http://"+instance.getHost() + ":" + instance.getPort()+"/user/";
ids.forEach(id -> {
// 咱們測試屢次查詢,
users.add(this.restTemplate.getForObject(baseUrl + id, User.class));
// 每次間隔500毫秒
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
return users;
}
}
5)Debug跟蹤運行:
生成的URL:
訪問結果:
Eureka架構中的三個核心角色:
服務註冊中心
Eureka的服務端應用,提供服務註冊和發現功能,就是剛剛咱們創建的eureka-demo
服務提供者
提供服務的應用,能夠是SpringBoot應用,也能夠是其它任意技術實現,只要對外提供的是Rest風格服務便可。本例中就是咱們實現的user-service-demo
服務消費者
Eureka Server即服務的註冊中心,在剛纔的案例中,咱們只有一個EurekaServer,事實上EurekaServer也能夠是一個集羣,造成高可用的Eureka中心。
服務同步
多個Eureka Server之間也會互相註冊爲服務,當服務提供者註冊到Eureka Server集羣中的某個節點時,該節點會把服務的信息同步給集羣中的每一個節點,從而實現數據同步。所以,不管客戶端訪問到Eureka Server集羣中的任意一個節點,均可以獲取到完整的服務列表信息。
動手搭建高可用的EurekaServer
咱們假設要搭建兩條EurekaServer的集羣,端口分別爲:10086和10087
1)咱們修改原來的EurekaServer配置:
所謂的高可用註冊中心,其實就是把EurekaServer本身也做爲一個服務進行註冊,這樣多個EurekaServer之間就能互相發現對方,從而造成集羣。所以咱們作了如下修改:
刪除了register-with-eureka=false和fetch-registry=false兩個配置。由於默認值是true,這樣就會吧本身註冊到註冊中心了。
把service-url的值改爲了另一臺EurekaServer的地址,而不是本身
2)另一臺配置剛好相反:
注意:idea中一個應用不能啓動兩次,咱們須要從新配置一個啓動器:
而後啓動便可。
3)啓動測試:
4)客戶端註冊服務到集羣
由於EurekaServer不止一個,所以註冊服務的時候,service-url參數須要變化:
服務提供者要向EurekaServer註冊服務,而且完成服務續約等工做。
服務註冊
服務提供者在啓動時,會檢測配置屬性中的:eureka.client.register-with-erueka=true
參數是否正確,事實上默認就是true。若是值確實爲true,則會向EurekaServer發起一個Rest請求,並攜帶本身的元數據信息,Eureka Server會把這些信息保存到一個雙層Map結構中。第一層Map的Key就是服務名稱,第二層Map的key是服務的實例id。
服務續約
在註冊服務完成之後,服務提供者會維持一個心跳(定時向EurekaServer發起Rest請求),告訴EurekaServer:「我還活着」。這個咱們稱爲服務的續約(renew);
有兩個重要參數能夠修改服務續約的行爲:
lease-expiration-duration-in-seconds:服務失效時間,默認值90秒
也就是說,默認狀況下每一個30秒服務會向註冊中心發送一次心跳,證實本身還活着。若是超過90秒沒有發送心跳,EurekaServer就會認爲該服務宕機,會從服務列表中移除,這兩個值在生產環境不要修改,默認便可。
可是在開發時,這個值有點太長了,常常咱們關掉一個服務,會發現Eureka依然認爲服務在活着。因此咱們在開發階段能夠適當調小。
先來看一下服務狀態信息:
在Eureka監控頁面,查看服務註冊信息:
在status一列中,顯示如下信息:
UP(1):表明如今是啓動了1個示例,沒有集羣
DESKTOP-2MVEC12:user-service:8081:是示例的名稱(instance-id),
默認格式是:${hostname} + ${spring.application.name} + ${server.port}
instance-id是區分同一服務的不一樣實例的惟一標準,所以不能重複。
咱們能夠經過instance-id屬性來修改它的構成:
eureka:
instance:
instance-id: ${spring.application.name}:${server.port}
重啓服務再試試看:
獲取服務列表
當服務消費者啓動是,會檢測eureka.client.fetch-registry=true
參數的值,若是爲true,則會從Eureka Server服務的列表只讀備份,而後緩存在本地。而且每隔30秒
會從新獲取並更新數據。咱們能夠經過下面的參數來修改:
eureka:
client:
registry-fetch-interval-seconds: 5
生產環境中,咱們不須要修改這個值。
可是爲了開發環境下,可以快速獲得服務的最新狀態,咱們能夠將其設置小一點。
失效剔除
有些時候,咱們的服務提供方並不必定會正常下線,可能由於內存溢出、網絡故障等緣由致使服務沒法正常工做。Eureka Server須要將這樣的服務剔除出服務列表。所以它會開啓一個定時任務,每隔60秒對全部失效的服務(超過90秒未響應)進行剔除。
能夠經過eureka.server.eviction-interval-timer-in-ms
參數對其進行修改,單位是毫秒,生成環境不要修改。
這個會對咱們開發帶來極大的不便,你對服務重啓,隔了60秒Eureka才反應過來。開發階段能夠適當調整,好比10S
自我保護
咱們關停一個服務,就會在Eureka面板看到一條警告:
這是觸發了Eureka的自我保護機制。當一個服務未按時進行心跳續約時,Eureka會統計最近15分鐘心跳失敗的服務實例的比例是否超過了85%。在生產環境下,由於網絡延遲等緣由,心跳失敗實例的比例頗有可能超標,可是此時就把服務剔除列表並不穩當,由於服務可能沒有宕機。Eureka就會把當前實例的註冊信息保護起來,不予剔除。生產環境下這頗有效,保證了大多數服務依然可用。
可是這給咱們的開發帶來了麻煩, 所以開發階段咱們都會關閉自我保護模式:
eureka:
server:
enable-self-preservation: false # 關閉自我保護模式(缺省爲打開)
eviction-interval-timer-in-ms: 1000 # 掃描失效服務的間隔時間(缺省爲60*1000ms)