在上一章節完成了一個簡單的微服務案例,下面就經過在這個案例的基礎上集成 Eureka 來學習 Eureka。html
Eureka 是 Netflix 的一個子模塊,也是核心模塊之一。Eureka 是一個基於 REST 的服務,用於定位服務,以實現雲端中間層服務發現和故障轉移。服務註冊與發現對於微服務架構來講是很是重要的,有了服務發現與註冊,只須要使用服務的標識符,就能夠訪問到服務。功能相似於 dubbo的註冊中心,好比 Zookeeper。java
SpringCloud 封裝了 Netflix 公司開發的 Eureka 模塊來實現服務註冊與發現,Eureka 採用了 C/S 的設計架構。web
Eureka Server 做爲服務註冊功能的服務器,它是服務註冊中心。而系統中的其它微服務,使用 Eureka 的客戶端鏈接到 Eureka Server 並維持心跳鏈接。這樣系統的維護人員就能夠經過 Eureka Server 來監控系統中的各個微服務是否正常運行。SpringCloud 的一些其它模塊(好比 Zuul)就能夠經過 Eureka Server 來發現系統中的其它微服務,並執行相關邏輯。算法
Eureka Server 包含兩個組件:spring
圖 1:Eureka 架構圖apache
圖 2:Dubbo 架構圖瀏覽器
一、新建名爲 "microservicecloud-eureka-7001" 的子工程做爲 Eureka 服務端,依賴以下:安全
<?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"> <parent> <artifactId>microservicecloud</artifactId> <groupId>zze.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>microservicecloud-eureka-7001</artifactId> <dependencies> <!--eureka-server服務端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <!-- 修改後當即生效,熱部署 --> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> </dependencies> </project>
二、配置 Eureka :服務器
server: port: 7001 eureka: instance: hostname: localhost # eureka 服務端實例名稱 client: register-with-eureka: false # 表示不向註冊中心註冊本身 fetch-registry: false # false 表示本身就是註冊中心,不須要檢索服務 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 設置與 Eureka Server 交互的地址,可用來查詢註冊的服務
三、編寫主啓動類,並使用註解開啓 Eureka Server 功能:網絡
package zze.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer // 開啓 Eureka Server 功能,標識當前程序就是一個 Eureka Server public class Application_7001 { public static void main(String[] args) { SpringApplication.run(Application_7001.class, args); } }
四、測試:
運行主啓動類,瀏覽器訪問 http://localhost:7001/ 進入 Eureka Server 圖形化頁面:
一、修更名爲 "microservicecloud-provider-dept-8001" 的工程的 pom 文件,添加以下 Eureka 客戶端依賴:
<!--Eureka 客戶端依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
二、將當前工程做爲 Eureka 客戶端註冊到 Eureka 服務,在配置文件添加以下配置:
eureka: client: # 將當前工程做爲 Eureka 客戶端 service-url: defaultZone: http://localhost:7001/eureka # Eureka 服務端地址
三、在主啓動類添加註解標識當前工程爲 Eureka 客戶端:
package zze.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient // 標註當前工程爲 Eureka 客戶端 public class Application_8001 { public static void main(String[] args) { SpringApplication.run(Application_8001.class, args); } }
四、測試:
先啓動 7001 Eureka 服務端工程,再啓動 8001 Eureka 客戶端工程,瀏覽器訪問 http://localhost:7001/,能夠看到 8001 客戶端實例已經被註冊到 Eureka 服務端:
服務發現實際上就是讓 EurekaServer 端可以掃描到咱們註冊的服務,默認咱們能夠經過 Web UI 的方式查看哪些服務註冊到了 EurekaServer,還能夠經過 Eureka 客戶端依賴提供的服務發現客戶端獲取註冊到 EurekaServer 的服務信息。
一、修更名爲 "microservicecloud-consumer-dept-80" 的服務工程添加以下 Eureka 客戶端依賴:
<!--Eureka 客戶端依賴--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
二、在其主啓動類上添加註解啓用 Eureka 客戶端功能:
package zze.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class Application_80 { public static void main(String[] args) { SpringApplication.run(Application_80.class, args); } }
三、修改 Controller:
package zze.springcloud.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.*; import org.springframework.web.client.RestTemplate; import zze.springcloud.entities.Dept; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/consumer/dept") public class DeptController { @Autowired private RestTemplate restTemplate; @PostMapping("/add") public boolean add(@RequestBody Dept dept) { return restTemplate.postForObject(getRestUrlPrefix("MICROSERVICECLOUD-PROVIDER-DEPT") + "/dept/add", dept,Boolean.class); } @GetMapping("/get/{id}") public Dept get(@PathVariable Long id){ return restTemplate.getForObject(getRestUrlPrefix("MICROSERVICECLOUD-PROVIDER-DEPT") + "/dept/get/" + id, Dept.class); } @GetMapping("/list") public List<Dept> list(){ return restTemplate.getForObject(getRestUrlPrefix("MICROSERVICECLOUD-PROVIDER-DEPT") + "/dept/list", List.class); } // 服務發現客戶端 @Autowired private DiscoveryClient discoveryClient; /** * 經過服務名稱獲取到服務實例對應的 url */ private String getRestUrlPrefix(String serviceName){ List<String> services = discoveryClient.getServices(); System.out.println("---------------------------"+services); List<ServiceInstance> instances = discoveryClient.getInstances(serviceName); System.out.println(instances); // 好比只註冊了一個 MICROSERVICECLOUD-PROVIDER-DEPT 微服務實例 ServiceInstance serviceInstance = instances.get(0); return String.format("http://%s:%s", serviceInstance.getHost(), serviceInstance.getPort()); } /** * 獲取全部註冊到 EurekaServer 的服務信息 * @return */ @GetMapping("/discovery") public Object discovery(){ Map<String, Object> map = new HashMap<>(); // 獲取全部註冊到 EurekaServer 的微服務名稱,對應 spring.application.name List<String> services = discoveryClient.getServices(); for (String service : services) { // 獲取對應服務全部實例 List<ServiceInstance> instances = discoveryClient.getInstances(service); map.put(service, instances); } return map; } }
四、測試:
正常訪問 http://localhost/consumer/dept/list:
訪問 http://localhost/consumer/dept/discovery 查看全部服務信息:
即經過服務發現咱們只須要使用約定的服務名稱就能夠經過註冊中心訪問到具體服務信息。
一、新建兩個子工程做爲兩個 EurekaServer 端,分別名爲 "microservicecloud-eureka-7002"、"microservicecloud-eureka-7003",主啓動類分別名爲 "Application_7002"、"Application_7003",依賴同 "microservicecloud-eureka-7001"。
二、因爲是單機測試,需修改本機 host 映射:
127.0.0.1 www.eurekaserver1.com
127.0.0.1 www.eurekaserver2.com
127.0.0.1 www.eurekaserver3.com
三、分別修改 700一、700二、7003 的配置文件:
server: port: 7001 eureka: instance: hostname: www.eurekaserver1.com # eureka 服務端實例名稱 client: register-with-eureka: false # 表示不向註冊中心註冊本身 fetch-registry: false # false 表示本身就是註冊中心,不須要檢索服務 service-url: defaultZone: http://www.eurekaserver2.com:7002/eureka/,http://www.eurekaserver3.com:7003/eureka/
server: port: 7002 eureka: instance: hostname: www.eurekaserver2.com # eureka 服務端實例名稱 client: register-with-eureka: false # 表示不向註冊中心註冊本身 fetch-registry: false # false 表示本身就是註冊中心,不須要檢索服務 service-url: defaultZone: http://www.eurekaserver1.com:7001/eureka/,http://www.eurekaserver3.com:7003/eureka/
server: port: 7003 eureka: instance: hostname: www.eurekaserver3.com # eureka 服務端實例名稱 client: register-with-eureka: false # 表示不向註冊中心註冊本身 fetch-registry: false # false 表示本身就是註冊中心,不須要檢索服務 service-url: # 單機版 # defaultZone: http://${e ureka.instance.hostname}:${server.port}/eureka/ # 設置與 Eureka Server 交互的地址,可用來查詢註冊的服務 defaultZone: http://www.eurekaserver1.com:7001/eureka/,http://www.eurekaserver2.com:7002/eureka/
四、修改 8001 客戶端 Eureka 配置,讓客戶端註冊到多個 Eureka 服務端:
eureka: client: # 將當前工程做爲 Eureka 客戶端 service-url: # 單機版 # defaultZone: http://localhost:7001/eureka # Eureka 服務端地址 defaultZone: http://www.eurekaserver1.com:7001/eureka,http://www.eurekaserver2.com:7002/eureka,http://www.eurekaserver3.com:7003/eureka instance: instance-id: microservicecloud-provider-dept prefer-ip-address: true # 訪問路徑顯示 IP
五、測試:
依次啓動 700一、700二、7003 EurekaServer 服務,接着再啓動 8001 客戶端服務,客戶端服務能夠正常訪問。 訪問 http://www.eurekaserver1.com:7001/:
訪問 http://www.eurekaserver2.com:7002/:
訪問 http://www.eurekaserver3.com:7003/:
由測試結果可知,客戶端服務已經註冊到了多個 Eureka 服務端,每一個 Eureka 服務端上又掛載了其它 Eureka 服務端的副本,Eureka 集羣搭建成功。
上面咱們經過訪問 Eureka 的 Web 頁看到以下界面:
該界面是描述的是有哪些 Eureka 客戶端實例註冊到了當前 Eureka 服務端,說明以下:
Status 欄所顯示的實例名稱是能夠點擊的,它所跳轉的頁面爲 隨機域名:端口/info ,以下:
若是咱們但願將這個地址的隨機域名改成 IP 地址,則能夠在配置文件中修改 eureka.instance.prefer-ip-address 屬性值爲 true 實現:
爲方便咱們使用,Eureka 容許咱們經過點擊客戶端實例訪問實例的詳細信息,對應路徑爲 /info ,但咱們訪問時會發現會返回 404 以下:
其實 SpringBoot 自己提供的監控功能就能夠幫咱們解決這個問題,須要在客戶端工程中引入監控相關依賴:
<!--監控信息--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
使用 maven 資源插件,讓項目屬性加載到項目環境變量中:
<build> <finalName>microservicecloud</finalName> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <delimiters> <delimit>$</delimit> </delimiters> </configuration> </plugin> </plugins> </build>
接着咱們就能夠在客戶端配置文件中添加 info 相關配置,例如:
info: host: ${java.rmi.server.hostname} port: ${server.port} app.name: microservicecloud-provider-dept-8001 build.artifactId: ${project.artifactId} build.version: ${project.version}
重啓客戶端程序,從新訪問 /info :
當咱們在作上述測試的時候,咱們可能會發現 Eureka 頁可能會顯示以下紅字:
這個其實就是由於 Eureka 的自我保護機制引發的。默認狀況下,若是 EurekaServer 在必定時間內沒有接收到某個微服務實例的心跳,EurekaServer 將會註銷該實例(默認爲 90 秒)。可是當網絡分區故障時,微服務與 EurekaServer之間沒法正常通訊,以上行爲可能就變得很是危險了——由於微服務自己實際上是健康的,此時本不該該註銷這個微服務。Eureka 經過「自我保護模式」來解決這個問題:當 EurekaServer 節點在短期內丟失過多的客戶端時(可能發生了網絡分區故障),那麼這個節點就會進入自我保護模式。一旦進入該模式,EurekaServer 就會保護服務註冊表中的信息,再也不刪除服務註冊表中的數據(也就是不會註銷任何微服務)。當網絡故障回覆後,EurekaServer 節點會自動退出自我保護模式。
在自我保護模式中,EurekaServer 會保護服務註冊表中的信息,不註銷任何服務實例。當它收到的心跳數從新恢復到閾值以上時,該 EurekaServer 就會自動退出自我保護模式。它的設計哲學就是寧肯保留錯誤的服務註冊信息,也不盲目註銷任何可能健康的服務實例,其目的是遵循 AP 原則。
關於 CAP 原則可參考【CAP 定理的含義】
綜上,自我保護模式是一種應對網絡異常的安全保護措施,它的架構哲學是寧肯同時保留全部微服務(不管微服務是健康仍是不健康),也不盲目註銷任何健康的微服務。使用自我保護模式,可讓 Eureka 集羣更加的健壯、穩定。
在 SpringCloud Eureka 服務端工程中,能夠經過 eureka.server.enable-self-preservation = false 來禁用自我保護模式。
做爲服務註冊中心,Eureka 比 Zookeeper 好在哪裏?
著名的 CAP 理論指出,一個分佈式系統不可能同時知足 C(一致性)、A(可用性)和 P(分區容錯性)。因爲分區容錯性 P 是在分佈式系統中必須保證的,所以咱們只能在 A 和 C 之間權衡。
所以 Zookeeper 保證的是 CP,而 Eureka 則是保證 AP。