SpringCloud學習筆記(狂神說)


1.學習前言

1.1 學習前提

  • 熟練使用SpringBoot 微服務快速開發框架

  • 了解過Dubbo + Zookeeper 分布式基礎

  • 電腦配置內存不低於8G(我自己的是16G)

    • 給大家看下多個服務跑起來后的內存開銷圖:

      在這里插入圖片描述

      在這里插入圖片描述

1.2 大綱

Spring Cloud 五大組件

  • 服務注冊與發現——Netflix Eureka
  • 負載均衡:
    • 客戶端負載均衡——Netflix Ribbon
    • 服務端負載均衡:——Feign(其也是依賴於Ribbon,只是將調用方式RestTemplete 更改成Service 接口)
  • 斷路器——Netflix Hystrix
  • 服務網關——Netflix Zuul
  • 分布式配置——Spring Cloud Config

1.3 常見面試題

1.1 什么是微服務?
1.2 微服務之間是如何獨立通訊的?
1.3 SpringCloud 和 Dubbo有那些區別?
1.4 SpringBoot 和 SpringCloud,請談談你對他們的理解
1.5 什么是服務熔斷?什么是服務降級?
1.6 微服務的優缺點分別是什么?說下你在項目開發中遇到的坑
1.7 你所知道的微服務技術棧有哪些?列舉一二
1.8 Eureka和Zookeeper都可以提供服務注冊與發現的功能,請說說兩者的區別

2. 微服務概述

2.1 什么是微服務?

什么是微服務?

微服務(Microservice Architecture) 是近幾年流行的一種架構思想,關於它的概念很難一言以蔽之。
究竟什么是微服務呢?我們在此引用ThoughtWorks 公司的首席科學家 Martin Fowler 於2014年提出的一段話:
原文:https://martinfowler.com/articles/microservices.html
漢化:https://www.cnblogs.com/liuning8023/p/4493156.html

  • 就目前而言,對於微服務,業界並沒有一個統一的,標准的定義。
  • 但通常而言,微服務架構是一種架構模式,或者說是一種架構風格,它體長將單一的應用程序划分成一組小的服務,每個服務運行在其獨立的自己的進程內,服務之間互相協調,互相配置,為用戶提供最終價值,服務之間采用輕量級的通信機制(HTTP)互相溝通,每個服務都圍繞着具體的業務進行構建,並且能狗被獨立的部署到生產環境中,另外,應盡量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應該根據業務上下文,選擇合適的語言,工具(Maven)對其進行構建,可以有一個非常輕量級的集中式管理來協調這些服務,可以使用不同的語言來編寫服務,也可以使用不同的數據存儲。

再來從技術維度角度理解下:

  • 微服務化的核心就是將傳統的一站式應用,根據業務拆分成一個一個的服務,徹底地去耦合,每一個微服務提供單個業務功能的服務,一個服務做一件事情,從技術角度看就是一種小而獨立的處理過程,類似進程的概念,能夠自行單獨啟動或銷毀,擁有自己獨立的數據庫。

2.2 微服務與微服務架構

微服務

強調的是服務的大小,它關注的是某一個點,是具體解決某一個問題/提供落地對應服務的一個服務應用,狹義的看,可以看作是IDEA中的一個個微服務工程,或者Moudel。IDEA 工具里面使用Maven開發的一個個獨立的小Moudel,它具體是使用SpringBoot開發的一個小模塊,專業的事情交給專業的模塊來做,一個模塊就做着一件事情。強調的是一個個的個體,每個個體完成一個具體的任務或者功能。

微服務架構

一種新的架構形式,Martin Fowler 於2014年提出。
微服務架構是一種架構模式,它體長將單一應用程序划分成一組小的服務,服務之間相互協調,互相配合,為用戶提供最終價值。每個服務運行在其獨立的進程中,服務與服務之間采用輕量級的通信機制(如HTTP)互相協作,每個服務都圍繞着具體的業務進行構建,並且能夠被獨立的部署到生產環境中,另外,應盡量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言、工具(如Maven)對其進行構建。

2.3 微服務優缺點

優點

  • 單一職責原則;
  • 每個服務足夠內聚,足夠小,代碼容易理解,這樣能聚焦一個指定的業務功能或業務需求;
  • 開發簡單,開發效率高,一個服務可能就是專一的只干一件事;
  • 微服務能夠被小團隊單獨開發,這個團隊只需2-5個開發人員組成;
  • 微服務是松耦合的,是有功能意義的服務,無論是在開發階段或部署階段都是獨立的;
  • 微服務能使用不同的語言開發;
  • 易於和第三方集成,微服務允許容易且靈活的方式集成自動部署,通過持續集成工具,如jenkins,Hudson,bamboo;
  • 微服務易於被一個開發人員理解,修改和維護,這樣小團隊能夠更關注自己的工作成果,無需通過合作才能體現價值;
  • 微服務允許利用和融合最新技術;
  • 微服務只是業務邏輯的代碼,不會和HTML,CSS,或其他的界面混合;
  • 每個微服務都有自己的存儲能力,可以有自己的數據庫,也可以有統

缺點

  • 開發人員要處理分布式系統的復雜性;

  • 多服務運維難度,隨着服務的增加,運維的壓力也在增大;

  • 系統部署依賴問題;

  • 服務間通信成本問題;

  • 數據一致性問題;

  • 系統集成測試問題;

  • 性能和監控問題;

2.4 微服務技術棧有那些?

微服務條目 技術 備注
服務開發 Springboot、Spring、SpringMVC
服務配置與管理 Netflix公司的Archaius、阿里的Diamond等
服務注冊與發現 Eureka、Consul、Zookeeper等
服務調用 REST、RPC、gRPC
服務熔斷器 Hystrix、Envoy等
負載均衡 Ribbon、Nginx等
服務接口調用(客戶端調用服務發簡單工具) Feign等
消息隊列 kafka、RabbitMQ、ActiveMQ等
服務配置中心管理 SpringCloudConfig、Chef等
服務路由(API網關) Zuul等
服務監控 Zabbix、Nagios、Metrics、Spectator等
全鏈路追蹤 Zipkin、Brave、Dapper等
服務部署 Docker、OpenStack、Kubernetes等
數據流操作開發包 SpringCloud Stream(封裝與Redis,Rabbit、Kafka等發送接收消息)
事件消息總線 SpringCloud Bus
......

2.5 為什么選擇SpringCloud作為微服務架構

選型依據

    • 整體解決方案和框架成熟度
    • 社區熱度
    • 可維護性
    • 學習曲線
  1. 當前各大IT公司用的微服務架構有那些?

    • 阿里:dubbo+HFS

    • 京東:JFS

    • 新浪:Motan

    • 當當網:DubboX

  2. 各微服務框架對比

功能點/服務框架 Netflix/SpringCloud Motan gRPC Thrift Dubbo/DubboX
功能定位 完整的微服務框架 RPC框架,但整合了ZK或Consul,實現集群環境的基本服務注冊/發現 RPC框架 RPC框架 服務框架
支持Rest 是,Ribbon支持多種可插拔的序列化選擇
支持RPC 是(Hession2)
支持多語言 是(Rest形式)?
負載均衡 是(服務端zuul+客戶端Ribbon),zuul-服務,動態路由,雲端負載均衡Eureka(針對中間層服務器) 是(客戶端) 是(客戶端)
配置服務 Netfix Archaius,Spring Cloud Config Server集中配置 是(zookeeper提供)
服務調用鏈監控 是(zuul),zuul提供邊緣服務,API網關
高可用/容錯 是(服務端Hystrix+客戶端Ribbon) 是(客戶端) 是(客戶端)
典型應用案例 Netflix Sina Google Facebook
社區活躍程度 一般 一般 2017年后重新開始維護,之前中斷了5年
學習難度 中等
文檔豐富程度 一般 一般 一般
其他 Spring Cloud Bus為我們的應用程序帶來了更多管理端點 支持降級 Netflix內部在開發集成gRPC IDL定義 實踐的公司比較多

