一.簡(jiǎn)介
1.SpringCloud
SpringCloud是一套微服務(wù)開發(fā)一站式解決方案,它提供了微服務(wù)開發(fā)所需要的很多功能組件,
比如服務(wù)統(tǒng)一管理、配置統(tǒng)一管理、路由網(wǎng)關(guān)、斷路器、事件總線、集群狀態(tài)配置等等。而且
SpringCloud與SpringBoot無(wú)縫銜接,配合SpringBoot能夠更輕松的搭建出一套微服務(wù)架構(gòu)平
臺(tái)。
2.微服務(wù)
微服務(wù)其實(shí)是一種架構(gòu)的設(shè)計(jì)風(fēng)格,并沒有明確的技術(shù)綁定和架構(gòu)概念。簡(jiǎn)單來說,微服務(wù)架構(gòu)
風(fēng)格其實(shí)就是將原來的單一架構(gòu)開發(fā)為一組小型服務(wù)(微服務(wù))的方法,每個(gè)服務(wù)都運(yùn)行在自己
獨(dú)立的進(jìn)程中(服務(wù)間的隔離),服務(wù)間采用輕量級(jí)的通訊機(jī)制(HTTP、RPC - Netty、 WebService),這些服務(wù)按照業(yè)務(wù)拆分,并且獨(dú)立部署(自動(dòng)化部署)。服務(wù)會(huì)由一個(gè)統(tǒng)一的管
理中心管理(Zookeeper、Eureka、Nacos),服務(wù)可以采用不同的語(yǔ)言開發(fā),并且使用不同
的存儲(chǔ)技術(shù)(數(shù)據(jù)庫(kù)垂直拆分)。
二.服務(wù)的注冊(cè)與發(fā)現(xiàn)--Eureka
1.注冊(cè)中心
注冊(cè)中心的作用就是為了減少服務(wù)間的調(diào)用耦合
2.Eureka
(1)Eureka是Netflix開發(fā)的服務(wù)發(fā)現(xiàn)框架,本身是一個(gè)基于REST的服務(wù),主要用于定位運(yùn)行在AWS域中的中間層服務(wù),以達(dá)到負(fù)載均衡和中間層服務(wù)故障轉(zhuǎn)移的目的。SpringCloud將它集成在其子項(xiàng)目spring-cloud-netflix中,以實(shí)現(xiàn)SpringCloud的服務(wù)發(fā)現(xiàn)功能。
(2)Eureka包含兩個(gè)組件:Eureka Server和Eureka Client。
(3)Eureka Server提供服務(wù)注冊(cè)服務(wù),各個(gè)節(jié)點(diǎn)啟動(dòng)后,會(huì)在Eureka Server中進(jìn)行注冊(cè),這樣EurekaServer中的服務(wù)注冊(cè)表中將會(huì)存儲(chǔ)所有可用服務(wù)節(jié)點(diǎn)的信息,服務(wù)節(jié)點(diǎn)的信息可以在界面中直觀的看到。
(4)Eureka Client是一個(gè)java客戶端,用于簡(jiǎn)化與Eureka Server的交互,客戶端同時(shí)也就是一個(gè)內(nèi)置的、使用輪詢(round-robin)負(fù)載算法的負(fù)載均衡器。
(5)在應(yīng)用啟動(dòng)后,將會(huì)向Eureka Server發(fā)送心跳,默認(rèn)周期為30秒,如果Eureka Server在多個(gè)心跳周期內(nèi)沒有接收到某個(gè)節(jié)點(diǎn)的心跳,Eureka Server將會(huì)從服務(wù)注冊(cè)表中把這個(gè)服務(wù)節(jié)點(diǎn)移除(默認(rèn)90秒)。
Eureka Server之間通過復(fù)制的方式完成數(shù)據(jù)的同步,Eureka還提供了客戶端緩存機(jī)制,即使所有的Eureka Server都掛掉,客戶端依然可以利用緩存中的信息消費(fèi)其他服務(wù)的API。綜上,Eureka通過心跳檢查、客戶端緩存等機(jī)制,確保了系統(tǒng)的高可用性、靈活性和可伸縮性。
3.Eureka 對(duì)比 Zookeeper
(1)zookeeper可以獨(dú)立運(yùn)行,但是Eureka不可以獨(dú)立運(yùn)行,需要依賴web服務(wù),tomcat
(2)zookeeper的分布式服務(wù)中存在著角色的關(guān)系,有服務(wù)的提供者與消費(fèi)者,但是Eureka每個(gè)微服務(wù)既是消費(fèi)者,也是服務(wù)的提供者.

