項目地址:https://gitee.com/zwtgit/spring-cloud-study
前言
微服務架構4個核心問題?
- 服務很多,客戶端該怎么訪問?
- 這么多服務? 服務之間如何通信?
- 這么多服務? 如何治理?
- 服務掛了怎么辦?
解決方案:
- Spring Cloud NetFlix 一站式解決方案!
api網關,zuu1組件
Feign ---HttpClinet ---- Http通信方式,同步,阻塞
服務注冊發現: Eureka
熔斷機制: Hystrix ………… - Apache Dubbo ZooKeeper 半自動,需要整合別人的東西
- Spring Cloud Alibaba 一站式解決方案!更簡單
新概念:服務網格,Server Mesh
但是他們的共同點是解決這幾個問題:
-
API網關,服務路由
-
HTTP,RPC框架,異步調用
-
服務注冊與發現,高可用
-
熔斷機制,服務降級
為什么有這幾個問題,還是因為網絡的不可靠,所以我們要研究解決他們的方案。
微服務概述
什么是微服務?
微服務(Microservice Architecture) 是近幾年流行的一種架構思想,關於它的概念很難一言以蔽之。
究竟什么是微服務呢?我們在此引用ThoughtWorks 公司的首席科學家 Martin Fowler 於2014年提出的一段話:
原文:https://martinfowler.com/articles/microservices.html
漢化:https://www.cnblogs.com/liuning8023/p/4493156.html
- 就目前而言,對於微服務,業界並沒有一個統一的,標准的定義。
- 但通常而言,微服務架構是一種架構模式,或者說是一種架構風格,它提倡將單一的應用程序划分成一組小的服務,每個服務運行在其獨立的自己的進程內,服務之間互相協調,互相配置,為用戶提供最終價值,服務之間采用輕量級的通信機制(HTTP)互相溝通,每個服務都圍繞着具體的業務進行構建,並且能夠被獨立的部署到生產環境中,另外,應盡量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應該根據業務上下文,選擇合適的語言,工具(Maven)對其進行構建,可以有一個非常輕量級的集中式管理來協調這些服務,可以使用不同的語言來編寫服務,也可以使用不同的數據存儲。
再來從技術維度角度理解下:
微服務化的核心就是將傳統的一站式應用,根據業務拆分成一個一個的服務,徹底地去耦合,每一個微服務提供單個業務功能的服務,一個服務做一件事情,從技術角度看就是一種小而獨立的處理過程,類似進程的概念,能夠自行單獨啟動或銷毀,擁有自己獨立的數據庫。
微服務與微服務架構
微服務
強調的是服務的大小,它關注的是某一個點,是具體解決某一個問題/提供落地對應服務的一個服務應用,狹義的看,可以看作是IDEA中的一個個微服務工程,或者Moudel。 IDEA 工具里面使用Maven開發的一個個獨立的小Moudel,它具體是使用SpringBoot開發的一個小模塊,專業的事情交給專業的模塊來做,一個模塊就做着一件事情。 強調的是一個個的個體,每個個體完成一個具體的任務或者功能。
微服務架構
一種新的架構形式,Martin Fowler 於2014年提出。
微服務架構是一種架構模式,它體長將單一應用程序划分成一組小的服務,服務之間相互協調,互相配合,為用戶提供最終價值。每個服務運行在其獨立的進程中,服務與服務之間采用輕量級的通信機制(如HTTP)互相協作,每個服務都圍繞着具體的業務進行構建,並且能夠被獨立的部署到生產環境中,另外,應盡量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言、工具(如Maven)對其進行構建。
微服務優缺點
優點
- 單一職責原則;
- 每個服務足夠內聚,足夠小,代碼容易理解,這樣能聚焦一個指定的業務功能或業務需求;
- 開發簡單,開發效率高,一個服務可能就是專一的只干一件事;
- 微服務能夠被小團隊單獨開發,這個團隊只需2-5個開發人員組成;
- 微服務是松耦合的,是有功能意義的服務,無論是在開發階段或部署階段都是獨立的;
- 微服務能使用不同的語言開發;
- 易於和第三方集成,微服務允許容易且靈活的方式集成自動部署,通過持續集成工具,如jenkins,Hudson,bamboo;
- 微服務易於被一個開發人員理解,修改和維護,這樣小團隊能夠更關注自己的工作成果,無需通過合作才能體現價值;
- 微服務允許利用和融合最新技術;
- 微服務只是業務邏輯的代碼,不會和HTML,CSS,或其他的界面混合;
- 每個微服務都有自己的存儲能力,可以有自己的數據庫,也可以有統一的數據庫;
缺點
- 開發人員要處理分布式系統的復雜性;
- 多服務運維難度,隨着服務的增加,運維的壓力也在增大;
- 系統部署依賴問題;
- 服務間通信成本問題;
- 數據一致性問題;
- 系統集成測試問題;
- 性能和監控問題;
微服務技術棧

