二、springcloud入門及學習筆記

第一頁:https://my.oschina.net/u/4139437/blog/5391678

負載均衡以及Ribbon

Ribbon是什麼?

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

Ribbon能幹嘛?

  • LB,即負載均衡 (LoadBalancer) ,在微服務或分佈式集羣中經常用的一種應用。
  • 負載均衡簡單的說就是將用戶的請求平攤的分配到多個服務上,從而達到系統的HA (高用)。
  • 常見的負載均衡軟件有 Nginx、Lvs 等等。
  • Dubbo、SpringCloud 中均給我們提供了負載均衡,SpringCloud 的負載均衡算法可以自定義
  • 負載均衡簡單分類:

    • 集中式LB

      • 即在服務的提供方和消費方之間使用獨立的LB設施,如Nginx(反向代理服務器),由該設施負責把訪問請求通過某種策略轉發至服務的提供方!
    • 進程式 LB

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

子項目springcloud-consumer-dept-80 集成Ribbon

pom文件添加依賴

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

在application.yml文件中配置Eureka

# Eureka配置
eureka:
  client:
    register-with-eureka: false # 不向 Eureka註冊自己
    service-url:
      #註冊中心地址7001-7003,每個地址用,隔開
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

啓動類加上@EnableEurekaClient註解,開啓Eureka

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

配置類添加@LoadBalanced註解 ,  配置負載均衡實現RestTemplate

@Configuration
public class ConfigBean {

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

修改controller地址常量,改爲application名稱(eureka服務名稱,eureka監控頁面上可以看到,provader的ymp配置中可以設置)

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

訪問:http://127.0.0.1/consumer/dept/list 即可得到數據

使用Ribbon實現負載均衡(基於客戶端)

創建db02,db03數據庫,表跟字段以及數據都是跟db01一樣的

創建子項目springcloud-provider-dept-8002,springcloud-provider-dept-8003

代碼都是一樣的,只做以下修改;

1、修改yml配置

server:
  port: 8002 # 修改對應的端口號
spring:
  datasource:
    # 修改對應的數據庫名字
    url: jdbc:mysql://localhost:3306/db02?useUnicode=true&characterEncoding=UTF-8&useSSL=false
info:
  # 修改項目的名稱,只是在監控頁面描述狀態顯示
  app.name: springcloud-provider-dept-8002

spring:
  application:
    name: springcloud-provider-dept #特別留意3個服務的應用名是一樣的!

2、修改對應啓動類的名字

啓動3個eureka集羣服務+3個provider服務+1個consumer服務,多次訪問http://127.0.0.1/consumer/dept/list

數據庫db_source字段的值依次db01,db02,db03。輪詢調用3個provider服務。默認策略是輪詢,輪詢算法在SpringCloud中可以自定義。

切換策略

在子項目springcloud-consumer-dept-80的configBean文件添加

@Bean
    public IRule myRule() {
        return new RandomRule();//使用隨機策略
        //return new RoundRobinRule();//使用輪詢策略
        //return new AvailabilityFilteringRule();//使用輪詢策略
        //return new RetryRule();//使用輪詢策略
    }

Irule的實現類有;

自定義策略

創建一個包跟啓動類包同級,這樣不會被spring掃描到

// rule配置類,註冊bean
@Configuration
public class RuleConfig {
    @Bean
    public IRule myRule(){
        return new CustomRule();//默認是輪詢RandomRule,現在自定義爲自己的
    }
}

 這個策略規則每個服務調用5次切換,只是展示怎麼自定義策略,有bug

// 自定義rule (RandomRule複製過來修改)
public class CustomRule extends AbstractLoadBalancerRule {
    /**
     * 每個服務訪問5次則換下一個服務(總共3個服務)
     * <p>
     * total=0,默認=0,如果=5,指向下一個服務節點
     * index=0,默認=0,如果total=5,index+1
     */
    private int total = 0;//被調用的次數
    private int currentIndex = 0;//當前是誰在提供服務

    //@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;
        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();//獲得當前活着的服務
            List<Server> allList = lb.getAllServers();//獲取所有的服務
            int serverCount = allList.size();
            if (serverCount == 0) {
                /*
                 * No servers. End regardless of pass, because subsequent passes
                 * only get more restrictive.
                 */
                return null;
            }
            //int index = chooseRandomInt(serverCount);//生成區間隨機數
            //server = upList.get(index);//從或活着的服務中,隨機獲取一個
            //=====================自定義代碼=========================
            if (total < 5) {
                server = upList.get(currentIndex);
                total++;
            } else {
                total = 0;
                currentIndex++;
                if (currentIndex > upList.size()) {
                    currentIndex = 0;
                }
                server = upList.get(currentIndex);//從活着的服務中,獲取指定的服務來進行操作
            }
            //======================================================
            if (server == null) {
                /*
                 * The only time this should happen is if the server list were
                 * somehow trimmed. This is a transient condition. Retry after
                 * yielding.
                 */
                Thread.yield();
                continue;
            }
            if (server.isAlive()) {
                return (server);
            }
            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }
        return server;
    }

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

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

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // TODO Auto-generated method stub
    }
}