3. SpringCloud入門概述

3.1 SpringCloud是什么?

Spring官網:https://spring.io/

img

img

3.2 SpringCloud和SpringBoot的關系

  • SpringBoot專注於開蘇方便的開發單個個體微服務;
  • SpringCloud是關注全局的微服務協調整理治理框架,它將SpringBoot開發的一個個單體微服務,整合並管理起來,為各個微服務之間提供:配置管理、服務發現、斷路器、路由、為代理、事件總棧、全局鎖、決策競選、分布式會話等等集成服務;
  • SpringBoot可以離開SpringCloud獨立使用,開發項目,但SpringCloud離不開SpringBoot,屬於依賴關系;
  • SpringBoot專注於快速、方便的開發單個個體微服務,SpringCloud關注全局的服務治理框架;

3.3 Dubbo 和 SpringCloud技術選型

1. 分布式+服務治理Dubbo

目前成熟的互聯網架構,應用服務化拆分 + 消息中間件

Dubbo 和 SpringCloud對比

可以看一下社區活躍度:

https://github.com/dubbo

https://github.com/spring-cloud

對比結果:

hUuEVK.png

最大區別:Spring Cloud 拋棄了Dubbo的RPC通信,采用的是基於HTTP的REST方式
嚴格來說,這兩種方式各有優劣。雖然從一定程度上來說,后者犧牲了服務調用的性能,但也避免了上面提到的原生RPC帶來的問題。而且REST相比RPC更為靈活,服務提供方和調用方的依賴只依靠一紙契約,不存在代碼級別的強依賴,這個優點在當下強調快速演化的微服務環境下,顯得更加合適。

品牌機和組裝機的區別

社區支持與更新力度的區別

總結:二者解決的問題域不一樣:Dubbo的定位是一款RPC框架,而SpringCloud的目標是微服務架構下的一站式解決方案。

3.4 SpringCloud能干嘛?

  • Distributed/versioned configuration 分布式/版本控制配置
  • Service registration and discovery 服務注冊與發現
  • Routing 路由Service-to-service calls 服務到服務的調用
  • Load balancing 負載均衡配置
  • Circuit Breakers 斷路器
  • Distributed messaging 分布式消息管理

3.5 SpringCloud下載

官網:http://projects.spring.io/spring-cloud/

版本號有點特別:

在這里插入圖片描述

SpringCloud沒有采用數字編號的方式命名版本號,而是采用了倫敦地鐵站的名稱,同時根據字母表的順序來對應版本時間順序,比如最早的Realse版本:Angel,第二個Realse版本:Brixton,然后是Camden、Dalston、Edgware,目前最新的是Hoxton SR4 CURRENT GA通用穩定版。

自學參考書

4. SpringCloud Rest學習環境搭建:服務提供者

4.1 介紹

  • 我們會使用一個Dept部門模塊做一個微服務通用案例Consumer消費者(Client)通過REST調用Provider提供者(Server)提供的服務。
  • 回顧Spring,SpringMVC,Mybatis等以往學習的知識。
  • Maven的分包分模塊架構復習。
一個簡單的Maven模塊結構是這樣的:

-- app-parent: 一個父項目(app-parent)聚合了很多子項目(app-util\app-dao\app-web...)
  |-- pom.xml
  |
  |-- app-core
  ||---- pom.xml
  |
  |-- app-web
  ||---- pom.xml
  ......

一個父工程帶着多個Moudule子模塊

MicroServiceCloud父工程(Project)下初次帶着3個子模塊(Module)

  • microservicecloud-api 【封裝的整體entity/接口/公共配置等】
  • microservicecloud-consumer-dept-80 【服務消費者】
  • microservicecloud-provider-dept-8001【服務提供者】

4.2 SpringCloud版本選擇

大版本說明

hUT6Wn.png

實際開發版本關系

hUTJJA.png

使用后兩個

4.3 創建父工程

  • 新建父工程項目springcloud,切記Packageing是pom模式
  • 主要是定義POM文件,將后續各個子模塊公用的jar包等統一提取出來,類似一個抽象父類

在這里插入圖片描述

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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.haust</groupId>
    <artifactId>springcloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>springcloud-api</module>
        <module>springcloud-provider-dept-8001</module>
        <module>springcloud-consumer-dept-80</module>
        <module>springcloud-eureka-7001</module>
        <module>springcloud-eureka-7002</module>
        <module>springcloud-eureka-7003</module>
        <module>springcloud-provider-dept-8002</module>
        <module>springcloud-provider-dept-8003</module>
        <module>springcloud-consumer-dept-feign</module>
        <module>springcloud-provider-dept-hystrix-8001</module>
        <module>springcloud-consumer-hystrix-dashboard</module>
        <module>springcloud-zuul-9527</module>
        <module>springcloud-config-server-3344</module>
        <module>springcloud-config-client-3355</module>
        <module>springcloud-config-eureka-7001</module>
        <module>springcloud-config-dept-8001</module>
    </modules>

    <!--打包方式  pom-->
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.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>

</project>

父工程為springcloud,其下有多個子mudule,詳情參考完整代碼了解

  • springcloud-consumer-dept-80訪問springcloud-provider-dept-8001下的controller使用REST方式

如DeptConsumerController.java

@RestController
public class DeptConsumerController {

    /**
     * 理解:消費者,不應該有service層~
     * RestTemplate .... 供我們直接調用就可以了! 注冊到Spring中
     * (地址:url, 實體:Map ,Class<T> responseType)
     * <p>
     * 提供多種便捷訪問遠程http服務的方法,簡單的Restful服務模板~
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 服務提供方地址前綴
     * <p>
     * Ribbon:我們這里的地址,應該是一個變量,通過服務名來訪問
     */
    private static final String REST_URL_PREFIX = "http://localhost:8001";
    //private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

    /**
     * 消費方添加部門信息
     * @param dept
     * @return
     */
    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept) {
        // postForObject(服務提供方地址(接口),參數實體,返回類型.class)
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
    }

    /**
     * 消費方根據id查詢部門信息
     * @param id
     * @return
     */
    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        // getForObject(服務提供方地址(接口),返回類型.class)
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
    }

    /**
     * 消費方查詢部門信息列表
     * @return
     */
    @RequestMapping("/consumer/dept/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    }
}

使用RestTemplete先需要放入Spring容器中

ConfigBean.java

@Configuration
public class ConfigBean {//@Configuration -- spring  applicationContext.xml

    //配置負載均衡實現RestTemplate
    // IRule
    // RoundRobinRule 輪詢
    // RandomRule 隨機
    // AvailabilityFilteringRule : 會先過濾掉,跳閘,訪問故障的服務~,對剩下的進行輪詢~
    // RetryRule : 會先按照輪詢獲取服務~,如果服務獲取失敗,則會在指定的時間內進行,重試
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

springcloud-provider-dept-8001的dao接口調用springcloud-api模塊下的pojo,可使用在springcloud-provider-dept-8001的pom文件導入springcloud-api模塊依賴的方式:

 <!--我們需要拿到實體類,所以要配置api module-->
        <dependency>
            <groupId>com.haust</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

springcloud-consumer-dept-80和springcloud-provider-dept-8001的pom.xml和父工程下的依賴基本一樣,直接看完整代碼即可,不再添加重復筆記。

5. Eureka服務注冊中心

5.1 什么是Eureka

  • Netflix在涉及Eureka時,遵循的就是API原則.Eureka是Netflix的一個子模塊,也是核心模塊之一。
  • Eureka是基於REST的服務,用於定位服務,以實現雲端中間件層服務發現和故障轉移,服務注冊與發現對於微服務來說是非常重要的,有了服務注冊與發現,只需要使用服務的標識符,就可以訪問到服務,而不需要修改服務調用的配置文件了,功能類似於Dubbo的注冊中心,比如Zookeeper.

5.2 原理理解

Eureka基本的架構

  • Springcloud 封裝了Netflix公司開發的Eureka模塊來實現服務注冊與發現 (對比Zookeeper).
  • Eureka采用了C-S的架構設計,EurekaServer作為服務注冊功能的服務器,他是服務注冊中心.
  • 而系統中的其他微服務,使用Eureka的客戶端連接到EurekaServer並維持心跳連接。這樣系統的維護人員就可以通過EurekaServer來監控系統中各個微服務是否正常運行,Springcloud 的一些其他模塊 (比如Zuul) 就可以通過EurekaServer來發現系統中的其他微服務,並執行相關的邏輯.
    在這里插入圖片描述

和Dubbo架構對比.

在這里插入圖片描述

  • 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中獲取注冊服務列表,從而找到消費服務

目前工程狀況

5.3 構建步驟

1. eureka-server

  • springcloud-eureka-7001 模塊建立

  • pom.xml 配置

<!--導包~-->
<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
    <!--導入Eureka Server依賴-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--熱部署工具-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>
  • application.yml
server:
  port: 7001

# Eureka配置
eureka:
  instance:
    # Eureka服務端的實例名字
    hostname: 127.0.0.1
  client:
    # 表示是否向 Eureka 注冊中心注冊自己(這個模塊本身是服務器,所以不需要)
    register-with-eureka: false
    # fetch-registry如果為false,則表示自己為注冊中心,客戶端的化為 ture
    fetch-registry: false
    # Eureka監控頁面~
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

源碼中Eureka的默認端口以及訪問路徑:

在這里插入圖片描述

  • 主啟動類
@SpringBootApplication
// @EnableEurekaServer 服務端的啟動類,可以接受別人注冊進來~
@EnableEurekaServer
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class,args);
    }
}

啟動成功后訪問 http://localhost:7001/ 得到以下頁面

在這里插入圖片描述

2. eureka-client

調整之前創建的springlouc-provider-dept-8001

  • 導入eureka依賴
<!--Eureka依賴-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
  • application中新增eureka配置
# Eureka配置:配置服務注冊中心地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  • 為主啟動類添加@EnableEurekaClient注解
@SpringBootApplication
// @EnableEurekaClient 開啟Eureka客戶端注解,在服務啟動后自動向注冊中心注冊服務
@EnableEurekaClient
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}
  • 先啟動7001服務端后啟動8001客戶端進行測試,然后訪問監控頁http://localhost:7001/ 產看結果如圖,成功

在這里插入圖片描述

  • 修改Eureka上的默認描述信息
# Eureka配置:配置服務注冊中心地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    instance-id: springcloud-provider-dept-8001 #修改Eureka上的默認描述信息

結果如圖:
在這里插入圖片描述

****如果此時停掉springcloud-provider-dept-8001 等30s后 監控會開啟保護機制:**在這里插入圖片描述

  • 配置關於服務加載的監控信息

在這里插入圖片描述

pom.xml中添加依賴

<!--actuator完善監控信息-->
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

application.yml中添加配置

# info配置
info:
# 項目的名稱
app.name: haust-springcloud
# 公司的名稱
company.name: 河南科技大學西苑校區軟件學院 

此時刷新監控頁,點擊進入在這里插入圖片描述

跳轉新頁面顯示如下內容:在這里插入圖片描述

3. 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【不推薦關閉自我保護機制】

    詳細內容可以參考下這篇博客內容:https://blog.csdn.net/wudiyong22/article/details/80827594

4. 注冊進來的微服務,獲取一些消息(團隊開發會用到)

DeptController.java新增方法

/**
 * DiscoveryClient 可以用來獲取一些配置的信息,得到具體的微服務!
 */
@Autowired
private DiscoveryClient client;

/**
 * 獲取一些注冊進來的微服務的信息~,
 *
 * @return
 */
@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;
}

在這里插入圖片描述

主啟動類中加入@EnableDiscoveryClient 注解

@SpringBootApplication
// @EnableEurekaClient 開啟Eureka客戶端注解,在服務啟動后自動向注冊中心注冊服務
@EnableEurekaClient
// @EnableEurekaClient 開啟服務發現客戶端的注解,可以用來獲取一些配置的信息,得到具體的微服務
@EnableDiscoveryClient
public class DeptProvider_8001 {
    //...
}

結果如圖:

在這里插入圖片描述

在這里插入圖片描述

5.4 Eureka:集群環境配置

在這里插入圖片描述

1.初始化

新建springcloud-eureka-7002、springcloud-eureka-7003 模塊

1.為pom.xml添加依賴 (與springcloud-eureka-7001相同)

<!--導包~-->
<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
    <!--導入Eureka Server依賴-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--熱部署工具-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>

2.application.yml配置(與springcloud-eureka-7001相同)

server:
  port: 7003

# Eureka配置
eureka:
  instance:
    hostname: localhost # Eureka服務端的實例名字
  client:
    register-with-eureka: false # 表示是否向 Eureka 注冊中心注冊自己(這個模塊本身是服務器,所以不需要)
    fetch-registry: false # fetch-registry如果為false,則表示自己為注冊中心
    service-url: # 監控頁面~
      # 重寫Eureka的默認端口以及訪問路徑 --->http://localhost:7001/eureka/
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

3.主啟動類(與springcloud-eureka-7001相同)

@SpringBootApplication
// @EnableEurekaServer 服務端的啟動類,可以接受別人注冊進來~
public class EurekaServer_7003 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7003.class,args);
    }
}

2.集群成員相互關聯

配置一些自定義本機名字,找到本機hosts文件並打開

在這里插入圖片描述

在hosts文件最后加上,要訪問的本機名稱,默認是localhost

在這里插入圖片描述

修改application.yml的配置,如圖為springcloud-eureka-7001配置,springcloud-eureka-7002/springcloud-eureka-7003同樣分別修改為其對應的名稱即可

在這里插入圖片描述

在集群中使springcloud-eureka-7001關聯springcloud-eureka-7002、springcloud-eureka-7003

完整的springcloud-eureka-7001下的application.yml如下

server:
  port: 7001

#Eureka配置
eureka:
  instance:
    hostname: eureka7001.com #Eureka服務端的實例名字
  client:
    register-with-eureka: false #表示是否向 Eureka 注冊中心注冊自己(這個模塊本身是服務器,所以不需要)
    fetch-registry: false #fetch-registry如果為false,則表示自己為注冊中心
    service-url: #監控頁面~
      #重寫Eureka的默認端口以及訪問路徑 --->http://localhost:7001/eureka/
      # 單機: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # 集群(關聯):7001關聯7002、7003
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

同時在集群中使springcloud-eureka-7002關聯springcloud-eureka-7001、springcloud-eureka-7003

完整的springcloud-eureka-7002下的application.yml如下

server:
  port: 7002

