一、springcloud入門及學習筆記

 

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的開發便利性巧妙地簡化了分佈式系統基礎設施的開發,如服務發現註冊、配置中心、消息總線、負載均衡、斷路器、數據監控等,都可以用Spring Boot的開發風格做到一鍵啓動和部署。Spring Cloud並沒有重複製造輪子,它只是將各家公司開發的比較成熟、經得起實際考驗的服務框架組合起來,通過Spring Boot風格進行再封裝屏蔽掉了複雜的配置和實現原理,最終給開發者留出了一套簡單易懂、易部署和易維護的分佈式系統開發工具包(百度)。

Spring Cloud 五大組件

  • 服務註冊與發現——Netflix Eureka
  • 負載均衡:

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

 什麼是微服務?

微服務(Microservice Architecture) 是近幾年流行的一種架構思想,關於它的概念很難一言以蔽之。

究竟什麼是微服務呢?我們在此引用ThoughtWorks 公司的首席科學家 Martin Fowler 於2014年提出的一段話:

原文:https://martinfowler.com/articles/microservices.html

漢化:https://www.cnblogs.com/liuning8023/p/4493156.html

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

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

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

微服務與微服務架構

微服務

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

微服務架構

一種新的架構形式,Martin Fowler 於2014年提出。

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

微服務優點

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

微服務的缺點

  • 開發人員要處理分佈式系統的複雜性;
  • 多服務運維難度,隨着服務的增加,運維的壓力也在增大;
  • 系統部署依賴問題;
  • 服務間通信成本問題;
  • 數據一致性問題;
  • 系統集成測試問題;
  • 性能和監控問題;

微服務技術棧有那些?

| **微服務技術條目**          | 落地技術                                             |
| -------------------- | ------------------------------------------------ |
| 服務開發                 | SpringBoot、Spring、SpringMVC等                     |
| 服務配置與管理              | Netfix公司的Archaius、阿里的Diamond等                    |
| 服務註冊與發現              | Eureka、Consul、Zookeeper等                         |
| 服務調用                 | Rest、PRC、gRPC                                    |
| 服務熔斷器                | Hystrix、Envoy等                                   |
| 負載均衡                 | Ribbon、Nginx等                                    |
| 服務接口調用(客戶端調用服務的簡化工具) | Fegin等                                           |
| 消息隊列                 | Kafka、RabbitMQ、ActiveMQ等                         |
| 服務配置中心管理             | SpringCloudConfig、Chef等                          |
| 服務路由(API網關)          | Zuul等                                            |
| 服務監控                 | Zabbix、Nagios、Metrics、Specatator等                |
| 全鏈路追蹤                | Zipkin、Brave、Dapper等                             |
| 數據流操作開發包             | SpringCloud Stream(封裝與Redis,Rabbit,Kafka等發送接收消息) |
| 時間消息總棧               | SpringCloud Bus                                  |
| 服務部署                 | Docker、OpenStack、Kubernetes等                     |

爲什麼選擇SpringCloud作爲微服務架構

  1. 選型依據

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

    • 阿里:dubbo+HFS
    • 京東:JFS
    • 新浪:Motan
    • 噹噹網:DubboX

  3. 各微服務框架對比

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

SpringCloud和SpringBoot的關係

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

Dubbo 和 SpringCloud技術選型

1. 分佈式+服務治理Dubbo

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

2. Dubbo 和 SpringCloud對比

可以看一下社區活躍度:

https://github.com/dubbo

https://github.com/spring-cloud

對比結果:

|        | Dubbo         | SpringCloud                  |
| ------ | ------------- | ---------------------------- |
| 服務註冊中心 | Zookeeper     | Spring Cloud Netfilx Eureka  |
| 服務調用方式 | RPC           | REST API                     |
| 服務監控   | Dubbo-monitor | Spring Boot Admin            |
| 斷路器    | 不完善           | Spring Cloud Netfilx Hystrix |
| 服務網關   | 無             | Spring Cloud Netfilx Zuul    |
| 分佈式配置  | 無             | Spring Cloud Config          |
| 服務跟蹤   | 無             | Spring Cloud Sleuth          |
| 消息總棧   | 無             | Spring Cloud Bus             |
| 數據流    | 無             | Spring Cloud Stream          |
| 批量任務   | 無             | Spring Cloud Task            |