啓動類添加註解

@SpringBootApplication
@EnableEurekaClient //開啓Eureka 客戶端
//在微服務啓動的時候就能加載自定義的Ribbon類(自定義的規則會覆蓋原有默認的規則)
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT", configuration = RuleConfig.class)//開啓負載均衡,並指定自定義的規則
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class, args);
    }
}

訪問:http://127.0.0.1/consumer/dept/list 完成測試

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

Feign簡介

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

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

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

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

Feign能幹什麼?

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

Feign默認集成了Ribbon

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

子項目springcloud-api增加DeptClientService類

// 微服務客戶端註解,value:指定微服務的名字,這樣就可以使Feign客戶端直接找到對應的微服務
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
public interface DeptClientService {
    @GetMapping("/dept/get/{id}")
    Dept queryById(@PathVariable("id") Long id);

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

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

基於springcloud-consumer-dept-80新建子項目springcloud-consumer-dept-feign

刪除自定義rule,使用默認即可

修改DeptConsumerController

@RestController
public class DeptConsumerController {

    @Autowired
    private DeptClientService service;

    // 此方法調用後數據庫確實生成了一條數據,但沒有內容,連接問題??
    @RequestMapping("consumer/dept/add")
    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註解

@SpringBootApplication
@EnableEurekaClient //開啓Eureka 客戶端
// 開啓feign並掃描包
@EnableFeignClients(basePackages = {"com.ykk.springcloud"})
public class FeignDeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(FeignDeptConsumer_80.class, args);
    }
}

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

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

Hystrix:服務熔斷

分佈式系統面臨的問題:複雜分佈式體系結構中的應用程序有數十個依賴關係,每個依賴關係在某些時候將不可避免失敗!

服務雪崩

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

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

我們需要,棄車保帥

什麼是Hystrix?

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

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

Hystrix能幹嘛?

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

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

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

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

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

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

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

服務熔斷

什麼是服務熔斷?

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

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

服務熔斷解決如下問題:

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

示例

基於springcloud-provider-dept–8001新建子項目springcloud-provider-dept-hystrix-8001

pom添加依賴

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

修改DeptController

@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;
    /**
     * 根據id查詢部門信息
     * 如果根據id查詢出現異常,則走hystrixGet這段備選代碼
     * @param id
     * @return
     */
    @HystrixCommand(fallbackMethod = "hystrixGet")
    @RequestMapping("/dept/get/{id}")//根據id查詢
    public Dept get(@PathVariable("id") Long id){
        Dept dept = deptService.queryById(id);
        if (dept==null){
            throw new RuntimeException("這個id=>"+id+",不存在該用戶,或信息無法找到~");
        }
        return dept;
    }
    /**
     * 根據id查詢備選方案(熔斷)
     * @param id
     * @return
     */
    public Dept hystrixGet(@PathVariable("id") Long id){
        // lombok註解不生效,儘量手寫實體類方法
        Dept dept = new Dept();
        dept.setDeptno(id);
        dept.setDname("這個id=>"+id+",沒有對應的信息,null---@Hystrix~");
        dept.setDb_source("在MySQL中沒有這個數據庫");
        return dept;
    }
}

啓動類添加對熔斷的支持註解@EnableCircuitBreaker

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

啓動springcloud-eureka-7001、7002、7003+springcloud-provider-dept-hystrix-8001+springcloud-consumer-dept-feign

訪問:http://127.0.0.1/consumer/dept/get/1可以得到數據,如果訪問不存在的id就會執行hystrixGe方法

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

服務降級

什麼是服務降級?

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

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

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

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

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

服務降級需要考慮的問題

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

自動降級分類

1)超時降級:主要配置好超時時間和超時重試次數和機制,並使用異步機制探測回覆情況

2)失敗次數降級:主要是一些不穩定的api,當失敗調用次數達到一定閥值自動降級,同樣要使用異步機制探測回覆情況