為什么選擇SpringCloud作為微服務架構
-
選型依據
- 整體解決方案和框架成熟度
- 社區熱度
- 可維護性
- 學習曲線
-
當前各大IT公司用的微服務架構有那些?
-
阿里:Dubbo+HFS
-
京東:JFS
-
新浪:Motan
-
當當網:DubboX
…
-
-
各微服務框架對比
SpringCloud入門
Spring官網:https://spring.io/

SpringCloud,基於SpringBoot提供了一套微服務解決方案, 包括服務注冊與發現,配置中心,全鏈路監控,服務網關,負載均衡,熔斷器等組件,除了基於NetFlix的開源組件做高度抽象封裝之外,還有一些選型中立的開源組件。
SpringCloud利用SpringBoot的開發便利性,巧妙地簡化了分布式系統基礎設施的開發, SpringCloud為開發人員提供了快速構建分布式系統的一些工具,包括配置管理,服務發現,斷路器,路由,微代理,事件總線,全局鎖,決策競選,分布式會話等等,他們都可以用SpringBoot的開發風格做到一鍵啟動和部署。
SpringBoot並沒有重復造輪子,它只是將目前各家公司開發的比較成熟,經得起實際考驗的服務框架組合起來,通過SpringBoot風格進行再封裝,屏蔽掉了復雜的配置和實現原理,最終給開發者留出了一套簡單易懂,易部署和易維護的分布式系統開發工具包。
SpringCloud是分布式微服務架構下的一站式解決方案,是各個微服務架構落地技術的集合體,俗稱微服務全家桶。
SpringCloud和SpringBoot的關系
- SpringBoot專注於開發方便的開發單個個體微服務;
- SpringCloud是關注全局的微服務協調整理治理框架,它將SpringBoot開發的一個個單體微服務,整合並管理起來,為各個微服務之間提供:配置管理、服務發現、斷路器、路由、為代理、事件總棧、全局鎖、決策競選、分布式會話等等集成服務;
- SpringBoot可以離開SpringCloud獨立使用,開發項目,但SpringCloud離不開SpringBoot,屬於依賴關系;
- SpringBoot專注於快速、方便的開發單個個體微服務,SpringCloud關注全局的服務治理框架;
Dubbo 和 SpringCloud技術選型
- 分布式+服務治理Dubbo
目前成熟的互聯網架構,應用服務化拆分+消息中間件
- Dubbo 和 SpringCloud對比
可以看一下社區活躍度:
https://github.com/spring-cloud
設計模式+微服務拆分

最大區別:Spring Cloud 拋棄了Dubbo的RPC通信,采用的是基於HTTP的REST方式
嚴格來說,這兩種方式各有優劣。雖然從一定程度上來說,后者犧牲了服務調用的性能,但也避免了上面提到的原生RPC帶來的問題。而且REST相比RPC更為靈活,服務提供方和調用方的依賴只依靠一紙契約,不存在代碼級別的強依賴,這個優點在當下強調快速演化的微服務環境下,顯得更加合適。
總結:
二者解決的問題域不一樣:Dubbo的定位是一款RPC框架,而SpringCloud的目標是微服務架構下的一站式解決方案。
SpringCloud能干嘛?
- Distributed/versioned configuration 分布式/版本控制配置
- Service registration and discovery 服務注冊與發現
- Routing 路由
- Service-to-service calls 服務到服務的調用
- Load balancing 負載均衡配置
- Circuit Breakers 斷路器
- Distributed messaging 分布式消息管理
- …
SpringCloud下載
官網:http://projects.spring.io/spring-cloud/
SpringCloud沒有采用數字編號的方式命名版本號,而是采用了倫敦地鐵站的名稱,同時根據字母表的順序來對應版本時間順序,比如最早的Realse版本:Angel,第二個Realse版本:Brixton,然后是Camden、Dalston、Edgware,目前最新的是Hoxton SR4 CURRENT GA通用穩定版。
學習資料:
SpringCloud Netflix 中文文檔:https://springcloud.cc/spring-cloud-netflix.html
SpringCloud 中文API文檔(官方文檔翻譯版):https://springcloud.cc/spring-cloud-dalston.html
SpringCloud中國社區:http://springcloud.cn/
SpringCloud中文網:https://springcloud.cc
Rest學習環境搭建
首先導入我們需要的依賴
<!--打包方式-->
<packaging>pom</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springCloud的依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SpringBoot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--數據庫-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--SpringBoot 啟動器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--日志測試~-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
API模塊
新建數據庫,Navicat中的springcloudstudy01,后面有多個數據庫,因為是一個分布式的項目。

