《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的五大神獸的作用以及使用

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

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

1. Eureka
1.1eureka簡介
Eureka是Springcloud推薦使用的註冊中心,它有什麼用呢,它相當於一個註冊機,服務提供者,服務消費者,網關等都會將自己信息註冊到上面,後面服務消費者就直接從Eureka中去獲取服務提供者的信息來進行調用。
示意圖:
在這裏插入圖片描述
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
在這裏插入圖片描述
如果能看到上面的界面,就說明單機版的Eureka的環境已經搭建成功了。
1.2eureka的集羣環境搭建
既然有了單機版的eureka,爲什麼還要搞集羣呢?
如果只有一個註冊中心服務器,會存在單點故障,所以要集羣。
既然是集羣,那麼毋庸置疑的是可以同時部署多個Euraka,我這裏就以兩個爲例,多個也是一樣的
因爲我是在本地機器上玩兒的,所有在搭建集羣的時候,我還需要做一些處理,就是把等本的ip地址做一下映射,如果不做映射的話,兩個eureka的ip地址都是127.0.0.1,測試的時候就很難看錯效果。如果是在不同的服務器上部署的話,就不用做映射了,直接用真實ip就可以了
做ip映射的話,只需要修改下面一個地方就可以了:
在我們本地C:\Windows\System32\drivers\etc下面的hosts文件,打開它修改一下就可以了。
在這裏插入圖片描述
在這裏插入圖片描述
下面我們就開始來玩兒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
在這裏插入圖片描述
eureka_7002
在這裏插入圖片描述
如果能看到上面的界面,那麼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:
在這裏插入圖片描述
如果能看到這個界面,就說明服務提供者搭建好了。
上面之所以顯示了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
在理想狀態下,一個應用依賴的服務都是健康可用的,我們可以正常的處理所有的請求。如下圖:
在這裏插入圖片描述
但是 當某一個服務出現延遲時,所有的請求都阻塞在依賴的服務Dependency I ,當依賴I 阻塞時,大多數服務器的線程池就出現阻塞(BLOCK),影響整個線上服務的穩定性。在複雜的分佈式架構的應用程序有很多的依賴,都會不可避免地在某些時候失敗。高併發的依賴失敗時如果沒有隔離措施,當前應用服務就有被拖垮的風險,如下圖:
在這裏插入圖片描述
對於上面的問題,我們就需要對依賴做隔離,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。如下圖:
統一配置中心的架構圖
那麼這個統一配置中心有什麼作用呢?
它的作用大概總結一下,有一下5點:
1、集中管理配置文件
2、不同環境不同配置,動態化的更新配置,分環境部署,如dev/test/prod/beta/release
3、運行期間動態調整配置,不在需要在每個服務部署的機器上編寫配置文件,服務會向配置中心統一拉取配置信息來配置自己的信息
4、當配置發生變動時,服務不需要重新啓動即可感知到配置文件的變化並應用新的配置文件
統一配置中心推薦和gitHub集成使用
下面測試代碼來一波:
首先需要在github上傳一個配置文件,這是我的github上的文件,大家可以自己在自己的github上上傳一份來進行測試
在這裏插入圖片描述
服務端:
(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微服務架構的筆記,如有不足之處,還望博友指教

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