4.Eureka服務(wù)端的搭建
(1)配置自定義的父工程(繼承SpringBoot、SpringCloud)
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>springcloud_Eureka</module>
<module>springcloud_microservice_student</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>nz.study</groupId>
<artifactId>springcloud_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- <name>springcloud_demo</name>
<description>Demo project for Spring Boot</description>-->
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR5</spring-cloud.version>
</properties>
<!-- 相當(dāng)于讓當(dāng)前得工程繼承了SpringCloud的父工程,用這種方式可以實(shí)現(xiàn)Maven的多繼承 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<!--導(dǎo)入pom工程,相當(dāng)于多繼承-->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 只有微服務(wù)才要依賴<build> -->
<!-- <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>-->
</project>
(2)創(chuàng)建Eureka服務(wù)
1)創(chuàng)建一個(gè)Maven工程(Eureka微服務(wù)),繼承自定義的父工程(springcloud_demo)
2)修改當(dāng)前maven工程的pom.xml
<?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>springcloud_demo</artifactId>
<groupId>nz.study</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud_Eureka</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3)配置當(dāng)前springboot的啟動(dòng)類
package nz.study.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
//run方法的第一個(gè)參數(shù)是添加了@SpringBootApplication注解的類
//不是當(dāng)前類
SpringApplication.run(EurekaServerApplication.class,args);
}
}
4)配置application.yml
server:
port: 20000
spring:
application:
#配置微服務(wù)的名稱,以后所有的微服務(wù),都必須配置這個(gè)屬性,不然注冊(cè)中心的名稱會(huì)變成unable
name: eureka-server
#配置eureka的相關(guān)屬性
eureka:
client:
service-url:
#配置eureka的注冊(cè)地址
#defaultZone沒有提示,必須手寫
defaultZone: http://eureka1:20000/eureka,http://eureka2:20001/eureka,http://eureka3:20002/eureka
#當(dāng)前的微服務(wù)就是注冊(cè)中心,注冊(cè)中心不能從注冊(cè)中心抓取服務(wù),所以該配置需要配置成false
fetch-registry: false
#使得當(dāng)前微服務(wù)不注冊(cè)到注冊(cè)中心
register-with-eureka: false
5)啟動(dòng)SpringBoot工程 - 啟動(dòng)Eureka服務(wù)端
6)訪問注冊(cè)中心,http://localhost:20000
5.微服務(wù)(Eureka客戶端)的搭建
(1)Eureka客戶端的搭建
- 創(chuàng)建一個(gè)Maven工程,繼承springcloud_demo工程
- 配置Maven工程的pom.xml
<?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>springcloud_demo</artifactId>
<groupId>nz.study</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud_microservice_student</artifactId>
<dependencies>
<!-- 微服務(wù)需要對(duì)外提供功能,所以需要添加web依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 同時(shí)為了能夠讓微服務(wù)注冊(cè)到注冊(cè)中心上 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!-- 打包插件,不寫不影響運(yùn)行,但是打包會(huì)出問題 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 配置啟動(dòng)類
package nz.study.student;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication(scanBasePackages = "nz.study")
//springcloud 2.x 之后 該注解可以省略
@EnableEurekaClient
public class StudentApplication {
public static void main(String[] args) {
SpringApplication.run(StudentApplication.class,args);
}
}
- 配置application.yml
server:
port: 8080
#微服務(wù)的名稱
spring:
application:
#特別注意:微服務(wù)的名稱一定不要用下劃線
name: micro-student
eureka:
client:
service-url:
#配置注冊(cè)中心的地址,因?yàn)槲⒎?wù)需要注冊(cè),也需要抓取服務(wù)所以另兩個(gè)配置默認(rèn)為true,可以不用配置.
defaultZone: http://localhost:20000/eureka
- 編寫微服務(wù)的功能接口(根據(jù)微服務(wù)本身的業(yè)務(wù)不同,編寫不同的接口)
package nz.study.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/stu")
public class StuController {
/**
* 根據(jù)學(xué)生id查詢學(xué)生姓名
*
* @RequestBody -> 返回json (×)
*
* @RequestBody -> 將方法的返回值,放入response響應(yīng)體中
*
* 響應(yīng)體 -> 數(shù)值,字符串(json),二進(jìn)制流,...
*
* @param sid
* @return
*/
@RequestMapping("/queryName")
public String getNameById(Integer sid){
switch (sid){
case 1:
return "小紅";
case 2:
return "小明";
case 3:
return "小剛";
default:
return "查無(wú)此人";
}
}
}
三.Eureka集群(偽)
1.eureka集群的工作模式

2.Eureka集群的搭建(偽集群)
1)將eureka服務(wù)端復(fù)制2份
2)修改eureka服務(wù)的端口(因?yàn)槭莻渭?,在一臺(tái)機(jī)器上,需要修改端口)
3)找到本機(jī)的hosts文件(C:\Windows\System32\drivers\etc\hosts),分別給本機(jī)設(shè)置3個(gè)主機(jī)名

注意:第一次修改hosts文件,一般沒有權(quán)限,所以需要修改下文件權(quán)限。
4)修改3個(gè)eureka服務(wù)的service-url配置
server:
port: 20002
spring:
application:
#配置微服務(wù)的名稱,以后所有的微服務(wù),都必須配置這個(gè)屬性,不然注冊(cè)中心的名稱會(huì)變成unable
name: eureka-server
#配置eureka的相關(guān)屬性
eureka:
client:
service-url:
#配置eureka的注冊(cè)地址
#defaultZone沒有提示,必須手寫
defaultZone: http://eureka1:20000/eureka,http://eureka2:20001/eureka,http://eureka3:20002/eureka
#當(dāng)前的微服務(wù)就是注冊(cè)中心,注冊(cè)中心不能從注冊(cè)中心抓取服務(wù),所以該配置需要配置成false
fetch-registry: false
#使得當(dāng)前微服務(wù)不注冊(cè)到注冊(cè)中心
register-with-eureka: false
5)啟動(dòng)三臺(tái)eureka服務(wù)
6)所有的微服務(wù)的注冊(cè)地址必須改成eureka集群的地址
server:
port: 8080
#微服務(wù)的名稱
spring:
application:
#特別注意:微服務(wù)的名稱一定不要用下劃線
name: micro-student
eureka:
client:
service-url:
#配置注冊(cè)中心的地址,因?yàn)槲⒎?wù)需要注冊(cè),也需要抓取服務(wù)所以另兩個(gè)配置默認(rèn)為true,可以不用配置.
defaultZone: http://eureka1:20000/eureka,http://eureka2:20001/eureka,http://eureka3:20002/eureka
四.Eureka的自我保護(hù)機(jī)制
1.自我保護(hù)機(jī)制
Eureka如果收到的微服務(wù)心跳相對(duì)于應(yīng)該收到的微服務(wù)心跳來說,如果低于了85%,那么就會(huì)觸
發(fā)Eureka的自我保護(hù)機(jī)制。一旦自我保護(hù)機(jī)制啟動(dòng),Eureka會(huì)保護(hù)所有的微服務(wù),不被移除,哪
怕當(dāng)前微服務(wù)已經(jīng)下線,仍然會(huì)保留到Eureka微服務(wù)列表中。
2.Eureka自我保護(hù)機(jī)制觸發(fā)后的表現(xiàn)

3. 自我保護(hù)機(jī)制的作用
(1)為什么Eureka要一種看起來如此傻逼的設(shè)計(jì)?- 自我保護(hù)機(jī)制是為了保證微服務(wù)間的高可用性,
避免腦裂的影響
(2)為什么Zookeeper要設(shè)計(jì)一個(gè)過半數(shù)存活原則?感覺特別浪費(fèi)資源? - 為了讓整個(gè)集群一定只有
一個(gè)Leader
(3)CAP原則:任何一個(gè)分布式系統(tǒng),都只能滿足CAP中的2個(gè)原則,因?yàn)榉謪^(qū)問題不可避免,所以P
是必選的,一般的系統(tǒng),都是從C和A中做選擇。Zookeeper是CP設(shè)計(jì),而Eureka是AP設(shè)計(jì)。
C(一致性)
A(可用性)
P(分區(qū)容錯(cuò)性)
4.zookeeper的過半數(shù)存活的設(shè)計(jì)

5.Eureka的自我保護(hù)設(shè)計(jì)

6.總結(jié)
自我保護(hù)機(jī)制一旦啟動(dòng),確實(shí)會(huì)保護(hù)那些真的出問題的微服務(wù),所以Eureka設(shè)計(jì)了一個(gè)閾值,當(dāng)
15%的心跳都丟失的時(shí)候,才會(huì)發(fā)生自我保護(hù)機(jī)制。因?yàn)檫@個(gè)時(shí)候,相對(duì)于15%的機(jī)器掛掉來
說,發(fā)生腦裂的概率大一些。但是有沒有可能真的是15%機(jī)器掛掉了?這個(gè)時(shí)候Eureka是不能幫
我們判斷的,需要客戶端本身有一定的容錯(cuò)機(jī)制,比如斷路器
7.在實(shí)際項(xiàng)目中,到底是AP設(shè)計(jì)好,還是CP設(shè)計(jì)好?
取決于業(yè)務(wù),對(duì)于注冊(cè)中心來說,偶爾拿到出問題的微服務(wù),其實(shí)對(duì)于整個(gè)架構(gòu)來講沒有太大問題,微服務(wù)架構(gòu)最害怕整個(gè)注冊(cè)中心服務(wù)不可用,因此對(duì)于注冊(cè)中心來說,AP(高可用)設(shè)計(jì)強(qiáng)于CP(一致性)設(shè)計(jì)。并不是說一致性不好,對(duì)于分布式鎖的業(yè)務(wù)來講,一定要CP,不然整個(gè)集群的業(yè)務(wù)會(huì)混亂.