然后編寫實體類,注意要序列化,ORM類表關系映射。
package com.zwt.springcloud.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true) //鏈式寫法
/*
Dept dept = new Dept()
dept.setDeptNo(11).setDname(''lijiatu).set…………
*/
public class Dept implements Serializable {
private Long deptno;
private String dname;
private String db_source;
public Dept(String dname) {
this.dname = dname;
}
}
這時候一個微服務就已經寫好了。
Provider-8001模塊
<!--因為要拿到實體類,所以要配置API模塊-->
<dependencies>
<dependency>
<groupId>groupId</groupId>
<artifactId>API</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<!--jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<!--熱部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.5.2</version>
</dependency>
</dependencies>
mybatis-config.xml里面編寫配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
dao層(或者叫mapper層)寫接口,再在mapper文件夾中編寫sql
package com.zwt.springcloud.dao;
import com.zwt.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DeptDao {
public boolean addDept(Dept dept);
public Dept queryDept(Long id);
public List<Dept> queryAll();
}
mapper層
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zwt.springcloud.dao">
<insert id="addDept" parameterType="Dept">
insert into dept (dname, db_source)
values (#{dname}, DATABASE())
</insert>
<select id="queryById" resultType="Dept" parameterType="Long">
select *
from dept
where deptno = #{deptno};
</select>
<select id="queryAll" resultType="Dept" parameterType="Long">
select *
from dept;
</select>
</mapper>
service層
package com.zwt.springcloud.service;
import com.zwt.springcloud.pojo.Dept;
import java.util.List;
public interface DeptService {
public boolean addDept(Dept dept);
public Dept queryById(Long id);
public List<Dept> queryAll();
}
//實現類
package com.zwt.springcloud.service;
import com.zwt.springcloud.dao.DeptDao;
import com.zwt.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
private DeptDao deptDao;
@Override
public boolean addDept(Dept dept) {
return deptDao.addDept(dept);
}
@Override
public Dept queryById(Long id) {
return deptDao.queryById(id);
}
@Override
public List<Dept> queryAll() {
return deptDao.queryAll();
}
}
controller層
package com.zwt.springcloud.controller;
import com.zwt.springcloud.pojo.Dept;
import com.zwt.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
//提供Restful服務
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/dept/add")
public boolean addDept(Dept dept) {
return deptService.addDept(dept);
}
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id) {
return deptService.queryById(id);
}
@PostMapping("/dept/list")
public List<Dept> queryAll() {
return deptService.queryAll();
}
}
啟動類
package com.zwt.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//啟動類
@SpringBootApplication
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class, args);
}
}
啟動后發現上面代碼中的 devtools版本不兼容,注釋即可。
啟動項目后報錯,Invalid bound statement (not found),后發現是DeptMapper.xml中
<mapper namespace="com.zwt.springcloud.dao.DeptDao">
寫成了
<mapper namespace="com.zwt.springcloud.dao">
導致沒有對應到相應的文件,所以編寫代碼的時候要細心,bug也不可避免,檢查要有條理,不着急。
consumer-80模塊
添加依賴
<?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>spring-cloud-study</artifactId>
<groupId>groupId</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>consumer-80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!--實體類+web-->
<dependencies>
<dependency>
<groupId>groupId</groupId>
<artifactId>API</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 80
controller
package com.zwt.spingcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author ML李嘉圖
* @version createtime: 2021-10-25
* Blog: https://www.cnblogs.com/zwtblog/
*/
@Configuration
public class ConfigBean {
// @Configuration----spring applicationContext.xml
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
package com.zwt.spingcloud.controller;
import com.zwt.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author ML李嘉圖
* @version createtime: 2021-10-25
* Blog: https://www.cnblogs.com/zwtblog/
*/
public class DeptConsumerController {
// 理解,消費者不應該有service層
// RestTemplate 供我們直接調用,注冊到spring中
@Autowired
private RestTemplate restTemplate;
private static final String REST_URL_PREFIX = "http://localhost:8001";
@RequestMapping("/consumer/dept/add")
public boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, boolean.class);
}
@RequestMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get" + id, Dept.class);
}
@RequestMapping("/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
然后編寫啟動類,先啟動provider載啟動consumer。
Eureka
什么是Eureka?
- Netflix在涉及Eureka時,遵循的就是API原則。
- Eureka是Netflix的有個子模塊,也是核心模塊之一。Eureka是基於REST的服務,用於定位服務,以實現雲端中間件層服務發現和故障轉移,服務注冊與發現對於微服務來說是非常重要的,有了服務注冊與發現,只需要使用服務的標識符,就可以訪問到服務,而不需要修改服務調用的配置文件了,功能類似於Dubbo的注冊中心,比如Zookeeper。
原理理解
Eureka基本的架構
- SpringCloud 封裝了Netflix公司開發的Eureka模塊來實現服務注冊與發現 (對比Zookeeper)。
- Eureka采用了C-S的架構設計,EurekaServer作為服務注冊功能的服務器,他是服務注冊中心。
- 而系統中的其他微服務,使用Eureka的客戶端連接到EurekaServer並維持心跳連接。這樣系統的維護人員就可以通過EurekaServer來監控系統中各個微服務是否正常運行,SpringCloud 的一些其他模塊 (比如Zuul) 就可以通過EurekaServer來發現系統中的其他微服務,並執行相關的邏輯。
Eureka 包含兩個組件:Eureka Server 和 Eureka Client。
-
Eureka Server 提供服務注冊,各個節點啟動后,回在EurekaServer中進行注冊,這樣Eureka Server中的服務注冊表中將會儲存所有課用服務節點的信息,服務節點的信息可以在界面中直觀的看到。
-
Eureka Client 是一個Java客戶端,用於簡化EurekaServer的交互,客戶端同時也具備一個內置的,使用輪詢負載算法的負載均衡器。在應用啟動后,將會向EurekaServer發送心跳 (默認周期為30秒) 。如果Eureka Server在多個心跳周期內沒有接收到某個節點的心跳,EurekaServer將會從服務注冊表中把這個服務節點移除掉 (默認周期為90s)。
-
三大角色
- Eureka Server:提供服務的注冊與發現
- Service Provider:服務生產方,將自身服務注冊到Eureka中,從而使服務消費方能狗找到
- Service Consumer:服務消費方,從Eureka中獲取注冊服務列表,從而找到消費服務
構建步驟(重點)-所有的服務收都是這個步驟
1. 導入依賴
2. 編寫配置文件
3. 開啟這個功能 @EnableXXX
4. 配置類
導入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
編寫配置文件
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
開啟這個功能 @EnableXXX
package com.zwt.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author ML李嘉圖
* @version createtime: 2021-10-25
* Blog: https://www.cnblogs.com/zwtblog/
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer_7001.class, args);
}
}
Eureka服務注冊
要注冊服務,所以在Provider-8001中添加Eureka的依賴並且編寫相應的配置。
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
info:
app.name: zwt
blog.name: https://www.cnblogs.com/zwtblog/
@EnableEurekaServer一下
Eureka自我保護機制
一句話總結就是:某時刻某一個微服務不可用,Eureka不會立即清理,依舊會對該微服務的信息進行保存!
- 默認情況下,當eureka server在一定時間內沒有收到實例的心跳,便會把該實例從注冊表中刪除(默認是90秒),但是,如果短時間內丟失大量的實例心跳,便會觸發eureka server的自我保護機制,比如在開發測試時,需要頻繁地重啟微服務實例,但是我們很少會把eureka server一起重啟(因為在開發過程中不會修改eureka注冊中心),當一分鍾內收到的心跳數大量減少時,會觸發該保護機制。可以在eureka管理界面看到Renews threshold和Renews(last min),當后者(最后一分鍾收到的心跳數)小於前者(心跳閾值)的時候,觸發保護機制,會出現紅色的警告:EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE. 從警告中可以看到,eureka認為雖然收不到實例的心跳,但它認為實例還是健康的,eureka會保護這些實例,不會把它們從注冊表中刪掉。
- 該保護機制的目的是避免網絡連接故障,在發生網絡故障時,微服務和注冊中心之間無法正常通信,但服務本身是健康的,不應該注銷該服務,如果eureka因網絡故障而把微服務誤刪了,那即使網絡恢復了,該微服務也不會重新注冊到eureka server了,因為只有在微服務啟動的時候才會發起注冊請求,后面只會發送心跳和服務列表請求,這樣的話,該實例雖然是運行着,但永遠不會被其它服務所感知。所以,eureka server在短時間內丟失過多的客戶端心跳時,會進入自我保護模式,該模式下,eureka會保護注冊表中的信息,不在注銷任何微服務,當網絡故障恢復后,eureka會自動退出保護模式。自我保護模式可以讓集群更加健壯。
- 但是我們在開發測試階段,需要頻繁地重啟發布,如果觸發了保護機制,則舊的服務實例沒有被刪除,這時請求有可能跑到舊的實例中,而該實例已經關閉了,這就導致請求錯誤,影響開發測試。所以,在開發測試階段,我們可以把自我保護模式關閉,只需在eureka server配置文件中加上如下配置即可:eureka.server.enable-self-preservation=false
DeptController.java新增方法, //獲取一些配置的信息,得到具體的微服務!
//獲取一些配置的信息,得到具體的微服務!
@Autowired
private DiscoveryClient client;
//注冊進來的微服務~,獲取一些消息~
@GetMapping("/dept/discovery")
public Object discovery() {
//獲取微服務列表的清單
List<String> services = client.getServices();
System.out.println("discovery=>services:" + services);
//得到一個具體的微服務信息,通過具體的微服務id,applicaioinName;
List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
for (ServiceInstance instance : instances) {
System.out.println(
instance.getHost() + "\t" + // 主機名稱
instance.getPort() + "\t" + // 端口號
instance.getUri() + "\t" + // uri
instance.getServiceId() // 服務id
);
}
return this.client;
}
Eureka集群配置
簡單復制一下