#Eureka配置
eureka:
  instance:
    hostname: eureka7002.com #Eureka服務端的實例名字
  client:
    register-with-eureka: false #表示是否向 Eureka 注冊中心注冊自己(這個模塊本身是服務器,所以不需要)
    fetch-registry: false #fetch-registry如果為false,則表示自己為注冊中心
    service-url: #監控頁面~
      #重寫Eureka的默認端口以及訪問路徑 --->http://localhost:7001/eureka/
      # 單機: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # 集群(關聯):7002關聯7001、7003
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/

springcloud-eureka-7003配置方式同理可得.

通過springcloud-provider-dept-8001下的yml配置文件,修改Eureka配置:配置服務注冊中心地址

# Eureka配置:配置服務注冊中心地址
eureka:
  client:
    service-url:
      # 注冊中心地址7001-7003
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-provider-dept-8001 #修改Eureka上的默認描述信息

這樣模擬集群就搭建號了,就可以把一個項目掛載到三個服務器上了在這里插入圖片描述

5.5 對比和Zookeeper區別

1. 回顧CAP原則

RDBMS (MySQL\Oracle\sqlServer) ===> ACID

NoSQL (Redis\MongoDB) ===> CAP

2. ACID是什么?

  • A (Atomicity) 原子性
  • C (Consistency) 一致性
  • I (Isolation) 隔離性
  • D (Durability) 持久性

3. CAP是什么?

  • C (Consistency) 強一致性
  • A (Availability) 可用性
  • P (Partition tolerance) 分區容錯性

CAP的三進二:CA、AP、CP

4. CAP理論的核心

  • 一個分布式系統不可能同時很好的滿足一致性,可用性和分區容錯性這三個需求
  • 根據CAP原理,將NoSQL數據庫分成了滿足CA原則,滿足CP原則和滿足AP原則三大類
    • CA:單點集群,滿足一致性,可用性的系統,通常可擴展性較差
    • CP:滿足一致性,分區容錯的系統,通常性能不是特別高
    • AP:滿足可用性,分區容錯的系統,通常可能對一致性要求低一些

5. 作為分布式服務注冊中心,Eureka比Zookeeper好在哪里?

著名的CAP理論指出,一個分布式系統不可能同時滿足C (一致性) 、A (可用性) 、P (容錯性),由於分區容錯性P再分布式系統中是必須要保證的,因此我們只能再A和C之間進行權衡。

  • Zookeeper 保證的是 CP —> 滿足一致性,分區容錯的系統,通常性能不是特別高
  • Eureka 保證的是 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那樣使整個注冊服務癱瘓

6. Ribbon:負載均衡(基於客戶端)

6.1 負載均衡以及Ribbon

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,它只是一個類庫,集成於消費方進程,消費方通過它來獲取到服務提供方的地址!

6.2 集成Ribbon

springcloud-consumer-dept-80向pom.xml中添加Ribbon和Eureka依賴

<!--Ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--Eureka: Ribbon需要從Eureka服務中心獲取要拿什么-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

在application.yml文件中配置Eureka

# Eureka配置
eureka:
  client:
    register-with-eureka: false # 不向 Eureka注冊自己
    service-url: # 從三個注冊中心中隨機取一個去訪問
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

主啟動類加上@EnableEurekaClient注解,開啟Eureka

//Ribbon 和 Eureka 整合以后,客戶端可以直接調用,不用關心IP地址和端口號
@SpringBootApplication
@EnableEurekaClient //開啟Eureka 客戶端
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}

自定義Spring配置類:ConfigBean.java 配置負載均衡實現RestTemplate

@Configuration
public class ConfigBean {//@Configuration -- spring  applicationContext.xml

    @LoadBalanced //配置負載均衡實現RestTemplate
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

修改controller:DteptConsumerController.java

//Ribbon:我們這里的地址,應該是一個變量,通過服務名來訪問
//private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

6.3 使用Ribbon實現負載均衡

流程圖:在這里插入圖片描述

1.新建兩個服務提供者Moudle:springcloud-provider-dept-8003、springcloud-provider-dept-8002

2.參照springcloud-provider-dept-8001 依次為另外兩個Moudle添加pom.xml依賴 、resourece下的mybatis和application.yml配置,Java代碼

3.啟動所有服務測試(根據自身電腦配置決定啟動服務的個數),訪問http://eureka7001.com:7002/查看結果

在這里插入圖片描述

測試訪問http://localhost/consumer/dept/list 這時候隨機訪問的是服務提供者(8003)

在這里插入圖片描述

再次訪問http://localhost/consumer/dept/list這時候隨機的是服務提供者8001

在這里插入圖片描述

以上這種每次訪問http://localhost/consumer/dept/list隨機訪問集群中某個服務提供者,這種情況叫做輪詢,輪詢算法在SpringCloud中可以自定義。

如何切換或者自定義規則呢?

在springcloud-provider-dept-80模塊下的ConfigBean中進行配置,切換使用不同的規則

@Configuration
public class ConfigBean {//@Configuration -- spring  applicationContext.xml

    /**
     * IRule:
     * RoundRobinRule 輪詢策略
     * RandomRule 隨機策略
     * AvailabilityFilteringRule : 會先過濾掉,跳閘,訪問故障的服務~,對剩下的進行輪詢~
     * RetryRule : 會先按照輪詢獲取服務~,如果服務獲取失敗,則會在指定的時間內進行,重試
     */
    @Bean
    public IRule myRule() {
        return new RandomRule();//使用隨機策略
        //return new RoundRobinRule();//使用輪詢策略
        //return new AvailabilityFilteringRule();//使用輪詢策略
        //return new RetryRule();//使用輪詢策略
    }
}

也可以自定義規則,在myRule包下自定義一個配置類MyRule.java,注意:該包不要和主啟動類所在的包同級,要跟啟動類所在包同級

在這里插入圖片描述

MyRule.java

/**
 * @Date: 2020/05/19/11:58
 * @Description: 自定義規則
 */
@Configuration
public class MyRule {

    @Bean
    public IRule myRule(){
        return new MyRandomRule();//默認是輪詢RandomRule,現在自定義為自己的
    }
}

主啟動類開啟負載均衡並指定自定義的MyRule配置類

//Ribbon 和 Eureka 整合以后,客戶端可以直接調用,不用關心IP地址和端口號
@SpringBootApplication
@EnableEurekaClient
//在微服務啟動的時候就能加載自定義的Ribbon類(自定義的規則會覆蓋原有默認的規則)
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)//開啟負載均衡,並指定自定義的規則
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}

自定義的規則(這里我們參考Ribbon中默認的規則代碼自己稍微改動):MyRandomRule.java

public class MyRandomRule 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
    }
}

7.Feign:負載均衡(基於服務端)

7.1 Feign簡介

Feign是聲明式Web Service客戶端,它讓微服務之間的調用變得更簡單,類似controller調用service。SpringCloud集成了Ribbon和Eureka,可以使用Feigin提供負載均衡的http客戶端

只需要創建一個接口,然后添加注解即可~

Feign,主要是社區版,大家都習慣面向接口編程。這個是很多開發人員的規范。調用微服務訪問兩種方法

  1. 微服務名字 【ribbon】
  2. 接口和注解 【feign】

Feign能干什么?

  • Feign旨在使編寫Java Http客戶端變得更容易
  • 前面在使用Ribbon + RestTemplate時,利用RestTemplate對Http請求的封裝處理,形成了一套模板化的調用方法。但是在實際開發中,由於對服務依賴的調用可能不止一處,往往一個接口會被多處調用,所以通常都會針對每個微服務自行封裝一個客戶端類來包裝這些依賴服務的調用。所以,Feign在此基礎上做了進一步的封裝,由他來幫助我們定義和實現依賴服務接口的定義,在Feign的實現下,我們只需要創建一個接口並使用注解的方式來配置它 (類似以前Dao接口上標注Mapper注解,現在是一個微服務接口上面標注一個Feign注解),即可完成對服務提供方的接口綁定,簡化了使用Spring Cloud Ribbon 時,自動封裝服務調用客戶端的開發量。