最大區別:Spring Cloud 拋棄了Dubbo的RPC通信,採用的是基於HTTP的REST方式

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

品牌機和組裝機的區別

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

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

SpringCloud能幹嘛?

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

自學參考書:

SpringCloud版本選擇

大版本說明

| SpringBoot | SpringCloud       | 關係                                 |
| ---------- | ----------------- | ---------------------------------- |
| 1.2.x      | Angel版本(天使)       | 兼容SpringBoot1.2x                   |
| 1.3.x      | Brixton版本(布里克斯頓)  | 兼容SpringBoot1.3x,也兼容SpringBoot1.4x |
| 1.4.x      | Camden版本(卡姆登)     | 兼容SpringBoot1.4x,也兼容SpringBoot1.5x |
| 1.5.x      | Dalston版本(多爾斯頓)   | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
| 1.5.x      | Edgware版本(埃奇韋爾)   | 兼容SpringBoot1.5x,不兼容SpringBoot2.0x |
| 2.0.x      | Finchley版本(芬奇利)   | 兼容SpringBoot2.0x,不兼容SpringBoot1.5x |
| 2.1.x      | Greenwich版本(格林威治) |                                    |

實際開發版本關係

| spring-boot-starter-parent |          | spring-cloud-dependencles |          |
|:--------------------------:| --------:|:-------------------------:|:--------:|
|          **版本號**        | **發佈日期** |          **版本號**          | **發佈日期** |
|       1.5.2.RELEASE        |  2017-03 |        Dalston.RC1        |  2017-x  |
|       1.5.9.RELEASE        |  2017-11 |      Edgware.RELEASE      | 2017-11  |
|       1.5.16.RELEASE       |  2018-04 |        Edgware.SR5        | 2018-10  |
|       1.5.20.RELEASE       |  2018-09 |        Edgware.SR5        | 2018-10  |
|       2.0.2.RELEASE        |  2018-05 |  Fomchiey.BULD-SNAPSHOT   |  2018-x  |
|       2.0.6.RELEASE        |  2018-10 |       Fomchiey-SR2        | 2018-10  |
|       2.1.4.RELEASE        |  2019-04 |       Greenwich.SR1       | 2019-03  |

使用後兩個

SpringCloud Rest學習環境搭建:

1、創建空的maven項目,刪除src目錄

父工程依賴(子項目引入依賴可以不寫版本號)

<?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.ykk</groupId>
    <artifactId>springcloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>springcloud-api</module>
        <module>springcloud-provider-dept-8001</module>
    </modules>

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

    <!--    依賴版本控制-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <springboot.version>2.1.4.RELEASE</springboot.version>
        <mysql-connector-java.version>5.1.47</mysql-connector-java.version>
        <druid.version>1.1.10</druid.version>
        <mybatis.version>1.3.2</mybatis.version>
        <logback-core.version>1.2.3</logback-core.version>
    </properties>

    <!--    依賴管理-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>0.2.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--springCloud的依賴-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--SpringBoot-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${springboot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--數據庫-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql-connector-java.version}</version>
            </dependency>
            <!--druid-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <!--mybatis-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.version}</version>
            </dependency>
            <!--日誌測試~-->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>${logback-core.version}</version>
            </dependency>
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <!--log4j-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

創建springcloud-api子項目

導入依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>com.ykk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-api</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

</project>

創建實體類Dept