但是這三個現在還沒有關系,所以我們配置他們之間的關系。

改一下host之后這三個的配置文件是:

server:
port: 7001
eureka:
instance:
hostname: eureka7001.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
server:
port: 7003
eureka:
instance:
hostname: eureka7003.com
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
然后把發布服務中的配置也修改一下,啟動三個注冊中心,再啟動服務。
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

EurekaCAP原則和對比Zookeeper
CAP原則又稱CAP定理,指的是在一個分布式系統中,一致性(Consistency)、可用性(Availability)、分區容錯性(Partition tolerance)。CAP 原則指的是,這三個要素最多只能同時實現兩點,不可能三者兼顧。
一個分布式系統不可能同時很好的滿足一致性,可用性和分區容錯性這三個需求,根據CAP原理,將NoSQL數據庫分成了滿足CA原則,滿足CP原則和滿足AP原則三大類:
- CA:單點集群,滿足一致性,可用性的系統,通常可擴展性較差
- CP:滿足一致性,分區容錯的系統,通常性能不是特別高
- AP:滿足可用性,分區容錯的系統,通常可能對一致性要求低一些
Zookeeper保證的是CP
當向注冊中心查詢服務列表時,我們可以容忍注冊中心返回的是幾分鍾以前的注冊信息,但不能接收服務直接down掉不可用。也就是說,服務注冊功能對可用性的要求要高於一致性。但zookeeper會出現這樣一種情況,當master節點因為網絡故障與其他節點失去聯系時,剩余節點會重新進行leader選舉。問題在於,選舉leader的時間太長,30-120s,且選舉期間整個zookeeper集群是不可用的,這就導致在選舉期間注冊服務癱瘓。在雲部署的環境下,因為網絡問題使得zookeeper集群失去master節點是較大概率發生的事件,雖然服務最終能夠恢復,但是,漫長的選舉時間導致注冊長期不可用,是不可容忍的。
Eureka保證的是AP
Eureka看明白了這一點,因此在設計時就優先保證可用性。Eureka各個節點都是平等的,幾個節點掛掉不會影響正常節點的工作,剩余的節點依然可以提供注冊和查詢服務。而Eureka的客戶端在向某個Eureka注冊時,如果發現連接失敗,則會自動切換至其他節點,只要有一台Eureka還在,就能保住注冊服務的可用性,只不過查到的信息可能不是最新的,除此之外,Eureka還有之中自我保護機制,如果在15分鍾內超過85%的節點都沒有正常的心跳,那么Eureka就認為客戶端與注冊中心出現了網絡故障,此時會出現以下幾種情況:
- Eureka不在從注冊列表中移除因為長時間沒收到心跳而應該過期的服務
- Eureka仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其他節點上 (即保證當前節點依然可用)
- 當網絡穩定時,當前實例新的注冊信息會被同步到其他節點中
因此,Eureka可以很好的應對因網絡故障導致部分節點失去聯系的情況,而不會像zookeeper那樣使整個注冊服務癱瘓。
Ribbon
是什么?
- Spring Cloud Ribbon 是基於Netflix Ribbon 實現的一套客戶端負載均衡的工具。
- 簡單的說,Ribbon 是 Netflix 發布的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將 Netflix 的中間層服務連接在一起。Ribbon 的客戶端組件提供一系列完整的配置項,如:連接超時、重試等。簡單的說,就是在配置文件中列出 LoadBalancer (簡稱LB:負載均衡) 后面所有的及其,Ribbon 會自動的幫助你基於某種規則 (如簡單輪詢,隨機連接等等) 去連接這些機器。我們也容易使用 Ribbon 實現自定義的負載均衡算法。
Ribbon能干嘛?
- LB,即負載均衡 (LoadBalancer) ,在微服務或分布式集群中經常用的一種應用。
- 負載均衡簡單的說就是將用戶的請求平攤的分配到多個服務上,從而達到系統的HA (高用)。
- 常見的負載均衡軟件有 Nginx、Lvs 等等。
- Dubbo、SpringCloud 中均給我們提供了負載均衡,SpringCloud 的負載均衡算法可以自定義。
- 負載均衡簡單分類:
- 集中式LB
- 即在服務的提供方和消費方之間使用獨立的LB設施,如Nginx,由該設施負責把訪問請求通過某種策略轉發至服務的提供方。
- 進程式LB
- 將LB邏輯集成到消費方,消費方從服務注冊中心獲知有哪些地址可用,然后自己再從這些地址中選出一個合適的服務器。
- Ribbon 就屬於進程內LB,它只是一個類庫,集成於消費方進程,消費方通過它來獲取到服務提供方的地址。
- 集中式LB
使用Ribbon實現負載均衡
導入依賴-80
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
配置
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
再使用@EnableEurekaClient開啟Eureka服務以及@LoadBalanced使用負載均衡。
更改controller的地址
// private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://PROVIDER";

為了觀察負載均衡的效果,我們設計多個數據庫來看
sql語句:
/*
Navicat MySQL Data Transfer
Source Server : MySQL
Source Server Version : 80020
Source Host : localhost:3306
Source Database : springcloudstudy01
Target Server Type : MYSQL
Target Server Version : 80020
File Encoding : 65001
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`deptno` bigint NOT NULL AUTO_INCREMENT,
`dname` varchar(60) DEFAULT NULL,
`db_source` varchar(60) DEFAULT NULL,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='部門表';
-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES ('1', '開發部', 'springcloudstudy01');
INSERT INTO `dept` VALUES ('2', '人事部', 'springcloudstudy01');
INSERT INTO `dept` VALUES ('3', '財務部', 'springcloudstudy01');
INSERT INTO `dept` VALUES ('4', '市場部', 'springcloudstudy01');
INSERT INTO `dept` VALUES ('5', '運維部', 'springcloudstudy01');
然后Provider-8001也要同步變為Provider-8002和Provider-8003。
效果圖:

自定義負載均衡算法
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "Provider",configuration = zwtRule.class)
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
@Configuration
public class zwtRule extends AbstractLoadBalancerRule {
/**
* 每個服務訪問5次則換下一個服務(總共3個服務)
* <p>
* total=0,默認=0,如果=5,指向下一個服務節點
* index=0,默認=0,如果total=5,index+1
*/
private int total = 0;//被調用的次數
private int currentIndex = 0;//當前是誰在提供服務
//@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();//獲得當前活着的服務
List<Server> allList = lb.getAllServers();//獲取所有的服務
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
//int index = chooseRandomInt(serverCount);//生成區間隨機數
//server = upList.get(index);//從或活着的服務中,隨機獲取一個
//=====================自定義代碼=========================
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex > upList.size()) {
currentIndex = 0;
}
server = upList.get(currentIndex);//從活着的服務中,獲取指定的服務來進行操作
}
//======================================================
if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
Feign
Feign簡介
Feign是聲明式Web Service客戶端,它讓微服務之間的調用變得更簡單,類似controller調用service。SpringCloud集成了Ribbon和Eureka,可以使用Feigin提供負載均衡的http客戶端
只需要創建一個接口,然后添加注解即可。
Feign,主要是社區版,大家都習慣面向接口編程。這個是很多開發人員的規范。調用微服務訪問兩種方法:
- 微服務名字 【ribbon】
- 接口和注解 【feign】
Feign能干什么?
- Feign旨在使編寫Java Http客戶端變得更容易
- 前面在使用Ribbon + RestTemplate時,利用RestTemplate對Http請求的封裝處理,形成了一套模板化的調用方法。但是在實際開發中,由於對服務依賴的調用可能不止一處,往往一個接口會被多處調用,所以通常都會針對每個微服務自行封裝一個客戶端類來包裝這些依賴服務的調用。所以,Feign在此基礎上做了進一步的封裝,由他來幫助我們定義和實現依賴服務接口的定義,在Feign的實現下,我們只需要創建一個接口並使用注解的方式來配置它 (類似以前Dao接口上標注Mapper注解,現在是一個微服務接口上面標注一個Feign注解),即可完成對服務提供方的接口綁定,簡化了使用Spring Cloud Ribbon 時,自動封裝服務調用客戶端的開發量。
Feign默認集成了Ribbon
利用Ribbon維護了MicroServiceCloud-Dept的服務列表信息,並且通過輪詢實現了客戶端的負載均衡,而與Ribbon不同的是,通過Feign只需要定義服務綁定接口且以聲明式的方法,優雅而簡單的實現了服務調用。
Hystrix
分布式系統面臨的問題
復雜分布式體系結構中的應用程序有數十個依賴關系,每個依賴關系在某些時候將不可避免失敗!
服務雪崩
多個微服務之間調用的時候,假設微服務A調用微服務B和微服務C,微服務B和微服務C又調用其他的微服務,這就是所謂的“扇出”,如果扇出的鏈路上某個微服務的調用響應時間過長,或者不可用,對微服務A的調用就會占用越來越多的系統資源,進而引起系統崩潰,所謂的“雪崩效應”。
對於高流量的應用來說,單一的后端依賴可能會導致所有服務器上的所有資源都在幾十秒內飽和。比失敗更糟糕的是,這些應用程序還可能導致服務之間的延遲增加,備份隊列,線程和其他系統資源緊張,導致整個系統發生更多的級聯故障,這些都表示需要對故障和延遲進行隔離和管理,以便單個依賴關系的失敗,不能取消整個應用程序或系統。
我們需要,棄車保帥。
什么是Hystrix?
Hystrix是一個應用於處理分布式系統的延遲和容錯的開源庫,在分布式系統里,許多依賴不可避免的會調用失敗,比如超時,異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導致整個體系服務失敗,避免級聯故障,以提高分布式系統的彈性。
“斷路器”本身是一種開關裝置,當某個服務單元發生故障之后,通過斷路器的故障監控 (類似熔斷保險絲) ,向調用方方茴一個服務預期的,可處理的備選響應 (FallBack) ,而不是長時間的等待或者拋出調用方法無法處理的異常,這樣就可以保證了服務調用方的線程不會被長時間,不必要的占用,從而避免了故障在分布式系統中的蔓延,乃至雪崩。
Hystrix能干嘛?
- 服務降級
- 服務熔斷
- 服務限流
- 接近實時的監控
…
當一切正常時,請求流可以如下所示:

當許多后端系統中有一個問題時,它可以阻止整個用戶請求:

隨着大容量通信量的增加,單個后端依賴項的潛在性會導致所有服務器上的所有資源在幾秒鍾內飽和。
應用程序中通過網絡或客戶端庫可能導致網絡請求的每個點都是潛在故障的來源。比失敗更糟糕的是,這些應用程序還可能導致服務之間的延遲增加,從而備份隊列、線程和其他系統資源,從而導致更多跨系統的級聯故障。
當使用hystrix包裝每個基礎依賴項時,上面的圖表中所示的體系結構會發生類似於以下關系圖的變化。每個依賴項是相互隔離的,限制在延遲發生時它可以填充的資源中,並包含在回退邏輯中,該邏輯決定在依賴項中發生任何類型的故障時要做出什么樣的響應:

官網資料:https://github.com/Netflix/Hystrix/wiki
服務熔斷
什么是服務熔斷?
熔斷機制是雪崩效應的一種微服務鏈路保護機制。
在微服務架構中,微服務之間的數據交互通過遠程調用完成,微服務A調用微服務B和微服務C,微服務B和微服務C又調用其它的微服務,此時如果鏈路上某個微服務的調用響應時間過長或者不可用,那么對微服務A的調用就會占用越來越多的系統資源,進而引起系統崩潰,導致“雪崩效應”。
服務熔斷是應對雪崩效應的一種微服務鏈路保護機制。例如在高壓電路中,如果某個地方的電壓過高,熔斷器就會熔斷,對電路進行保護。同樣,在微服務架構中,熔斷機制也是起着類似的作用。當調用鏈路的某個微服務不可用或者響應時間太長時,會進行服務熔斷,不再有該節點微服務的調用,快速返回錯誤的響應信息。當檢測到該節點微服務調用響應正常后,恢復調用鏈路。
當扇出鏈路的某個微服務不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的調用,快速返回錯誤的響應信息。檢測到該節點微服務調用響應正常后恢復調用鏈路。在SpringCloud框架里熔斷機制通過Hystrix實現。Hystrix會監控微服務間調用的狀況,當失敗的調用到一定閥值缺省是5秒內20次調用失敗,就會啟動熔斷機制。熔斷機制的注解是:@HystrixCommand 。
服務熔斷解決如下問題:
- 當所依賴的對象不穩定時,能夠起到快速失敗的目的;
- 快速失敗后,能夠根據一定的算法動態試探所依賴對象是否恢復。
為了避免因某個微服務后台出現異常或錯誤而導致整個應用或網頁報錯,使用熔斷是必要的。
服務降級
服務降級是指 當服務器壓力劇增的情況下,根據實際業務情況及流量,對一些服務和頁面有策略的不處理或換種簡單的方式處理,從而釋放服務器資源以保證核心業務正常運作或高效運作。說白了,就是盡可能的把系統資源讓給優先級高的服務。
資源有限,而請求是無限的。如果在並發高峰期,不做服務降級處理,一方面肯定會影響整體服務的性能,嚴重的話可能會導致宕機某些重要的服務不可用。所以,一般在高峰期,為了保證核心功能服務的可用性,都要對某些服務降級處理。比如當雙11活動時,把交易無關的服務統統降級,如查看螞蟻深林,查看歷史訂單等等。
服務降級主要用於什么場景呢?
當整個微服務架構整體的負載超出了預設的上限閾值或即將到來的流量預計將會超過預設的閾值時,為了保證重要或基本的服務能正常運行,可以將一些 不重要 或 不緊急 的服務或任務進行服務的 延遲使用 或 暫停使用。降級的方式可以根據業務來,可以延遲服務,比如延遲給用戶增加積分,只是放到一個緩存中,等服務平穩之后再執行 ;或者在粒度范圍內關閉服務,比如關閉相關文章的推薦。
例子

