天天看點

《SpringCloud微服務架構》學習筆記

一、SpringCloud概述

說到SpringCloud,相信大家都不陌生,它主要是用來管理微服務的,說直白有點,它就是基于SpringBoot實作的一套微服務治理工具包,它并不是一個架構,而是一系列架構的集合,管理各個微服務之間的互相協調、互相調用,最終實作使用者的價值。

在這裡,我覺得我有必要提一下微服務的概念。其實微服務就是一種思想,就是将一個單體項目根據業務的不同進而劃分為一個一個的微服務,這些微服務可以獨立開發,獨立選型(開發語言),獨立測試,獨立運維,獨立部署。微服務之間互相調用,互相協調,最終實作使用者的價值。

其實我們知道,用于管理微服務的架構不僅僅隻有Springcloud,還有我們比較熟悉的dubbo,它也是用于管理微服務架構的。Springcloud和dubbo最大的差別就是調用的方式不同,SpringCloud使用的RestFul調用方式,而Dubbo使用的RPC遠端調用。

前面也說到,Springcloud不是一個架構,而是許多架構的集合,其中極為重要的就是Springcloud的五大神獸

1、Eureka------注冊中心

2、ribbon/feign-----負載均衡

3、hystrix ----- 熔斷器

4、zuul ------網關

5、統一配置中心

這裡有一個Springcloud的示意圖,可以參考一下,下面我會一一的講解Springcloud的五大神獸的作用以及使用

《SpringCloud微服務架構》學習筆記

二、五大神獸的概述與使用

在講解五大神獸之前,我預設大家對SpringBoot都比較熟悉了,因為Springcloud是在SpringBoot的基礎上實作的,所有對SpringBoot的熟練度有一定的要求,如果對SpringBoot不熟悉的,建議可以先去看一下關于SpringBoot的相關知識。

1. Eureka

1.1eureka簡介

Eureka是Springcloud推薦使用的注冊中心,它有什麼用呢,它相當于一個注冊機,服務提供者,服務消費者,網關等都會将自己資訊注冊到上面,後面服務消費者就直接從Eureka中去擷取服務提供者的資訊來進行調用。

示意圖:

《SpringCloud微服務架構》學習筆記

1.2eureka的單機環境搭建

(1)建立一個普通的maven項目

(2)導入依賴包

在這裡插入代碼片<!--springboot支援-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

<!--Eureka服務端支援-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
           

這裡的依賴為Eureka的基礎依賴。如需要要其他的依賴,請根據具體的業務添加

(3)application.yml配置

server:
  port: 7001
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false #是否要注冊到eureka
    fetchRegistry: false #表示是否從Eureka Server擷取注冊資訊
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #單機配置
           

(4)啟動類

@SpringBootApplication
@EnableEurekaServer  //表示Eureka可用
public class EurekaServerApplication_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication_7001.class);
    }
}
           

(5)啟動并通路eureka

《SpringCloud微服務架構》學習筆記

如果能看到上面的界面,就說明單機版的Eureka的環境已經搭建成功了。

1.2eureka的叢集環境搭建

既然有了單機版的eureka,為什麼還要搞叢集呢?

如果隻有一個注冊中心伺服器,會存在單點故障,是以要叢集。

既然是叢集,那麼毋庸置疑的是可以同時部署多個Euraka,我這裡就以兩個為例,多個也是一樣的

因為我是在本地機器上玩兒的,所有在搭建叢集的時候,我還需要做一些處理,就是把等本的ip位址做一下映射,如果不做映射的話,兩個eureka的ip位址都是127.0.0.1,測試的時候就很難看錯效果。如果是在不同的伺服器上部署的話,就不用做映射了,直接用真實ip就可以了

做ip映射的話,隻需要修改下面一個地方就可以了:

在我們本地C:\Windows\System32\drivers\etc下面的hosts檔案,打開它修改一下就可以了。

《SpringCloud微服務架構》學習筆記
《SpringCloud微服務架構》學習筆記

下面我們就開始來玩兒erueka的叢集

叢集的eureka其實和單機版的erueka差不多,pom,主類都是一樣的,唯一不同的就是application.yml的配置有點不同

下面是eureka_7001和eureka_7002的配置檔案内容

eureka_7001:

server:
  port: 7001
eureka:
  instance:
    hostname: eureka-7001.com
  client:
    registerWithEureka: false #是否要注冊到eureka
    fetchRegistry: false #表示是否從Eureka Server擷取注冊資訊
    serviceUrl:
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #單機配置
      defaultZone: http://eureka-7002.com:7002/eureka/ #叢集配置
           