@Data
@NoArgsConstructor
// 開啓鏈式寫法
@Accessors(chain = true)
public class Dept implements Serializable {
    private Long deptno;
    private String dname;
    // 這個數據存在那個數據庫的字段,微服務,一個服務對應一個數據庫,同一個信息可能存在不同的數據庫
    private String db_source;

    public Dept(String dname) {
        this.dname = dname;
    }
}

服務提供者

創建子項目springcloud-provider-dept-8001

導入依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>com.ykk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-provider-dept-8001</artifactId>

    <dependencies>
<!--        我們需要拿到dept實體類,所有需要導入springcloud-api模塊-->
        <dependency>
            <groupId>com.ykk</groupId>
            <artifactId>springcloud-api</artifactId>
        </dependency>
<!--        因爲父項目有依賴管理,所以子項目的依賴可以不寫版本號-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <!--數據庫-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <!--日誌測試~-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <!--SpringBoot 啓動器-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>
        <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--jetty-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
        <!--熱部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ykk</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>


</project>

這裏特別留意,需要使用到springcloud-api子項目的dept類,所有需要導入

application.yml配置文件

server:
  port: 8001

mybatis:
  type-aliases-package: com.ykk.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root

mybatis-config.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--    開啓二級緩存-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
</configuration>

開始寫實現代碼,按照Dao-Service-controller開發順序

DeptDao接口

@Mapper
// @Repository註解修飾哪個類,則表明這個類具有對對象進行CRUD(增刪改查)的功能
@Repository
public interface DeptDao {
    // 添加dept
    boolean addDept(Dept dept);
    // 根據id查詢dept
    Dept queryById(Long id);
    // 查詢所有
    List<Dept> queryAll();
}

DeptDao.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.ykk.springcloud.dao.DeptDao">
    <insert id="addDept" parameterType="Dept">
        INSERT INTO dept (dname,db_source)
        VALUES (#{dname},DATABASE());
    </insert>

    <select id="queryById" resultType="Dept" parameterType="Long">
        SELECT * FROM dept
        WHERE deptno=#{deptno};
    </select>

    <select id="queryAll" resultType="Dept">
        SELECT * FROM dept;
    </select>
</mapper>

DeptService

public interface DeptService {
    boolean addDept(Dept dept);

    Dept queryById(Long id);

    List<Dept> queryAll();
}

DeptServiceImpl (特別注意要加註解,容易漏寫)

@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
    private DeptDao deptDao;

    @Override
    public boolean addDept(Dept dept) {
        return deptDao.addDept(dept);
    }

    @Override
    public Dept queryById(Long id) {
        return deptDao.queryById(id);
    }

    @Override
    public List<Dept> queryAll() {
        return deptDao.queryAll();
    }
}

DeptController

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

    @PostMapping("dept/add")
    public boolean addDept(Dept dept) {
        return deptService.addDept(dept);
    }

    // http://127.0.0.1:8001/dept/get/1
    @GetMapping("dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        System.out.println(id);
        return deptService.queryById(id);
    }

    @GetMapping("dept/list")
    public List<Dept> getAll() {
        return deptService.queryAll();
    }
}

DeptProvider_8001動類

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

消費者

什麼是RestTemplate
RestTemplate 是從 Spring3.0 開始支持的一個 HTTP 請求工具,它提供了常見的REST請求方案的模版,例如 GET 請求、POST 請求、PUT 請求、DELETE 請求以及一些通用的請求執行方法 exchange 以及 execute。RestTemplate 繼承自 InterceptingHttpAccessor 並且實現了 RestOperations 接口,其中 RestOperations 接口定義了基本的 RESTful 操作,這些操作在 RestTemplate 中都得到了實現。接下來我們就來看看這些操作方法的使用。

創建子項目springcloud-consumer-dept-80

導入依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>com.ykk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-consumer-dept-80</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.ykk</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>


    </dependencies>


</project>

application.yml(80端口好處是URL可以不寫具體端口號)

server:
  port: 80

ConfigBean