由上圖可得,當某一時間內服務A的訪問量暴增,而B和C的訪問量較少,為了緩解A服務的壓力,這時候需要B和C暫時關閉一些服務功能,去承擔A的部分服務,從而為A分擔壓力,叫做服務降級。
服務降級需要考慮的問題
1)那些服務是核心服務,哪些服務是非核心服務?
2)那些服務可以支持降級,那些服務不能支持降級,降級策略是什么?
3)除服務降級之外是否存在更復雜的業務放通場景,策略是什么?
自動降級分類
1)超時降級:主要配置好超時時間和超時重試次數和機制,並使用異步機制探測回復情況。
2)失敗次數降級:主要是一些不穩定的api,當失敗調用次數達到一定閥值自動降級,同樣要使用異步機制探測回復情況。
3)故障降級:比如要調用的遠程服務掛掉了(網絡故障、DNS故障、Http服務返回錯誤的狀態碼、RPC服務拋出異常),則可以直接降級。降級后的處理方案有:默認值(比如庫存服務掛了,返回默認現貨)、兜底數據(比如廣告掛了,返回提前准備好的一些靜態頁面)、緩存(之前暫存的一些緩存數據)。
4)限流降級:秒殺或者搶購一些限購商品時,此時可能會因為訪問量太大而導致系統崩潰,此時會使用限流來進行限制訪問量,當達到限流閥值,后續請求會被降級;降級后的處理方案可以是:排隊頁面(將用戶導流到排隊頁面等一會重試)、無貨(直接告知用戶沒貨了)、錯誤頁(如活動太火爆了,稍后重試)。
服務熔斷和降級的區別
- 服務熔斷—>服務端:某個服務超時或異常,引起熔斷,類似於保險絲(自我熔斷)。
- 服務降級—>客戶端:從整體網站請求負載考慮,當某個服務熔斷或者關閉之后,服務將不再被調用,此時在客戶端,我們可以准備一個 FallBackFactory ,返回一個默認的值(缺省值)。會導致整體的服務下降,但是好歹能用,比直接掛掉強。
- 觸發原因不太一樣,服務熔斷一般是某個服務(下游服務)故障引起,而服務降級一般是從整體負荷考慮;管理目標的層次不太一樣,熔斷其實是一個框架級的處理,每個微服務都需要(無層級之分),而降級一般需要對業務有層級之分(比如降級一般是從最外圍服務開始)。
- 實現方式不太一樣,服務降級具有代碼侵入性(由控制器完成/或自動降級),熔斷一般稱為自我熔斷。
限流:限制並發的請求訪問量,超過閾值則拒絕。
降級:服務分優先級,犧牲非核心服務(不可用),保證核心服務穩定;從整體負荷考慮。
熔斷:依賴的下游服務故障觸發熔斷,避免引發本系統崩潰;系統自動執行和恢復。
入門案例
服務雪崩&服務熔斷
package com.zwt.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.zwt.service.DeptService;
import com.zwt.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @author ML李嘉圖
* @version createtime: 2021-10-26
* Blog: https://www.cnblogs.com/zwtblog/
*/
@RestController
public class DeptController {
@Autowired
private DeptService service;
@GetMapping("/dept/get/id")
@HystrixCommand(fallbackMethod = "hystrixGet")
public Dept get(@PathVariable("id") Long id) {
Dept dept = service.queryDeptById(id);
if (dept == null) {
throw new RuntimeException("id=>" + id + "不存在,無法獲取");
}
return dept;
}
public Dept Hystrix_get(@PathVariable("id") Long id) {
return new Dept()
.setDeptno(id)
.setDname("id" + id)
.setDb_source("no");
}
}
啟動類中使用@EnableCircuitBreaker啟動,CircuitBreaker(斷路器)
服務降級
@FeignClient(fallbackFactory = DeptServiceFallbackFactory.class)
package com.zwt.service;
import com.zwt.springcloud.pojo.Dept;
import com.zwt.springcloud.service.DeptClientService;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* @author ML李嘉圖
* @version createtime: 2021-10-26
* Blog: https://www.cnblogs.com/zwtblog/
*/
//降級
@Component
public class DeptServiceFallbackFactory implements FallbackFactory {
@Override
public Object create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept queryById(Long id) {
return new Dept()
.setDeptno(id)
.setDname("id" + id)
.setDb_source("no");
}
@Override
public Dept queryAll() {
return null;
}
@Override
public Dept add(Dept dept) {
return null;
}
};
}
}
feign:
hystrix:
enabled: true
Dashboard 流監控
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
//開啟Dashboard
@EnableHystrixDashboard
被監控的服務添加一個bean
public class DeptProvider_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProvider_8001.class, args);
}
//time: 2021/10/26
public ServletRegistrationBean hystrixMetricsStreamServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");
return servletRegistrationBean;
}
}
Zuul
什么是Zuul?
Zull包含了對請求的路由(用來跳轉的)和過濾兩個最主要功能:
其中路由功能負責將外部請求轉發到具體的微服務實例上,是實現外部訪問統一入口的基礎,而過濾器功能則負責對請求的處理過程進行干預,是實現請求校驗,服務聚合等功能的基礎。Zuul和Eureka進行整合,將Zuul自身注冊為Eureka服務治理下的應用,同時從Eureka中獲得其他服務的消息,也即以后的訪問微服務都是通過Zuul跳轉后獲得。
注意:Zuul服務最終還是會注冊進Eureka。
提供:代理+路由+過濾 三大功能
Zuul能干嘛?
-
路由
-
過濾
官方文檔:https://github.com/Netflix/zuul/
入門案例
<!--導入zuul依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId
<version>1.4.6.RELEASE</version>
</dependency>
application.yml
zuul:
routes:
mydept.serviceId: springcloud-provider-dept # 原來id的名字
mydept.path: /mydept/**
ignored-services: "*" # 不能再使用某個(*:全部)路徑訪問了,ignored : 忽略,隱藏全部的~
prefix: /ljt # 設置公共的前綴,實現隱藏原有路由
@SpringBootApplication
@EnableZuulProxy //開啟Zuul
public class ZuulApplication_9527 {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication_9527.class,args);
}
}
詳情參考springcloud中文社區zuul組件 :https://www.springcloud.cc/spring-cloud-greenwich.html#_router_and_filter_zuul
Config
Spring Cloud Config為分布式系統中的外部配置提供服務器和客戶端支持。
使用Config Server,您可以在所有環境中管理應用程序的外部屬性。客戶端和服務器上的概念映射與Spring Environment和PropertySource抽象相同,因此它們與Spring應用程序非常契合,但可以與任何以任何語言運行的應用程序一起使用。隨着應用程序通過從開發人員到測試和生產的部署流程,您可以管理這些環境之間的配置,並確定應用程序具有遷移時需要運行的一切。服務器存儲后端的默認實現使用git,因此它輕松支持標簽版本的配置環境,以及可以訪問用於管理內容的各種工具。很容易添加替代實現,並使用Spring配置將其入。
總結
學無止境,加油。