Feign默認集成了Ribbon

  • 利用Ribbon維護了MicroServiceCloud-Dept的服務列表信息,並且通過輪詢實現了客戶端的負載均衡,而與Ribbon不同的是,通過Feign只需要定義服務綁定接口且以聲明式的方法,優雅而簡單的實現了服務調用。

7.2 Feign的使用步驟

  1. 創建springcloud-consumer-dept-feign模塊

    在這里插入圖片描述

    拷貝springcloud-consumer-dept-80模塊下的pom.xml,resource,以及java代碼到springcloud-consumer-feign模塊,並添加feign依賴。

<!--Feign的依賴-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

通過Ribbon實現:—原來的controller:DeptConsumerController.java**

@RestController
public class DeptConsumerController {

    /**
     * 理解:消費者,不應該有service層~
     * RestTemplate .... 供我們直接調用就可以了! 注冊到Spring中
     * (地址:url, 實體:Map ,Class<T> responseType)
     * <p>
     * 提供多種便捷訪問遠程http服務的方法,簡單的Restful服務模板~
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 服務提供方地址前綴
     * <p>
     * Ribbon:我們這里的地址,應該是一個變量,通過服務名來訪問
     */
//    private static final String REST_URL_PREFIX = "http://localhost:8001";
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";

    /**
     * 消費方添加部門信息
     * @param dept
     * @return
     */
    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept) {
        // postForObject(服務提供方地址(接口),參數實體,返回類型.class)
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
    }

    /**
     * 消費方根據id查詢部門信息
     * @param id
     * @return
     */
    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        // getForObject(服務提供方地址(接口),返回類型.class)
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
    }

    /**
     * 消費方查詢部門信息列表
     * @return
     */
    @RequestMapping("/consumer/dept/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    }
}

通過Feign實現:—改造后controller:DeptConsumerController.java

@RestController
public class DeptConsumerController {

    @Autowired
    private DeptClientService deptClientService;

    /**
     * 消費方添加部門信息
     * @param dept
     * @return
     */
    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept) {
        return deptClientService.addDept(dept);
    }

    /**
     * 消費方根據id查詢部門信息
     * @param id
     * @return
     */
    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
       return deptClientService.queryById(id);
    }

    /**
     * 消費方查詢部門信息列表
     * @return
     */
    @RequestMapping("/consumer/dept/list")
    public List<Dept> list() {
        return deptClientService.queryAll();
    }
}

Feign和Ribbon二者對比,前者顯現出面向接口編程特點,代碼看起來更清爽,而且Feign調用方式更符合我們之前在做SSM或者SprngBoot項目時,Controller層調用Service層的編程習慣!

主配置類

@SpringBootApplication
@EnableEurekaClient
// feign客戶端注解,並指定要掃描的包以及配置接口DeptClientService
@EnableFeignClients(basePackages = {"com.haust.springcloud"})
// 切記不要加這個注解,不然會出現404訪問不到
//@ComponentScan("com.haust.springcloud")
public class FeignDeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(FeignDeptConsumer_80.class, args);
    }
}
  1. 改造springcloud-api模塊

    pom.xml添加feign依賴

<!--Feign的依賴-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

新建service包,並新建DeptClientService.java接口,

// @FeignClient:微服務客戶端注解,value:指定微服務的名字,這樣就可以使Feign客戶端直接找到對應的微服務
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {

    @GetMapping("/dept/get/{id}")
    public Dept queryById(@PathVariable("id") Long id);

    @GetMapping("/dept/list")
    public Dept queryAll();

    @GetMapping("/dept/add")
    public Dept addDept(Dept dept);
}

7.3 Feign和Ribbon如何選擇?

根據個人習慣而定,如果喜歡REST風格使用Ribbon;如果喜歡社區版的面向接口風格使用Feign.

Feign 本質上也是實現了 Ribbon,只不過后者是在調用方式上,為了滿足一些開發者習慣的接口調用習慣!

下面我們關閉springcloud-consumer-dept-80 這個服務消費方,換用springcloud-consumer-dept-feign(端口還是80) 來代替:(依然可以正常訪問,就是調用方式相比於Ribbon變化了)

8. Hystrix:服務熔斷

分布式系統面臨的問題

復雜分布式體系結構中的應用程序有數十個依賴關系,每個依賴關系在某些時候將不可避免失敗!

8.1 服務雪崩

多個微服務之間調用的時候,假設微服務A調用微服務B和微服務C,微服務B和微服務C又調用其他的微服務,這就是所謂的“扇出”,如果扇出的鏈路上某個微服務的調用響應時間過長,或者不可用,對微服務A的調用就會占用越來越多的系統資源,進而引起系統崩潰,所謂的“雪崩效應”。

在這里插入圖片描述

​ 對於高流量的應用來說,單一的后端依賴可能會導致所有服務器上的所有資源都在幾十秒內飽和。比失敗更糟糕的是,這些應用程序還可能導致服務之間的延遲增加,備份隊列,線程和其他系統資源緊張,導致整個系統發生更多的級聯故障,這些都表示需要對故障和延遲進行隔離和管理,以達到單個依賴關系的失敗而不影響整個應用程序或系統運行
我們需要,

棄車保帥

8.2 什么是Hystrix?

Hystrix是一個應用於處理分布式系統的延遲和容錯的開源庫,在分布式系統里,許多依賴不可避免的會調用失敗,比如超時,異常等,==Hystrix 能夠保證在一個依賴出問題的情況下,不會導致整個體系服務失敗,避免級聯故障,以提高分布式系統的彈性==
“斷路器”本身是一種開關裝置,當某個服務單元發生故障之后,通過斷路器的故障監控 (類似熔斷保險絲) ,**向調用方返回一個服務預期的,可處理的備選響應 (FallBack) ,而不是長時間的等待或者拋出調用方法無法處理的異常,這樣就可以保證了服務調用方的線程不會被長時間,不必要的占用,從而避免了故障在分布式系統中的蔓延,乃至雪崩。**

img

8.3 Hystrix能干嘛?

  • 服務降級
  • 服務熔斷
  • 服務限流
  • 接近實時的監控

當一切正常時,請求流可以如下所示:

img

當許多后端系統中有一個潛在阻塞服務時,它可以阻止整個用戶請求:

img

​ 隨着大容量通信量的增加,單個后端依賴項的潛在性會導致所有服務器上的所有資源在幾秒鍾內飽和。

​ 應用程序中通過網絡或客戶端庫可能導致網絡請求的每個點都是潛在故障的來源。比失敗更糟糕的是,這些應用程序還 可能導致服務之間的延遲增加,從而備份隊列、線程和其他系統資源,從而導致更多跨系統的級聯故障。

img

​ 當使用Hystrix包裝每個基礎依賴項時,上面的圖表中所示的體系結構會發生類似於以下關系圖的變化。每個依賴項是相互隔離的,限制在延遲發生時它可以填充的資源中,並包含在回退邏輯中,該邏輯決定在依賴項中發生任何類型的故障時要做出什么樣的響應:

在這里插入圖片描述

官網資料https://github.com/Netflix/Hystrix/wiki

8.4 服務熔斷

什么是服務熔斷?

