1. Eureka
1.1 Eureka了解
什麼是服務治理
Spring Cloud封裝了
Netflix
公司開發的Eurkeka子產品來實作服務治理
在傳統的
rpc遠端調用
架構中,管理每個服務與服務之間依賴關系比較複雜。管理比較複雜服務之間的依賴關系可以實作服務調用,負載均衡,容錯等,實作服務發現與注冊。
什麼是服務注冊與發現
Eureka采用了CS的設計架構,
Eureka Server
作為服務注冊功能的伺服器,它是服務注冊中心,而系統中的其他微服務,使用Eureka的用戶端連接配接到Eureka Server并維持 心跳連結 。這樣系統的維護人員就可以通過Eureka Server來監控系統中各個微服務是否正常運作。
在服務注冊與發現中,有一個注冊中心。當伺服器啟動的時候會把目前自己伺服器的資訊(比如:服務位址、通訊位址等)以别名方式注冊到注冊中心中。另一方(消費者/服務提供者)以該别名的方式去注冊中心上擷取到實際的服務通訊位址,然後再實作本地RPC調用。RPC遠端調用架構核心設計思想:在于注冊中心,因為使用注冊中心管理每個服務與服務之間的依賴關系(服務治理概念)。在任何RPC遠端架構中,都會有一個注冊中心(存放服務位址相關資訊(接口位址))
下圖左邊是Eureka系統架構,右邊是Dubbo系統架構
Eureka包含兩個元件:Eureka Server 和 Eureka Client
-
Eureka Server
提供服務注冊中心
各個微服務節點通過配置啟動後,會在EurekaServer中進行注冊,這樣EurekaServer中的服務系統資料庫中将存儲所有可用服務節點的資訊,服務節點的資訊可以在界面中直覺看到。
-
Eureka Client
通過注冊中心進行通路
是一個Java用戶端,用于簡化Eureka Server的互動,用戶端同時也具備一個内置的、使用輪詢(round-robin)負載算法的負載均衡器。在應用啟動後将會向Eureka Server發送心跳(預設周期為30秒)。如果Eureka Server在多個心跳周期内沒有接收到某個節點的心跳,Eureka Server将會從服務系統資料庫中表把這個服務節點移除(預設90秒)
1.2 單機Eureka建構步驟
IDEA生成EurekaServer端服務注冊中心
類似物業公司
- 建Module cloud-eureka-server7001
- pom.xml
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 公共子產品 -->
<dependency>
<groupId>com.polaris</groupId>
<artifactId>cloud-api-common</artifactId>
<version>${project.version}</version>
</dependency>
<!-- boot web actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 通用配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
SpringBoot 1x 和 SpringBoot 2x對比
<!-- SpringBoot1.X對應的SpringCloud eureka,不要再用了!!! -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- SpringBoot2.X對應的SpringCloud eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
- yml配置檔案
server:
port: 7001
eureka:
instance:
hostname: localhost # eureka服務端的執行個體名稱
client:
# false表示不向注冊中心注冊自己
register-with-eureka: false
# false表示自己端就是注冊中心,我的職責就是維護服務執行個體,并不需要去檢索服務
fetch-registry: false
service-url:
# 設定與Eureka Server互動的位址查詢服務和注冊服務都需要依賴這個位址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- 主啟動類
@SpringBootApplication
@EnableEurekaServer // 聲明我是服務注冊中心
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain.class);
}
}
- 測試
運作該Eureka Server主啟動類,通路 localhost:7001,就會看到下面的服務注冊中心,可以發現目前還沒有任何服務入駐進服務注冊中心中,在應用中顯示:No instances available
EurekaClient端 服務提供者cloud-provider-payment8001修改
- pom.xml添加依賴
<!-- 注意:與Eureka Server一樣,這裡SpringBoot2x不再使用spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- yml配置檔案添加配置
spring:
application:
name: cloud-payment-service # 入駐Eureka服務注冊中心的服務名稱
eureka:
client:
# 表示是否将自己注冊進EurekaServer預設為true。
register-with-eureka: true
# 是否從EurekaServer抓取已有的注冊資訊,預設為true。
# 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
fetchRegistry: true
service-url:
# 單機版
defaultZone: http://localhost:7001/eureka # 入駐的服務注冊中心位址
- 主啟動類添加注解
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain {
public static void main(String[] args) {
SpringApplication.run(PaymentMain.class,args);
}
}
- 測試
注意先要啟動EurekaServer,因為有了服務注冊中心具體的服務提供者才能後向其中注冊自己的服務
可以發現注冊到服務注冊中心的服務名(圖中藍框)即為我們在yml配置檔案中設定的服務名,下面頁面中出現的 紅字 是Eureka的 自我保護機制
EurekaClient端 服務消費者cloud-sonsumer-order80修改
與服務提供者cloud-provider-payment8001修改差不多,不再贅述
spring:
application:
name: cloud-order-service # 入駐Eureka服務注冊中心的服務名稱
eureka:
client:
# 表示是否将自己注冊進EurekaServer預設為true。
register-with-eureka: true
# 是否從EurekaServer抓取已有的注冊資訊,預設為true。
# 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
fetchRegistry: true
service-url:
# 單機
defaultZone: http://localhost:7001/eureka
總結
此時再回看最開始的Eureka系統架構,在服務注冊中心和服務提供者沒有叢集的情況下,7001端口的微服務就對應了服務注冊中心,而該服務不需要向服務注冊中心注冊自己,8001端口的微服務作為服務提供方入住到服務注冊中心,8002端口的微服務作為服務消費方也同樣注冊到服務注冊中心
1.3 叢集Eureka建構步驟
叢集Eureka原理
服務注冊中心Eureka Server中分為
服務注冊
和
服務發現
,服務注冊過程将服務資訊注冊進服務注冊中心,服務發現過程從服務注冊中心上擷取服務資訊,而這個過程的實質就是:将服務名作為key存儲,然後根據value取得服務的調用位址。
整個Eureka的過程如下:
- 先啟動Eureka注冊中心
- 啟動服務提供者服務
- 服務提供者服務将自身資訊(比如服務位址)以别名方式注冊到Eureka注冊中心
- 消費者服務在需要調用接口時,使用服務别名到注冊中心擷取實際的RPC遠端調用位址
- 消費者獲得調用位址後,底層實際是利用 HttpClient 技術實作遠端調用
- 消費者獲得服務位址後會緩存字本地JVM記憶體中,預設每間隔30秒更新一次服務調用位址
那麼微服務RPC遠端服務調用最核心的是什麼呢?
高可用!如果注冊中心隻有一個,而這個注冊中心出現了故障那麼整個微服務就直接GG了,整個微服務環境就不可用了,是以應該搭建Eureka注冊中心叢集, 實作 負載均衡 + 故障容錯
那怎麼實作Eureka注冊中心的叢集呢?用一句話總結就是 - 互相注冊,互相守望。如下圖所示,服務注冊中心實作互相注冊讓彼此都知道對方的存在,也就是注冊中心叢集中的每一個注冊中心都知道整個叢集中的其他注冊中心,比如如果有三個注冊服務中心7001,7002,7003,那麼就将7002和7003注冊給7001, 将7002和7001注冊給7003, 将7003和7001注冊給7002, 以此類推,而這些個注冊服務中心 作為一個整體對外看做一個注冊服務中心。
Eureaka叢集環境建構
參考cloud-eureka-server7001建立一個服務注冊中心cloud-eureka-server7002
- 修改pom.xml
copy複制cloud-eureka-server7001的POM檔案即可
- 修改映射配置(域名映射),用不同的端口号來映射同一個位址
找到C:\Windows\System32\drivers\etc路徑下的hosts檔案,将其内容修改成如下内容:
# learn-spring-cloud
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
- yml配置檔案,7001與7002都修改一下(以前是單機)
以前是單機版配置,而現在已經有兩個注冊中心可以看做兩台機器,顯然 hostname 不能再叫localhost
更改了服務端的執行個體名稱後,最重要的是在defaultZone中将自己注冊給其他注冊中心
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com # eureka服務端的執行個體名稱
client:
register-with-eureka: false
fetch-registry: true
service-url:
# 互相注冊,互相守望
defaultZone: http://eureka7002.com:7002/eureka/
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com
client:
register-with-eureka: false
fetch-registry: true
service-url:
# 互相注冊,互相守望
defaultZone: http://eureka7001.com:7001/eureka/
- 測試
兩個服務中心已經完成了互相注冊。首頁面DS Replicas下面的資訊就表示是這個Eureka Server相鄰節點且這些節點加上自己互為一個叢集。
- 将服務提供者8001和服務消費者80釋出到2台Eureka叢集配置中
修改其配置檔案即可,就是将自己的微服務注冊到每一個服務注冊中心裡去,見配置檔案中的defaultZone。
eureka:
client:
# 表示是否将自己注冊進EurekaServer預設為true。
register-with-eureka: true
# 是否從EurekaServer抓取已有的注冊資訊,預設為true。
# 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
fetchRegistry: true
service-url:
# 叢集版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
- 測試
服務提供者叢集環境建構
- 參考8001服務建立8002服務
- pom.xml
8002和8001的POM檔案一樣
- yml配置檔案
将端口改為8002,其他和8001相同,兩個微服務 對外暴露的服務名相同均為cloud-payment-service 進而構成叢集。
server:
port: 8002
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 目前資料源操作類型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驅動包
url: jdbc:mysql://mpolaris.top:3306/cloud-test?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
eureka:
client:
# 表示是否将自己注冊進EurekaServer預設為true。
register-with-eureka: true
# 是否從EurekaServer抓取已有的注冊資訊,預設為true。
# 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
fetchRegistry: true
service-url:
# 叢集版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.polaris.springcloud.entities # 所有Entity别名類所在包
- 主啟動類與業務類與8001基本一緻
主啟動類名字分别為PaymentMain8001與PaymentMain8002
- controller
修改controller,添加端口号以區分這兩個具體的微服務:讀取配置檔案中設定的端口号。8002的修改同8001。
@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@PostMapping("/save")
public CommonResult save(@RequestBody Payment payment) {
int result = paymentService.save(payment);
log.info("===> result: " + result);
if(result > 0) {
return new CommonResult(200,
"儲存到資料庫成功,端口号:" + serverPort,result);
}
return new CommonResult(400,"儲存到資料庫失敗",null);
}
@GetMapping("/get/{id}")
public CommonResult<Payment> save(@PathVariable("id") Long id) {
Payment paymentById = paymentService.getPaymentById(id);
log.info("===> payment: " + paymentById);
if(paymentById != null) {
return new CommonResult(200,
"查詢成功,端口号:" + serverPort,paymentById);
}
return new CommonResult(400,"查詢失敗",null);
}
}
- 測試
如圖可以看到此時服務注冊中心構成叢集,而相同名字的服務提供方的實際提供者已經出現了兩個,分别是8001和8002,也就是說服務提供方微服務也實作了叢集。
負載均衡
- 發現問題:通過服務消費者80通路,隻能通路到服務提供者8001
- 也就是說每次通路的具體微服務都是8001端口的CLOUD-PAYMENT-SERVICE服務,這明顯是不符合業務邏輯的,原因就是在消費方代碼中我們将服務通路位址寫死了,沒有實作負載均衡,這顯然是不對的,是以我們應該讓80通路服務名而不是具體的服務,即應該将其改為服務提供者 微服務名稱!
- 同時在配置檔案中通過
注解賦予RestTemplate負載均衡能力,該負載均衡預設為輪詢方式。是以将80服務的配置檔案修改如下:@LoadBalanced
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //使用該注解賦予RestTemplate負載均衡的能力
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
- 然後重新開機80端口,發現每次通路
服務時,具體的微服務在8001和8002之間進行輪詢切換。CLOUD-PAYMENT-SERVICE
- 當然此時負載均衡我們還沒有用到
,在Ribbon和Eureka整合後,消費者可以直接調用服務而不用再關心位址和端口号,且該服務還有負載功能。Ribbon
總結
1.4 actuator微服務資訊完善
主機名稱:服務名稱修改
發現問題:在注冊中心顯示的微服務中,我們發現服務名含有主機名稱,這顯然不是我們希望看到的
怎麼能解決這個問題呢,隻需要修改服務提供方(8001和8002)的配置檔案,向其中的eureka部分加入
instance執行個體
即可配置該服務顯示的服務名稱
instance:
instance-id: payment8001
最終的整體配置檔案如下:
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 目前資料源操作類型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驅動包
url: jdbc:mysql://mpolaris.top:3306/cloud-test?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 1234321
eureka:
client:
# 表示是否将自己注冊進EurekaServer預設為true。
register-with-eureka: true
# 是否從EurekaServer抓取已有的注冊資訊,預設為true。
# 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
fetchRegistry: true
service-url:
# 叢集版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
# 單機版
# defaultZone: http://localhost:7001/eureka
instance:
instance-id: payment8001
mybatis:
mapperLocations: classpath:mapper/*.xml
type-aliases-package: com.polaris.springcloud.entities
8002服務的修改同上,此時再通路注冊中心,看到的服務具體名稱中就沒有主機名了,而是我們配置好的服務名稱:
通路資訊有IP資訊提示
發現問題:我們在滑鼠移動到具體服務時,提示的位址資訊中并沒有服務所在具體主機的IP位址,這在開發中不友善定位具體微服務。
解決方式仍然是通過配置檔案,在配置檔案中向其中的eureka部分加入
優先ip位址
即可配置該服務通路路徑可以顯示IP位址:
instance:
prefer-ip-address: true # 通路路徑可以顯示IP位址
最終的配置檔案如下:
eureka:
client:
# 表示是否将自己注冊進EurekaServer預設為true。
register-with-eureka: true
# 是否從EurekaServer抓取已有的注冊資訊,預設為true。
# 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
fetchRegistry: true
service-url:
# 叢集版
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
instance:
instance-id: payment8001
prefer-ip-address: true #通路路徑可以顯示IP位址
1.5 服務發現Discovery
對于注冊進Eureka服務注冊中心的微服務,可以
通過服務發現來擷取該服務的資訊
。
修改微服務的Controller
向其中注入DiscoveryClient,并編寫相應Controller方法
DiscoveryClient對象中的
getServices
方法用于擷取服務清單的資訊,也就是有哪些服務,如cloud-payment-service服務,
getInstances
方法用于擷取服務清單對應的具體服務執行個體,如cloud-payment-service服務對應的8001和8002服務。
import org.springframework.cloud.client.discovery.DiscoveryClient;
//...
@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
//...
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/discovery")
public Object discovery() {
//擷取服務清單的資訊
List<String> services = discoveryClient.getServices();
for (String service : services) {
log.info("===> service:" + service);
}
//根據微服務名稱擷取具體服務執行個體
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance : instances) {
log.info("===> " + instance.getServiceId()
+ "\t" + instance.getHost()
+ "\t" + instance.getPort()
+ "\t" + instance.getUri());
}
return this.discoveryClient;
}
//...
}
修改主啟動類
隻需要在主啟動類上添加注解
@EnableDiscoveryClient
,修改後的主啟動類:
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
測試
通路位址http://localhost:8001/payment/discovery,我們可以看到擷取的服務資訊,即完成了服務發現:
背景也對服務清單進行了日志列印:
<img src="https://gitee.com/mp2333/blog-img/raw/master/SpringCloud/20210126234348.png" alt="image-20210126234348515" https://gitee.com/mp2333/blog-img/raw/master/SpringCloud/20210126234854.png />
1.6 Eureka自我保護(屬于CAP裡面的AP分支)
自我保護機制
保護模式主要用于一組用戶端和EurekaServer之間存在網絡分區場景下的保護。一旦進入保護模式,Eureka Server将會嘗試保護其服務系統資料庫中的資訊,不再删除服務系統資料庫中的資料,也就是不會登出任何微服務。換句話說就是,某時刻某一個微服務不可用了,Eureka不會立刻清理,而是依舊會對該微服務的資訊進行儲存。
如果在Eureka Server的首頁看到以下提示,說明Eureka進入了保護模式
産生原因
為什麼會産生Eureka自我保護機制? => 為了防止 EurekaClient可以正常運作,但是與EurekaServer網絡不通情況下,EurekaServer不會立刻将EurekaClient服務剔除。
什麼是自我保護模式? => 預設情況下,如果EurekaServer在一定時間内沒有接收到某個微服務執行個體的心跳,EurekaServer将會登出該執行個體(預設90秒)。但是當網絡分區故障發生(延時、卡頓、擁擠)時,微服務與EurekaServer之前無法正常通信,以上行為可能變得非常危險 - 因為微服務本身是健康的,隻是由于網絡問題連結不到EurekaServer,此時本不應該登出這個微服務。Eureka通過“自我保護模式”來解決這個問題 :當EurekaServer節點在短時間内丢失過多用戶端時(可能發生了網絡分區故障,網絡延時),那麼這個節點就會進入自我保護模式。在自我保護模式中,EurekaServer會保護服務系統資料庫中的資訊,不再登出任何服務執行個體,甯可保留錯誤的服務注冊資訊,也不盲目登出任何可能健康的服務執行個體。使用自我保護模式,可以讓Eureka叢集更加的健壯、穩定。
怎麼禁止自我保護
先在EurekaServer端修改配置檔案即可設定關閉自我保護機制
eureka:
server:
# 關閉自我保護機制,保證不可用服務被及時剔除。預設為true開啟
enable-self-preservation: false
# 時間間隔,機關ms
eviction-interval-time-in-ms: 2000
然後在EurekaClient端修改配置檔案
eureka:
instance:
instance-id: payment8001
# Eureka客戶單向服務端發送心跳的時間間隔,默然是30秒,這裡改成1秒
lease-renewal-interval-in-seconds: 1
# Eureka服務端在收到最後一次心跳後等待時間上限,默然為90秒,逾時将剔除服務,這裡改成2秒
lease-expiration-duration-in-seconds: 2
這樣就會使EurekaClient用戶端的微服務很快死亡。
2. Zookeeper
2.1 Eureka停止更新
https://github.com/Netflix/eureka/wiki
我們可以使用SpringCloud整合Zookeeper替代Eureka
2.2 Zookeeper了解
Zookeeper是一個分布式協調工具,可以實作注冊中心功能
安裝Zookeeper
# 關閉Linux伺服器防火牆(關閉預設端口2181也行)
# 2181 對Client端提供服務的端口
# 3888 選舉Leader
# 2888 叢集内的機器通訊使用。(Leader使用此端口)
systemctl stop firewalld
systemctl status firewalld
# 我這裡使用的是zookeeper-3.4.11.tar.gz,解壓即可
# bin目錄下啟動zookeeper伺服器
./zkServer.sh start
# 連接配接zookeeper用戶端
# 如果是連接配接同一台主機上的zk程序,那麼直接運作bin/目錄下的kCli.sh,即可連接配接上zk。
# 直接執行zkCli.sh指令預設以主機号 127.0.0.1,端口号 2181 來連接配接zk
# 如果要連接配接不同機器上的zk,可以使用 -server 參數,例如:
./zkCli.sh -server 192.168.0.1:2181
# 啟動報錯?
# grep: /usr/local/zookeeper-3.4.11/bin/../conf/zoo.cfg: No such file or directory
# 這裡的原因是因為下載下傳下來的zoo.cfg名字是zoo_sample.cfg,隻需要改名字即可
mv zoo_sample.cfg zoo.cfg
Zookeeper伺服器取代Eureka伺服器,zk作為服務注冊中心
2.3 服務提供者
建立cloud-provider-payment8004
pom.xml
<dependencies>
<!-- 公共子產品 -->
<dependency>
<groupId>com.polaris</groupId>
<artifactId>cloud-api-common</artifactId>
<version>${project.version}</version>
</dependency>
<!-- boot web actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--SpringBoot整合Zookeeper用戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
<!-- 通用配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml配置檔案
server:
# 8004表示注冊到zookeeper伺服器的支付服務提供者端口号
port: 8004
spring:
application:
# 服務别名 - 注冊zookeeper到注冊中心的名稱
name: cloud-provider-payment
cloud:
zookeeper:
# zookeeper通路位址
connect-string: mpolaris.top:2181
主啟動類
@SpringBootApplication
//該注解用于向使用consul或zookeeper作為注冊中心時注冊服務
@EnableDiscoveryClient
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class,args);
}
}
controller
@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping(value = "/zk")
public String paymentZk() {
return "===> SpringCloud with zookeeper:"
+ serverPort
+ "\t"
+ UUID.randomUUID().toString();
}
}
啟動8004注冊進zookeeper
- 啟動zk: zkServer.sh start
- 啟動8004後報錯
- why?
- 解決zookeeper版本jar包沖突問題
- 排除zk沖突後的新pom.xml
<!--SpringBoot整合Zookeeper用戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<!-- 先排除自帶的zookeeper3.5.3 -->
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加zookeeper3.4.11版本zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.11</version>
</dependency>
驗證測試1
通路 http://localhost:8004/payment/zk
驗證測試2
獲得json串後用線上工具檢視如下
思考
服務節點是臨時節點還是持久節點? => 臨時節點
2.4 服務消費者
建立cloud-consumerzk-order80
pom.xml
<dependencies>
<!-- 公共子產品 -->
<dependency>
<groupId>com.polaris</groupId>
<artifactId>cloud-api-common</artifactId>
<version>${project.version}</version>
</dependency>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringBoot整合Zookeeper用戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<!--先排除自帶的zookeeper3.5.3-->
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加zookeeper3.4.11版本-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 通用配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
yml配置檔案
server:
port: 80
spring:
application:
# 服務别名
name: cloud-consumer-order
cloud:
zookeeper:
# 注冊到zookeeper位址
connect-string: mpolaris.top:2181
主啟動類
@SpringBootApplication
@EnableDiscoveryClient
public class OrderZkMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderZkMain80.class, args);
}
}
業務類
配置類注入RestTemplate
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
controller
@RestController
@Slf4j
@RequestMapping("/consumer")
public class OrderZkController {
public static final String INVOKE_URL = "http://cloud-provider-payment";
@Resource
private RestTemplate restTemplate;
/**
* http://localhost/consumer/payment/zk
* @return
*/
@GetMapping("/payment/zk")
public String paymentInfo() {
return restTemplate.getForObject(INVOKE_URL
+ "/payment/zk", String.class);
}
}
測試驗證
通路位址 http://localhost/consumer/payment/zk
3. Consul
3.1 了解
什麼是Consul
Consul是一套開源的
分布式服務發現
和
配置管理
系統,由HashiCorp公司用
Go語言
開發。提供了微服務系統中的
服務治理
、
配置中心
、
控制總線
等功能。這些功能中的每一個都可以根據單獨需要使用,也可以一起使用以建構全方位的服務網絡,總之Consul提供了一種完整的服務網絡解決方案。其官方介紹見Consul官網。
它具有很多優點。包括
基于raft協定
,比較簡潔;支援
健康檢查
,同時支援
HTTP
和
DNS
協定,支援跨資料中心的
WAN叢集
,提供圖形界面,跨平台,支援Linux/Mac/Windows。
Consul作用
-
:提供HTTP和DNS兩種發現方式。服務發現Service Discovery
-
:支援多張方式,HTTP、TCP、Docker、Shell腳本定制化健康監測Health Checking
-
:Key、Value的存儲方式KV存儲
-
:Consul支援多資料中心多資料中心
-
可視化Web界面
3.2 安裝并運作Consul
在官網下載下傳Windows的64位版本Consul後下載下傳的是zip壓縮包,将壓縮包解壓後裡面隻有一個
consul.exe
檔案。在該路徑下輕按兩下其exe檔案就可以運作Consul,進入cmd指令行運作,輸入以下指令檢視Consul的版本号資訊,我用的是1.7.3版本:
consul --version
當然也可以不用有點low的輕按兩下Consul啟動,可以在指令行中用開發模式啟動,輸入以下指令
consul agent -dev
啟動後我們通路 localhost:8500 ,就可以看到Consul和Eureka一樣,有一個前端可視化Web界面。這樣的話Consul服務注冊中心就已經啟動了,且運作在8500端口
3.3 服務提供者
建立cloud-provider-consul-payment8006
pom.xml
在Eureka的服務提供方中我們引入了如下的依賴:
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
而我們現在服務注冊中心用的不再是Eureka而是Consul,隻需要被Eureka Client的依賴更改為如下依賴,這樣引入了讓Consul服務注冊中心發現自己微服務的相關jar包。
<!--SpringCloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
yml配置檔案
server:
port: 8006 # consul服務端口号
spring:
application:
name: consul-provider-payment
# consul注冊中心位址
cloud:
consul:
host: localhost
port: 8500
discovery:
#hostname: 127.0.0.1
service-name: ${spring.application.name}
主啟動類
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class);
}
}
業務類Controller
@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/consul")
public String paymentConsul() {
return "springcloud with consul: "
+ serverPort
+ "\t"
+ UUID.randomUUID().toString();
}
}
測試
經過以上配置,我們會在Consul服務注冊中心中發現入駐的 consul-provider-payment 服務
3.4 服務消費者
建立cloud-consumer-consul-order80
pom.xml
為了可以将自己注冊到Consul服務注冊中心,和服務提供方一樣,仍然是引入如下依賴:
<!--SpringCloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
yml配置檔案
和服務提供方幾乎完全相同,隻需要修改下自己的端口号和自己的服務名稱
# consul服務端口号
server:
port: 80
spring:
application:
name: cloud-consumer-order
# consul注冊中心位址
cloud:
consul:
host: localhost
port: 8500
discovery:
# hostname: 127.0.0.1
service-name: ${spring.application.name}
主啟動類
@SpringBootApplication
@EnableDiscoveryClient //用于向使用Consul或Zookeeper作為注冊中心時注冊服務
public class OrderConsulMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderConsulMain80.class);
}
}
配置類注入RestTemplate
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //使用該注解賦予RestTemplate負載均衡的能力
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
業務類Controller
@RestController
@Slf4j
public class OrderConsulController {
//要通路的服務提供方的微服務名稱
public static final String INVOKE_URL = "http://consul-provider-payment";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/consul")
public String paymentInfo() {
return restTemplate.getForObject(INVOKE_URL
+ "/payment/consul", String.class);
}
}
測試
将服務消費方微服務啟動後,在Consul服務注冊中心我們可以發現同時有了提供者和消費者
服務提供方自測是沒有問題的,下面我們通過服務消費方來通路提供方服務,發現也可以正常通路
4. 三者異同
4.1 比較
元件名 | 編寫語言 | CAP | 服務健康檢查 | 對外暴露接口 | SpringCloud內建 |
---|---|---|---|---|---|
Eureka | Java | AP | 可配支援 | HTTP | 已內建 |
Consul | Go | CP | 支援 | HTTP/DNS | 已內建 |
Zookeeper | Java | CP | 支援 | 用戶端 | 已內建 |
4.2 什麼是CAP
CAP理論:首先我們要知道CAP對應的都是什麼。
C | A | P |
---|---|---|
Consistency | Available | Partition tolerance |
強一緻性 | 可用性 | 分區容錯性 |
所謂CAP原則又稱CAP定理,指的是在一個分布式系統中,一緻性、可用性、分區容錯性。CAP 原則指的是這三個要素 最多隻能同時實作兩點,不可能三者兼顧。在分布式架構中,P永遠要求被保證,是以目前的分布式架構隻有AP和CP兩種。是以根據CAP原理将NoSQL資料庫分成了滿足CA原則、滿足CP原則和滿足AP原則三大類:
-
:單點叢集,滿足一緻性、可用性的系統,通長在可擴充性上不太強大。CA
-
:滿足一緻性、分許容錯性的系統,通常性能不是特别高。CP
-
:滿足可用性、分區容錯性的系統,通常可能對一緻性要求低一些。AP