eureka_7002:

server:
  port: 7002
eureka:
  instance:
    hostname: eureka-7002.com
  client:
    registerWithEureka: false #是否要注冊到eureka
    fetchRegistry: false #表示是否從Eureka Server擷取注冊資訊
    serviceUrl:
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #單機配置
      defaultZone: http://eureka-7001.com:7001/eureka/ #叢集配置
           

從上面的兩個配置檔案内容我們可以看出,就是隻有最下面的注冊位址不同。**在叢集的情況下,配檔案中的注冊位址的地方,除了不配自己之外,其他的eureka位址都要配上,多個就用“,”隔開即可。**就如上面的代碼,我現在隻有兩個eureka,在eureka_7001中隻配置了eureka_7002,相反,在eureka_7002中也隻配置了eureka_7001,都沒有配置自己。

配置完成之後,可以将兩個erueka啟動起來,并且通路這兩個eureka,你會發現,在任何一個eureka中可以看到其他的eureka,如下圖:

eureka_7001

《SpringCloud微服務架構》學習筆記

eureka_7002

《SpringCloud微服務架構》學習筆記

如果能看到上面的界面,那麼eureka的叢集環境就搭建好了。

2服務的提供者以及服務消費者的搭建

這裡解釋一下,服務提供者就是暴露接口的服務,服務消費者就是調用接口的服務

2.1服務的提供者搭建:

(1)建立一個普通的maven項目,名稱為user_provider_8001

(2)導入jar包

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

    <!--eureka用戶端支援 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
           

(3)application.yml的配置

server:
  port: 8001
spring:
  application:
    name: USER-PROVIDER #不要使用下劃線
    #配置資料庫資訊
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
    username: xxxxxx
    password: xxxxxx
    #将服務提供者注冊到注冊中心
eureka:
  client:
    service-url:
#      defaultZone: http://localhost:7001/eureka #單機注冊
      defaultZone: http://eureka-7001.com:7001/eureka,http://eureka-7002.com:7002/eureka #叢集注冊
  instance:
    prefer-ip-address: true #顯示用戶端真實ip
logging:  #顯示日志
  level:
    cn.itsource.springcloud.mapper: debug
           

填寫注冊位址時,有多少個eureka,就要填寫多少個注冊位址

(4)啟動類

@SpringBootApplication
@EnableEurekaClient  //表示為Eureka用戶端
public class UserProviderApplication8001 {
    public static void main(String[] args) {
        SpringApplication.run(UserProviderApplication8001.class);
    }
}

           

啟動類中最重要的@EnableEurekaClient這個注解

當然,既然erueka可以做叢集部署多個,服務提供者同樣是可以做叢集的,同樣的一個服務提工作也可以同時部署多份在服務其上面,可以解決單點故障的問題,也可以滿足負載均衡調用,負載均衡調用我們在後面會講到,在這裡就不做概述。

将上面的配置都配置好後,将erueka啟動起來,然後将服務提供和也啟動起來,通路eureka:

《SpringCloud微服務架構》學習筆記

如果能看到這個界面,就說明服務提供者搭建好了。

上面之是以顯示了8001和8002,是因為我啟動兩個服務提供者。

2.2服務的消費者搭建:

我們先來玩兒一個比較老的技術,就是不通過Eureka,直接進行遠端調用,雖然這玩意兒比較老,還是可以記錄一下,以防以後需要維護一些老的使用了遠端調用的,但是并沒有使用springcloud的項目,就可以用的上了。

我們先來建立服務的消費者,因為這裡我們不是Springcloud的eureka來注冊,是以現在所建立的服務消費者其實就是一個普普通通的Springboot項目,如下:

(1)建立一個普通的maven項目,名稱為user_consumer_9001

(2)導入jar包

<!--springboot支援-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
           

什麼依賴都不要,就隻要這兩個,但是有業務上的依賴,該加的還是得加

(3)application.yml的配置

server:
  port: 9001
spring:
  application:
    name: USER-CONSUMER
  #配置資料庫資訊
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
    username: xxxxx
    password: xxxxx
           

(4)啟動類

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

現在服務的消費者建立好了,服務的提供者上面也建立好了,controller裡面的業務代碼請自行編寫。下面我們就來實作不通過eureka的方式來進行遠端調用。

我們可以使用RestTemplate來進行遠端調用,隻需要兩部,很簡單,步驟如下:

(1)在服務的消費者下面自定義一個類,這類的一定要和入口類平級或者在入口類的子包裡面,因為啟動入口類在啟動的時候會自動的去掃描我們寫的這個自定義類。自定義類的類名可以随便寫,但是裡面的内容是固定的,代碼如下:

@Configuration  //表示交給spring管理,進而建立一個ConfigBean的bean
public class ConfigBean {
    @Bean
    public RestTemplate getTemplate(){
        return new RestTemplate();
    }
}
           

注意:@Configuration注解不能少,如果不加這個注解,Spring是管理不到這個類的。

從代碼中我們可以看到,其實就是擷取一個RestTemplate的執行個體。

現在自定義類編寫完畢,接下來就可以在消費者的Controller中調用服務了,代碼如下:

@RestController
@RequestMapping("/consumer")
public class UserController {
    /**
     * 通過原始的方式,即通過RestTemplate來進行遠端調用,不通過Eureka
     */
    //多個方法調用隻需改一處就ok
    public static  final String URL_PREFIX = "http://localhost:8001";
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/getUserById")
    public User getUserById(@RequestParam Integer id){
        User user = restTemplate.getForObject(URL_PREFIX+"/provider/getUserById?id="+id, User.class);
        return user;
    }
}
           

稍微的解釋一下上面的代碼,就是注入一個RsetTemplate,然後通過調用getForObject方法來擷取資料。getForObject方法中有兩個參數,第一個參數是url,即需要調用服務提供者接口的url,第二個參數傳回值的class

代碼編寫完成隻用,就可以啟動服務提供者和服務消費者。直接通路服務消費者的接口位址,就可以擷取到服務提供者中的資料了。

以上這種方式是沒有通過Springcloud的eureka來實作的,下面,我們開始講解通過eureka來實作遠端調用,即五大神獸之二的負債均衡調用ribbon/feign的使用

3、負債均衡ribbon/feign

為了提供并發量,有時同一個服務提供者可以部署多個。這個用戶端在調用時要根據一定的負責均衡政策完成負載調用。

Springcloud的負載均衡有兩種技術,ribbon和feign。下面來一一的講解

3.1Ribbon

其實ribbon的底層還是使用的RestTemplate來實作的。

服務的提供者不需要做任何的改變,就使用我們上面建立好的就行了,我們隻需要改變服務的消費者就可以了,現在我們重新來建立一個消費者

直接搞代碼:

(1)建立一個普通的maven項目,名稱為user_consumer_9002

(2)導入依賴

<!--springboot支援-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!--eureka用戶端,服務消費者也要從注冊中心擷取可用服務清單-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--用戶端負載均衡實作 ribbon-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>

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

每一個依賴都是有注釋的

(3)application.yml的配置:

server:
  port: 9002
spring:
  #配置資料庫資訊
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
    username: xxxxx
    password: xxxxx
eureka:
  client:
    registerWithEureka: false #不注冊到Eureka,不在注冊中心顯示
    service-url:
      #defaultZone: http://localhost:7001/eureka  #單機配置
      defaultZone: http://eureka-7001.com:7001/eureka,http://eureka-7002.com:7002/eureka #叢集配置
           

因為現在要通過eureka來進行遠端調用,那麼服務的消費者也需要注冊到eureka中

(4)啟動類

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

           

(5)進行負載均衡調用

這時需要服務消費者中自定一個類,類名可以随便寫,但是位置必須和啟動類平級或者在啟動類的子包裡面。類中的代碼如下:

@Configuration   //表示将該類交給Spring管理
public class ConfigBean {
    @Bean
    @LoadBalanced  //開啟負載均衡
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
//    修改負載均衡政策,預設是輪詢
    @Bean
    public IRule myRule(){
        return new RandomRule();//通過随機算法替代輪詢
    }
}
           

咋眼一看,怎麼和之前的RestTemplate的那個配置類那麼相似呢。沒錯,是相似但不相同,仔細一看,在getRestTemplate方法上多了一個@LoadBalanced 注解,這個注解就是用來開啟負載均衡的。

下面還有一個方法myRule,這個方法的作用就是對負載均衡的政策進行設定的,預設的輪詢調用。還有幾種政策,可以去網上查一下,這裡就不做過多的講解。

配置類編寫完畢之後哦,下面就可以在消費者的Controller中進行遠端調用了,業務調用代碼如下:

@RestController
@RequestMapping("/consumer")
public class UserController {

    /**
     * 通過Ribbon來實作負載均衡調用
     */
    //多個方法調用隻需改一處就ok
    public static  final String URL_PREFIX = "http://USER-PROVIDER";
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/getUserById")
    public User getUserById(@RequestParam Integer id){
        User user = restTemplate.getForObject(URL_PREFIX + "/provider/getUserById?id=" + id, User.class);
        return user;
    }
}
           

因為現在是通過SpringCloud的ribbon來進行調用的,所有就和上面隻用使用RestTemplate調用有所不同。從上面的代碼可以看到,服務提供者的接口位址已經不是使用的ip加端口号了,因為是負載均衡調用,同一個服務提供者可能會部署多份,但是多個相同的服務提供者的服務名是一樣的,是以這裡就需要拼接服務提供者的服務名和接口名來進行調用。

現在代碼就編寫完了,這是啟動eureka,服務消費者,服務提供者。最後通路服務消費者的接口位址,就能通路到服務提供者的資料了。這裡我就不示範了,自己可以去測試一下。

現在功能雖然是實作了,但是還有一個問題,就是在調用服務提供者接口的時候,服務提供者需要的參數都是拼在url後面的,如果參數個數少,還可以接受,如果參數有100個乃至更多,就很惡心了,所有我們一般不用ribbon來做負載均衡,我們會選擇使用SpringCloud的第二種負載均衡技術feign來實作,當然,這還是要根據業務的需求來定。下面我們來看看feign的具體使用。

3.2feign

上面我們已經說了Ribbon的缺點就是在參數太多的時候拼接參數時很不爽,如果使用feign的話,就不會出現這個問題了,因為feign調用的時候不是通過拼接url和參數來實作的,而是通過接口的方式來實作的,這樣的話不管服務提供者需要多少參數,消費者在調用的時候直接将需要的參數傳過去就完事兒。下面直接搞代碼,我們需要重新建立一個服務的消費者user_consumer_9003,友善測試。

(1)建立一個普通的maven項目user_consumer_9003

(2)導入jar包

<!--springboot支援-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!--eureka用戶端,服務消費者也要從注冊中心擷取可用服務清單-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--feign的支援-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
           

(3)aplication.yml配置

server:
  port: 9003
eureka:
  client:
    registerWithEureka: false #不注冊到Eureka,不在注冊中心顯示
    service-url:
      #defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka-7001.com:7001/eureka,http://eureka-7002.com:7002/eureka
spring:
  #配置資料庫資訊
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
    username: xxxxx
    password: xxxxx
           

(4)入口類

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"cn.itsource.springcloud"})
public class UserConsumerFeignApplication_9003 {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumerFeignApplication_9003.class);
    }
}
           

因為使用feign的方式來進行調用時,也需要在消費者建立一個feign的接口,所有我們需要在入口類上面打上@EnableFeignClients注解,并且在後面配置feign接口的全包名。

下面建立一個feign接口,該接口名可以随便寫,但是位置必須和入口類平級或者在入口類的子包裡面。

接口代碼:

@FeignClient("USER-PROVIDER")
public interface UserFeign {
    @GetMapping("/provider/getAllUsers")
     List<User> getAllUsers();
}
           

注意:

1、@FeignClient注解一定不要忘記,後面需要配置需要調用的服務提供者的服務名。

2、接口的方法名和傳回值類型需要和需要調用的接口的方法名和傳回值類型一緻,請求方式也必須一緻,請求路徑為需要調用的服務提供者接口位址。

3、如果接口需要參數,那麼參數前面需要加上@RequestParam注解,并且括号類需要寫入參數名稱,如:User getUserById(@RequestParam(“id”) Integer id);這一點要切記啊,因為本人在這個坑裡面跳了幾個小時,就因為括号裡面沒有寫參數名稱

接口寫好以後,接下來就可以在Controller中調用服務了

代碼如下:

@RestController
@RequestMapping("/consumer")
public class UserController {
    @Autowired
    private UserFeign userFeign;

    @GetMapping("/getAllUsers")
    public List<User> getAllUsers() {
        List<User> allUsers = userFeign.getAllUsers();
        return allUsers;
    }
}
           

就是注入我們剛才寫的那個feign接口,然後使用注入進來的執行個體來調用接口中的方法,這樣直接通路服務消費者的接口,就可以獲得服務提供者的資料了。

4.熔斷器Hystrix

在理想狀态下,一個應用依賴的服務都是健康可用的,我們可以正常的處理所有的請求。如下圖:

《SpringCloud微服務架構》學習筆記

但是 當某一個服務出現延遲時,所有的請求都阻塞在依賴的服務Dependency I ,當依賴I 阻塞時,大多數伺服器的線程池就出現阻塞(BLOCK),影響整個線上服務的穩定性。在複雜的分布式架構的應用程式有很多的依賴,都會不可避免地在某些時候失敗。高并發的依賴失敗時如果沒有隔離措施,目前應用服務就有被拖垮的風險,如下圖:

《SpringCloud微服務架構》學習筆記

對于上面的問題,我們就需要對依賴做隔離,Hystrix就是處理依賴隔離的架構,同時也是可以幫我們做依賴服務的治理和監控.。

4.1Hystrix簡介

Hystrix是國外知名的視訊網站Netflix所開源的非常流行的高可用架構架構。Hystrix能夠完美的解決分布式系統架構中打造高可用服務面臨的一系列技術難題。

Hystrix是保證微服務群健壯架構,做了隔離,熔斷,降級等操作.最終達到不會由于某一個服務出問題而導緻雪崩現象,讓整體群死掉.

Hystrix “豪豬”,具有自我保護的能力。hystrix 通過如下機制來解決雪崩效應問題。

**資源隔離(限流):**包括線程池隔離和信号量隔離,限制調用分布式服務的資源使用,某一個調用的服務出現問題不會影響其他服務調用。

**融斷:**當失敗率達到閥值自動觸發降級(如因網絡故障/逾時造成的失敗率高),熔斷器觸發的快速失敗會進行快速恢複。

**降級機制:**逾時降級、資源不足時(線程或信号量)降級,降級後可以配合降級接口傳回托底資料。

**緩存:**提供了請求緩存、請求合并實作。

4.2服務的熔斷:

正常情況下,斷路器處于關閉狀态,如果調用持續出錯或者逾時,電路被打開進入熔斷狀态(Open),後續一段時間内的所有調用都會被拒絕,一段時間以後,保護器會嘗試進入半熔斷狀态(Half-Open),允許少量請求進來嘗試,如果調用仍然失敗,則回到熔斷狀态,如果調用成功,則回到電路閉合狀态。

熔斷的參數配置

Hystrix提供了如下的幾個關鍵參數,來對一個熔斷器進行配置:

circuitBreaker.requestVolumeThreshold //滑動視窗的大小,預設為20

circuitBreaker.sleepWindowInMilliseconds //過多長時間,熔斷器再次檢測是否開啟,預設為5000,即5s鐘

circuitBreaker.errorThresholdPercentage //錯誤率,預設50%

3個參數放在一起,所表達的意思就是:

每當20個請求中,有50%失敗時,熔斷器就會打開,此時再調用此服務,将會直接傳回失敗,不再調遠端服務。直到5s鐘之後,重新檢測該觸發條件,判斷是否把熔斷器關閉,或者繼續打開。

4.3Hystrix的實作

我們通過上面的知識已經知道,服務的負載均衡調用有兩種方式,ribbon和feign,如果要使用Hystrix來實作熔斷,就隻能在ribbon下面使用。feign有自己的熔斷機制,但是底層還是使用的Hystrix。下面就來學習一下分别在ribbon和feign下面如何來實作熔斷功能吧

4.3.1在ribbon下面實作熔斷機制

在ribbon下面實作熔斷機制的話,我們就需要在服務的提供者上面做配置,然後在接口後面定義一個多托底方法,當真正的接口調用失敗的時候,就會自動的調用這個托底方法,從何擷取托底資料。下面我們通過代碼來實作一波:

(1)建立一個服務的消費者和一個服務提供者。服務的消費者就可以使用我們上面已經內建了ribbon的user_consumer_9002這個項目即可,隻要建立一個普通的Maven項目來作為服務的提供者,名為user_provider_hystrix_8003

(3)application.yml檔案的配置

server:
  port: 8003
spring:
  application:
    name: USER-PROVIDER #不要使用下劃線
    #配置資料庫資訊
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
    username: xxxxxx
    password: xxxxxx
    #将服務提供者注冊到注冊中心
eureka:
  client:
    service-url:
#      defaultZone: http://localhost:7001/eureka #告訴服務提供者要把服務注冊到哪兒
      defaultZone: http://eureka-7001.com:7001/eureka,http://eureka-7002.com:7002/eureka #告訴服務提供者要把服務注冊到哪兒
  instance:
    prefer-ip-address: true #顯示用戶端真實ip
logging:
  level:
    cn.itsource.springcloud.mapper: debug
           

(3)導入jar包

<!--        公共的依賴-->
        <dependency>
            <groupId>cn.itsource.springcloud</groupId>
            <artifactId>user_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--        SpringBoot的基本配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!--eureka用戶端支援 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--斷路器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

           

(4)入口類

@SpringBootApplication
@EnableEurekaClient
@EnableHystrix //支援熔斷器
public class UserProviderHystrixApplication_8003 {
    public static void main(String[] args) {
        SpringApplication.run(UserProviderHystrixApplication_8003.class);
    }
}

           

(5)隻需要在服務提提供者的controller中的方法下面自定義一個方法即可,代碼如下:

@RestController
@RequestMapping("/provider")
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @GetMapping("/getUserById")
    @HystrixCommand(fallbackMethod = "failGet")
    public User getUserById(@RequestParam Integer id){
        User user = userMapper.selectById(id);
        return user;
    }

//    托底方法
    public User failGet(Integer id){
        User user = new User();
        user.setId(new Long(id));
        user.setName("請聯系管理者");
        return user;
    }
}
           

需要強調的是,在接口上面方法上面需要添加一個@HystrixCommand注解,後面需要配置上下面托底方法的方法名。

對于托底方法,參數必須和接口的參數保持一緻

4.3.2在feign下面實作熔斷機制

從上面ribbon的熔斷實作上可以看出,雖然實作了熔斷,但是又一個問題,那就是如果服務提供者的controller中的接口方法太多,如果每一個接口方法都要對應一個托底方法的話,controller中的代碼就會要邊寫大量的代碼,不僅可讀性不好,并且維護性也不好

為了解決riibbon實作熔斷時的不足,我們就可以使用feign來實作熔斷,剛才說過,feign有一套自己的熔斷機制,雖然底層也是通過Hystrix來實作的,但是我們不用直接去使用Hystrix。

feign實作熔斷機制的原理其實就是使用spring面向切面程式設計,為feign的接口建立一個代理對象,完成對服務調用,當發現熔斷後就調用同名托底方法.

使用feign來實作熔斷機制,我們需要在服務的消費方做配置,代碼如下:

(1)建立一個新的服務消費者,名稱為user_consumer_feign_hystrix_9004

(2)導入jar包

<dependency>
            <groupId>cn.itsource.springcloud</groupId>
            <artifactId>user_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--springboot支援-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!--eureka用戶端,服務消費者也要從注冊中心擷取可用服務清單-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--feign的支援-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
           

(3)application.yml檔案的配置

server:
  port: 9004
eureka:
  client:
    registerWithEureka: false #不注冊到Eureka,不在注冊中心顯示
    service-url:
      #defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka-7001.com:7001/eureka,http://eureka-7002.com:7002/eureka

#開啟熔斷器支援以及設定逾時時間
feign:
  hystrix:
    enabled: true #開啟熔斷支援
  client:
    config:
      remote-service:           #服務名,填寫default為所有服務
        connectTimeout: 3000
        readTimeout: 3000
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
spring:
  #配置資料庫資訊
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8
    username: root
    password: root
           

使用feign實作熔斷時,需要在配置檔案中加入開啟熔斷器支援以及設定逾時時間

(4)入口類

@SpringBootApplication(scanBasePackages ="cn.itsource.springcloud")
@EnableEurekaClient
@EnableFeignClients(basePackages = {"cn.itsource.springcloud"})
public class UserConsumerFeignHystrixApplication_9004 {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumerFeignHystrixApplication_9004.class);
    }
}
           

(5)添加feign的配置接口

在入口類平級或者子包裡面添加一個feign的配置類

@FeignClient(value = "USER-PROVIDER",fallbackFactory = UserFeignHystrixFallbackFactory.class)
public interface UserFeign {

    @GetMapping("/provider/getUserById")
    User getUserById(@RequestParam("id") Integer id);
    @GetMapping("/provider/getUser")
    User getUser();
}
           

這個配置類其實和上面通過feign來做負載均衡的配置類差不多,不過有一點不同,那就是在@FeignClient注解後面不僅要配置服務提供者的服務名,還需要配置我們即将要建立的托底代理類的class.

(6)建立托底代理類