熔斷機制是應對服務雪崩效應的一種微服務鏈路保護機制,當扇出鏈路的某個微服務不可用或者響應時間太長時,會進行服務的降級進而熔斷該節點微服務的調用,快速返回”錯誤”的響應信息。當檢測到該節點微服務響應正常后恢復調用鏈路,在SpringCloud框架機制通過Hystrix實現,Hystrix會監控微服務見調用的狀況,當失敗的調用到一個閾值,缺省是5秒內20次調用失敗就會啟動熔斷機制,熔斷機制的注解是@HystrixCommand

服務熔斷解決如下問題:

  • 當所依賴的對象不穩定時,能夠起到快速失敗的目的;
  • 快速失敗后,能夠根據一定的算法動態試探所依賴對象是否恢復。

入門案例

新建springcloud-provider-dept-hystrix-8001模塊並拷貝springcloud-provider-dept–8001內的pom.xml、resource和Java代碼進行初始化並調整。

導入hystrix依賴

<!--導入Hystrix依賴-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>

調整yml配置文件

server:
  port: 8001

# mybatis配置
mybatis:
  # springcloud-api 模塊下的pojo包
  type-aliases-package: com.haust.springcloud.pojo
  # 本模塊下的mybatis-config.xml核心配置文件類路徑
  config-location: classpath:mybatis/mybatis-config.xml
  # 本模塊下的mapper配置文件類路徑
  mapper-locations: classpath:mybatis/mapper/*.xml

# spring配置
spring:
  application:
    #項目名
    name: springcloud-provider-dept
  datasource:
    # 德魯伊數據源
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=utf-8
    username: root
    password: root

# Eureka配置:配置服務注冊中心地址
eureka:
  client:
    service-url:
      # 注冊中心地址7001-7003
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-provider-dept-hystrix-8001 #修改Eureka上的默認描述信息
    prefer-ip-address: true #改為true后默認顯示的是ip地址而不再是localhost

#info配置
info:
  app.name: haust-springcloud #項目的名稱
  company.name: com.haust #公司的名稱

prefer-ip-address: false:

在這里插入圖片描述

prefer-ip-address: true

在這里插入圖片描述

修改controller

/**
 * @Description: 提供Restful服務
 */
@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

    /**
     * 根據id查詢部門信息
     * 如果根據id查詢出現異常,則走hystrixGet這段備選代碼
     * @param id
     * @return
     */
    @HystrixCommand(fallbackMethod = "hystrixGet")
    @RequestMapping("/dept/get/{id}")//根據id查詢
    public Dept get(@PathVariable("id") Long id){
        Dept dept = deptService.queryById(id);
        if (dept==null){
            throw new RuntimeException("這個id=>"+id+",不存在該用戶,或信息無法找到~");
        }
        return dept;
    }

    /**
     * 根據id查詢備選方案(熔斷)
     * @param id
     * @return
     */
    public Dept hystrixGet(@PathVariable("id") Long id){
        return new Dept().setDeptno(id)
                .setDname("這個id=>"+id+",沒有對應的信息,null---@Hystrix~")
                .setDb_source("在MySQL中沒有這個數據庫");
    }
}

為主啟動類添加對熔斷的支持注解@EnableCircuitBreaker

/**
 * @Description: 啟動類
 */
@SpringBootApplication
@EnableEurekaClient // 客戶端的啟動類,在服務啟動后自動向注冊中心注冊服務
@EnableDiscoveryClient // 服務發現~
@EnableCircuitBreaker // 添加對熔斷的支持注解
public class HystrixDeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDeptProvider_8001.class,args);
    }
}

測試

使用熔斷后,當訪問一個不存在的id時,前台頁展示數據如下:

在這里插入圖片描述

而不適用熔斷的springcloud-provider-dept–8001模塊訪問相同地址會出現下面狀況:

在這里插入圖片描述

因此,為了避免因某個微服務后台出現異常或錯誤而導致整個應用或網頁報錯,使用熔斷是必要的

8.5 服務降級

什么是服務降級?

服務降級是指 當服務器壓力劇增的情況下,根據實際業務情況及流量,對一些服務和頁面有策略的不處理,或換種簡單的方式處理,從而釋放服務器資源以保證核心業務正常運作或高效運作。說白了,就是盡可能的把系統資源讓給優先級高的服務。

資源有限,而請求是無限的。如果在並發高峰期,不做服務降級處理,一方面肯定會影響整體服務的性能,嚴重的話可能會導致宕機某些重要的服務不可用。所以,一般在高峰期,為了保證核心功能服務的可用性,都要對某些服務降級處理。比如當雙11活動時,把交易無關的服務統統降級,如查看螞蟻森林,查看歷史訂單等等。

服務降級主要用於什么場景呢?當整個微服務架構整體的負載超出了預設的上限閾值或即將到來的流量預計將會超過預設的閾值時,為了保證重要或基本的服務能正常運行,可以將一些 不重要 或 不緊急 的服務或任務進行服務的 延遲使用 或 暫停使用。

降級的方式可以根據業務來,可以延遲服務,比如延遲給用戶增加積分,只是放到一個緩存中,等服務平穩之后再執行 ;或者在粒度范圍內關閉服務,比如關閉相關文章的推薦。
在這里插入圖片描述

由上圖可得,當某一時間內服務A的訪問量暴增,而B和C的訪問量較少,為了緩解A服務的壓力,這時候需要B和C暫時關閉一些服務功能,去承擔A的部分服務,從而為A分擔壓力,叫做服務降級

服務降級需要考慮的問題
  • 1)那些服務是核心服務,哪些服務是非核心服務
  • 2)那些服務可以支持降級,那些服務不能支持降級,降級策略是什么
  • 3)除服務降級之外是否存在更復雜的業務放通場景,策略是什么?

自動降級分類

1)超時降級:主要配置好超時時間和超時重試次數和機制,並使用異步機制探測回復情況
2)失敗次數降級:主要是一些不穩定的api,當失敗調用次數達到一定閥值自動降級,同樣要使用異步機制探測回復情況
3)故障降級:比如要調用的遠程服務掛掉了(網絡故障、DNS故障、http服務返回錯誤的狀態碼、rpc服務拋出異常),則可以直接降級。降級后的處理方案有:默認值(比如庫存服務掛了,返回默認現貨)、兜底數據(比如廣告掛了,返回提前准備好的一些靜態頁面)、緩存(之前暫存的一些緩存數據)
4)限流降級:秒殺或者搶購一些限購商品時,此時可能會因為訪問量太大而導致系統崩潰,此時會使用限流來進行限制訪問量,當達到限流閥值,后續請求會被降級;降級后的處理方案可以是:排隊頁面(將用戶導流到排隊頁面等一會重試)、無貨(直接告知用戶沒貨了)、錯誤頁(如活動太火爆了,稍后重試)。

入門案例

在springcloud-api模塊下的service包中新建降級配置類DeptClientServiceFallBackFactory.java

/**
 * @Description: Hystrix服務降級 ~
 */
@Component
public class DeptClientServiceFallBackFactory implements FallbackFactory {

    @Override
    public DeptClientService create(Throwable cause) {
        return new DeptClientService() {
            @Override
            public Dept queryById(Long id) {
                return new Dept()
                        .setDeptno(id)
                        .setDname("id=>" + id + "沒有對應的信息,客戶端提供了降級的信息,這個服務現在已經被關閉")
                        .setDb_source("沒有數據~");
            }
            @Override
            public List<Dept> queryAll() {
                return null;
            }

            @Override
            public Boolean addDept(Dept dept) {
                return false;
            }
        };
    }
}