3)故障降級:比如要調用的遠程服務掛掉了(網絡故障、DNS故障、http服務返回錯誤的狀態碼、rpc服務拋出異常),則可以直接降級。降級後的處理方案有:默認值(比如庫存服務掛了,返回默認現貨)、兜底數據(比如廣告掛了,返回提前準備好的一些靜態頁面)、緩存(之前暫存的一些緩存數據)

4)限流降級:秒殺或者搶購一些限購商品時,此時可能會因爲訪問量太大而導致系統崩潰,此時會使用限流來進行限制訪問量,當達到限流閥值,後續請求會被降級;降級後的處理方案可以是:排隊頁面(將用戶導流到排隊頁面等一會重試)、無貨(直接告知用戶沒貨了)、錯誤頁(如活動太火爆了,稍後重試)。

示例

在子項目springcloud-api下的service包中新建降級配置類DeptClientServiceFallBackFactory.java

import com.ykk.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 cause) {
        return new DeptClientService() {
            @Override
            public Dept queryById(Long id) {
                Dept dept = new Dept();
                dept.setDeptno(id);
                dept.setDname("id=>" + id + "沒有對應的信息,客戶端提供了降級的信息,這個服務現在已經被關閉");
                dept.setDb_source("沒有數據~");
                return dept;
            }

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

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

在DeptClientService中指定降級配置類DeptClientServiceFallBackFactory

// fallbackFactory指定降級配置類
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallBackFactory.class)
public interface DeptClientService {
    @GetMapping("/dept/get/{id}")
    Dept queryById(@PathVariable("id") Long id);

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

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

在子項目springcloud-consumer-dept-feign中的YML配置文件開啓降級:

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

啓動springcloud-eureka-7001、7002、7003+springcloud-provider-dept-hystrix-8001+springcloud-consumer-dept-feign

訪問:http://127.0.0.1/consumer/dept/get/1

服務熔斷和降級的區別

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

熔斷,降級,限流

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

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

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

Dashboard 流監控

新建子項目springcloud-consumer-hystrix-dashboard(只是用於啓動啓動監控)

pom依賴

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

yml配置文件

server:
  port: 9001

eureka:
  client:
    # 不註冊eureka,否則一直報連接不到任何服務
    register-with-eureka: false
    fetch-registry: false
    serviceUrl:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

新建啓動類並添加註解

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

在需要監控的模塊啓動類中添加

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

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

啓動springcloud-eureka-7001、7002、7003+springcloud-provider-dept-hystrix-8001+springcloud-consumer-hystrix-dashboard

訪問http://127.0.0.1:9001/hystrix

跳轉後訪問:http://127.0.0.1:8001/dept/get/1即可看到監控信息

Zull路由網關

Zull包含了對請求的路由(用來跳轉的)和過濾兩個最主要功能:

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

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

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

Zuul 能幹嘛?

  • 路由
  • 過濾

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

示例

新建子項目springcloud-zuul

pom依賴

    <dependencies>
        <!--導入zuul依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--Hystrix依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--dashboard依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--Ribbon-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--Eureka-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <!--實體類+web-->
        <dependency>
            <groupId>com.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>

yml配置文件,配置路由可以隱藏原來服務的名稱

server:
  port: 9527
spring:
  application:
    name: springcloud-zuul #微服務名稱
# eureka 註冊中心配置
eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  instance: #實例的id
    instance-id: springcloud-zuul
    prefer-ip-address: true # 顯示ip
info:
  app.name: springcloud-zuul
  company.name: ykk
# zull 路由網關配置
zuul:
  # 路由相關配置
  # 原來訪問路由 eg:http://127.0.0.1:9527/springcloud-provider-dept/dept/get/1
  # zull路由配置後訪問路由 eg:http://127.0.0.1:9527/ykk/mydept/dept/get/1
  routes:
    mydept.serviceId: springcloud-provider-dept # eureka註冊中心的服務提供方路由名稱
    mydept.path: /mydept/** # 將eureka註冊中心的服務提供方路由名稱 改爲自定義路由名稱
  # 不能再使用這個路徑訪問了,*: 忽略,隱藏全部的服務名稱~
  ignored-services: "*"
  # 設置公共的前綴
  prefix: /ykk

創建啓動類ZuulApplication_9527

@SpringBootApplication
@EnableZuulProxy // 開啓Zuul
public class ZuulApplication_9527 {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication_9527.class,args);
    }
}

啓動springcloud-eureka-7001、7002、7003+springcloud-provider-dept-hystrix-8001+ZuulApplication_9527

訪問:http://127.0.0.1:7001/ 監控頁面可以看到已經註冊了

訪問我們用路由設置的路徑

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

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

Spring Cloud Config 分佈式配置

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

概述

分佈式系統面臨的–配置文件問題

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

什麼是SpringCloud config分佈式配置中心?

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

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

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

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

spring cloud config 分佈式配置中心能幹嘛?

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

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

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

示例:服務端連接git配置

gitee創建倉庫並初始化,clone到本地,新建application.yml文件,最後push到倉庫

spring:
  profiles:
    active: dev

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

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

新建子項目springcloud-config-server-3344

pom依賴

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

application.yml配置文件

server:
  port: 3344
spring:
  application:
    name: springcloud-config-server
  # 連接碼雲遠程倉庫
  cloud:
    config:
      server:
        git:
          # 倉庫地址,注意是https的而不是ssh
          uri: https://gitee.com/ak2019/springcloud-config-server.git
          # 通過 config-server可以連接到git,訪問其中的資源以及配置~
# 不加這個配置會報Cannot execute request on any known server 這個錯:連接Eureka服務端地址不對
# 或者直接註釋掉eureka依賴 這裏暫時用不到eureka
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

啓動類開啓註解

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

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

HTTP服務具有以下格式的資源:其實就是訪問地址的各種寫法

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

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

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

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

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

以上都可以訪問的gitee上文件的內容

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

示例:客戶端連接git配置

新建config-client.yml,並且push到gitee倉庫上

spring:
  profiles:
    active: dev

---
server:
  port: 8201
spring:
  profiles: dev
  application:
    name: springcloud-provider-dept

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

---
server:
  port: 8202
spring:
  profiles: test
  application:
    name: springcloud-provider-dept

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

新建子項目springcloud-config-client-3355

pom依賴

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

ConfigClientController測試接口

@RestController
public class ConfigClientController {
    @Value("${spring.application.name}")
    private String applicationName; //獲取微服務名稱
    @Value("${eureka.client.service-url.defaultZone}")
    private String eurekaServer; //獲取Eureka服務
    @Value("${server.port}")
    private String port; //獲取服務端的端口號

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

啓動類

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

啓動springcloud-config-server-3344+springcloud-config-client-3355

訪問:http://localhost:8201/config 即可得到數據

客戶端跟服務端整合案例:

新建config-eureka.yml並push到gitee倉庫

spring:
  profiles:
    active: dev

---
spring:
  profiles: dev
  application:
    name: springcloud-config-eureka

server:
  port: 7001

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

---
spring:
  profiles: test
  application:
    name: springcloud-config-eureka

server:
  port: 7002

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

啓動子項目springcloud-config-server-3344,

訪問http://127.0.0.1:3344/master/config-eureka-dev.yml,返回如上配置即可

基於springcloud-eureka-7001新建子項目springcloud-config-eureka-7001(即複製一個相同的項目)

pom依賴增加添加

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

新建application.yml配置文件

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

新建bootstrap.yml配置文件

# 系統級別的配置
spring:
  cloud:
    config:
      name: config-eureka # 需要從git上讀取yml配置文件的資源名稱,不要後綴
      profile: dev
      label: master
      uri: http://localhost:3344 # 從服務器獲取

啓動服務,訪問:http://127.0.0.1:7001,打開監控頁面即可

新建config-dept.yml並push到gitee倉庫

spring:
  profiles:
    active: dev
---
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:
  profiles: dev
  application:
    name: springcloud-config-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

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

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

---
server:
  port: 8002

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

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

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

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

啓動子項目springcloud-config-server-3344,

訪問http://127.0.0.1:3344/master/config-dept-dev.yml,返回如上配置即可

基於springcloud-provider-dept-8001新建子項目springcloud-config-dept-8001(即複製一個相同的項目)

pom依賴增加添加

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

新建application.yml配置文件

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

新建bootstrap.yml配置文件

# 系統級別的配置
spring:
  cloud:
    config:
      name: config-dept # 需要從git上讀取yml配置文件的資源名稱,不要後綴
      profile: dev
      label: master
      uri: http://localhost:3344 # 從服務器獲取

啓動:springcloud-config-server-3344+pringcloud-config-eureka-7001+springcloud-config-dept-8001

訪問:http://localhost:8001/dept/list 返回數據即可

肝完-----------

感謝老師的教程

學習源碼:https://gitee.com/ak2019/springcloud-learning.git

文檔原地址:https://www.kuangstudy.com/bbs/1374942542566551554

教程地址:https://www.bilibili.com/video/BV1jJ411S7xr?p=10&spm_id_from=pageDriver

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