此類需要實作一個FallbackFactory接口,泛型就為上面建立的那個配置類。并且這個托底代理類的類名後半部分需要用FallbackFactory結尾,前面可以随便寫。代碼如下:

@Component
public class UserFeignHystrixFallbackFactory implements FallbackFactory<UserFeign> {
    public UserFeign create(Throwable throwable) {
        return new UserFeign() {
            public User getUserById(Integer id) {
                User user = new User();
                user.setId(new Long(id));
                user.setName("出問題了,請聯系管理者");
                return user;
            }
        };
    }
}
           

這個代理類需要做一下解釋:

1、當我們實作了FallbackFactory接口時,會有一個實作方法,傳回值就是我們編寫的那個配置類,那麼我們在方法裡面就直接new一個配置類,然後在new的那個配置類裡面寫我們的托底業務即可

2、托底代理類上面的@Component一定不要忘記加上

代碼編寫完之後,我們就可進行測試了,當調用某一個配置接口中的某個方法失敗後,就會到托底代理類裡面去找和所調用的方法相同方法名的托底方法,進而傳回托底資料

可以通過關閉服務提供者或者是在提供者上面打斷點來測試得到結果

5、ZUUL路由網關

微服務架構體系中,通常一個業務系統會有很多的微服務,上面開始也說過,微服務的好處自已就是可以自由選型,不同的服務可以使用不同的語言來開發,但是問題來了, 隻有所用的語言裡面用到了Spring架構,才能注冊到Springcloud的eureka中去進行遠端調用,但是如果某一個服務所用的開發語言是C++,或者是andrio,又或者是python,這些語言壓根兒和Spring沒有什麼關系,那就不能往eureka上面進行注冊。那如果真是這樣,是不是就沒法進行遠端調用了呢。其實不是的,這是我們的zuul路由網關就派上用場了。

當所有的服務都是使用java語言來開發的話,就可以不用考慮zuul網關這個東西了,因為它們都可以直接注冊到eureka中來進行遠端調用。

當選用和Spring沒有關系的語言來開發服務,雖然不能直接往eureka上注冊,但是我們可以将zuul注冊到eureka上面,然後這些服務就可以通過zuul來轉發請求,進而調用服務。

是以zuul最大的一個作用就是可以統一入口,所有的服務消費者都可以通過zuul來實作遠端調用。而且zuul的内部是已經封裝好負載均衡的,隻要通過zuul來進行遠端調用,預設就會使用負載均衡調用。

下面就來實作一波代碼,代碼如下:

(1)建立一個新工程,名為zuul_gateway_9527

(2)導入jar包

<!--        SpringBoot的基本配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!--eureka用戶端支援 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
<!--網關的依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
           

(3)application.yal檔案的配置

server:
  port: 9527
spring:
  application:
    name: MICROSERVICE-ZUUL-GATEWAY
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    instance-id: gateway-9527.com   #在注冊中心顯示的名字
    prefer-ip-address: true  #是否顯示ip
zuul:
  routes:
    myUser.serviceId: user-provider  #服務提供者的服務名
    myUser.path: /user/**  #所有以user開頭的通路位址都将user映射到user-provider,防止服務名暴露
  ignored-services: "*"    #此配置的作用是限制所有的myUer.serviceId都不能通路,隻有用myUser.path才能通路。保證安全
  prefix: /services  #可加可不加,主要作用就是限制在通路路徑前面必須加想這段内容,保證通路更加安全。

           

該配置檔案最重要的地方就是zuul下面的這一坨配置,解釋一下:

1、 myUser.serviceId後面配置的是服務提供者的服務名,因為zuul是做了負載均衡調用的,負載均衡調用是通過服務提供者的服務名來确定服務的

2、myUser.path: /user/**:所有調用時需要通過上面所配置的服務名打頭的接口位址,都可以用user來替換打頭進行調用,user會直接映射到user-provider上面。這麼可以使得服務名不會暴露

3、ignored-services: “*”,做了這個配置的話,如果是用服務名打頭的url是通路不了的,隻能通過myUser.path後面所配置的形式進行調用,可以保證更加的安全

4、prefix: /services,字首,就是在調用的時候,必須在url的最前面加上所配置的内容才能進行正常通路。

(4)入口類

@SpringBootApplication
@EnableZuulProxy  //表示開啟網關
public class ZuulGatewayApplication_9527 {
    public static void main(String[] args) {
        SpringApplication.run(ZuulGatewayApplication_9527.class);
    }
}

           

@EnableZuulProxy一定不要忘記加上,這個表示開啟網關

将以上的四部配置完畢後就可以進行測試了。

這裡需要提一點,上面也提到過,如果所有的服務都是使用java開發的話,那麼就可以直接往eureka中進行注冊來進行遠端調用,雖然也可以通過zuul來進行調用,但是通過zuul調用會多一次請求的轉發,調用會有一點的延時。如果其中有些服務是通過C++等一些與Spring無關的語言開發的,又要調用通過java開發的服務提供者的接口,那麼就需要用到Zuul。

其實zuul路由網關的作用遠不止隻用來做入口,它還可以做登入攔截等其他操作,隻是本人值玩兒了這一個功能,這裡就隻記錄了這一個,等後面玩兒了其他的再回來添加。大家也可以上ZUUL的官網進行學習更全面的知識。

6、統一配置中心

在分布式系統中,由于服務數量巨多,為了友善服務配置檔案統一管理,實時更新,是以需要分布式配置中心元件。在Spring Cloud中,有分布式配置中心元件spring cloud config ,它支援配置服務放在配置服務的記憶體中(即本地),也支援放在遠端Git倉庫中。在spring cloud config 元件中,分兩個角色,一是config server,二是config client。如下圖:

《SpringCloud微服務架構》學習筆記

那麼這個統一配置中心有什麼作用呢?

它的作用大概總結一下,有一下5點:

1、集中管理配置檔案

2、不同環境不同配置,動态化的更新配置,分環境部署,如dev/test/prod/beta/release

3、運作期間動态調整配置,不在需要在每個服務部署的機器上編寫配置檔案,服務會向配置中心統一拉取配置資訊來配置自己的資訊

4、當配置發生變動時,服務不需要重新啟動即可感覺到配置檔案的變化并應用新的配置檔案

統一配置中心推薦和gitHub內建使用

下面測試代碼來一波:

首先需要在github上傳一個配置檔案,這是我的github上的檔案,大家可以自己在自己的github上上傳一份來進行測試

《SpringCloud微服務架構》學習筆記

服務端:

(1)建立一個項目

(2)導入jar包

<dependencies>
    <!--springboot支援-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <!--eureka用戶端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--配置中心支援-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
      </dependency>
</dependencies>
           

(3)application.yml配置檔案

server:
  port: 1299
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    prefer-ip-address: true
spring:
  application:
    name: spring-cloud-config-server
  cloud:
    config:
      server:
        git:
          uri: 配置檔案在github上的git位址
          username:git的使用者名
          password: git的密碼
           

(4)啟動類

@SpringBootApplication
@EnableEurekaClient //加入注冊中心
@EnableConfigServer //啟用配置服務端
public class ConfigServerApplication_1299 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication_1299.class);
    }
}
           

配置完畢後,可以通過http://localhost:1299/application-user/test來進行測試,改位址表示找到 application-user這個項目profiles為test的檔案

用戶端的配置(修改上面任何一個服務消費者或者服務提供者即可):

(1)建立一個maven項目,名稱為config_client_3355

(2)導入jar包

<!--配置中心支援-->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
           

(3)準備yml配置檔案

使用bootstrap.yml,因為bootstrap.yml是全局的,application.yml是局部的,我們使用全局的

spring:
  cloud:
    config:
      name: application-user #github上面名稱
      profile: test #環境
      label: master #分支
      uri: http://127.0.0.1:1299 #配置伺服器
eureka:
  client:
    service-url:
     defaultZone: http://localhost:7001/eureka  #告訴服務提供者要把服務注冊到哪兒 #單機環境
  instance:
    prefer-ip-address: true #顯示用戶端真實ip
           

(4)測試,啟動eureka,服務端和用戶端,看idea的控制台顯示的端口,以及eureka上面的注冊的名字

這裡要說一下,雖然可以将服務的配置放在github上面進行統一管理,但是eureka和服務端的配置檔案還是要在項目裡面配置,不能放在github上面進行統一配置,因為需要服務端啟動起來後,用戶端才能通過服務端到github上去擷取配置資訊。是以服務端的配置檔案不能放在gitHub上進行管理。

同理,服務端和用戶端首先需要注冊到eureka上面才能進行調用,是以要啟動服務端和用戶端,就必須先要啟動eureka,是以eureka的配置檔案也不能放在github上面進行管理。

以上就是本人學習SpringCloud微服務架構的筆記,如有不足之處,還望博友指教

繼續閱讀