在DeptClientService中指定降級配置類DeptClientServiceFallBackFactory

@Component //注冊到spring容器中
//@FeignClient:微服務客戶端注解,value:指定微服務的名字,這樣就可以使Feign客戶端直接找到對應的微服務
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallBackFactory.class)//fallbackFactory指定降級配置類
public interface DeptClientService {

    @GetMapping("/dept/get/{id}")
    public Dept queryById(@PathVariable("id") Long id);

    @GetMapping("/dept/list")
    public List<Dept> queryAll();

    @GetMapping("/dept/add")
    public Boolean addDept(Dept dept);
}

springcloud-consumer-dept-feign模塊中開啟降級:

server:
  port: 80

# Eureka配置
eureka:
  client:
    register-with-eureka: false # 不向 Eureka注冊自己
    service-url: # 從三個注冊中心中隨機取一個去訪問
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

# 開啟降級feign.hystrix
feign:
  hystrix:
    enabled: true

8.6 服務熔斷和降級的區別

  • 服務熔斷—>服務端:某個服務超時或異常,引起熔斷~,類似於保險絲(自我熔斷)
  • 服務降級—>客戶端:從整體網站請求負載考慮,當某個服務熔斷或者關閉之后,服務將不再被調用,此時在客戶端,我們可以准備一個 FallBackFactory ,返回一個默認的值(缺省值)。會導致整體的服務下降,但是好歹能用,比直接掛掉強。
  • 觸發原因不太一樣,服務熔斷一般是某個服務(下游服務)故障引起,而服務降級一般是從整體負荷考慮;管理目標的層次不太一樣,熔斷其實是一個框架級的處理,每個微服務都需要(無層級之分),而降級一般需要對業務有層級之分(比如降級一般是從最外圍服務開始)
  • 實現方式不太一樣,服務降級具有代碼侵入性(由控制器完成/或自動降級),熔斷一般稱為自我熔斷。

熔斷,降級,限流

限流:限制並發的請求訪問量,超過閾值則拒絕;

降級:服務分優先級,犧牲非核心服務(不可用),保證核心服務穩定;從整體負荷考慮;

熔斷:依賴的下游服務故障觸發熔斷,避免引發本系統崩潰;系統自動執行和恢復

8.7 Dashboard 流監控

新建springcloud-consumer-hystrix-dashboard模塊

<!--Hystrix依賴-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--dashboard依賴-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--Ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--Eureka-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
<!--實體類+web-->
<dependency>
    <groupId>com.haust</groupId>
    <artifactId>springcloud-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--熱部署-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

主啟動類

@SpringBootApplication
// 開啟Dashboard
@EnableHystrixDashboard
public class DeptConsumerDashboard_9001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumerDashboard_9001.class,args);
    }
}

給springcloud-provider-dept-hystrix-8001模塊下的主啟動類添加如下代碼,添加監控

@SpringBootApplication
@EnableEurekaClient //EnableEurekaClient 客戶端的啟動類,在服務啟動后自動向注冊中心注冊服務
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }

    //增加一個 Servlet
    @Bean
    public ServletRegistrationBean hystrixMetricsStreamServlet(){
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        //訪問該頁面就是監控頁面
        registrationBean.addUrlMappings("/actuator/hystrix.stream");
       
        return registrationBean;
    }
}

訪問:http://localhost:9001/hystrix

在這里插入圖片描述

進入監控頁面:在這里插入圖片描述

效果如下圖

在這里插入圖片描述

9. Zull路由網關

概述

什么是zuul?

Zull包含了對請求的路由(用來跳轉的)和過濾兩個最主要功能:
其中路由功能負責將外部請求轉發到具體的微服務實例上,是實現外部訪問統一入口的基礎,而過濾器功能則負責對請求的處理過程進行干預,是實現請求校驗,服務聚合等功能的基礎。**Zuul和Eureka進行整合,將Zuul自身注冊為Eureka服務治理下的應用,同時從Eureka中獲得其他服務的消息,也即以后的訪問微服務都是通過Zuul跳轉后獲得。

注意:Zuul 服務最終還是會注冊進 Eureka
提供:代理 + 路由 + 過濾三大功能!

Zuul 能干嘛?

  • 路由
  • 過濾

入門案例

新建springcloud-zuul模塊,並導入依賴

<dependencies>
    <!--導入zuul依賴-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zuul</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--Hystrix依賴-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--dashboard依賴-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-hystrix-dashboar</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--Ribbon-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-ribbon</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--Eureka-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
    <!--實體類+web-->
    <dependency>
        <groupId>com.haust</groupId>
        <artifactId>springcloud-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--熱部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>

application.yml

server:
  port: 9527

spring:
  application:
    name: springcloud-zuul #微服務名稱

# eureka 注冊中心配置
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance: #實例的id
    instance-id: zuul9527.com
    prefer-ip-address: true # 顯示ip

info:
  app.name: haust.springcloud # 項目名稱
  company.name: 阿里巴巴 # 公司名稱

# zull 路由網關配置
zuul:
  # 路由相關配置
  # 原來訪問路由 eg:http://www.cspstudy.com:9527/springcloud-provider-dept/dept/get/1
  # zull路由配置后訪問路由 eg:http://www.cspstudy.com:9527/haust/mydept/dept/get/1
  routes:
    mydept.serviceId: springcloud-provider-dept # eureka注冊中心的服務提供方路由名稱
    mydept.path: /mydept/** # 將eureka注冊中心的服務提供方路由名稱 改為自定義路由名稱
  # 不能再使用這個路徑訪問了,*: 忽略,隱藏全部的服務名稱~
  ignored-services: "*"
  # 設置公共的前綴
  prefix: /haust

主啟動類

/**
 * @Description: Zull路由網關主啟動類
 */
@SpringBootApplication
@EnableZuulProxy // 開啟Zuul
public class ZuulApplication_9527 {

    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication_9527.class,args);
    }
}

測試:****

在這里插入圖片描述

在這里插入圖片描述

上圖是沒有經過Zull路由網關配置時,服務接口訪問的路由,可以看出直接用微服務(服務提供方)名稱去訪問,這樣不安全,不能將微服務名稱暴露!

所以經過Zull路由網關配置后,訪問的路由為:

在這里插入圖片描述

我們看到,微服務名稱被替換並隱藏,換成了我們自定義的微服務名稱mydept,同時加上了前綴haust,這樣就做到了對路由fan訪問的加密處理!

詳情參考springcloud中文社區zuul組件 : https://www.springcloud.cc/spring-cloud-greenwich.html#_router_and_filter_zuul

10. Spring Cloud Config 分布式配置

Dalston.RELEASE

**Spring Cloud Config為分布式系統中的外部配置提供服務器和客戶端支持****。使用Config Server,您可以在所有環境中管理應用程序的外部屬性。客戶端和服務器上的概念映射與Spring Environment和PropertySource抽象相同,因此它們與Spring應用程序非常契合,但可以與任何以任何語言運行的應用程序一起使用。隨着應用程序通過從開發人員到測試和生產的部署流程,您可以管理這些環境之間的配置,並確定應用程序具有遷移時需要運行的一切。服務器存儲后端的默認實現使用git,因此它輕松支持標簽版本的配置環境,以及可以訪問用於管理內容的各種工具。很容易添加替代實現,並使用Spring配置將其插入。

概述

分布式系統面臨的–配置文件問題
微服務意味着要將單體應用中的業務拆分成一個個子服務,每個服務的粒度相對較小,因此系統中會出現大量的服務,由於每個服務都需要必要的配置信息才能運行,所以一套集中式的,動態的配置管理設施是必不可少的。spring cloud提供了configServer來解決這個問題,我們每一個微服務自己帶着一個application.yml,那上百個的配置文件修改起來,令人頭疼!

