主頁 > 軟體設計 > Spring Cloud 學習筆記(1 / 3)

Spring Cloud 學習筆記(1 / 3)

2021-03-04 10:44:25 軟體設計

Spring Cloud 學習筆記(2 / 3)

Spring Cloud 學習筆記(3 / 3)

---
01_前言閑聊和課程說明02_零基礎微服務架構理論入門03_第二季Boot和Cloud版本選型
04_Cloud組件停更說明05_父工程Project空間新建06_父工程pom檔案
07_復習DependencyManagement和Dependencies08_支付模塊構建(上)09_支付模塊構建(中)
10_支付模塊構建(下)11_熱部署Devtools12_消費者訂單模塊(上)
13_消費者訂單模塊(下)14_工程重構15_Eureka基礎知識
16_EurekaServer服務端安裝17_支付微服務8001入駐進EurekaServer18_訂單微服務80入駐進EurekaServer
19_Eureka集群原理說明20_Eureka集群環境構建21_訂單支付兩微服務注冊進Eureka集群
22_支付微服務集群配置23_actuator微服務資訊完善24_服務發現Discovery
25_Eureka自我保護理論知識26_怎么禁止自我保護27_Eureka停更說明
28_支付服務注冊進zookeeper29_臨時還是持久節點30_訂單服務注冊進zookeeper
31_Consul簡介32_安裝并運行Consul33_服務提供者注冊進Consul
34_服務消費者注冊進Consul35_三個注冊中心異同點36_Ribbon入門介紹
37_Ribbon的負載均衡和Rest呼叫38_Ribbon默認自帶的負載規則39_Ribbon負載規則替換
40_Ribbon默認負載輪詢演算法原理41_RoundRobinRule原始碼分析42_Ribbon之手寫輪詢演算法
43_OpenFeign是什么44_OpenFeign服務呼叫45_OpenFeign超時控制
46_OpenFeign日志增強47_Hystrix是什么48_Hystrix停更進維
49_Hystrix的服務降級熔斷限流概念初講50_Hystrix支付微服務構建51_JMeter高并發壓測后卡頓
52_訂單微服務呼叫支付服務出現卡頓53_降級容錯解決的維度要求54_Hystrix之服務降級支付側fallback
55_Hystrix之服務降級訂單側fallback--

01_前言閑聊和課程說明

教學視頻

原始碼檔案1、原始碼檔案2

02_零基礎微服務架構理論入門

什么是微服務

In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.——James Lewis and Martin Fowler (2014)

  • 微服務是一種架構風格
  • 一個應用拆分為一組小型服務
  • 每個服務運行在自己的行程內,也就是可獨立部署和升級
  • 服務之間使用輕量級HTTP互動
  • 服務圍繞業務功能拆分
  • 可以由全自動部署機制獨立部署
  • 去中心化,服務自治,服務可以使用不同的語言、不同的存盤技術

主題詞01:現代數字化生活-落地維度

  • 手機
  • PC
  • 智能家居

主題詞02:分布式微服務架構-落地維度

滿足哪些維度?支撐起這些維度的具體技術?

  • 服務呼叫
  • 服務降級
  • 服務注冊與發先
  • 服務熔斷
  • 負載均衡
  • 服務訊息佇列
  • 服務網關
  • 配置中心管理
  • 自動化構建部署
  • 服務監控
  • 全鏈路追蹤
  • 服務定時任務
  • 調度操作

Spring Cloud簡介

是什么?符合微服務技術維度

SpringCloud=分布式微服務架構的站式解決方案,是多種微服務架構落地技術的集合體,俗稱微服務全家桶

猜猜SpringCloud這個大集合里有多少種技術?

SpringCloud儼然已成為微服務開發的主流技術堆疊,在國內開發者社區非常火爆,

“微”力十足,互聯網大廠微服務架構案例

京東的:

京東的

阿里的:

阿里的

京東物流的:

京東物流

微服務的簡單概括

Spring Cloud技術堆疊

netflix

總結

03_第二季Boot和Cloud版本選型

  • Spring Boot 2.X 版

    • 原始碼地址
    • Spring Boot 2 的新特性
    • 通過上面官網發現,Boot官方強烈建議你升級到2.X以上版本
  • Spring Cloud H版

    • 原始碼地址
    • 官網
  • Spring Boot 與 Spring Cloud 兼容性查看

    • 檔案
    • JSON介面
  • 接下來開發用到的組件版本

    • Cloud - Hoxton.SR1
    • Boot - 2.2.2.RELEASE
    • Cloud Alibaba - 2.1.0.RELEASE
    • Java - Java 8
    • Maven - 3.5及以上
    • MySQL - 5.7及以上

04_Cloud組件停更說明

  • 停更引發的“升級慘案”

    • 停更不停用
    • 被動修復bugs
    • 不再接受合并請求
    • 不再發布新版本
  • Cloud升級

    • 服務注冊中心

      • × Eureka
      • √ Zookeeper
      • √ Consul
      • √ Nacos
    • 服務呼叫

      • √ Ribbon
      • √ LoadBalancer
    • 服務呼叫2

      • × Feign
      • √ OpenFeign
    • 服務降級

      • × Hystrix
      • √ resilience4j
      • √ sentienl
    • 服務網關

      • × Zuul
      • ! Zuul2
      • √ gateway
    • 服務配置

      • × Config
      • √ Nacos
    • 服務總線

      • × Bus
      • √ Nacos

Spring Cloud官方檔案

Spring Cloud中文檔案

Spring Boot官方檔案

05_父工程Project空間新建

約定 > 配置 > 編碼

創建微服務cloud整體聚合父工程Project,有8個關鍵步驟:

  1. New Project - maven工程 - create from archetype: maven-archetype-site
  2. 聚合總父工程名字
  3. Maven選版本
  4. 工程名字
  5. 字符編碼 - Settings - File encoding
  6. 注解生效激活 - Settings - Annotation Processors
  7. Java編譯版本選8
  8. File Type過濾 - Settings - File Type

archetype 英 [?ɑ?kita?p] 美 [?ɑ?rkita?p]
n. 典型

site 英 [sa?t] 美 [sa?t]
n. (建筑物、城鎮等的)地點,位置,建筑工地;現場;發生地;場所;網站;站點
v. 使坐落在;為…選址

06_父工程pom檔案

<?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.lun</groupId>
    <artifactId>LearnCloud</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging><!-- 這里添加,注意不是jar或war -->
    
    <!-- 統一管理jar包版本 -->
    <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>
        <mysql.version>5.1.47</mysql.version>
        <druid.version>1.1.16</druid.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>
    
    <!-- 子模塊繼承之后,提供作用:
		鎖定版本+子modlue不用寫groupId和version -->
    <dependencyManagement>
        <dependencies>
            <!--spring boot 2.2.2-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud Hoxton.SR1-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud alibaba 2.1.0.RELEASE-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</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>
                <optional>true</optional>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

07_復習DependencyManagement和Dependencies

Maven使用dependencyManagement元素來提供了一種管理依賴版本號的方式,

通常會在一個組織或者專案的最頂層的父POM中看到dependencyManagement元素,

使用pom.xml中的dependencyManagement元素能讓所有在子專案中參考個依賴而不用顯式的列出版本量,

Maven會沿著父子層次向上走,直到找到一個擁有dependencyManagement元素的專案,然后它就會使用這個
dependencyManagement元素中指定的版本號,

<dependencyManagement>
    <dependencies>
        <dependency>
        <groupId>mysq1</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.2</version>
        </dependency>
    <dependencies>
</dependencyManagement>

然后在子專案里就可以添加mysql-connector時可以不指定版本號,例如:

<dependencies>
    <dependency>
    <groupId>mysq1</groupId>
    <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

這樣做的好處就是:如果有多個子專案都參考同一樣依賴,則可以避免在每個使用的子專案里都宣告一個版本號,這樣當想升級或切換到另一個版本時,只需要在頂層父容器里更新,而不需要一個一個子專案的修改;另外如果某個子專案需要另外的一個版本,只需要宣告version就可,

  • dependencyManagement里只是宣告依賴,并不實作引入,因此子專案需要顯示的宣告需要用的依賴
  • 如果不在子專案中宣告依賴,是不會從父專案中繼承下來的;只有在子專案中寫了該依賴項,并且沒有指定具體版本,才會從父專案中繼承該項,并且version和scope都讀取自父pom,
  • 如果子專案中指定了版本號,那么會使用子專案中指定的jar版本,

IDEA右側旁的Maven插件有Toggle ' Skip Tests' Mode按鈕,這樣maven可以跳過單元測驗


父工程創建完成執行mvn : install將父工程發布到倉庫方便子工程繼承,

08_支付模塊構建(上)

創建微服務模塊套路:

  1. 建Module
  2. 改POM
  3. 寫YML
  4. 主啟動
  5. 業務類
<style>#mermaid-svg-klNt7RqAVEZollVb .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-klNt7RqAVEZollVb .label text{fill:#333}#mermaid-svg-klNt7RqAVEZollVb .node rect,#mermaid-svg-klNt7RqAVEZollVb .node circle,#mermaid-svg-klNt7RqAVEZollVb .node ellipse,#mermaid-svg-klNt7RqAVEZollVb .node polygon,#mermaid-svg-klNt7RqAVEZollVb .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-klNt7RqAVEZollVb .node .label{text-align:center;fill:#333}#mermaid-svg-klNt7RqAVEZollVb .node.clickable{cursor:pointer}#mermaid-svg-klNt7RqAVEZollVb .arrowheadPath{fill:#333}#mermaid-svg-klNt7RqAVEZollVb .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-klNt7RqAVEZollVb .flowchart-link{stroke:#333;fill:none}#mermaid-svg-klNt7RqAVEZollVb .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-klNt7RqAVEZollVb .edgeLabel rect{opacity:0.9}#mermaid-svg-klNt7RqAVEZollVb .edgeLabel span{color:#333}#mermaid-svg-klNt7RqAVEZollVb .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-klNt7RqAVEZollVb .cluster text{fill:#333}#mermaid-svg-klNt7RqAVEZollVb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-klNt7RqAVEZollVb .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-klNt7RqAVEZollVb text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-klNt7RqAVEZollVb .actor-line{stroke:grey}#mermaid-svg-klNt7RqAVEZollVb .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-klNt7RqAVEZollVb .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-klNt7RqAVEZollVb #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-klNt7RqAVEZollVb .sequenceNumber{fill:#fff}#mermaid-svg-klNt7RqAVEZollVb #sequencenumber{fill:#333}#mermaid-svg-klNt7RqAVEZollVb #crosshead path{fill:#333;stroke:#333}#mermaid-svg-klNt7RqAVEZollVb .messageText{fill:#333;stroke:#333}#mermaid-svg-klNt7RqAVEZollVb .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-klNt7RqAVEZollVb .labelText,#mermaid-svg-klNt7RqAVEZollVb .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-klNt7RqAVEZollVb .loopText,#mermaid-svg-klNt7RqAVEZollVb .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-klNt7RqAVEZollVb .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-klNt7RqAVEZollVb .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-klNt7RqAVEZollVb .noteText,#mermaid-svg-klNt7RqAVEZollVb .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-klNt7RqAVEZollVb .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-klNt7RqAVEZollVb .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-klNt7RqAVEZollVb .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-klNt7RqAVEZollVb .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb .section{stroke:none;opacity:0.2}#mermaid-svg-klNt7RqAVEZollVb .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-klNt7RqAVEZollVb .section2{fill:#fff400}#mermaid-svg-klNt7RqAVEZollVb .section1,#mermaid-svg-klNt7RqAVEZollVb .section3{fill:#fff;opacity:0.2}#mermaid-svg-klNt7RqAVEZollVb .sectionTitle0{fill:#333}#mermaid-svg-klNt7RqAVEZollVb .sectionTitle1{fill:#333}#mermaid-svg-klNt7RqAVEZollVb .sectionTitle2{fill:#333}#mermaid-svg-klNt7RqAVEZollVb .sectionTitle3{fill:#333}#mermaid-svg-klNt7RqAVEZollVb .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-klNt7RqAVEZollVb .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb .grid path{stroke-width:0}#mermaid-svg-klNt7RqAVEZollVb .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-klNt7RqAVEZollVb .task{stroke-width:2}#mermaid-svg-klNt7RqAVEZollVb .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb .taskText:not([font-size]){font-size:11px}#mermaid-svg-klNt7RqAVEZollVb .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-klNt7RqAVEZollVb .task.clickable{cursor:pointer}#mermaid-svg-klNt7RqAVEZollVb .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-klNt7RqAVEZollVb .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-klNt7RqAVEZollVb .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-klNt7RqAVEZollVb .taskText0,#mermaid-svg-klNt7RqAVEZollVb .taskText1,#mermaid-svg-klNt7RqAVEZollVb .taskText2,#mermaid-svg-klNt7RqAVEZollVb .taskText3{fill:#fff}#mermaid-svg-klNt7RqAVEZollVb .task0,#mermaid-svg-klNt7RqAVEZollVb .task1,#mermaid-svg-klNt7RqAVEZollVb .task2,#mermaid-svg-klNt7RqAVEZollVb .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-klNt7RqAVEZollVb .taskTextOutside0,#mermaid-svg-klNt7RqAVEZollVb .taskTextOutside2{fill:#000}#mermaid-svg-klNt7RqAVEZollVb .taskTextOutside1,#mermaid-svg-klNt7RqAVEZollVb .taskTextOutside3{fill:#000}#mermaid-svg-klNt7RqAVEZollVb .active0,#mermaid-svg-klNt7RqAVEZollVb .active1,#mermaid-svg-klNt7RqAVEZollVb .active2,#mermaid-svg-klNt7RqAVEZollVb .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-klNt7RqAVEZollVb .activeText0,#mermaid-svg-klNt7RqAVEZollVb .activeText1,#mermaid-svg-klNt7RqAVEZollVb .activeText2,#mermaid-svg-klNt7RqAVEZollVb .activeText3{fill:#000 !important}#mermaid-svg-klNt7RqAVEZollVb .done0,#mermaid-svg-klNt7RqAVEZollVb .done1,#mermaid-svg-klNt7RqAVEZollVb .done2,#mermaid-svg-klNt7RqAVEZollVb .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-klNt7RqAVEZollVb .doneText0,#mermaid-svg-klNt7RqAVEZollVb .doneText1,#mermaid-svg-klNt7RqAVEZollVb .doneText2,#mermaid-svg-klNt7RqAVEZollVb .doneText3{fill:#000 !important}#mermaid-svg-klNt7RqAVEZollVb .crit0,#mermaid-svg-klNt7RqAVEZollVb .crit1,#mermaid-svg-klNt7RqAVEZollVb .crit2,#mermaid-svg-klNt7RqAVEZollVb .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-klNt7RqAVEZollVb .activeCrit0,#mermaid-svg-klNt7RqAVEZollVb .activeCrit1,#mermaid-svg-klNt7RqAVEZollVb .activeCrit2,#mermaid-svg-klNt7RqAVEZollVb .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-klNt7RqAVEZollVb .doneCrit0,#mermaid-svg-klNt7RqAVEZollVb .doneCrit1,#mermaid-svg-klNt7RqAVEZollVb .doneCrit2,#mermaid-svg-klNt7RqAVEZollVb .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-klNt7RqAVEZollVb .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-klNt7RqAVEZollVb .milestoneText{font-style:italic}#mermaid-svg-klNt7RqAVEZollVb .doneCritText0,#mermaid-svg-klNt7RqAVEZollVb .doneCritText1,#mermaid-svg-klNt7RqAVEZollVb .doneCritText2,#mermaid-svg-klNt7RqAVEZollVb .doneCritText3{fill:#000 !important}#mermaid-svg-klNt7RqAVEZollVb .activeCritText0,#mermaid-svg-klNt7RqAVEZollVb .activeCritText1,#mermaid-svg-klNt7RqAVEZollVb .activeCritText2,#mermaid-svg-klNt7RqAVEZollVb .activeCritText3{fill:#000 !important}#mermaid-svg-klNt7RqAVEZollVb .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-klNt7RqAVEZollVb g.classGroup text .title{font-weight:bolder}#mermaid-svg-klNt7RqAVEZollVb g.clickable{cursor:pointer}#mermaid-svg-klNt7RqAVEZollVb g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-klNt7RqAVEZollVb g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-klNt7RqAVEZollVb .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-klNt7RqAVEZollVb .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-klNt7RqAVEZollVb .dashed-line{stroke-dasharray:3}#mermaid-svg-klNt7RqAVEZollVb #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb .commit-id,#mermaid-svg-klNt7RqAVEZollVb .commit-msg,#mermaid-svg-klNt7RqAVEZollVb .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-klNt7RqAVEZollVb g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-klNt7RqAVEZollVb g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-klNt7RqAVEZollVb g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-klNt7RqAVEZollVb g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-klNt7RqAVEZollVb .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-klNt7RqAVEZollVb .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-klNt7RqAVEZollVb .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-klNt7RqAVEZollVb .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-klNt7RqAVEZollVb .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-klNt7RqAVEZollVb .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-klNt7RqAVEZollVb .edgeLabel text{fill:#333}#mermaid-svg-klNt7RqAVEZollVb .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-klNt7RqAVEZollVb .node circle.state-start{fill:black;stroke:black}#mermaid-svg-klNt7RqAVEZollVb .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-klNt7RqAVEZollVb #statediagram-barbEnd{fill:#9370db}#mermaid-svg-klNt7RqAVEZollVb .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-klNt7RqAVEZollVb .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-klNt7RqAVEZollVb .statediagram-state .divider{stroke:#9370db}#mermaid-svg-klNt7RqAVEZollVb .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-klNt7RqAVEZollVb .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-klNt7RqAVEZollVb .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-klNt7RqAVEZollVb .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-klNt7RqAVEZollVb .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-klNt7RqAVEZollVb .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-klNt7RqAVEZollVb .note-edge{stroke-dasharray:5}#mermaid-svg-klNt7RqAVEZollVb .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-klNt7RqAVEZollVb .error-icon{fill:#522}#mermaid-svg-klNt7RqAVEZollVb .error-text{fill:#522;stroke:#522}#mermaid-svg-klNt7RqAVEZollVb .edge-thickness-normal{stroke-width:2px}#mermaid-svg-klNt7RqAVEZollVb .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-klNt7RqAVEZollVb .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-klNt7RqAVEZollVb .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-klNt7RqAVEZollVb .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-klNt7RqAVEZollVb .marker{fill:#333}#mermaid-svg-klNt7RqAVEZollVb .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;}</style> <style>#mermaid-svg-klNt7RqAVEZollVb { color: rgba(0, 0, 0, 0.75); font: ; }</style>
客戶端消費者80
order
微服務提供者8001
payment

創建cloud-provider-payment8001微服務提供者支付Module模塊:

1.建名為cloud-provider-payment8001的Maven工程

2.改POM

<?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>LearnCloud</artifactId>
        <groupId>com.lun</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8001</artifactId>

    <dependencies>
        <!--包含了sleuth+zipkin-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
            <!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
        <!--
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        -->
        <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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</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>

</project>

3.寫YML

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://localhost:3306/my?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 1234

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.lun.springcloud.entities    # 所有Entity別名類所在包

4.主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class PaymentMain001 {

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

09_支付模塊構建(中)

5.業務類

SQL

CREATE TABLE `payment`(
	`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
    `serial` varchar(200) DEFAULT '',
	PRIMARY KEY (id)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4

Entities

物體類Payment:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}

JSON封裝體CommonResult:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{
    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code, String message){
        this(code, message, null);
    }
}

DAO

介面PaymentDao:

import com.atguigu.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 */
@Mapper
//@Repository不用Spring的
public interface PaymentDao
{
    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") Long id);
}

MyBatis映射檔案PaymentMapper.xml,路徑:resources/mapper/PaymentMapper.xml

<?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.lun.springcloud.dao.PaymentDao">

    <insert id="create" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        insert into payment(serial)  values(#{serial});
    </insert>
    
    <resultMap id="BaseResultMap" type="com.lun.springcloud.entities.Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <id column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>

    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap">
        select * from payment where id=#{id};
    </select>

</mapper>

Service

介面PaymentService

import com.lun.springcloud.entities.Payment;
import org.apache.ibatis.annotations.Param;

/**
 */
public interface PaymentService
{
    public int create(Payment payment);

    public Payment getPaymentById(@Param("id") Long id);
}

實作類

import com.lun.springcloud.dao.PaymentDao;
import com.lun.springcloud.entities.Payment;
import com.lun.springcloud.service.PaymentService;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 */
@Service
public class PaymentServiceImpl implements PaymentService
{
    @Resource
    private PaymentDao paymentDao;

    public int create(Payment payment)
    {
        return paymentDao.create(payment);
    }

    public Payment getPaymentById(Long id)
    {
        return paymentDao.getPaymentById(id);
    }
}

Controller

import com.lun.springcloud.entities.CommonResult;
import com.lun.springcloud.entities.Payment;
import com.lun.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.web.bind.annotation.*;
import org.springframework.cloud.client.discovery.DiscoveryClient;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 */
@RestController
@Slf4j
public class PaymentController{
    @Resource
    private PaymentService paymentService;

    @PostMapping(value = "/payment/create")
    public CommonResult create(Payment payment)
    {
        int result = paymentService.create(payment);
        log.info("*****插入結果:"+result);

        if(result > 0)
        {
            return new CommonResult(200,"插入資料庫成功,serverPort: "+serverPort,result);
        }else{
            return new CommonResult(444,"插入資料庫失敗",null);
        }
    }

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
    {
        Payment payment = paymentService.getPaymentById(id);

        if(payment != null)
        {
            return new CommonResult(200,"查詢成功,serverPort:  "+serverPort,payment);
        }else{
            return new CommonResult(444,"沒有對應記錄,查詢ID: "+id,null);
        }
    }
}

10_支付模塊構建(下)

6.測驗

  1. 瀏覽器 - http://localhost:8001/payment/get/1
  2. Postman - http://localhost:8001/payment/create?serial=lun2

7.小總結

創建微服務模塊套路:

  1. 建Module
  2. 改POM
  3. 寫YML
  4. 主啟動
  5. 業務類

11_熱部署Devtools

開發時使用,生產環境關閉

1.Adding devtools to your project

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

2.Adding plugin to your pom.xml

下段配置復制到聚合父類總工程的pom.xml

<build>
    <!--
	<finalName>你的工程名</finalName>(單一工程時添加)
    -->
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <fork>true</fork>
                <addResources>true</addResources>
            </configuration>
        </plugin>
    </plugins>
</build>

3.Enabling automatic build

File -> Settings(New Project Settings->Settings for New Projects) ->Complier

下面項勾選

  • Automatically show first error in editor
  • Display notification on build completion
  • Build project automatically
  • Compile independent modules in parallel

4.Update the value of

鍵入Ctrl + Shift + Alt + / ,打開Registry,勾選:

  • compiler.automake.allow.when.app.running

  • actionSystem.assertFocusAccessFromEdt

5.重啟IDEA

12_消費者訂單模塊(上)

1.建Module

創建名為cloud-consumer-order80的maven工程,

2.改POM

<?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>LearnCloud</artifactId>
    <groupId>com.lun</groupId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>

  <artifactId>cloud-consumer-order80</artifactId>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <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>
</project>

3.寫YML

server:
  port: 80

4.主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Hello world!
 *
 */
@SpringBootApplication
public class OrderMain80
{
    public static void main( String[] args ){
        SpringApplication.run(OrderMain80.class, args);
    }
}

5.業務類

物體類:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
    private Long id;
    private String serial;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T>{
    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code, String message){
        this(code, message, null);
    }
}

控制層:

import com.lun.springcloud.entities.CommonResult;
import com.lun.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Slf4j
@RestController
public class OrderController {

    public static final String PAYMENT_URL = "http://localhost:8001";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/create")
    public CommonResult<Payment> create(Payment payment){

        return restTemplate.postForObject(PAYMENT_URL+"/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id, CommonResult.class);
    }
}

配置類:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}

6.測驗

運行cloud-consumer-order80與cloud-provider-payment8001兩工程

  • 瀏覽器 - http://localhost/consumer/payment/get/1

RestTemplate

RestTemplate提供了多種便捷訪問遠程Http服務的方法,是一種簡單便捷的訪問restful服務模板類,是Spring提供的用于訪問Rest服務的客戶端模板工具集

官網地址

使用:

  • 使用restTemplate訪問restful介面非常的簡單粗暴無腦,
  • (url, requestMap, ResponseBean.class)這三個引數分別代表,
  • REST請求地址、請求引數、HTTP回應轉換被轉換成的物件型別,

13_消費者訂單模塊(下)

瀏覽器 - http://localhost/consumer/payment/create?serial=lun3

雖然,回傳成功,但是觀測資料庫中,并沒有創建seriallun3的行,

解決之道:在loud-provider-payment8001工程的PaymentController中添加@RequestBody注解,

public class PaymentController
{

    @PostMapping(value = "/payment/create")
    public CommonResult create(@RequestBody/*添加到這里*/ Payment payment){
		...
    }
}

通過修改idea的workspace.xml的方式來快速打開Run Dashboard視窗(這個用來顯示哪些Spring Boot工程運行,停止等資訊,我idea 2020.1版本在名為Services視窗就可以顯示哪些Spring Boot工程運行,停止等資訊出來,所以這僅作記錄參考),

  • 開啟Run DashBoard

    1. 打開工程路徑下的.idea檔案夾的workspace.xml

    2. <component name="RunDashboard">中修改或添加以下代碼:

<option name="configurationTypes">
	<set>
		<option value="SpringBootApplicationConfigurationType"/>
    </set>
</option>

由于idea版本差異,可能需要關閉重啟,

14_工程重構

觀察cloud-consumer-order80與cloud-provider-payment8001兩工程有重復代碼(entities包下的物體)(壞味道),重構,

1.新建 - cloud-api-commons

2.POM

<?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>LearnCloud</artifactId>
        <groupId>com.lun.springcloud</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-api-commons</artifactId>

    <dependencies>
        <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>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>

</project>

3.entities

將cloud-consumer-order80與cloud-provider-payment8001兩工程的公有entities包移至cloud-api-commons工程下,

4.maven clean、install cloud-api-commons工程,以供給cloud-consumer-order80與cloud-provider-payment8001兩工程呼叫,

5.訂單80和支付8001分別改造

  • 將cloud-consumer-order80與cloud-provider-payment8001兩工程的公有entities包移除
  • 引入cloud-api-commons依賴
<dependency>
    <groupId>com.lun.springcloud</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>${project.version}</version>
</dependency>

6.測驗

15_Eureka基礎知識

什么是服務治理

Spring Cloud封裝了Netflix 公司開發的Eureka模塊來實作服務治理

在傳統的RPC遠程呼叫框架中,管理每個服務與服務之間依賴關系比較復雜,管理比較復雜,所以需要使用服務治理,管理服務于服務之間依賴關系,可以實作服務呼叫、負載均衡、容錯等,實作服務發現與注冊,

什么是服務注冊與發現

Eureka采用了CS的設計架構,Eureka Sever作為服務注冊功能的服務器,它是服務注冊中心,而系統中的其他微服務,使用Eureka的客戶端連接到 Eureka Server并維持心跳連接,這樣系統的維護人員就可以通過Eureka Server來監控系統中各個微服務是否正常運行,

在服務注冊與發現中,有一個注冊中心,當服務器啟動的時候,會把當前自己服務器的資訊比如服務地址通訊地址等以別名方式注冊到注冊中心上,另一方(消費者服務提供者),以該別名的方式去注冊中心上獲取到實際的服務通訊地址,然后再實作本地RPC呼叫RPC遠程呼叫框架核心設計思想:在于注冊中心,因為使用注冊中心管理每個服務與服務之間的一個依賴關系(服務治理概念),在任何RPC遠程框架中,都會有一個注冊中心存放服務地址相關資訊(介面地址)

Eureka包含兩個組件:Eureka Server和Eureka Client

Eureka Server提供服務注冊服務

各個微服務節點通過配置啟動后,會在EurekaServer中進行注冊,這樣EurekaServer中的服務注冊表中將會存盤所有可用服務節點的資訊,服務節點的資訊可以在界面中直觀看到,

EurekaClient通過注冊中心進行訪問

它是一個Java客戶端,用于簡化Eureka Server的互動,客戶端同時也具備一個內置的、使用輪詢(round-robin)負載演算法的負載均衡器,在應用啟動后,將會向Eureka Server發送心跳(默認周期為30秒),如果Eureka Server在多個心跳周期內沒有接收到某個節點的心跳,EurekaServer將會從服務注冊表中把這個服務節點移除(默認90秒)

16_EurekaServer服務端安裝

IDEA生成eurekaServer端服務注冊中心,類似物業公司

1.創建名為cloud-eureka-server7001的Maven工程

2.修改pom.xml

<!-- eureka新舊版本 -->
<!-- 以前的老版本(2018)-->
<dependency>
    <groupid>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

<!-- 現在新版本(2020.2)--><!-- 我們使用最新的 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<?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>LearnCloud</artifactId>
        <groupId>com.lun.springcloud</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-eureka-server7001</artifactId>

    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.lun.springcloud</groupId>
            <artifactId>cloud-api-commons</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>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

</project>

3.添加application.yml

server:
  port: 7001

eureka:
  instance:
    hostname: locathost #eureka服務端的實體名稱
  client:
    #false表示不向注冊中心注冊自己,
    register-with-eureka: false
    #false表示自己端就是注冊中心,我的職責就是維護服務實體,并不需要去檢索服務
    fetch-registry: false
    service-url:
      #設定與Eureka server互動的地址查詢服務和注冊服務都需要依賴這個地址,
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

4.主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

5.測驗運行EurekaMain7001,瀏覽器輸入http://localhost:7001/回車,會查看到Spring Eureka服務主頁,

17_支付微服務8001入駐進EurekaServer

EurekaClient端cloud-provider-payment8001將注冊進EurekaServer成為服務提供者provider,類似學校對外提供授課服務,

1.修改cloud-provider-payment8001

2.改POM

添加spring-cloud-starter-netflix-eureka-client依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

3.寫YML

eureka:
  client:
    #表示是否將自己注冊進Eurekaserver默認為true,
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊資訊,默認為true,單節點無所謂,集群必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      defaultzone: http://localhost:7001/eureka

4.主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient//<-----添加該注解
public class PaymentMain001 {

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

5.測驗

  • 啟動cloud-provider-payment8001和cloud-eureka-server7001工程,

  • 瀏覽器輸入 - http://localhost:7001/ 主頁內的Instances currently registered with Eureka會顯示cloud-provider-payment8001的組態檔application.yml設定的應用名cloud-payment-service

spring:
  application:
    name: cloud-payment-service

6.自我保護機制

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARELESSER THAN THRESHOLD AND HENCFT ARE NOT BEING EXPIRED JUST TO BE SAFE.

緊急情況!EUREKA可能錯誤地聲稱實體在沒有啟動的情況下啟動了,續訂小于閾值,因此實體不會為了安全而過期,

18_訂單微服務80入駐進EurekaServer

EurekaClient端cloud-consumer-order80將注冊進EurekaServer成為服務消費者consumer,類似來上課消費的同學

1.cloud-consumer-order80

2.POM

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

3.YML

server:
  port: 80

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    #表示是否將自己注冊進Eurekaserver默認為true,
    register-with-eureka: true
    #是否從EurekaServer抓取已有的注冊資訊,默認為true,單節點無所謂,集群必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

4.主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient//<--- 添加該標簽
public class OrderMain80
{
    public static void main( String[] args ){
        SpringApplication.run(OrderMain80.class, args);
    }
}

5.測驗

  • 啟動cloud-provider-payment8001、cloud-eureka-server7001和cloud-consumer-order80這三工程,
  • 瀏覽器輸入 http://localhost:7001 , 在主頁的Instances currently registered with Eureka將會看到cloud-provider-payment8001、cloud-consumer-order80兩個工程名,

注意,application.yml配置中層次縮進和空格,兩者不能少,否則,會拋出例外Failed to bind properties under 'eureka.client.service-url' to java.util.Map <java.lang.String, java.lang.String>

19_Eureka集群原理說明

1.Eureka集群原理說明

服務注冊:將服務資訊注冊進注冊中心

服務發現:從注冊中心上獲取服務資訊

實質:存key服務命取value閉用地址

1先啟動eureka注主冊中心

2啟動服務提供者payment支付服務

3支付服務啟動后會把自身資訊(比服務地址L以別名方式注朋進eureka

4消費者order服務在需要呼叫介面時,使用服務別名去注冊中心獲取實際的RPC遠程呼叫地址

5消去者導呼叫地址后,底屋實際是利用HttpClient技術實作遠程呼叫

6消費者實癸導服務地址后會快取在本地jvm記憶體中,默認每間隔30秒更新—次服務呼叫地址

問題:微服務RPC遠程服務呼叫最核心的是什么
高可用,試想你的注冊中心只有一個only one,萬一它出故障了,會導致整個為服務環境不可用,

解決辦法:搭建Eureka注冊中心集群,實作負載均衡+故障容錯,

互相注冊,相互守望

20_Eureka集群環境構建

創建cloud-eureka-server7002工程,程序參考16_EurekaServer服務端安裝

<style>#mermaid-svg-HSnZLDB6aGZzjsWC .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .label text{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .node rect,#mermaid-svg-HSnZLDB6aGZzjsWC .node circle,#mermaid-svg-HSnZLDB6aGZzjsWC .node ellipse,#mermaid-svg-HSnZLDB6aGZzjsWC .node polygon,#mermaid-svg-HSnZLDB6aGZzjsWC .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-HSnZLDB6aGZzjsWC .node .label{text-align:center;fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .node.clickable{cursor:pointer}#mermaid-svg-HSnZLDB6aGZzjsWC .arrowheadPath{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-HSnZLDB6aGZzjsWC .flowchart-link{stroke:#333;fill:none}#mermaid-svg-HSnZLDB6aGZzjsWC .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-HSnZLDB6aGZzjsWC .edgeLabel rect{opacity:0.9}#mermaid-svg-HSnZLDB6aGZzjsWC .edgeLabel span{color:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-HSnZLDB6aGZzjsWC .cluster text{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-HSnZLDB6aGZzjsWC .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-HSnZLDB6aGZzjsWC text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-HSnZLDB6aGZzjsWC .actor-line{stroke:grey}#mermaid-svg-HSnZLDB6aGZzjsWC .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-HSnZLDB6aGZzjsWC #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .sequenceNumber{fill:#fff}#mermaid-svg-HSnZLDB6aGZzjsWC #sequencenumber{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC #crosshead path{fill:#333;stroke:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .messageText{fill:#333;stroke:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-HSnZLDB6aGZzjsWC .labelText,#mermaid-svg-HSnZLDB6aGZzjsWC .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-HSnZLDB6aGZzjsWC .loopText,#mermaid-svg-HSnZLDB6aGZzjsWC .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-HSnZLDB6aGZzjsWC .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-HSnZLDB6aGZzjsWC .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-HSnZLDB6aGZzjsWC .noteText,#mermaid-svg-HSnZLDB6aGZzjsWC .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-HSnZLDB6aGZzjsWC .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-HSnZLDB6aGZzjsWC .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-HSnZLDB6aGZzjsWC .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-HSnZLDB6aGZzjsWC .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC .section{stroke:none;opacity:0.2}#mermaid-svg-HSnZLDB6aGZzjsWC .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-HSnZLDB6aGZzjsWC .section2{fill:#fff400}#mermaid-svg-HSnZLDB6aGZzjsWC .section1,#mermaid-svg-HSnZLDB6aGZzjsWC .section3{fill:#fff;opacity:0.2}#mermaid-svg-HSnZLDB6aGZzjsWC .sectionTitle0{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .sectionTitle1{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .sectionTitle2{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .sectionTitle3{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-HSnZLDB6aGZzjsWC .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC .grid path{stroke-width:0}#mermaid-svg-HSnZLDB6aGZzjsWC .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-HSnZLDB6aGZzjsWC .task{stroke-width:2}#mermaid-svg-HSnZLDB6aGZzjsWC .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC .taskText:not([font-size]){font-size:11px}#mermaid-svg-HSnZLDB6aGZzjsWC .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-HSnZLDB6aGZzjsWC .task.clickable{cursor:pointer}#mermaid-svg-HSnZLDB6aGZzjsWC .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-HSnZLDB6aGZzjsWC .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-HSnZLDB6aGZzjsWC .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-HSnZLDB6aGZzjsWC .taskText0,#mermaid-svg-HSnZLDB6aGZzjsWC .taskText1,#mermaid-svg-HSnZLDB6aGZzjsWC .taskText2,#mermaid-svg-HSnZLDB6aGZzjsWC .taskText3{fill:#fff}#mermaid-svg-HSnZLDB6aGZzjsWC .task0,#mermaid-svg-HSnZLDB6aGZzjsWC .task1,#mermaid-svg-HSnZLDB6aGZzjsWC .task2,#mermaid-svg-HSnZLDB6aGZzjsWC .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-HSnZLDB6aGZzjsWC .taskTextOutside0,#mermaid-svg-HSnZLDB6aGZzjsWC .taskTextOutside2{fill:#000}#mermaid-svg-HSnZLDB6aGZzjsWC .taskTextOutside1,#mermaid-svg-HSnZLDB6aGZzjsWC .taskTextOutside3{fill:#000}#mermaid-svg-HSnZLDB6aGZzjsWC .active0,#mermaid-svg-HSnZLDB6aGZzjsWC .active1,#mermaid-svg-HSnZLDB6aGZzjsWC .active2,#mermaid-svg-HSnZLDB6aGZzjsWC .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-HSnZLDB6aGZzjsWC .activeText0,#mermaid-svg-HSnZLDB6aGZzjsWC .activeText1,#mermaid-svg-HSnZLDB6aGZzjsWC .activeText2,#mermaid-svg-HSnZLDB6aGZzjsWC .activeText3{fill:#000 !important}#mermaid-svg-HSnZLDB6aGZzjsWC .done0,#mermaid-svg-HSnZLDB6aGZzjsWC .done1,#mermaid-svg-HSnZLDB6aGZzjsWC .done2,#mermaid-svg-HSnZLDB6aGZzjsWC .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-HSnZLDB6aGZzjsWC .doneText0,#mermaid-svg-HSnZLDB6aGZzjsWC .doneText1,#mermaid-svg-HSnZLDB6aGZzjsWC .doneText2,#mermaid-svg-HSnZLDB6aGZzjsWC .doneText3{fill:#000 !important}#mermaid-svg-HSnZLDB6aGZzjsWC .crit0,#mermaid-svg-HSnZLDB6aGZzjsWC .crit1,#mermaid-svg-HSnZLDB6aGZzjsWC .crit2,#mermaid-svg-HSnZLDB6aGZzjsWC .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-HSnZLDB6aGZzjsWC .activeCrit0,#mermaid-svg-HSnZLDB6aGZzjsWC .activeCrit1,#mermaid-svg-HSnZLDB6aGZzjsWC .activeCrit2,#mermaid-svg-HSnZLDB6aGZzjsWC .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-HSnZLDB6aGZzjsWC .doneCrit0,#mermaid-svg-HSnZLDB6aGZzjsWC .doneCrit1,#mermaid-svg-HSnZLDB6aGZzjsWC .doneCrit2,#mermaid-svg-HSnZLDB6aGZzjsWC .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-HSnZLDB6aGZzjsWC .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-HSnZLDB6aGZzjsWC .milestoneText{font-style:italic}#mermaid-svg-HSnZLDB6aGZzjsWC .doneCritText0,#mermaid-svg-HSnZLDB6aGZzjsWC .doneCritText1,#mermaid-svg-HSnZLDB6aGZzjsWC .doneCritText2,#mermaid-svg-HSnZLDB6aGZzjsWC .doneCritText3{fill:#000 !important}#mermaid-svg-HSnZLDB6aGZzjsWC .activeCritText0,#mermaid-svg-HSnZLDB6aGZzjsWC .activeCritText1,#mermaid-svg-HSnZLDB6aGZzjsWC .activeCritText2,#mermaid-svg-HSnZLDB6aGZzjsWC .activeCritText3{fill:#000 !important}#mermaid-svg-HSnZLDB6aGZzjsWC .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-HSnZLDB6aGZzjsWC g.classGroup text .title{font-weight:bolder}#mermaid-svg-HSnZLDB6aGZzjsWC g.clickable{cursor:pointer}#mermaid-svg-HSnZLDB6aGZzjsWC g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-HSnZLDB6aGZzjsWC g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-HSnZLDB6aGZzjsWC .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-HSnZLDB6aGZzjsWC .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-HSnZLDB6aGZzjsWC .dashed-line{stroke-dasharray:3}#mermaid-svg-HSnZLDB6aGZzjsWC #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC .commit-id,#mermaid-svg-HSnZLDB6aGZzjsWC .commit-msg,#mermaid-svg-HSnZLDB6aGZzjsWC .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-HSnZLDB6aGZzjsWC g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-HSnZLDB6aGZzjsWC g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-HSnZLDB6aGZzjsWC g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-HSnZLDB6aGZzjsWC .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-HSnZLDB6aGZzjsWC .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-HSnZLDB6aGZzjsWC .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-HSnZLDB6aGZzjsWC .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-HSnZLDB6aGZzjsWC .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-HSnZLDB6aGZzjsWC .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-HSnZLDB6aGZzjsWC .edgeLabel text{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-HSnZLDB6aGZzjsWC .node circle.state-start{fill:black;stroke:black}#mermaid-svg-HSnZLDB6aGZzjsWC .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-HSnZLDB6aGZzjsWC #statediagram-barbEnd{fill:#9370db}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-state .divider{stroke:#9370db}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-HSnZLDB6aGZzjsWC .note-edge{stroke-dasharray:5}#mermaid-svg-HSnZLDB6aGZzjsWC .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-HSnZLDB6aGZzjsWC .error-icon{fill:#522}#mermaid-svg-HSnZLDB6aGZzjsWC .error-text{fill:#522;stroke:#522}#mermaid-svg-HSnZLDB6aGZzjsWC .edge-thickness-normal{stroke-width:2px}#mermaid-svg-HSnZLDB6aGZzjsWC .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-HSnZLDB6aGZzjsWC .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-HSnZLDB6aGZzjsWC .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-HSnZLDB6aGZzjsWC .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-HSnZLDB6aGZzjsWC .marker{fill:#333}#mermaid-svg-HSnZLDB6aGZzjsWC .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;}</style> <style>#mermaid-svg-HSnZLDB6aGZzjsWC { color: rgba(0, 0, 0, 0.75); font: ; }</style>
Eureka
7001
Eureka
7002
  • 找到C:\Windows\System32\drivers\etc路徑下的hosts檔案,修改映射配置添加進hosts檔案
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
  • 修改cloud-eureka-server7001組態檔
server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com #eureka服務端的實體名稱
  client:
    register-with-eureka: false     #false表示不向注冊中心注冊自己,
    fetch-registry: false     #false表示自己端就是注冊中心,我的職責就是維護服務實體,并不需要去檢索服務
    service-url:
    #集群指向其它eureka
      defaultZone: http://eureka7002.com:7002/eureka/
    #單機就是7001自己
      #defaultZone: http://eureka7001.com:7001/eureka/
  • 修改cloud-eureka-server7002組態檔
server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服務端的實體名稱
  client:
    register-with-eureka: false     #false表示不向注冊中心注冊自己,
    fetch-registry: false     #false表示自己端就是注冊中心,我的職責就是維護服務實體,并不需要去檢索服務
    service-url:
    #集群指向其它eureka
      defaultZone: http://eureka7001.com:7001/eureka/
    #單機就是7002自己
      #defaultZone: http://eureka7002.com:7002/eureka/

實踐的時候,遇到例外情況

在開啟cloud-eureka-server7002時,開啟失敗,說7002埠被占用,然后在cmd中輸入netstat -ano | find "7002",查不到任何東西,

納悶一陣,重啟電腦,問題解決,

21_訂單支付兩微服務注冊進Eureka集群

  • 將支付服務8001微服務,訂單服務80微服務發布到上面2臺Eureka集群配置中

將它們的組態檔的eureka.client.service-url.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
  • 測驗01
    1. 先要啟動EurekaServer,7001/7002服務
    2. 再要啟動服務提供者provider,8001
    3. 再要啟動消費者,80
    4. 瀏覽器輸入 - http://localhost/consumer/payment/get/1

22_支付微服務集群配置

支付服務提供者8001集群環境構建

參考cloud-provicer-payment8001

1.新建cloud-provider-payment8002

2.改POM

3.寫YML - 埠8002

4.主啟動

5.業務類

6.修改8001/8002的Controller,添加serverPort

@RestController
@Slf4j
public class PaymentController{

    @Value("${server.port}")
    private String serverPort;//添加serverPort

    @PostMapping(value = "/payment/create")
    public CommonResult create(@RequestBody Payment payment)
    {
        int result = paymentService.create(payment);
        log.info("*****插入結果:" + result);

        if(result > 0) {
            return new CommonResult(200,"插入資料庫成功,serverPort: "+serverPort/*添加到此處*/, result);
        }else{
            return new CommonResult(444,"插入資料庫失敗",null);
        }
    }
}

負載均衡

cloud-consumer-order80訂單服務訪問地址不能寫死

@Slf4j
@RestController
public class OrderController {

    //public static final String PAYMENT_URL = "http://localhost:8001";
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
    
    ...
}

使用@LoadBalanced注解賦予RestTemplate負載均衡的能力

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced//使用@LoadBalanced注解賦予RestTemplate負載均衡的能力
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}

ApplicationContextBean - 提前說一下Ribbon的負載均衡功能

測驗

先要啟動EurekaServer,7001/7002服務

再要啟動服務提供者provider,8001/8002服務

瀏覽器輸入 - http://localhost/consumer/payment/get/31

結果:負載均衡效果達到,8001/8002埠交替出現

Ribbon和Eureka整合后Consumer可以直接呼叫服務而不用再關心地址和埠號,且該服務還有負載功能,

相互注冊,相互守望

23_actuator微服務資訊完善

主機名稱:服務名稱修改(也就是將IP地址,換成可讀性高的名字)

修改cloud-provider-payment8001,cloud-provider-payment8002

修改部分 - YML - eureka.instance.instance-id

eureka:
  ...
  instance:
    instance-id: payment8001 #添加此處
eureka:
  ...
  instance:
    instance-id: payment8002 #添加此處

修改之后

eureka主頁將顯示payment8001,payment8002代替原來顯示的IP地址,


訪問資訊有IP資訊提示,(就是將滑鼠指標移至payment8001,payment8002名下,會有IP地址提示)

修改部分 - YML - eureka.instance.prefer-ip-address

eureka:
  ...
  instance:
    instance-id: payment8001 
    prefer-ip-address: true #添加此處
eureka:
  ...
  instance:
    instance-id: payment8002
    prefer-ip-address: true #添加此處

24_服務發現Discovery

對于注冊進eureka里面的微服務,可以通過服務發現來獲得該服務的資訊

  • 修改cloud-provider-payment8001的Controller
@RestController
@Slf4j
public class PaymentController{
	...
    
    @Resource
    private DiscoveryClient discoveryClient;

    ...

    @GetMapping(value = "/payment/discovery")
    public Object discovery()
    {
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            log.info("*****element: "+element);
        }

        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;
    }
}
  • 8001主啟動類
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient//添加該注解
public class PaymentMain001 {

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

  • 自測

先要啟動EurekaSeryer

再啟動8001主啟動類,需要稍等一會兒

瀏覽器輸入http://localhost:8001/payment/discovery

瀏覽器輸出:

{"services":["cloud-payment-service"],"order":0}

后臺輸出:

*****element: cloud-payment-service
CLOUD-PAYMENT-SERVICE	192.168.199.218	8001	http://192.168.199.218:8001

25_Eureka自我保護理論知識

概述

保護模式主要用于一組客戶端和Eureka Server之間存在網路磁區場景下的保護,一旦進入保護模式,Eureka Server將會嘗試保護其服務注冊表中的資訊,不再洗掉服務注冊表中的資料,也就是不會注銷任何微服務,

如果在Eureka Server的首頁看到以下這段提示,則說明Eureka進入了保護模式:

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THANTHRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUSTTO BE SAFE

導致原因

一句話:某時刻某一個微服務不可用了,Eureka不會立刻清理,依舊會對該微服務的資訊進行保存,

屬于CAP里面的AP分支,

為什么會產生Eureka自我保護機制?

為了EurekaClient可以正常運行,防止與EurekaServer網路不通情況下,EurekaServer不會立刻將EurekaClient服務剔除

什么是自我保護模式?

默認情況下,如果EurekaServer在一定時間內沒有接收到某個微服務實體的心跳,EurekaServer將會注銷該實體(默認90秒),但是當網路磁區故障發生(延時、卡頓、擁擠)時,微服務與EurekaServer之間無法正常通信,以上行為可能變得非常危險了——因為微服務本身其實是健康的,此時本不應該注銷這個微服務,Eureka通過“自我保護模式”來解決這個問題——當EurekaServer節點在短時間內丟失過多客戶端時(可能發生了網路磁區故障),那么這個節點就會進入自我保護模式,

自我保護機制∶默認情況下EurekaClient定時向EurekaServer端發送心跳包

如果Eureka在server端在一定時間內(默認90秒)沒有收到EurekaClient發送心跳包,便會直接從服務注冊串列中剔除該服務,但是在短時間( 90秒中)內丟失了大量的服務實體心跳,這時候Eurekaserver會開啟自我保護機制,不會剔除該服務(該現象可能出現在如果網路不通但是EurekaClient為出現宕機,此時如果換做別的注冊中心如果一定時間內沒有收到心跳會將剔除該服務,這樣就出現了嚴重失誤,因為客戶端還能正常發送心跳,只是網路延遲問題,而保護機制是為了解決此問題而產生的),

在自我保護模式中,Eureka Server會保護服務注冊表中的資訊,不再注銷任何服務實體

它的設計哲學就是寧可保留錯誤的服務注冊資訊,也不盲目注銷任何可能健康的服務實體,一句話講解:好死不如賴活著

綜上,自我保護模式是一種應對網路例外的安全保護措施,它的架構哲學是寧可同時保留所有微服務(健康的微服務和不健康的微服務都會保留)也不盲目注銷任何健康的微服務,使用自我保護模式,可以讓Eureka集群更加的健壯、穩定,

26_怎么禁止自我保護

  • 在eurekaServer端7001處設定關閉自我保護機制

出廠默認,自我保護機制是開啟的

使用eureka.server.enable-self-preservation = false可以禁用自我保護模式

eureka:
  ...
  server:
    #關閉自我保護機制,保證不可用服務被及時踢除
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 2000

關閉效果:

spring-eureka主頁會顯示出一句:

THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.


  • 生產者客戶端eureakeClient端8001

默認:

eureka.instance.lease-renewal-interval-in-seconds=30

eureka.instance.lease-expiration-duration-in-seconds=90

eureka:
  ...
  instance:
    instance-id: payment8001
    prefer-ip-address: true
    #心跳檢測與續約時間
    #開發時沒置小些,保證服務關閉后注冊中心能即使剔除服務
    #Eureka客戶端向服務端發送心跳的時間間隔,單位為秒(默認是30秒)
    lease-renewal-interval-in-seconds: 1
    #Eureka服務端在收到最后一次心跳后等待時間上限,單位為秒(默認是90秒),超時將剔除服務
    lease-expiration-duration-in-seconds: 2

  • 測驗
    • 7001和8001都配置完成
    • 先啟動7001再啟動8001

結果:先關閉8001,馬上被洗掉了

27_Eureka停更說明

https://github.com/Netflix/eureka/wiki

Eureka 2.0 (Discontinued)

The existing open source work on eureka 2.0 is discontinued. The code base and artifacts that were released as part of the existing repository of work on the 2.x branch is considered use at your own risk.

Eureka 1.x is a core part of Netflix’s service discovery system and is still an active project.

我們用ZooKeeper代替Eureka功能,

28_支付服務注冊進zookeeper

  • 注冊中心Zookeeper

zookeeper是一個分布式協調工具,可以實作注冊中心功能

關閉Linux服務器防火墻后,啟動zookeeper服務器

用到的Linux命令列:

  • systemctl stop firewalld關閉防火墻
  • systemctl status firewalld查看防火墻狀態
  • ipconfig查看IP地址
  • ping查驗結果

zookeeper服務器取代Eureka服務器,zk作為服務注冊中心


視頻里是用虛擬機CentOS開啟ZooKeeper,我打算在本機啟動ZooKeeper,具體操作參考ZooKeeper學習筆記,

  • 服務提供者

1.新建名為cloud-provider-payment8004的Maven工程,

2.POM

<?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>LearnCloud</artifactId>
        <groupId>com.lun.springcloud</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8004</artifactId>
    <dependencies>
        <!-- SpringBoot整合Web組件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency><!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
            <groupId>com.lun.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- SpringBoot整合zookeeper客戶端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <!--先排除自帶的zookeeper3.5.3 防止與3.4.9起沖突-->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--添加zookeeper3.4.9版本-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
        </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>

</project>

3.YML

#8004表示注冊到zookeeper服務器的支付服務提供者埠號
server:
  port: 8004

#服務別名----注冊zookeeper到注冊中心名稱
spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: 127.0.0.1:2181 # 192.168.111.144:2181 #

4.主啟動類

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient//該注解用于向使用consul或者zookeeper作為注冊中心時注冊服務
public class PaymentMain8004 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8004.class, args);
    }
}

5.Controller

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@Slf4j
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    @RequestMapping(value = "/payment/zk")
    public String paymentzk()
    {
        return "springcloud with zookeeper: "+serverPort+"\t"+ UUID.randomUUID().toString();
    }
}

6.啟動8004注冊進zookeeper(要先啟動zookeeper的server)

  • 驗證測驗:瀏覽器 - http://localhost:8004/payment/zk

  • 驗證測驗2 :接著用zookeeper客戶端操作

[zk: localhost:2181(CONNECTED) 0] ls /
[services, zookeeper]
[zk: localhost:2181(CONNECTED) 1] ls /services/cloud-provider-payment
[a4567f50-6ad9-47a3-9fbb-7391f41a9f3d]
[zk: localhost:2181(CONNECTED) 2] get /services/cloud-provider-payment/a4567f50-6ad9-47a3-9fbb-7391f41a9f3d
{"name":"cloud-provider-payment","id":"a4567f50-6ad9-47a3-9fbb-7391f41a9f3d","address":"192.168.199.218","port":8004,"ss
lPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1","
name":"cloud-provider-payment","metadata":{}},"registrationTimeUTC":1612811116918,"serviceType":"DYNAMIC","uriSpec":{"pa
rts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":"
:","variable":false},{"value":"port","variable":true}]}}
[zk: localhost:2181(CONNECTED) 3]

json格式化get /services/cloud-provider-payment/a4567f50-6ad9-47a3-9fbb-7391f41a9f3d的結果:

{
    "name": "cloud-provider-payment", 
    "id": "a4567f50-6ad9-47a3-9fbb-7391f41a9f3d", 
    "address": "192.168.199.218", 
    "port": 8004, 
    "sslPort": null, 
    "payload": {
        "@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance", 
        "id": "application-1", 
        "name": "cloud-provider-payment", 
        "metadata": { }
    }, 
    "registrationTimeUTC": 1612811116918, 
    "serviceType": "DYNAMIC", 
    "uriSpec": {
        "parts": [
            {
                "value": "scheme", 
                "variable": true
            }, 
            {
                "value": "://", 
                "variable": false
            }, 
            {
                "value": "address", 
                "variable": true
            }, 
            {
                "value": ":", 
                "variable": false
            }, 
            {
                "value": "port", 
                "variable": true
            }
        ]
    }
}

29_臨時還是持久節點

ZooKeeper的服務節點是臨時節點,沒有Eureka那含情脈脈,

30_訂單服務注冊進zookeeper

1.新建cloud-consumerzk-order80

2.POM

<?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>LearnCloud</artifactId>
        <groupId>com.lun.springcloud</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumerzk-order80</artifactId>

    <dependencies>
        <!-- SpringBoot整合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>
            <!--先排除自帶的zookeeper-->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--添加zookeeper3.4.9版本-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
        </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>

</project>

3.YML

server:
  port: 80

#服務別名----注冊zookeeper到注冊中心名稱
spring:
  application:
    name: cloud-consumer-order
  cloud:
    zookeeper:
      connect-string: 127.0.0.1:2181 # 192.168.111.144:2181 #

4.主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

5.業務類

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}
import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderZKController
{
    public static final String INVOKE_URL = "http://cloud-provider-payment";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "/consumer/payment/zk")
    public String paymentInfo()
    {
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/zk",String.class);
        return result;
    }
}

6.驗證測驗

運行ZooKeeper服務端,cloud-consumerzk-order80,cloud-provider-payment8004,

打開ZooKeeper客戶端:

[zk: localhost:2181(CONNECTED) 0] ls /
[services, zookeeper]
[zk: localhost:2181(CONNECTED) 1] ls /services
[cloud-consumer-order, cloud-provider-payment]
[zk: localhost:2181(CONNECTED) 2]

7.訪問測驗地址 - http://localhost/consumer/payment/zk

31_Consul簡介

Consul官網

Consul下載地址

What is Consul?

Consul is a service mesh solution providing a full featured control plane with service discovery, configuration, and segmentation functionality. Each of these features can be used individually as needed, or they can be used together to build a full service mesh. Consul requires a data plane and supports both a proxy and native integration model. Consul ships with a simple built-in proxy so that everything works out of the box, but also supports 3rd party proxy integrations such as Envoy. link

Consul是一個服務網格解決方案,它提供了一個功能齊全的控制平面,具有服務發現、配置和分段功能,這些特性中的每一個都可以根據需要單獨使用,也可以一起用于構建全服務網格,Consul需要一個資料平面,并支持代理和本機集成模型,Consul船與一個簡單的內置代理,使一切作業的開箱即用,但也支持第三方代理集成,如Envoy,

consul
英 [?k?nsl] 美 [?kɑ?nsl]
n. 領事

Consul是一套開源的分布式服務發現和配置管理系統,由HashiCorp 公司用Go語言開發,

提供了微服務系統中的服務治理、配置中心、控制總線等功能,這些功能中的每一個都可以根據需要單獨使用,也可以一起使用以構建全方位的服務網格,總之Consul提供了一種完整的服務網格解決方案,

它具有很多優點,包括:基于raft協議,比較簡潔;支持健康檢查,同時支持HTTP和DNS協議支持跨資料中心的WAN集群提供圖形界面跨平臺,支持Linux、Mac、Windows,

The key features of Consul are:

  • Service Discovery: Clients of Consul can register a service, such as api or mysql, and other clients can use Consul to discover providers of a given service. Using either DNS or HTTP, applications can easily find the services they depend upon.
  • Health Checking: Consul clients can provide any number of health checks, either associated with a given service (“is the webserver returning 200 OK”), or with the local node (“is memory utilization below 90%”). This information can be used by an operator to monitor cluster health, and it is used by the service discovery components to route traffic away from unhealthy hosts.
  • KV Store: Applications can make use of Consul’s hierarchical key/value store for any number of purposes, including dynamic configuration, feature flagging, coordination, leader election, and more. The simple HTTP API makes it easy to use.
  • Secure Service Communication: Consul can generate and distribute TLS certificates for services to establish mutual TLS connections. Intentions can be used to define which services are allowed to communicate. Service segmentation can be easily managed with intentions that can be changed in real time instead of using complex network topologies and static firewall rules.
  • Multi Datacenter: Consul supports multiple datacenters out of the box. This means users of Consul do not have to worry about building additional layers of abstraction to grow to multiple regions.

link

能干嘛?

  • 服務發現 - 提供HTTP和DNS兩種發現方式,
  • 健康監測 - 支持多種方式,HTTP、TCP、Docker、Shell腳本定制化
  • KV存盤 - Key、Value的存盤方式
  • 多資料中心 - Consul支持多資料中心
  • 可視化Web界面

怎么玩

32_安裝并運行Consul

官網安裝說明

windows版解壓縮后,得consul.exe,打開cmd

  • 查看版本consul -v
D:\Consul>consul -v
Consul v1.9.3
Revision f55da9306
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)
  • 開發模式啟動consul agent -dev

瀏覽器輸入 - http://localhost:8500/ - 打開Consul控制頁,

33_服務提供者注冊進Consul

1.新建Module支付服務provider8006

2.POM

<?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>LearnCloud</artifactId>
        <groupId>com.lun.springcloud</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-providerconsul-payment8006</artifactId>
    <dependencies>
        <!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.lun.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--SpringCloud consul-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <!-- SpringBoot整合Web組件 -->
        <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>
        <!--日常通用jar包配置-->
        <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>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

3.YML

###consul服務埠號
server:
  port: 8006

spring:
  application:
    name: consul-provider-payment
####consul注冊中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}

4.主啟動類

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

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

5.業務類Controller

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

@RestController
@Slf4j
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    @RequestMapping(value = "/payment/consul")
    public String paymentConsul()
    {
        return "springcloud with consul: "+serverPort+"\t   "+ UUID.randomUUID().toString();
    }
}

6.驗證測驗

  • http://localhost:8006/payment/consul
  • http://localhost:8500 - 會顯示provider8006

34_服務消費者注冊進Consul

1.新建Module消費服務order80 - cloud-consumerconsul-order80

2.POM

<?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>LearnCloud</artifactId>
        <groupId>com.lun.springcloud</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumerconsul-order80</artifactId>
    <dependencies>
        <!--SpringCloud consul-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <!-- SpringBoot整合Web組件 -->
        <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>
        <!--日常通用jar包配置-->
        <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>
</project>

3.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}

4.主啟動類

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient //該注解用于向使用consul或者zookeeper作為注冊中心時注冊服務
public class OrderConsulMain80
{
    public static void main(String[] args) {
            SpringApplication.run(OrderConsulMain80.class, args);
    }
}

5.配置Bean

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 */
@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

6.Controller

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderConsulController
{
    public static final String INVOKE_URL = "http://consul-provider-payment";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "/consumer/payment/consul")
    public String paymentInfo()
    {
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
        return result;
    }
}

7.驗證測驗

運行consul,cloud-providerconsul-payment8006,cloud-consumerconsul-order80

http://localhost:8500/ 主頁會顯示出consul,cloud-providerconsul-payment8006,cloud-consumerconsul-order80三服務,

8.訪問測驗地址 - http://localhost/consumer/payment/consul

35_三個注冊中心異同點

組件名語言CAP服務健康檢查對外暴露介面Spring Cloud集成
EurekaJavaAP可配支持HTTP
ConsulGoCP支持HTTP/DNS
ZookeeperJavaCP支持客戶端已集成

CAP:

  • C:Consistency (強一致性)

  • A:Availability (可用性)

  • P:Partition tolerance (磁區容錯性)

最多只能同時較好的滿足兩個

CAP理論的核心是:一個分布式系統不可能同時很好的滿足一致性,可用性和磁區容錯性這三個需求

因此,根據CAP原理將NoSQL資料庫分成了滿足CA原則、滿足CP原則和滿足AP原則三大類:

  • CA - 單點集群,滿足—致性,可用性的系統,通常在可擴展性上不太強大,
  • CP - 滿足一致性,磁區容忍必的系統,通常性能不是特別高,
  • AP - 滿足可用性,磁區容忍性的系統,通常可能對一致性要求低一些,

AP架構(Eureka)

當網路磁區出現后,為了保證可用性,系統B可以回傳舊值,保證系統的可用性,

結論:違背了一致性C的要求,只滿足可用性和磁區容錯,即AP


CP架構(ZooKeeper/Consul)

當網路磁區出現后,為了保證一致性,就必須拒接請求,否則無法保證一致性,

結論:違背了可用性A的要求,只滿足一致性和磁區容錯,即CP,

CP 與 AP 對立同一的矛盾關系,

36_Ribbon入門介紹

Spring Cloud Ribbon是基于Netflix Ribbon實作的一套客戶端負載均衡的工具

簡單的說,Ribbon是Netflix發布的開源專案,主要功能是提供客戶端的軟體負載均衡演算法和服務呼叫,Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等,

簡單的說,就是在組態檔中列出Load Balancer(簡稱LB)后面所有的機器,Ribbon會自動的幫助你基于某種規則(如簡單輪詢,隨機連接等)去連接這些機器,我們很容易使用Ribbon實作自定義的負載均衡演算法,

ribbon

英 [?r?b?n] 美 [?r?b?n]

n. (用于捆綁或裝飾的)帶子;絲帶;帶狀物;狹長的東西;綬帶;勛帶

Github - Ribbon

Ribbon目前也進入維護模式,

Ribbon未來可能被Spring Cloud LoadBalacer替代,

LB負載均衡(Load Balance)是什么

簡單的說就是將用戶的請求平攤的分配到多個服務上,從而達到系統的HA (高可用),

常見的負載均衡有軟體Nginx,LVS,硬體F5等,

Ribbon本地負載均衡客戶端VS Nginx服務端負載均衡區別

Nginx是服務器負載均衡,客戶端所有請求都會交給nginx,然后由nginx實作轉發請求,即負載均衡是由服務端實作的,
Ribbon本地負載均衡,在呼叫微服務介面時候,會在注冊中心上獲取注冊資訊服務串列之后快取到JVM本地,從而在本地實作RPC遠程服務呼叫技術,

集中式LB

即在服務的消費方和提供方之間使用獨立的LB設施(可以是硬體,如F5, 也可以是軟體,如nginx),由該設施負責把訪問請求通過某種策略轉發至服務的提供方;

行程內LB

將LB邏輯集成到消費方,消費方從服務注冊中心獲知有哪些地址可用,然后自己再從這些地址中選擇出一個合適的服務器,

Ribbon就屬于行程內LB,它只是一個類別庫,集成于消費方行程,消費方通過它來獲取到服務提供方的地址,

一句話

負載均衡 + RestTemplate呼叫

37_Ribbon的負載均衡和Rest呼叫

架構說明

總結:Ribbon其實就是一個軟負載均衡的客戶端組件,它可以和其他所需請求的客戶端結合使用,和Eureka結合只是其中的一個實體,

Ribbon在作業時分成兩步:

  • 第一步先選擇EurekaServer ,它優先選擇在同一個區域內負載較少的server,

  • 第二步再根據用戶指定的策略,在從server取到的服務注冊串列中選擇一個地址,

其中Ribbon提供了多種策略:比如輪詢、隨機和根據回應時間加權,

POM

先前工程專案沒有引入spring-cloud-starter-ribbon也可以使用ribbon,

<dependency>
    <groupld>org.springframework.cloud</groupld>
    <artifactld>spring-cloud-starter-netflix-ribbon</artifactid>
</dependency>

這是因為spring-cloud-starter-netflix-eureka-client自帶了spring-cloud-starter-ribbon參考,

二說RestTemplate的使用

RestTemplate Java Doc

getForObject() / getForEntity() - GET請求方法

getForObject():回傳物件為回應體中資料轉化成的物件,基本上可以理解為Json,

getForEntity():回傳物件為ResponseEntity物件,包含了回應中的一些重要資訊,比如回應頭、回應狀態碼、回應體等,

@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPayment2(@PathVariable("id") Long id)
{
    ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);

    if(entity.getStatusCode().is2xxSuccessful()){
        return entity.getBody();//getForObject()
    }else{
        return new CommonResult<>(444,"操作失敗");
    }
}

postForObject() / postForEntity() - POST請求方法

38_Ribbon默認自帶的負載規則

lRule:根據特定演算法中從服務串列中選取一個要訪問的服務

  • RoundRobinRule 輪詢
  • RandomRule 隨機
  • RetryRule 先按照RoundRobinRule的策略獲取服務,如果獲取服務失敗則在指定時間內會進行重
  • WeightedResponseTimeRule 對RoundRobinRule的擴展,回應速度越快的實體選擇權重越大,越容易被選擇
  • BestAvailableRule 會先過濾掉由于多次訪問故障而處于斷路器跳閘狀態的服務,然后選擇一個并發量最小的服務
  • AvailabilityFilteringRule 先過濾掉故障實體,再選擇并發較小的實體
  • ZoneAvoidanceRule 默認規則,復合判斷server所在區域的性能和server的可用性選擇服務器

39_Ribbon負載規則替換

1.修改cloud-consumer-order80

2.注意配置細節

官方檔案明確給出了警告:

這個自定義配置類不能放在@ComponentScan所掃描的當前包下以及子包下,

否則我們自定義的這個配置類就會被所有的Ribbon客戶端所共享,達不到特殊化定制的目的了,

也就是說不要將Ribbon配置類與主啟動類同包

3.新建package - com.lun.myrule

4.在com.lun.myrule下新建MySelfRule規則類

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MySelfRule {

    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}

5.主啟動類添加@RibbonClient

import com.lun.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication
@EnableEurekaClient
//添加到此處
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80
{
    public static void main( String[] args ){
        SpringApplication.run(OrderMain80.class, args);
    }
}

6.測驗

開啟cloud-eureka-server7001,cloud-consumer-order80,cloud-provider-payment8001,cloud-provider-payment8002

瀏覽器-輸入http://localhost/consumer/payment/get/1

回傳結果中的serverPort在8001與8002兩種間反復橫跳,

40_Ribbon默認負載輪詢演算法原理

默認負載輪訓演算法: rest介面第幾次請求數 % 服務器集群總數量 = 實際呼叫服務器位置下標,每次服務重啟動后rest介面計數從1開始

List<Servicelnstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");

如:

  • List [0] instances = 127.0.0.1:8002
  • List [1] instances = 127.0.0.1:8001

8001+ 8002組合成為集群,它們共計2臺機器,集群總數為2,按照輪詢演算法原理:

  • 當總請求數為1時:1%2=1對應下標位置為1,則獲得服務地址為127.0.0.1:8001
  • 當總請求數位2時:2%2=О對應下標位置為0,則獲得服務地址為127.0.0.1:8002
  • 當總請求數位3時:3%2=1對應下標位置為1,則獲得服務地址為127.0.0.1:8001
  • 當總請求數位4時:4%2=О對應下標位置為0,則獲得服務地址為127.0.0.1:8002
  • 如此類推…

41_RoundRobinRule原始碼分析

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    //重點關注這方法
    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}
package com.netflix.loadbalancer;

import com.netflix.client.config.IClientConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * The most well known and basic load balancing strategy, i.e. Round Robin Rule.
 *
 * @author stonse
 * @author Nikos Michalakis <nikos@netflix.com>
 *
 */
public class RoundRobinRule extends AbstractLoadBalancerRule {

    private AtomicInteger nextServerCyclicCounter;
    private static final boolean AVAILABLE_ONLY_SERVERS = true;
    private static final boolean ALL_SERVERS = false;

    private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);

    public RoundRobinRule() {
        nextServerCyclicCounter = new AtomicInteger(0);
    }

    public RoundRobinRule(ILoadBalancer lb) {
        this();
        setLoadBalancer(lb);
    }

    //重點關注這方法,
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }

        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            List<Server> allServers = lb.getAllServers();
            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }

            int nextServerIndex = incrementAndGetModulo(serverCount);
            server = allServers.get(nextServerIndex);

            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }

            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }

            // Next.
            server = null;
        }

        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }

    /**
     * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
     *
     * @param modulo The modulo to bound the value of the counter.
     * @return The next value.
     */
    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;//求余法
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

42_Ribbon之手寫輪詢演算法

自己試著寫一個類似RoundRobinRule的本地負載均衡器,

  • 7001/7002集群啟動

  • 8001/8002微服務改造- controller

@RestController
@Slf4j
public class PaymentController{

    ...
    
	@GetMapping(value = "/payment/lb")
    public String getPaymentLB() {
        return serverPort;//回傳服務介面
    }
    
    ...
}
  • 80訂單微服務改造

1.ApplicationContextConfig去掉注解@LoadBalanced,OrderMain80去掉注解@RibbonClient

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

    @Bean
    //@LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}

2.創建LoadBalancer介面

import org.springframework.cloud.client.ServiceInstance;

import java.util.List;

/**
 */
public interface LoadBalancer
{
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

3.MyLB

實作LoadBalancer介面

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 */
@Component//需要跟主啟動類同包,或者在其子孫包下,
public class MyLB implements LoadBalancer
{

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement()
    {
        int current;
        int next;

        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        }while(!this.atomicInteger.compareAndSet(current,next));
        System.out.println("*****第幾次訪問,次數next: "+next);
        return next;
    }

    //負載均衡演算法:rest介面第幾次請求數 % 服務器集群總數量 = 實際呼叫服務器位置下標  ,每次服務重啟動后rest介面計數從1開始,
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances)
    {
        int index = getAndIncrement() % serviceInstances.size();

        return serviceInstances.get(index);
    }
}

4.OrderController

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import com.lun.springcloud.lb.LoadBalancer;

@Slf4j
@RestController
public class OrderController {

    //public static final String PAYMENT_URL = "http://localhost:8001";
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

	...

    @Resource
    private LoadBalancer loadBalancer;

    @Resource
    private DiscoveryClient discoveryClient;

	...

    @GetMapping(value = "/consumer/payment/lb")
    public String getPaymentLB()
    {
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");

        if(instances == null || instances.size() <= 0){
            return null;
        }

        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        URI uri = serviceInstance.getUri();

        return restTemplate.getForObject(uri+"/payment/lb",String.class);

    }
}

5.測驗 不停地重繪http://localhost/consumer/payment/lb,可以看到8001/8002交替出現,

43_OpenFeign是什么

官方檔案

Github地址

Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Ribbon and Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign. link

Feign是一個宣告式WebService客戶端,使用Feign能讓撰寫Web Service客戶端更加簡單,它的使用方法是定義一個服務介面然后在上面添加注解,Feign也支持可拔插式的編碼器和解碼器,Spring Cloud對Feign進行了封裝,使其支持了Spring MVC標準注解和HttpMessageConverters,Feign可以與Eureka和Ribbon組合使用以支持負載均衡,

Feign能干什么

Feign旨在使撰寫Java Http客戶端變得更容易,

前面在使用Ribbon+RestTemplate時,利用RestTemplate對http請求的封裝處理,形成了一套模版化的呼叫方法,但是在實際開發中,由于對服務依賴的呼叫可能不止一處,往往一個介面會被多處呼叫,所以通常都會針對每個微服務自行封裝一些客戶端類來包裝這些依賴服務的呼叫,所以,Feign在此基礎上做了進一步封裝,由他來幫助我們定義和實作依賴服務介面的定義,在Feign的實作下,我們只需創建一個介面并使用注解的方式來配置它(以前是Dao介面上面標注Mapper注解,現在是一個微服務介面上面標注一個Feign注解即可),即可完成對服務提供方的介面系結,簡化了使用Spring cloud Ribbon時,自動封裝服務呼叫客戶端的開發量,

Feign集成了Ribbon

利用Ribbon維護了Payment的服務串列資訊,并且通過輪詢實作了客戶端的負載均衡,而與Ribbon不同的是,通過feign只需要定義服務系結介面且以宣告式的方法,優雅而簡單的實作了服務呼叫,

Feign和OpenFeign兩者區別

Feign是Spring Cloud組件中的一個輕量級RESTful的HTTP服務客戶端Feign內置了Ribbon,用來做客戶端負載均衡,去呼叫服務注冊中心的服務,Feign的使用方式是:使用Feign的注解定義介面,呼叫這個介面,就可以呼叫服務注冊中心的服務,

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

OpenFeign是Spring Cloud在Feign的基礎上支持了SpringMVC的注解,如@RequesMapping等等,OpenFeign的@Feignclient可以決議SpringMVc的@RequestMapping注解下的介面,并通過動態代理的方式產生實作類,實作類中做負載均衡并呼叫其他服務,

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

feign
英 [fe?n] 美 [fe?n]
v. 假裝,裝作,佯裝(有某種感徑訓生病、疲倦等)

44_OpenFeign服務呼叫

介面+注解:微服務呼叫介面 + @FeignClient

1.新建cloud-consumer-feign-order80

2.POM

<?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>LearnCloud</artifactId>
        <groupId>com.lun.springcloud</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-feign-order80</artifactId>

    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.lun.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--web-->
        <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>

</project>

3.YML

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

4.主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

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

5.業務類

業務邏輯介面+@FeignClient配置呼叫provider服務

新建PaymentFeignService介面并新增注解@FeignClient

import com.lun.springcloud.entities.CommonResult;
import com.lun.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;


@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService
{
    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);

}

控制層Controller

import com.lun.springcloud.entities.CommonResult;
import com.lun.springcloud.entities.Payment;
import com.lun.springcloud.service.PaymentFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderFeignController
{
    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping(value = "/consumer/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
    {
        return paymentFeignService.getPaymentById(id);
    }

}

6.測驗

先啟動2個eureka集群7001/7002

再啟動2個微服務8001/8002

啟動OpenFeign啟動

http://localhost/consumer/payment/get/1

Feign自帶負載均衡配置項

45_OpenFeign超時控制

超時設定,故意設定超時演示出錯情況

1.服務提供方8001/8002故意寫暫停程式

@RestController
@Slf4j
public class PaymentController {
    
    ...
    
    @Value("${server.port}")
    private String serverPort;

    ...
    
    @GetMapping(value = "/payment/feign/timeout")
    public String paymentFeignTimeout()
    {
        // 業務邏輯處理正確,但是需要耗費3秒鐘
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return serverPort;
    }
    
    ...
}

2.服務消費方80添加超時方法PaymentFeignService

@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService{

    ...

    @GetMapping(value = "/payment/feign/timeout")
    public String paymentFeignTimeout();
}

3.服務消費方80添加超時方法OrderFeignController

@RestController
@Slf4j
public class OrderFeignController
{
    @Resource
    private PaymentFeignService paymentFeignService;

    ...

    @GetMapping(value = "/consumer/payment/feign/timeout")
    public String paymentFeignTimeout()
    {
        // OpenFeign客戶端一般默認等待1秒鐘
        return paymentFeignService.paymentFeignTimeout();
    }
}

4.測驗:

多次重繪http://localhost/consumer/payment/feign/timeout

將會跳出錯誤Spring Boot默認錯誤頁面,主要例外:feign.RetryableException:Read timed out executing GET http://CLOUD-PAYMENT-SERVCE/payment/feign/timeout

OpenFeign默認等待1秒鐘,超過后報錯

YML檔案里需要開啟OpenFeign客戶端超時控制

#設定feign客戶端超時時間(OpenFeign默認支持ribbon)(單位:毫秒)
ribbon:
  #指的是建立連接所用的時間,適用于網路狀況正常的情況下,兩端連接所用的時間
  ReadTimeout: 5000
  #指的是建立連接后從服務器讀取到可用資源所用的時間
  ConnectTimeout: 5000

46_OpenFeign日志增強

日志列印功能

Feign提供了日志列印功能,我們可以通過配置來調整日恙級別,從而了解Feign 中 Http請求的細節,

說白了就是對Feign介面的呼叫情況進行監控和輸出

日志級別

  • NONE:默認的,不顯示任何日志;
  • BASIC:僅記錄請求方法、URL、回應狀態碼及執行時間;
  • HEADERS:除了BASIC中定義的資訊之外,還有請求和回應的頭資訊;
  • FULL:除了HEADERS中定義的資訊之外,還有請求和回應的正文及元資料,

配置日志bean

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig
{
    @Bean
    Logger.Level feignLoggerLevel()
    {
        return Logger.Level.FULL;
    }
}

YML檔案里需要開啟日志的Feign客戶端

logging:
  level:
    # feign日志以什么級別監控哪個介面
    com.lun.springcloud.service.PaymentFeignService: debug

后臺日志查看

得到更多日志資訊,

47_Hystrix是什么

概述

分布式系統面臨的問題

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

服務雪崩

多個微服務之間呼叫的時候,假設微服務A呼叫微服務B和微服務C,微服務B和微服務C又呼叫其它的微服務,這就是所謂的“扇出”,如果扇出的鏈路上某個微服務的呼叫回應時間過長或者不可用,對微服務A的呼叫就會占用越來越多的系統資源,進而引起系統崩潰,所謂的“雪崩效應”.
對于高流量的應用來說,單一的后避依賴可能會導致所有服務器上的所有資源都在幾秒鐘內飽和,比失敗更糟糕的是,這些應用程式還可能導致服務之間的延遲增加,備份佇列,執行緒和其他系統資源緊張,導致整個系統發生更多的級聯故障,這些都表示需要對故障和延遲進行隔離和管理,以便單個依賴關系的失敗,不能取消整個應用程式或系統,

所以,通常當你發現一個模塊下的某個實體失敗后,這時候這個模塊依然還會接收流量,然后這個有問題的模塊還呼叫了其他的模塊,這樣就會發生級聯故障,或者叫雪崩,

Hystrix是什么

Hystrix是一個用于處理分布式系統的延遲容錯的開源庫,在分布式系統里,許多依賴不可避免的會呼叫失敗,比如超時、例外等,Hystrix能夠保證在一個依賴出問題的情況下,不會導致整體服務失敗,避免級聯故障,以提高分布式系統的彈性

"斷路器”本身是一種開關裝置,當某個服務單元發生故障之后,通過斷路器的故障監控(類似熔斷保險絲),向呼叫方回傳一個符合預期的、可處理的備選回應(FallBack),而不是長時間的等待或者拋出呼叫方無法處理的例外,這樣就保證了服務呼叫方的執行緒不會被長時間、不必要地占用,從而避免了故障在分布式系統中的蔓延,乃至雪崩,

hystrix
n. 豪豬屬;猬草屬;豪豬;豪豬亞屬

48_Hystrix停更進維

能干嘛

  • 服務降級
  • 服務熔斷
  • 接近實對的監控

官網資料

link

Hystrix官宣,停更進維

link

  • 被動修bugs
  • 不再接受合并請求
  • 不再發布新版本

49_Hystrix的服務降級熔斷限流概念初講

服務降級

服務器忙,請稍后再試,不讓客戶端等待并立刻回傳一個友好提示,fallback

哪些情況會出發降級

  • 程式運行導常
  • 超時
  • 服務熔斷觸發服務降級
  • 執行緒池/信號量打滿也會導致服務降級

服務熔斷

類比保險絲達到最大服務訪問后,直接拒絕訪問,拉閘限電,然后呼叫服務降級的方法并回傳友好提示,

服務的降級 -> 進而熔斷 -> 恢復呼叫鏈路

服務限流

秒殺高并發等操作,嚴禁一窩蜂的過來擁擠,大家排隊,一秒鐘N個,有序進行,

50_Hystrix支付微服務構建

將cloud-eureka-server7001改配置成單機版

1.新建cloud-provider-hygtrix-payment8001

2.POM

<?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>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-hystrix-payment8001</artifactId>

    <dependencies>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--web-->
        <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><!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>

</project>

3.YML

server:
  port: 8001

spring:
  application:
    name: cloud-provider-hystrix-payment

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
      defaultZone: http://eureka7001.com:7001/eureka

4.主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

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

5.業務類

service

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 */
@Service
public class PaymentService {
    /**
     */
    public String paymentInfo_OK(Integer id)
    {
        return "執行緒池:  "+Thread.currentThread().getName()+"  paymentInfo_OK,id:  "+id+"\t"+"O(∩_∩)O哈哈~";
    }

    public String paymentInfo_TimeOut(Integer id)
    {
        try { TimeUnit.MILLISECONDS.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
        return "執行緒池:  "+Thread.currentThread().getName()+" id:  "+id+"\t"+"O(∩_∩)O哈哈~"+"  耗時(秒): 3";
    }
}

controller

import com.lun.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 */
@RestController
@Slf4j
public class PaymentController
{
    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id)
    {
        String result = paymentService.paymentInfo_OK(id);
        log.info("*****result: "+result);
        return result;
    }

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
    {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("*****result: "+result);
        return result;
    }
}

6.正常測驗

啟動eureka7001

啟動cloud-provider-hystrix-payment8001

訪問

success的方法 - http://localhost:8001/payment/hystrix/ok/1
每次呼叫耗費5秒鐘 - http://localhost:8001/payment/hystrix/timeout/1

上述module均OK

以上述為根基平臺,從正確 -> 錯誤 -> 降級熔斷 -> 恢復,

51_JMeter高并發壓測后卡頓

上述在非高并發情形下,還能勉強滿足

Jmeter壓測測驗

JMeter官網

The Apache JMeter? application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance. It was originally designed for testing Web Applications but has since expanded to other test functions.

開啟Jmeter,來20000個并發壓死8001,20000個請求都去訪問paymentInfo_TimeOut服務

1.測驗計劃中右鍵添加-》執行緒-》執行緒組(執行緒組202102,執行緒數:200,執行緒數:100,其他引數默認)

2.剛剛新建執行緒組202102,右鍵它-》添加-》取樣器-》Http請求-》基本 輸入http://localhost:8001/payment/hystrix/ok/1

3.點擊綠色三角形圖示啟動,

看演示結果:拖慢,原因:tomcat的默認的作業執行緒數被打滿了,沒有多余的執行緒來分解壓力和處理,

Jmeter壓測結論

上面還是服務提供者8001自己測驗,假如此時外部的消費者80也來訪問,那消費者只能干等,最終導致消費端80不滿意,服務端8001直接被拖慢,

52_訂單微服務呼叫支付服務出現卡頓

看熱鬧不嫌棄事大,80新建加入

1.新建 - cloud-consumer-feign-hystrix-order80

2.POM

<?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>LearnCloud</artifactId>
        <groupId>com.lun.springcloud</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-feign-hystrix-order80</artifactId>

    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.lun.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--web-->
        <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>

</project>

3.YML

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

4.主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 */
@SpringBootApplication
@EnableFeignClients
//@EnableHystrix
public class OrderHystrixMain80
{
    public static void main(String[] args)
    {
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

5.業務類

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 */
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT" /*,fallback = PaymentFallbackService.class*/)
public interface PaymentHystrixService
{
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}

import com.lun.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderHystirxController {
    @Resource
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id)
    {
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
}

6.正常測驗

http://localhost/consumer/payment/hystrix/ok/1

7.高并發測驗

2W個執行緒壓8001

消費端80微服務再去訪問正常的Ok微服務8001地址

http://localhost/consumer/payment/hystrix/ok/32

消費者80被拖慢

原因:8001同一層次的其它介面服務被困死,因為tomcat執行緒池里面的作業執行緒已經被擠占完畢,

正因為有上述故障或不佳表現才有我們的降級/容錯/限流等技術誕生,

53_降級容錯解決的維度要求

超時導致服務器變慢(轉圈) - 超時不再等待

出錯(宕機或程式運行出錯) - 出錯要有兜底

解決:

  • 對方服務(8001)超時了,呼叫者(80)不能一直卡死等待,必須有服務降級,
  • 對方服務(8001)down機了,呼叫者(80)不能一直卡死等待,必須有服務降級,
  • 對方服務(8001)OK,呼叫者(80)自己出故障或有自我要求(自己的等待時間小于服務提供者),自己處理降級,

54_Hystrix之服務降級支付側fallback

降級配置 - @HystrixCommand

8001先從自身找問題

設定自身呼叫超時時間的峰值,峰值內可以正常運行,超過了需要有兜底的方法處埋,作服務降級fallback

8001fallback

業務類啟用 - @HystrixCommand報例外后如何處理

—旦呼叫服務方法失敗并拋出了錯誤資訊后,會自動呼叫@HystrixCommand標注好的fallbackMethod呼叫類中的指定方法

@Service
public class PaymentService{

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler"/*指定善后方法名*/,commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
    })
    public String paymentInfo_TimeOut(Integer id)
    {
        //int age = 10/0;
        try { TimeUnit.MILLISECONDS.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); }
        return "執行緒池:  "+Thread.currentThread().getName()+" id:  "+id+"\t"+"O(∩_∩)O哈哈~"+"  耗時(秒): ";
    }

    //用來善后的方法
    public String paymentInfo_TimeOutHandler(Integer id)
    {
        return "執行緒池:  "+Thread.currentThread().getName()+"  8001系統繁忙或者運行報錯,請稍后再試,id:  "+id+"\t"+"o(╥﹏╥)o";
    }
    
}

上面故意制造兩種例外:

  1. int age = 10/0,計算例外
  2. 我們能接受3秒鐘,它運行5秒鐘,超時例外,

當前服務不可用了,做服務降級,兜底的方案都是paymentInfo_TimeOutHandler

主啟動類激活

添加新注解@EnableCircuitBreaker

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker//添加到此處
public class PaymentHystrixMain8001{
    public static void main(String[] args) {
            SpringApplication.run(PaymentHystrixMain8001.class, args);
    }
}

55_Hystrix之服務降級訂單側fallback

80訂單微服務,也可以更好的保護自己,自己也依樣畫葫蘆進行客戶端降級保護

題外話,切記 - 我們自己配置過的熱部署方式對java代碼的改動明顯

但對@HystrixCommand內屬性的修改建議重啟微服務

YML

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

#開啟
feign:
  hystrix:
    enabled: true

主啟動

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients
@EnableHystrix//添加到此處
public class OrderHystrixMain80{
    
    public static void main(String[] args){
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

業務類

import com.lun.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderHystirxController {
    @Resource
    private PaymentHystrixService paymentHystrixService;


    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        //int age = 10/0;
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
    
    //善后方法
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
        return "我是消費者80,對方支付系統繁忙請10秒鐘后再試或者自己運行出錯請檢查自己,o(╥﹏╥)o";
    }

}

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/265893.html

標籤:其他

上一篇:牛年第一個招聘季!帶你了解「互聯網不可或缺」的三大技術崗

下一篇:zabbix5.0系統監控難點匯總

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more