@Configuration
public class ConfigBean {
    @Bean
    public RestTemplate getRestTemplate(){
        // 將RestTemplate註冊到bean中,使用時通過Autowried自動裝配
        return new RestTemplate();
    }
}

DeptConsumerController

@RestController
public class DeptConsumerController {

    // 理解:消費者不應該有service層
    // RestTemplate提供方法調用,並註冊到spring中
    @Autowired
    private RestTemplate restTemplate;
    private static final String REST_URL_PREFIX = "http://localhost:8001";

    // 此方法調用後數據庫確實生成了一條數據,但沒有內容,連接問題??
    @RequestMapping("consumer/dept/add")
    public boolean add(Dept dept) {
        System.out.println(dept.toString());
        return restTemplate.postForObject(REST_URL_PREFIX + "dept/add", Dept.class, Boolean.class);
    }

    @RequestMapping("consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return restTemplate.getForObject(REST_URL_PREFIX + "dept/get/" + id, Dept.class);
    }

    @RequestMapping("consumer/dept/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX + "dept/list", List.class);
    }
}

DeptConsumer_80啓動類

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

Eureka服務註冊中心

5.1 什麼是Eureka

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

5.2 原理理解

  • Eureka基本的架構

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

和Dubbo架構對比.

  • Eureka 包含兩個組件:Eureka Server  Eureka Client.
  • Eureka Server 提供服務註冊,各個節點啓動後,回在EurekaServer中進行註冊,這樣Eureka Server中的服務註冊表中將會儲存所有課用服務節點的信息,服務節點的信息可以在界面中直觀的看到.
  • Eureka Client 是一個Java客戶端,用於簡化EurekaServer的交互,客戶端同時也具備一個內置的,使用輪詢負載算法的負載均衡器。在應用啓動後,將會向EurekaServer發送心跳 (默認週期爲30秒) 。如果Eureka Server在多個心跳週期內沒有接收到某個節點的心跳,EurekaServer將會從服務註冊表中把這個服務節點移除掉 (默認週期爲90s).
  • 三大角色

    • Eureka Server:提供服務的註冊與發現
    • Service Provider:服務生產方,將自身服務註冊到Eureka中,從而使服務消費方能狗找到
    • Service Consumer:服務消費方,從Eureka中獲取註冊服務列表,從而找到消費服務

創建子項目springcloud-eureka-7001

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>com.ykk</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-eureka-7001</artifactId>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <!--Eureka服務依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
</project>

applicationyml配置文件

server:
  port: 7001
eureka:
  instance:
    hostname: localhost # Eureka服務端示例名稱
  client:
    register-with-eureka: false # 表示是否想Eureka註冊中心註冊自己
    fetch-registry: false # 表示自己爲註冊中心
    service-url: # 監控畫面
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

EurekaServer_7001啓動類

@SpringBootApplication
// 開啓eureka服務
@EnableEurekaServer 
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class, args);
    }
}

訪問:http://127.0.0.1:7001/ 即可看到監控頁面

將子項目springcloud-provider-dept-8001註冊到eureka服務中

切換到子項目springcloud-provider-dept-8001

pom文件添加eureka依賴

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

application.yml配置文件添加eureka配置

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/ #註冊到eureka服務地址

啓動類加註解

@SpringBootApplication
// 開啓eureka註冊服務
@EnableEurekaClient
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}

先啓動springcloud-eureka-7001再啓動springcloud-provider-dept-8001,訪問:http://127.0.0.1:7001/

修改狀態描述

yml配置文件添加

eureka:
  instance:
    instance-id: 888 #修改Eureka上的默認描述信息

狀態描述完善

pom文件添加依賴

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

yml配置文件添加

# info配置
info:
  # 項目的名稱
  app.name: springcloud-provider-dept-8001
  # 公司的名稱
  company.name: ykk

點擊監控頁面狀態描述

如果此時停掉springcloud-provider-dept-8001 等30s後 監控會開啓保護機制:

EureKa自我保護機制:好死不如賴活着

一句話總結就是:某時刻某一個微服務不可用,eureka不會立即清理,依舊會對該微服務的信息進行保存!

  • 默認情況下,當eureka server在一定時間內沒有收到實例的心跳,便會把該實例從註冊表中刪除(默認是90秒),但是,如果短時間內丟失大量的實例心跳,便會觸發eureka server的自我保護機制,比如在開發測試時,需要頻繁地重啓微服務實例,但是我們很少會把eureka server一起重啓(因爲在開發過程中不會修改eureka註冊中心),當一分鐘內收到的心跳數大量減少時,會觸發該保護機制。可以在eureka管理界面看到Renews threshold和Renews(last min),當後者(最後一分鐘收到的心跳數)小於前者(心跳閾值)的時候,觸發保護機制,會出現紅色的警告:EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.從警告中可以看到,eureka認爲雖然收不到實例的心跳,但它認爲實例還是健康的,eureka會保護這些實例,不會把它們從註冊表中刪掉。
  • 該保護機制的目的是避免網絡連接故障,在發生網絡故障時,微服務和註冊中心之間無法正常通信,但服務本身是健康的,不應該註銷該服務,如果eureka因網絡故障而把微服務誤刪了,那即使網絡恢復了,該微服務也不會重新註冊到eureka server了,因爲只有在微服務啓動的時候纔會發起註冊請求,後面只會發送心跳和服務列表請求,這樣的話,該實例雖然是運行着,但永遠不會被其它服務所感知。所以,eureka server在短時間內丟失過多的客戶端心跳時,會進入自我保護模式,該模式下,eureka會保護註冊表中的信息,不在註銷任何微服務,當網絡故障恢復後,eureka會自動退出保護模式。自我保護模式可以讓集羣更加健壯。
  • 但是我們在開發測試階段,需要頻繁地重啓發布,如果觸發了保護機制,則舊的服務實例沒有被刪除,這時請求有可能跑到舊的實例中,而該實例已經關閉了,這就導致請求錯誤,影響開發測試。所以,在開發測試階段,我們可以把自我保護模式關閉,只需在eureka server配置文件中加上如下配置即可:eureka.server.enable-self-preservation=false【不推薦關閉自我保護機制】

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

獲取微服務註冊的信息(團隊開發會用到?)

DeptController增加接口測試

 // 導包留意是springcloud的
    @Autowired
    private DiscoveryClient client;

    @GetMapping("/dept/discovery")
    public Object discovery() {
        // 獲取微服務列表的清單
        List<String> services = client.getServices();
        System.out.println("discovery=>services:" + services);
        // 得到一個具體的微服務信息,通過具體的微服務id,applicaioinName;
        List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
        for (ServiceInstance instance : instances) {
            System.out.println(
                    instance.getHost() + "\t" + // 主機名稱
                            instance.getPort() + "\t" + // 端口號
                            instance.getUri() + "\t" + // uri
                            instance.getServiceId() // 服務id
            );
        }
        return this.client;
    }

啓動類開啓註解

@SpringBootApplication
// 開啓eureka註冊服務
@EnableEurekaClient
// @EnableEurekaClient 開啓服務發現客戶端的註解,可以用來獲取一些配置的信息,得到具體的微服務
// 沒開啓也能拿到
@EnableDiscoveryClient
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}

Eureka:集羣環境配置

在本機C:\Windows\System32\drivers\etc目錄下的hosts文件下自定義3個地址(模擬網絡地址);

127.0.0.1                                eureka7001.com
127.0.0.1                                eureka7002.com
127.0.0.1                                eureka7003.com

創建子項目springcloud-eureka-7002,springcloud-eureka-7003,共3個eureka服務

3個項目pom文件、yml配置文件、啓動類都一樣,只是個別配置參數不同

application。yml配置文件

server:
  port: 7001