什么是SpringCloud config分布式配置中心?

img

spring cloud config 為微服務架構中的微服務提供集中化的外部支持,配置服務器為各個不同微服務應用的所有環節提供了一個中心化的外部配置。

spring cloud config 分為服務端和客戶端兩部分。

服務端也稱為 分布式配置中心,它是一個獨立的微服務應用,用來連接配置服務器並為客戶端提供獲取配置信息,加密,解密信息等訪問接口。

客戶端則是通過指定的配置中心來管理應用資源,以及與業務相關的配置內容,並在啟動的時候從配置中心獲取和加載配置信息。配置服務器默認采用git來存儲配置信息,這樣就有助於對環境配置進行版本管理。並且可用通過git客戶端工具來方便的管理和訪問配置內容。

spring cloud config 分布式配置中心能干嘛?

  • 集中式管理配置文件
  • 不同環境,不同配置,動態化的配置更新,分環境部署,比如 /dev /test /prod /beta /release
  • 運行期間動態調整配置,不再需要在每個服務部署的機器上編寫配置文件,服務會向配置中心統一拉取配置自己的信息
  • 當配置發生變動時,服務不需要重啟,即可感知到配置的變化,並應用新的配置
  • 將配置信息以REST接口的形式暴露

spring cloud config 分布式配置中心與GitHub整合

由於spring cloud config 默認使用git來存儲配置文件 (也有其他方式,比如自持SVN 和本地文件),但是最推薦的還是git ,而且使用的是 http / https 訪問的形式。入門案例

服務端

新建springcloud-config-server-3344模塊導入pom.xml依賴

<dependencies>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--config-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
        <version>2.1.1.RELEASE</version>
    </dependency>
    <!--eureka-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
        <version>1.4.6.RELEASE</version>
    </dependency>
</dependencies>

resource下創建application.yml配置文件,Spring Cloud Config服務器從git存儲庫(必須提供)為遠程客戶端提供配置:

server:
  port: 3344

spring:
  application:
    name: springcloud-config-server
  # 連接碼雲遠程倉庫
  cloud:
    config:
      server:
        git:
          # 注意是https的而不是ssh
          uri: https://gitee.com/cao_shi_peng/springcloud-config.git 
            # 通過 config-server可以連接到git,訪問其中的資源以及配置~

# 不加這個配置會報Cannot execute request on any known server 這個錯:連接Eureka服務端地址不對
# 或者直接注釋掉eureka依賴 這里暫時用不到eureka
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

主啟動類

@EnableConfigServer // 開啟spring cloud config server服務
@SpringBootApplication
public class Config_server_3344 {
    public static void main(String[] args) {
        SpringApplication.run(Config_server_3344.class,args);
    }
}

將本地git倉庫springcloud-config文件夾下新建的application.yml提交到碼雲倉庫:

在這里插入圖片描述

定位資源的默認策略是克隆一個git倉庫(在spring.cloud.config.server.git.uri),並使用它來初始化一個迷你SpringApplication。小應用程序的Environment用於枚舉屬性源並通過JSON端點發布。

HTTP服務具有以下格式的資源:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

其中“應用程序”作為SpringApplication中的spring.config.name注入(即常規的Spring Boot應用程序中通常是“應用程序”),“配置文件”是活動配置文件(或逗號分隔列表的屬性),“label”是可選的git標簽(默認為“master”)。

測試訪問http://localhost:3344/application-dev.yml

在這里插入圖片描述

測試訪問 http://localhost:3344/application/test/master

在這里插入圖片描述

在這里插入圖片描述

如果測試訪問不存在的配置則不顯示 如:http://localhost:3344/master/application-aaa.yml

在這里插入圖片描述

客戶端

將本地git倉庫springcloud-config文件夾下新建的config-client.yml提交到碼雲倉庫:

在這里插入圖片描述

新建一個springcloud-config-client-3355模塊,並導入依賴

<!--config-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-start -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
    <version>2.1.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

resources下創建application.yml和bootstrap.yml配置文件

bootstrap.yml 是系統級別的配置

# 系統級別的配置
spring:
  cloud:
    config:
      name: config-client # 需要從git上讀取的資源名稱,不要后綴
      profile: dev
      label: master
      uri: http://localhost:3344

application.yml 是用戶級別的配置

# 用戶級別的配置
spring:
  application:
    name: springcloud-config-client

創建controller包下的ConfigClientController.java 用於測試

@RestController
public class ConfigClientController {

    @Value("${spring.application.name}")
    private String applicationName; //獲取微服務名稱

    @Value("${eureka.client.service-url.defaultZone}")
    private String eurekaServer; //獲取Eureka服務

    @Value("${server.port}")
    private String port; //獲取服務端的端口號


    @RequestMapping("/config")
    public String getConfig(){
        return "applicationName:"+applicationName +
         "eurekaServer:"+eurekaServer +
         "port:"+port;
    }
}

主啟動類

@SpringBootApplication
public class ConfigClient {
    public static void main(String[] args) {
        SpringApplication.run(ConfigClient.class,args);
    }
}

測試:

啟動服務端Config_server_3344 再啟動客戶端ConfigClient

訪問:http://localhost:8201/config/

在這里插入圖片描述

小案例

本地新建config-dept.yml和config-eureka.yml並提交到碼雲倉庫

在這里插入圖片描述

在這里插入圖片描述

這里配置文件內容不再列舉直接到代碼中看把。
新建springcloud-config-eureka-7001模塊,並將原來的springcloud-eureka-7001模塊下的內容拷貝的該模塊。
1.清空該模塊的application.yml配置,並新建bootstrap.yml連接遠程配置

spring:
  cloud:
    config:
      name: config-eureka # 倉庫中的配置文件名稱
      label: master
      profile: dev
      uri: http://localhost:3344

2.在pom.xml中添加spring cloud config依賴

<!--config-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-config -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
    <version>2.1.1.RELEASE</version>
</dependency>

3.主啟動類

@SpringBootApplication
@EnableEurekaServer //EnableEurekaServer 服務端的啟動類,可以接受別人注冊進來~
public class ConfigEurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigEurekaServer_7001.class,args);
    }
}

4.測試

第一步:啟動 Config_Server_3344,並訪問 http://localhost:3344/master/config-eureka-dev.yml 測試

在這里插入圖片描述

第二部:啟動ConfigEurekaServer_7001,訪問 http://localhost:7001/ 測試

在這里插入圖片描述

顯示上圖則成功

新建springcloud-config-dept-8001模塊並拷貝springcloud-provider-dept-8001的內容

同理導入spring cloud config依賴、清空application.yml 、新建bootstrap.yml配置文件並配置

spring:
  cloud:
    config:
      name: config-dept
      label: master
      profile: dev
      uri: http://localhost:3344

主啟動類

@SpringBootApplication
@EnableEurekaClient //在服務啟動后自動注冊到Eureka中!
@EnableDiscoveryClient //服務發現~
@EnableCircuitBreaker //
public class ConfigDeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigDeptProvider_8001.class,args);
    }

    //增加一個 Servlet
    @Bean
    public ServletRegistrationBean hystrixMetricsStreamServlet(){
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        registrationBean.addUrlMappings("/actuator/hystrix.stream");
        return registrationBean;
    }
}

測試 (略)

學習視頻鏈接(狂神說):https://www.bilibili.com/video/BV1jJ411S7xr

筆記參考:https://blog.csdn.net/weixin_43591980/article/details/106255122


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM