1. 程式人生 > 實用技巧 >Mycat+haproxy中保障haproxy的高可用

Mycat+haproxy中保障haproxy的高可用

目錄

SpringCloud

回顧之前的知識

  • JavaSE
  • 資料庫
  • 前端
  • Servlet
  • Http
  • Mybatis
  • Spring
  • SpringMVC
  • SpringBoot
  • Dubbo、Zookeeper、分散式基礎
  • Maven、Git
  • Ajax,Json
  • 。。。

串一下自己會的東西

這個階段該如何學

三層架構+MVC
框架:
	Spring   IOC   AOP
	
	SpringBOOT,新一代的JavaEE開發標準,自動裝配
	
	模組化~All in one
	模組化的開發====All in one  程式碼沒變化


微服務架構4個核心問題:
	- 服務很多,客戶端如何訪問
	- 這麼多服務?服務之間如何通訊
	- 這麼多服務?如何治理
	- 服務掛了怎麼辦
	
解決方案:
	SpringCloud  生態   SpringBoot
	1.Spring Cloud NetFlix  一站式解決方案
		api閘道器,zuul元件
		Feign ---HttpClient  --https通訊方式,同步,阻塞
		服務註冊與發現:Eureka
		熔斷機制:Hystrix
	2.Apache Dubbo Zookeeper  半自動,需要整合別人的
		API:沒有,找第三方元件,或者自己實現
		Dubbo
		Zookeeper
		沒有:藉助Hystrix
	3.Spring Cloud Alibaba  一站式解決方案,更簡單
		
新概念:服務網格~Server Mesh
		istio
		
萬變不離其宗
	API
	HTTP,RPC
	註冊和發現
	熔斷機制
	
網路不可靠

微服務和微服務架構

微服務架構是一種架構模式,或者說是以一種架構風格,它提倡將單一的應用程式劃分成一組小的服務,每個服務執行在其獨立的自己的程序內,服務之間互相協調,互相配置,為使用者提供最終價值。服務之間採用輕量級的通訊機制互相溝通,每個服務圍繞著及具體的業務進行構建,並且能夠被獨立的部署到生產環境中,另外,應儘量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言,工具對其進行構建

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

微服務

微服務強調的是服務的大小,他關注的是某一個點,是具體解決某一個問題/提供落地對應服務的一個服務應用,狹義的看,可以看作是IDEA中的一個個微服務工程或者Module

微服務架構

微服務架構是一種架構模式,或者說是以一種架構風格,它提倡將單一的應用程式劃分成一組小的服務,每個服務執行在其獨立的自己的程序內,服務之間互相協調,互相配置,為使用者提供最終價值。服務之間採用輕量級的通訊機制互相溝通,每個服務圍繞著及具體的業務進行構建,並且能夠被獨立的部署到生產環境中,另外,應儘量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言,工具對其進行構建

微服務優缺點

優點

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

缺點

  • 開發人員要處理分散式系統的複雜性
  • 多服務運維難度,隨著服務的增加,運維的壓力也在增大
  • 系統部署依賴
  • 服務間通訊成本
  • 資料一致性
  • 系統整合測試
  • 效能監控

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

  1. 選型依據

    整體解決方案和框架成熟度

    社群熱度

    可維護性

    學習曲線 註解

  2. 當前各大IT公司用的微服務架構有哪些

    阿里:dubbo+HFS

    京東:JSF

    新浪:Motan

    噹噹網:DubboX

    。。。

    什麼是SpringCloud

    SpringCloud是什麼

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

​ SpringCloud,基於SpringBoot提供了一套微服務解決方案,包括服務註冊與發現,配置中心,全鏈路監控,服務閘道器,負載均衡,熔斷器等元件,除了基於NetFlix的開源元件做高度抽象封裝之外,還有一些選型中立的開源元件

SpringCloud利用SpringBoot的開發便利性,巧妙的簡化了分散式系統基礎設施的開發,SprinCloud為開發人員提供了快速構建分散式系統的一些工具,包括配置管理,服務發現,斷路器,路由,微代理,事件匯流排,全域性鎖,決策競選,分散式會話等等,他們都可以用SpringBoot的開發風格做到一鍵啟動和部署

SpringCloud並沒有重複造輪子,只是將目前各家公司開發的比較成熟,經得起實際考驗的服務框架組合起來,通過SpringBoot風格進行再封裝,遮蔽掉了複雜的配置和實現原理,最終給開發者流出了一套簡單易懂,易部署和易維護的分散式系統開發工具包

SpringCloud是分散式微服務架構下的一站式解決方案,是各個微服務架構落地技術的集合體,俗稱微服務全家桶

SpringBoot專注於快速方便的開發單個個體微服務

SpringCloud是關注全域性的微服務協調整理治理框架,它將SpringBoot開發的一個個單體微服務整合並管理起來,為各個微服務之間提供:配置管理,服務發現,斷路器,路由,微代理,事件匯流排,全域性鎖,決策競選,分散式會話等等整合服務

SpringBoot可以離開SpringCloud獨立使用,開發專案,但是SpringCloud離不開SpringBoot,屬於依賴關係

SpringBoot專注於快速,方便的開發單個個體微服務,SpringCloud關注全域性的服務治理框架

Dubbo和SpringCloud技術選型

  • 分散式+服務治理Dubbo

    目前成熟的網際網路架構:應用服務拆分+訊息中介軟體

  • Dubbo和SpringCloud對比

    可以看一下社群活躍度

    https://github.com/dubbo

    https://github.com/spring-cloud

    SpringCloud拋棄了Dubbo的RPC通訊,採用的是基於HTTP的REST 方式

SpringCloud能幹嗎

  • Distributed/versioned configuration 分散式/版本控制配置
  • Service registration and discovery 服務註冊與發現
  • Routing 路由
  • Service-to-service calls 服務到服務的呼叫
  • Load balancing 負載均衡配置
  • Circuit Breakers 斷路器
  • Global locks
  • Leadership election and cluster state
  • Distributed messaging 分散式訊息管理

官網:https://project.spring.io/spring-cloud/

參考書:

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

總專案pom配置

建立一個Maven專案,刪掉src

總專案pom檔案配置

注意這裡是dependency management ,使用的時候還需要匯入

<?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>org.company</groupId>
    <artifactId>springcloud</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--打包方式 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.13.1</junit.version>
        <lombok.version>1.18.16</lombok.version>
        <log4j.version>1.2.17</log4j.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!--springcloud的依賴-->
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--springboot的依賴-->
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--資料庫-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.22</version>
            </dependency>
            <!--資料來源-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.2.2</version>
            </dependency>
            <!--springboot啟動器-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.3</version>
            </dependency>


            <!--日誌測試相關的包-->
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!--log4j-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <!--logback-->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>1.2.3</version>
            </dependency>
        </dependencies>
    </dependencyManagement>


</project>

子模組springcloud-api

新建子模組 maven專案 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>org.company</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-api</artifactId>


    <!--當前的module自己需要的依賴,如果父依賴中已經配置了版本,這裡就不用寫了-->
    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

搭建資料庫環境

本地建立資料庫db01

再通過IDEA的Database建立表,右擊資料庫,視覺化填寫表結構,自動生成sql語句

create table dept
(
   deptno bigint auto_increment,
   dname varchar(60) null,
   db_source varchar(60) null,
   constraint dept_pk
      primary key (deptno)
)
comment '部門表';

插入資料

insert into dept(dname,db_source) values ('開發部',DATABASE());
insert into dept(dname,db_source) values ('人事部',DATABASE());
insert into dept(dname,db_source) values ('財務部',DATABASE());
insert into dept(dname,db_source) values ('市場部',DATABASE());
insert into dept(dname,db_source) values ('運維部',DATABASE());

建立實體類(必須序列化)

package com.company.springcloud.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;


@Data
@Accessors(chain = true)           //鏈式寫法
@NoArgsConstructor
public class Dept implements Serializable { //Dept  實體類   ,orm,  類表關係對映

    private Long deptno;//主鍵
    private String dname;

    //這個資料存在哪個資料庫的欄位~  微服務架構,一個服務對應一個數據庫,同一個資訊可能存在不同的資料庫
    private String db_source;

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

子模組 springcloud-provider-dept

建立一個maven專案springcloud-provider-dept-8001

配置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>springcloud</artifactId>
        <groupId>org.company</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

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

    <dependencies>
        <!--我們需要拿到實體類,需要配置api module-->
        <dependency>
            <groupId>org.company</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--資料來源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>

        <!--logback-core-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>

        <!--mybatis-->
        <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>

        <!--spring-starter-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>
    </dependencies>

</project>

配置檔案

server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.company.springcloud.pojo
  mapper-locations: classpath:mybatis/mapper/*.xml
  config-location: classpath:mybatis/mybatis-config.xml


#spring的配置
spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456

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介面和mapper.xml

package com.company.springcloud.dao;


import com.company.springcloud.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface DeptDao {

    boolean addDept(Dept dept);
    Dept queryById(Long id);
    List<Dept> queryAll();
}

mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.company.springcloud.dao.DeptDao">

    <insert id="addDept" parameterType="Dept">
        insert into dept(dname,db_source) values (#{dname},DATABASE())
    </insert>

    <select id="queryById" parameterType="Long" resultType="Dept">
        select * from dept where deptno=#{deptno}
    </select>

    <select id="queryAll"  resultType="Dept">
        select * from dept 
    </select>

</mapper>

Service 介面和實現類

package com.company.springcloud.service;

import com.company.springcloud.pojo.Dept;

import java.util.List;


public interface DeptService {


    boolean addDept(Dept dept);
    Dept queryById(Long id);
    List<Dept> queryAll();
}

介面實現類

package com.company.springcloud.service;

import com.company.springcloud.dao.DeptDao;
import com.company.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;


@Service
public class DeptServiceImpl implements DeptService{

    @Autowired
    DeptDao deptDao;

    @Override
    public boolean addDept(Dept dept) {

        return deptDao.addDept(dept);
    }

    @Override
    public Dept queryById(Long id) {

        return deptDao.queryById(id);
    }

    @Override
    public List<Dept> queryAll() {

        return deptDao.queryAll();
    }
}

Controller類

package com.company.springcloud.controller;


//提供Restful服務

import com.company.springcloud.pojo.Dept;
import com.company.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

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

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


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

}

啟動測試

出錯原因:

  • 配置檔案要寫對:路徑要寫對,尤其是mybatis,mapper,不然會出現class not found
  • pojo實體類屬性要與資料庫裡的欄位完全一致,否則會出現物件或屬性為null

Rest學習環境搭建:服務消費者

建立maven專案:springcloud-consumer-dept-80

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>springcloud</artifactId>
        <groupId>org.company</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

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

    <!--實體類+web-->
    <dependencies>
        <dependency>
            <groupId>org.company</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>

config

package com.company.springcloud.config;


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

@Configuration
public class ConfigBean {//@Configuration     --spring    applicationext.xml
    
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
    
}

controller

package com.company.springcloud.controller;


import com.company.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
public class DeptConsumerController {

    //理解:消費者不應該有service層~
    //Restful
    //RestTemplate。。。。。供我們直接呼叫就可以了,註冊到Spring中
    //(url,實體:Map,Class<T> responseType)

    @Autowired
    private RestTemplate restTemplate;//提供多種邊界訪問遠端http服務的方法,簡單的Restful服務模板

    private static final String REST_URI_PREFIX = "http://localhost:8001";

    @RequestMapping("/consumer/dept/get")
    public boolean add(Dept dept){
        return restTemplate.postForObject(REST_URI_PREFIX+"/dept/add",dept,boolean.class);
    }

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

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

}

啟動類

package com.company.springcloud;


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

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

}

測試

先啟動服務提供端

再啟動消費者

服務註冊與發現

什麼是Eureka

  • Eureka:怎麼讀
  • Netflix在設計Eureka時,遵循的是AP原則
  • Eureka是Netflix的一個子模組,也是核心模組之一,Eureka是一個基於REST的服務,用於定位服務,以實現雲端中間層服務發現和故障轉移,服務註冊與發現對於微服務來說是非常重要的,有了服務發現與註冊,只需要使用服務的識別符號,就可以訪問到服務,而不需要修改服務呼叫的配置檔案了。功能類似於Dubbo的註冊中心,比如Zookeeper

原理講解

  • Eureka的基本架構

    • SpringCloud封裝了Netflix公司開發的Eureka模組來實現服務註冊與發現(對比Zookeeper)
    • Eureka採用了C-S的架構設計,EurekaServer作為服務註冊功能的伺服器,他是服務註冊中心
    • 而系統中的其他微服務,使用Eureka的客戶端連線到EurekaServer並維持心跳連線。這樣系統的維護人員就可以通過EurekaServer來發現系統中的其他微服務,並執行相關的邏輯
    • 和Dubbo架構對比
    • Eureka 包含兩個元件:Eureka Server和Eureka Client
    • Eureka Server提供服務註冊服務,各個節點啟動後,會在EurekaServer中進行註冊,這樣Eureka Server中的服務登錄檔中將會儲存所有可用服務節點的資訊,服務節點的資訊可以在介面中直觀的看到
    • Eureka Client是一個java客戶端,用於簡化EurekaServer的互動,客戶端同時也具備一個內建的,使用輪詢負載演算法的負載均衡器,在應用啟動後,將會向EurekaServer傳送心跳(預設週期為30秒)。如果Eureka Server在多個心跳週期內沒有接收到某個節點的心跳,EurekaServer將會從服務登錄檔中把這個服務節點移除掉(預設週期為90秒)
  • 三個角色

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

新建一個模組springcloud-eureka-7001

  • 匯入依賴

  • 編寫配置檔案

  • 開啟這個功能@Enablexxxx

  • 配置類

    導包

<!--導包-->
<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
        <version>1.4.7.RELEASE</version>
    </dependency>

    <!--熱部署工具-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
</dependencies>

配置

server:
  port: 7001


#Eureka配置
eureka:
  instance:
    hostname: localhost         #Eureka服務端的例項名稱
  client:
    register-with-eureka: false  #表示是否向eureka註冊中心註冊自己
    fetch-registry: false  #如果  fetch-registry  為false,則表示自己為註冊中心
    service-url:        #監控頁面~
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

啟動類

package com.company.springcloud;


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


//啟動之後訪問 http://localhost:7001/
@SpringBootApplication
@EnableEurekaServer            //EnableEurekaServer        服務端啟動類,可以接受別人註冊進來
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class,args);
    }
}

Eureka:服務註冊-資訊配置-自我保護機制

服務端

服務端匯入eureka依賴包

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

配置檔案

# Eureka配置,服務註冊到哪裡
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    instance-id: springcloud-provider-dept8001  #修改eureka上的預設描述資訊!

啟動類開啟Eureka

package com.company.springcloud;


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

//啟動類
@SpringBootApplication
@EnableEurekaClient  //自動在服務啟動後自動註冊到Eureka中
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}

測試

開啟7001

開啟8001

訪問:http://localhost:7001/ 看到服務已經被註冊到Eureka

加上監控依賴

<!--完善監控資訊-->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置檔案

server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.company.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml


#spring的配置
spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456

# Eureka配置,服務註冊到哪裡
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    instance-id: springcloud-provider-dept8001  #修改eureka上的預設描述資訊!

#info配置
info:
  app.name: ming-springcloud
  company.name: blog.mingstudy.com

重啟測試

自我保護機制

某時刻某一個微服務不可以用了,eureka不會立刻清理,依舊會對該微服務的資訊進行儲存

  • 預設情況下:如果EurekaServer在一定事件內沒有接收到某個微服務例項的心跳,EurekaServer將會登出該例項(預設90秒),但是當網路分割槽故障發生時,微服務與Eureka之間無法正常通訊,以上行為可能變的非常危險了,因為微服務本身其實是健康的,此時本不應該登出這個功能,Eureka通過自我保護機制來解決這個問題--當EurekaServer節點在短時間內丟失過多客戶端時(可能發生了網路分割槽故障),那麼這個節點就會進入自我保護模式,一旦進入該模式,EurekaServer就會保護服務登錄檔中的資訊,不再刪除服務登錄檔中的資料(也就是不會登出任何微服務)。當網路故障恢復時,該EurekaServer節點會自動退出自我保護模式
  • 在自我保護模式中,EurekaServer會保護服務登錄檔中的資訊,不再登出任何服務例項,當它收到的心跳數重新恢復到閾值以上時,該EurekaServer節點就會自動推出自我保護模式,它的設計哲學就是寧可保留錯誤的服務註冊資訊,也不盲目登出任何可能健康的服務例項,一句話:好死不如賴活者
  • 綜上,自我保護模式是一種應對網路異常的安全保護措施,它的架構哲學是寧可同時保留所有微服務(健康的微服務和不健康的微服務都會保留),也不盲目的登出任何健康的微服務,使用自我保護模式,可以讓Eureka叢集更加的健壯和穩定
  • 在SpringCloud中,可以使用eureka.server.enable-self-preservation = false 禁用保護模式【不推薦關閉自我保護機制】

服務端增加註冊發現Controller

package com.company.springcloud.controller;


//提供Restful服務

import com.company.springcloud.pojo.Dept;
import com.company.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;
    
    //獲取一些配置的資訊,得到具體的微服務!
    @Autowired
    private DiscoveryClient client;

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

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


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

    //註冊進來的微服務~,獲取一些訊息
    @GetMapping("/dept/discovery")
    public Object discover(){
        //獲取微服務列表的清單
        List<String> services = client.getServices();
        System.out.println("discovery=>service"+services);
        
        //得到一個具體的微服務資訊,通過具體的微服務id,applicationName
        List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
        for (ServiceInstance instance : instances) {
            System.out.println(
                    instance.getHost()+"\t"+
                            instance.getPort()+"\t"+
                            instance.getUri()+"\t"+
                            instance.getServiceId()
            );
        }
        return this.client;
    }
    
}

啟動類配置服務發現

package com.company.springcloud;


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

//啟動類
@SpringBootApplication
@EnableEurekaClient  //自動在服務啟動後自動註冊到Eureka中
@EnableDiscoveryClient  //服務發現
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}

啟動Eureka

啟動服務端

訪問http://localhost:8001/dept/discovery 獲得微服務資訊

Eureka:叢集環境配置

建立新模組 springcloud-eureka-7002

建立新模組 springcloud-eureka-7003

把7001的pom檔案依賴包複製到7002,7003

application 複製兩份,修改埠號

複製啟動類,修改類名稱

C:\Windows\System32\drivers\etc hosts檔案修改

增加以下資訊:一個IP繫結三個域名

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

配置檔案

7001

server:
  port: 7001


#Eureka配置
eureka:
  instance:
    hostname: eureka7001.com        #Eureka服務端的例項名稱
  client:
    register-with-eureka: false  #表示是否向eureka註冊中心註冊自己
    fetch-registry: false  #如果  fetch-registry  為false,則表示自己為註冊中心
    service-url:        #監控頁面~
      #單機:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      #叢集(關聯):
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

7002

server:
  port: 7002


#Eureka配置
eureka:
  instance:
    hostname: eureka7002.com        #Eureka服務端的例項名稱
  client:
    register-with-eureka: false  #表示是否向eureka註冊中心註冊自己
    fetch-registry: false  #如果  fetch-registry  為false,則表示自己為註冊中心
    service-url:        #監控頁面~
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/

7003

server:
  port: 7003


#Eureka配置
eureka:
  instance:
    hostname: eureka7003.com         #Eureka服務端的例項名稱
  client:
    register-with-eureka: false  #表示是否向eureka註冊中心註冊自己
    fetch-registry: false  #如果  fetch-registry  為false,則表示自己為註冊中心
    service-url:        #監控頁面~
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

服務端Eureka 地址修改

server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.company.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml


#spring的配置
spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456

# Eureka配置,服務註冊到哪裡
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-provider-dept8001  #修改eureka上的預設描述資訊!

#info配置
info:
  app.name: ming-springcloud
  company.name: blog.mingstudy.com

測試

啟動7001,7002,7003

啟動8001

7001,7002,7003掛掉一個,服務還能訪問

Eureka:CAP原則及對比Zookeeper

CAP原則

RDBMS (Mysql,Oracle,sqlServer)===》ACID

NoSQL(redis,mongodb)===>CAP

ACID是什麼

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

CAP是什麼

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

CAP的三進二:CA,AP,CP

CAP理論的核心

  • 一個分散式系統不可能同時很好的滿足一致性,可用性,和分割槽容錯性這三個需求
  • 根據CAP原理,將NoSQL資料庫分成了滿足CA原則,滿足CP原則和滿足AP原則三大類
    • CA:單點叢集,滿足一致性 ,可用性的系統,通常可擴充套件性較差
    • CP:滿足一致性,分割槽容錯性的系統,通常效能不是特別高
    • AP:滿足可用性,分割槽容錯性的系統,通常可能對一致性要求低一點

作為服務註冊中心,Eureka比Zookeeper好在哪裡

著名的CAP理論指出,一個分散式系統不可能同時滿足C(一致性),A(可用性),P(容錯性)

由於分割槽容錯性P在分散式系統中是必須要保證的,因此我們只能在A和C之間進行權衡

  • Zookeeper保證的是CP
  • Eureka保證的是AP

Zookeeper保證的是CP

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

Eureka保證的是AP

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

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

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

Ribbon:負載均衡及Ribbon

Ribbon是什麼

  • Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡的工具
  • 簡單的說,Ribbon是Netflix釋出的開源專案,主要功能是提供客戶端的軟體負載均衡演算法,將Netflix的中間層服務連線在一起,Ribbon的客戶端元件提供一系列完整的配置須知如:連線超時,重試等等,簡單的說,就是在配置檔案中列出LoadBalancer(簡稱LB:負載均衡)後面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連線等等)去連線這些機器,我們也很容易使用Ribbon實現自定義的負載均衡演算法

Ribbon能幹嘛

  • LB,即負載均衡(Load Balance),在微服務或分散式叢集中經常用的一種應用

  • 負載均衡簡單的說就是將使用者的請求平攤的分配到多個服務上,從而達到系統的HA(高可用)

  • 常見的負載均衡軟體有Nginx,Lvs等等

  • dubbo,SpringCloud中均給我們提供了負載均衡,SpringCloud的負載均衡演算法可以自定義

  • 負載均衡簡單分類:

    • 集中式LB

      • 即在服務的消費方和提供方之間使用獨立的LB設施,如Nginx,由該設施負載把訪問請求通過某種策略轉發至服務的提供方
    • 程序式LB

      • 將LB邏輯整合到消費方,消費方從服務註冊中心獲知有哪些地址可用,然後自己再從這些地址中選出一個合適的伺服器
      • Ribbon就屬於程序內LB,它只是一個類庫,集成於消費方程序,消費方通過它來獲取到服務提供方的地址

消費者匯入ribbon依賴

增加pom依賴

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

        <!--eureka-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

配置檔案

server:
  port: 80
  
  
#Eureka配置
eureka:
  client:
    register-with-eureka: false   #不向Eureka註冊自己
    service-url: 
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

啟動類加上@EnableEurekaClient

package com.company.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//Ribbon和Eureka整合以後,客戶端可以直接呼叫,不用關心IP地址和埠號~
@SpringBootApplication
@EnableEurekaClient  //客戶端
public class DeptConsumer_80 {
    public static void main(String[] args) {

        SpringApplication.run(DeptConsumer_80.class,args);
    }

}

配置類負載均衡

package com.company.springcloud.config;


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 ConfigBean {//@Configuration     --spring    applicationext.xml
    //配置負載均衡實現
    @Bean
    @LoadBalanced //Ribbon
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }


    
    
}

控制類訪問地址變更

package com.company.springcloud.controller;


import com.company.springcloud.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
public class DeptConsumerController {

    //理解:消費者不應該有service層~
    //Restful
    //RestTemplate。。。。。供我們直接呼叫就可以了,註冊到Spring中
    //(url,實體:Map,Class<T> responseType)

    @Autowired
    private RestTemplate restTemplate;//提供多種邊界訪問遠端http服務的方法,簡單的Restful服務模板

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

    @RequestMapping("/consumer/dept/get")
    public boolean add(Dept dept){
        return restTemplate.postForObject(REST_URI_PREFIX+"/dept/add",dept,boolean.class);
    }

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

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

}

啟動7001,7002

啟動8001

啟動80 訪問http://localhost/consumer/dept/list 獲取到資料,測試通過

Ribbon:使用Ribbon實現負載均衡

複製資料庫db01,建立資料庫db02,db03,插入資料

建立maven專案 springcloud-provider-dept-8002,springcloud-provider-dept-8003

匯入pom依賴,與8001一致

配置檔案對應修改一下

三個服務的名稱要一致

啟動7001,8001,8002,8003

啟動80

訪問http://localhost/consumer/dept/list

查詢結果再db01,db02,db03中輪詢

Ribbon:自定義負載均衡演算法

自定義MyRule,不能在啟動類同一目錄下,應在上一級

package com.company.myrule;


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 MingRule {


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

}

啟動類加上註解@RibbonClient

package com.company.springcloud;


import com.company.myrule.MingRule;
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;

//Ribbon和Eureka整合以後,客戶端可以直接呼叫,不用關心IP地址和埠號~
@SpringBootApplication
@EnableEurekaClient  //客戶端
//在微服務啟動的時候就能去載入我們自定義Ribbon類
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MingRule.class)
public class DeptConsumer_80 {
    public static void main(String[] args) {

        SpringApplication.run(DeptConsumer_80.class,args);
    }

}

自定義MingRandomRule

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.company.myrule;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class MingRandomRule extends AbstractLoadBalancerRule {
    public MingRandomRule() {
    }
    
    //每個服務訪問5次,換下一個服務(3個)
    
    //total=0,預設=0,如果=5,我們指向下一個服務節點
    //index=0,預設0,如果total=5,index+1,
    
    private int total=0;//被呼叫的次數
    private int currentIndex=0;//當前是誰在提供服務~
    

    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        } else {
            Server server = null;

            while(server == null) {
                if (Thread.interrupted()) {
                    return null;
                }

                List<Server> upList = lb.getReachableServers();  //獲得活著的服務
                List<Server> allList = lb.getAllServers();  //獲得全部的服務
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }

//                int index = this.chooseRandomInt(serverCount);  //生成區間隨機數
//                server = (Server)upList.get(index);  //從活著的服務中隨機獲取一個
                
                //=====================================================
                
                if(total<5){
                    server = upList.get(currentIndex);
                    total++;
                }else{
                    total=0;
                    currentIndex++;
                    if(currentIndex>=upList.size()){
                        currentIndex=0;
                    }
                    server = upList.get(currentIndex);    //從活著的服務中,獲取指定的服務來進行操作
                }
                
                //=====================================================
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

與啟動類不在同級目錄的config

package com.company.myrule;


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

@Configuration
public class MingRule {


    @Bean
    public IRule myRule(){
        return new MingRandomRule();//預設是輪詢,現在我們自定義
    }

}

Feign負載均衡

簡介

feign是宣告式的web service客戶端,它讓微服務之間的呼叫變得更簡單了,類似controller呼叫service,SpringCloud集成了Ribbon和Eureka,可在使用Feign時提供負載均衡的http客戶端

只需要建立一個介面,然後添加註解即可

feign,只要是社群,大家都習慣面向介面程式設計,這個是很多開發人員的規範,呼叫微服務訪問兩種方法

  • 微服務名字(ribbon)
  • 介面和註解(feign)

Feign能幹什麼

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

Feign集成了Ribbon

  • 利用Ribbon維護了MicroServiceCloud-Dept的服務列表資訊,並且通過輪詢實現了客戶端的負載均衡,而與Ribbon不同的是,通過Feign只需要定義服務繫結介面且以宣告式的方法,優雅而且簡單的實現了服務呼叫

新建module springcloud-consumer-dept-feign

把springcloud-consumer-dept-80 檔案複製一份到springcloud-consumer-dept-feign

啟動類不包含Ribbon自定義設定

package com.company.springcloud;


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

//Ribbon和Eureka整合以後,客戶端可以直接呼叫,不用關心IP地址和埠號~
@SpringBootApplication
@EnableEurekaClient  //客戶端
public class FeignDeptConsumer_80 {
    public static void main(String[] args) {

        SpringApplication.run(FeignDeptConsumer_80.class,args);
    }

}

匯入feign依賴

<!--feign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

module springcloud-api也匯入feign依賴

<!--feign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

springcloudapi 新建service層,新建DeptClienService介面

package com.company.springcloud.service;

import com.company.springcloud.pojo.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.List;


@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {

    
    @GetMapping("/dept/get/{id}")
    public Dept queryById(@PathVariable("id") Long id);
    @GetMapping("/dept/list")
    public List<Dept> queryAll();

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

springcloud-consumer-dept-feign Controller

package com.company.springcloud.controller;


import com.company.springcloud.pojo.Dept;
import com.company.springcloud.service.DeptClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class DeptConsumerController {
    
    @Autowired
    private DeptClientService service=null;

    @RequestMapping("/consumer/dept/get")
    public boolean add(Dept dept){
        return this.service.addDept(dept);
    }

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id){
        return this.service.queryById(id);
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> list(){
        return this.service.queryAll();
    }


}

feign啟動類配置

package com.company.springcloud;


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

//Ribbon和Eureka整合以後,客戶端可以直接呼叫,不用關心IP地址和埠號~
@SpringBootApplication
@EnableEurekaClient  //客戶端
@EnableFeignClients(basePackages = {"com.company.springcloud"})

public class FeignDeptConsumer_80 {
    public static void main(String[] args) {

        SpringApplication.run(FeignDeptConsumer_80.class,args);
    }

}

啟動7001,8001,8002,

啟動feign,訪問http://localhost/consumer/dept/list 測試通過

Hystrix:服務熔斷

分散式系統面臨的問題

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

服務雪崩

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

對於高流量的應用來說,單一的後端依賴可能會導致所有伺服器上的所有資源都在幾秒中內飽和,比失敗更糟糕的式,這些應用程式還可能導致服務之間的延遲增加,備份佇列,執行緒和其他系統資源緊張,導致整個系統發生更多的級聯故障,這些都表示需要對故障和延遲進行隔離和管理,以便單個依賴關係的失敗,不能取消整個應用程式或系統

我們需要棄車保帥

什麼是Hystrix

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

“斷路器”本身是一種開關裝置,當某個服務單元發生故障之後,通過斷路器的故障監控(類似熔斷保險絲),向呼叫方法返回一個服務預期的,可處理的備選響應(FallBack),而不是長時間的等待或者丟擲呼叫方法無法處理的異常,這樣就可以保證了服務呼叫方的執行緒不會被長時間,不必要的佔用,從而避免了故障在分散式系統中的蔓延,乃至雪崩

能幹嗎

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

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

服務熔斷

是什麼

熔斷機制是對應雪崩效應的一種微服務鏈路保護機制

當扇出鏈路的某個微服務不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的呼叫,快速返回錯誤的響應資訊。當檢測到該節點微服務呼叫響應正常後恢復呼叫鏈路。在SpringCloud框架裡熔斷機制通過Hystrix實現。Hystrix會監控微服務間呼叫的狀況,當失敗的呼叫到一定閾值,預設是5秒內20次呼叫失敗就會啟動熔斷機制。熔斷機制的註解是@HystrixCommand

新建模組springcloud-provider-dept-hystrix-8001

拷貝8001內容到springcloud-provider-dept-hystrix-8001

匯入依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

controller

package com.company.springcloud.controller;


//提供Restful服務

import com.company.springcloud.pojo.Dept;
import com.company.springcloud.service.DeptService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;


    
    @GetMapping("/dept/get/{id}")
    @HystrixCommand(fallbackMethod = "hystrixGet")
    public Dept get(@PathVariable("id") Long id){
        Dept dept = deptService.queryById(id);
        if(dept==null){
            throw new RuntimeException("id=>"+id+",不存在該使用者,或者資訊無法找到~");
            
        }
        return dept;
    }
    
    //備選方法
    public Dept hystrixGet(@PathVariable("id") Long id){
        
        return new Dept()
                .setDeptno(id)
                .setDname("id=>"+id+"沒有對應的資訊,為null--@Hystrix")
                .setDb_source("no this database in MySQL");
    }

}

啟動類新增支援

package com.company.springcloud;


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

//啟動類
@SpringBootApplication
@EnableEurekaClient  //自動在服務啟動後自動註冊到Eureka中
@EnableDiscoveryClient  //服務發現
@EnableCircuitBreaker  //新增對熔斷的支援
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class,args);
    }
}

啟動7001,7002,hystrix-8001,80

訪問http://localhost/consumer/dept/get/8

得到{"deptno":8,"dname":"id=>8沒有對應的資訊,為null--@Hystrix","db_source":"no this database in MySQL"}

配置檔案中

eureka.prefer-ip-address: true          #可以顯示服務的ip地址

Hystrix:服務降級

在springcloud-api service層寫fallbackfactory

package com.company.springcloud.service;

import com.company.springcloud.pojo.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

import java.util.List;

//降級~
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory {

    @Override
    public DeptClientService create(Throwable throwable) {
        return new DeptClientService() {
            @Override
            public Dept queryById(Long id) {
                return new Dept()
                        .setDeptno(id)
                        .setDname("id=>"+id+"沒有對應的資訊,客戶端提供了降級的資訊,這個服務現在已經被關閉")
                        .setDb_source("沒有資料~");
            }

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

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

加上fallbackfactory資訊

package com.company.springcloud.service;

import com.company.springcloud.pojo.Dept;
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;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.List;


@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {


    @GetMapping("/dept/get/{id}")
    public Dept queryById(@PathVariable("id") Long id);
    @GetMapping("/dept/list")
    public List<Dept> queryAll();

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

springcloud-consumer-dept-feign 配置檔案開啟hystrix

server:
  port: 80

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


#Eureka配置
eureka:
  client:
    register-with-eureka: false   #不向Eureka註冊自己
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

測試

啟動7001,沒有hystrix的8001,dept-feign-80

訪問http://localhost/consumer/dept/get/1 得到{"deptno":1,"dname":"開發部","db_source":"db01"}

關掉8001,訪問得到{"deptno":1,"dname":"id=>1沒有對應的資訊,客戶端提供了降級的資訊,這個服務現在已經被關閉","db_source":"沒有資料~"}

服務熔斷:服務端~某個服務超時或者異常,引起熔斷~  保險絲~

服務降級:客戶端~  從整體的網站請求負載考慮~,當某個服務熔斷或者關閉之後,服務將不再被呼叫~
        此時在客戶端,我們可以準備一個FallbackFactory,返回一個預設的值(預設值),整體的服務水平下降了~但是,好歹能用~
        比直接掛掉強~

Hystrix:Dashboard流監控

新建模組springcloud-consumer-hystrix-dashboard

匯入consumer-dept-80的依賴 ,依賴是一樣的

增加hystrix,dashboard的依賴

<!--hystrix-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

配置檔案

server:
  port: 9001

hystrix:
  dashboard:
    proxy-stream-allow-list: "*"

啟動類

package com.company.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@SpringBootApplication
@EnableHystrixDashboard   //開啟監控
public class DeptConsumerDashBoard_9001 {

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

確保服務端有監控資訊依賴

<!--完善監控資訊-->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

之前啟動了7001,8001,dept-deign ,這次再啟動9001

訪問http://localhost:9001/hystrix 得到監控頁面

springcloud-provider-hystrix-8001

在啟動類增加一個servlet

package com.company.springcloud;


import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

//啟動類
@SpringBootApplication
@EnableEurekaClient  //自動在服務啟動後自動註冊到Eureka中
@EnableDiscoveryClient  //服務發現
@EnableCircuitBreaker  //新增對熔斷的支援
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class,args);
    }

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

測試

啟動7001, 9001,hystrix-8001,

呼叫@HystrixCommand(fallbackMethod = "hystrixGet")介面的方法

訪問http://localhost:8001/dept/get/1

再訪問http://localhost:9001/hystrix 填入url http://localhost:8001/actuator/hystrix.stream 得到監控頁面

Zuul:路由閘道器

概述

什麼是zuul?

zuul包含了對請求的路由和過濾兩個最主要的功能:

其中路由功能負責將外部請求轉發到具體的微服務例項上,是實現外部訪問統一入口的基礎,而過濾器則負責對請求的處理過程進行干預,是實現請求校驗,服務聚合等功能的基礎。Zuul和Eureka進行整合,將zuul自身註冊為Eureka服務治理下的應用,同時從Eureka中獲得其他微服務的訊息,也即以後的訪問微服務都是通過zuul跳轉後獲得

注意:zuul服務最終還是會註冊進Eureka

提供:代理+路由+過濾三大功能

zuul能幹嘛?

  • 路由

  • 過濾

官方文件:https://github.com/Netflix/zuul

新建模組springcloud-zuul-9527

把dashboard的pom依賴複製到 9527

新增zuul依賴

<!--zull-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

配置

server:
  port: 9527

spring:
  application:
    name: springcloud-zuul

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance:
    instance-id: zuul9527.com
    prefer-ip-address: true

info:
  app.name: ming-springcloud
  company.name: blog.ming.study.com

啟動類開啟zuul

package com.company.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableZuulProxy  //開啟zuul
public class ZuulApplication_9527 {

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

啟動7001,hystrix8001,9527

訪問http://localhost:8001/dept/get/1 得到結果

訪問http://localhost:9527/springcloud-provider-dept/dept/get/1 得到的結果一致

9527增加zuul配置(隱藏真實服務名),重啟

zuul:
  routes: 
    mydept.serviceId: springcloud-provider-dept
    mydept.path: /mydept/**

訪問http://localhost:9527/mydept/dept/get/1

得到相同的結果

增加zuul忽略服務,重啟,原來路徑訪問不了

zuul:
  routes:
    mydept.serviceId: springcloud-provider-dept
    mydept.path: /mydept/**
  ignored-services: springcloud-provider-dept  #不能再使用這個路徑訪問了,ignored:忽略

訪問:http://localhost:9527/springcloud-provider-dept/dept/get/1

訪問不了

訪問http://localhost:9527/mydept/dept/get/1 能得到結果

其他設定:

zuul:
	ignored-services: "*"      #隱藏全部的
	prefix: /ming     #設定公共的字首

SpringCloud config 分散式配置

概述

分散式系統面臨的--配置檔案的問題

​ 微服務意味著要將單體應用中心的業務拆分成一個個子服務,每個服務的粒度相對較小,因此係統中會出現大量的服務,由於各個服務都需要必要的配置資訊才能執行,所以一套集中式的,動態的配置管理設施是必不可少的。SpringCloud提供了ConfigServer來解決這個問題,我們每一個微服務自己帶著一個application.yml,那上百的配置檔案要修改起來,豈不是要發瘋!

什麼是SpringCloud config分散式配置中心

​ SpringCloud config為微服務架構中的微服務提供集中化的外部配置支援,配置伺服器為各個不同微服務應用的環節提供了一個中心化的外部配置

SpringCloud Config分為服務端客戶端兩部分

服務端也稱為分散式配置中心,它是一個獨立的微服務應用,用來連線配置伺服器併為客戶端提供獲取配置資訊,加密,解密資訊等訪問介面

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

SpringCloud Config分散式配置中心能幹嘛

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

SpringCloud config分散式配置中心與github整合

由於SpringCloud config 預設使用Git來儲存配置檔案(也有其他方式,比如支援SVN和本地檔案),但是最推薦的還是Git,而且使用的時http/https 訪問的形式

Config:Git環境搭建

coding gitee github

在gitee上新建一個倉庫 springcloud-config

git客戶端 git clone 遠端倉庫

本地倉庫新建application.yml

spring:
  profiles:
    active: dev
    
---
spring:
  profiles: dev
  application:
    name: springcloud-config-dev
    
---
spring:
  profiles: test
  application:
    name: springcloud-config-test

執行命令

如果第一次輸入密碼出錯,記得清除本地密碼:git config --system --unset credential.helper

git add .
git commit -m 'first commit'
git push origin master

Config:服務端連線Git配置

新建模組springcloud-config-server-3344

匯入依賴

<dependencies>

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

        <!--完善監控資訊-->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>



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

    </dependencies>

配置資訊

server:
  port: 3344

spring:
  application:
    name: springcloud-config-server
    #連線遠端倉庫

  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/uxquan/springcloud-config.git   #https  不是git
               

啟動類開啟config

package com.company.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer   //開啟config
public class Config_Server_3344 {
    public static void main(String[] args) {
        SpringApplication.run(Config_Server_3344.class,args);
    }
}

啟動3344

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

訪問http://localhost:3344/application-test.yml

也可以 這種格式

http://localhost:3344/application/dev/master

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

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

通過config-server 可以連線到git,訪問其中的資源以及配置

Config:客戶端連線服務端訪問遠端

在springcloud-config本地專案裡新建config-client.yml檔案

spring:
  profiles:
    active: dev
    
---
server:
  port: 8201
#spring的配置
spring:
  profiles: dev
  application:
    name: springcloud-provider-dept

# Eureka配置,服務註冊到哪裡
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/


---
server:
  port: 8202
#spring的配置
spring:
  profiles: test
  application:
    name: springcloud-provider-dept

# Eureka配置,服務註冊到哪裡
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

執行命令

git add .
git commit -m '2'
git push origin master

新建模組springcloud-client-3355

匯入依賴 spring-cloud-starter-config

<dependencies>

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


        <!--完善監控資訊-->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>



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

    </dependencies>

配置檔案

bootstrap.yml 系統級別的配置

# 系統級別的配置

spring:
  cloud:
    config:
      name: config-client  #需要從git上讀取的資源的名稱,不要字尾
      profile: dev
      label: master
      uri: http://localhost:3344
      

application.yml 使用者級別的配置

#使用者級別的配置

spring:
  application:
    name: springcloud-config-client-3355

啟動類

package com.company.springcloud;


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

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

寫一個controller

package com.company.springcloud.controller;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigClientController {

    @Value("${spring.application.name}")
    private String applicationName;
    @Value("${eureka.client.service-url.defaultZone}")
    private String eurekaServer;
    @Value("${server.port}")
    private String port;


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

啟動3344,3355

訪問http://localhost:3344/master/config-client-dev.yml 可以得到資訊

訪問http://localhost:8201/config 得到資訊,預設遠端的dev配置檔案的埠

修改bootstrap.yml profiles為test,即遠端埠為8202

# 系統級別的配置

spring:
  cloud:
    config:
      name: config-client  #需要從git上讀取的資源的名稱,不要字尾
      profile: test
      label: master
      uri: http://localhost:3344

Config:遠端配置實戰測試

本地git專案新建config-eureka.yml

spring:
  profiles:
    active: dev

---
server:
  port: 7001
  
  
#spring的配置
spring:
  profiles: dev
  application:
    name: springcloud-config-eureka
#Eureka配置
eureka:
  instance:
    hostname: eureka7001.com        #Eureka服務端的例項名稱
  client:
    register-with-eureka: false  #表示是否向eureka註冊中心註冊自己
    fetch-registry: false  #如果  fetch-registry  為false,則表示自己為註冊中心
    service-url:        #監控頁面~
      #單機:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      #叢集(關聯):
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
      
      
---
server:
  port: 7001
  
#spring的配置
spring:
  profiles: test
  application:
    name: springcloud-config-eureka

#Eureka配置
eureka:
  instance:
    hostname: eureka7001.com        #Eureka服務端的例項名稱
  client:
    register-with-eureka: false  #表示是否向eureka註冊中心註冊自己
    fetch-registry: false  #如果  fetch-registry  為false,則表示自己為註冊中心
    service-url:        #監控頁面~
      #單機:defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      #叢集(關聯):
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
      

新建config-dept.yml

資料庫一個是db01,一個是db02

spring:
  profiles:
    active: dev

---

server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.company.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml


#spring的配置
spring:
  profiles: dev
  application:
    name: springcloud-config-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456

# Eureka配置,服務註冊到哪裡
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-provider-dept8001  #修改eureka上的預設描述資訊!
    prefer-ip-address: true          #可以顯示服務的ip地址

#info配置
info:
  app.name: ming-springcloud
  company.name: blog.mingstudy.com
  
  
---

server:
  port: 8001

#mybatis配置
mybatis:
  type-aliases-package: com.company.springcloud.pojo
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml


#spring的配置
spring:
  profiles: test
  application:
    name: springcloud-config-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db02?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456

# Eureka配置,服務註冊到哪裡
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance:
    instance-id: springcloud-provider-dept8001  #修改eureka上的預設描述資訊!
    prefer-ip-address: true          #可以顯示服務的ip地址

#info配置
info:
  app.name: ming-springcloud
  company.name: blog.mingstudy.com

執行命令

git add .
git commit -m '3'
git push origin master

新建module springcloud-config-eureka-7001

匯入依賴

<!--導包-->
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>

        <!--熱部署工具-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>

eureka-7001 src下的檔案複製一份到config-eureka-7001

匯入config依賴

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

配置檔案

applciation.yml

spring:
  application:
    name: springcloud-config-eureka-7001

bootstrap.yml

# 系統級別的配置

spring:
  cloud:
    config:
      name: config-eureka  #需要從git上讀取的資源的名稱,不要字尾
      profile: dev
      label: master
      uri: http://localhost:3344

測試

啟動3344,自測訪問:http://localhost:3344/master/config-eureka-dev.yml 可以得到資訊

啟動config-eureka-7001 訪問http://localhost:7001/ 有eureka頁面,說明讀到了遠端資訊

新建module springcloud-config-dept-8001

匯入 provider-dept-8001相同的依賴

<dependencies>

    <!--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.7.RELEASE</version>
    </dependency>

    <!--完善監控資訊-->
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>


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

    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--資料來源-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
    </dependency>

    <!--logback-core-->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
    </dependency>

    <!--mybatis-->
    <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>

    <!--spring-starter-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>
</dependencies>

同時拷貝src下的其他檔案

匯入config包

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

bootstrap.yml 查詢的是db01資料庫

# 系統級別的配置

spring:
  cloud:
    config:
      name: config-dept  #需要從git上讀取的資源的名稱,不要字尾
      profile: dev
      label: master
      uri: http://localhost:3344

application.yml

spring:
  application:
    name: springcloud-config-dept-8001

之前啟動了config7001,3344啟動config-8001

訪問http://localhost:8001/dept/get/1 得到db01的資料

這樣就可以通過遠端修改配置