eureka:
  instance:
    # 3個項目對應3個地址
    hostname: eureka7001.com # Eureka服務端示例名稱,這個地址是本地自定義的
  client:
    register-with-eureka: false # 表示是否想Eureka註冊中心註冊自己
    fetch-registry: false # 表示自己爲註冊中心
    service-url: # 監控畫面
      # 通過,間隔其他地址,3個項目對應綁定其他2個地址
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

3個eureka服務啓動後,訪問其中一個即可看到其他兩個eureka服務

http://127.0.0.1:7001
http://127.0.0.1:7002
http://127.0.0.1:7003

子項目springcloud-provider-dept-8001註冊到3個eureka集羣服務上

eureka:
  client:
    service-url:
      #註冊中心地址7001-7003,每個地址用,隔開
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

運行即可在上圖任何一個頁面上看到這個模塊

對比和Zookeeper區別

1. 回顧CAP原則

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

NoSQL (Redis\MongoDB) ===> CAP

2. ACID是什麼?

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

3. CAP是什麼?

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

CAP的三進二:CA、AP、CP

4. CAP理論的核心

  • 一個分佈式系統不可能同時很好的滿足一致性,可用性和分區容錯性這三個需求
  • 根據CAP原理,將NoSQL數據庫分成了滿足CA原則,滿足CP原則和滿足AP原則三大類

    • CA:單點集羣,滿足一致性,可用性的系統,通常可擴展性較差
    • CP:滿足一致性,分區容錯的系統,通常性能不是特別高
    • AP:滿足可用性,分區容錯的系統,通常可能對一致性要求低一些

5. 作爲分佈式服務註冊中心,Eureka比Zookeeper好在哪裏?

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

  • Zookeeper 保證的是 CP —> 滿足一致性,分區容錯的系統,通常性能不是特別高
  • Eureka 保證的是 AP —> 滿足可用性,分區容錯的系統,通常可能對一致性要求低一些

Zookeeper保證的是CP

當向註冊中心查詢服務列表時,我們可以容忍註冊中心返回的是幾分鐘以前的註冊信息,但不能接收服務直接down掉不可用。也就是說,服務註冊功能對可用性的要求要高於一致性。但zookeeper會出現這樣一種情況,當master節點因爲網絡故障與其他節點失去聯繫時,剩餘節點會重新進行leader選舉。問題在於,選舉leader的時間太長,30-120s,且選舉期間整個zookeeper集羣是不可用的,這就導致在選舉期間註冊服務癱瘓。在雲部署的環境下,因爲網絡問題使得zookeeper集羣失去master節點是較大概率發生的事件,雖然服務最終能夠恢復,但是,漫長的選舉時間導致註冊長期不可用,是不可容忍的。

Eureka保證的是AP

Eureka看明白了這一點,因此在設計時就優先保證可用性。Eureka各個節點都是平等的,幾個節點掛掉不會影響正常節點的工作,剩餘的節點依然可以提供註冊和查詢服務。而Eureka的客戶端在向某個Eureka註冊時,如果發現連接失敗,則會自動切換至其他節點,只要有一臺Eureka還在,就能保住註冊服務的可用性,只不過查到的信息可能不是最新的,除此之外,Eureka還有之中自我保護機制,如果在15分鐘內超過85%的節點都沒有正常的心跳,那麼Eureka就認爲客戶端與註冊中心出現了網絡故障,此時會出現以下幾種情況:

  • Eureka不在從註冊列表中移除因爲長時間沒收到心跳而應該過期的服務
  • Eureka仍然能夠接受新服務的註冊和查詢請求,但是不會被同步到其他節點上 (即保證當前節點依然可用)
  • 當網絡穩定時,當前實例新的註冊信息會被同步到其他節點中

因此,Eureka可以很好的應對因網絡故障導致部分節點失去聯繫的情況,而不會像zookeeper那樣使整個註冊服務癱瘓

第二頁:https://my.oschina.net/u/4139437/blog/5391683

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章