微服務(wù)-04-SpringCloud

1. Spring Cloud簡(jiǎn)介

Spring Cloud只是將各家公司開(kāi)發(fā)的比較成熟瘟滨、經(jīng)得起實(shí)際考驗(yàn)的服務(wù)框架組合起來(lái)翠储,通過(guò)Spring Boot風(fēng)格進(jìn)行再封裝,屏蔽掉了復(fù)雜的配置和實(shí)現(xiàn)原理,最終給開(kāi)發(fā)者留出了一套簡(jiǎn)單易懂、易部署和易維護(hù)的分布式系統(tǒng)開(kāi)發(fā)工具包饺鹃。

Spring Cloud官網(wǎng):https://spring.io/projects/spring-cloud/

Spring Cloud的子項(xiàng)目,大致可分成兩類:

  1. 一類是對(duì)現(xiàn)有成熟框架“Spring Boot化”的封裝和抽象
  2. 另一類是開(kāi)發(fā)了一部分分布式系統(tǒng)的基礎(chǔ)設(shè)施的實(shí)現(xiàn)

常見(jiàn)的遠(yuǎn)程調(diào)用方式有RPC和HTTP:

  1. RPC特點(diǎn):基于Socket、自定義數(shù)據(jù)格式悔详、速度快
  2. HTTP特點(diǎn):基于TCP/IP镊屎、規(guī)定數(shù)據(jù)傳輸格式、消息封裝比較臃腫伟端、傳輸速度比較慢杯道、對(duì)服務(wù)提供和調(diào)用方式?jīng)]有任何技術(shù)限定匪煌,自由靈活责蝠,更符合微服務(wù)理念
  • 微服務(wù)更加強(qiáng)調(diào)的是獨(dú)立、自治萎庭、靈活霜医,而RPC方式的限制較多,因此微服務(wù)框架中一般都會(huì)采用基于HTTP的Rest風(fēng)格服務(wù)

2. Spring Cloud Netflix Eureka

Eureka是一個(gè)基于REST的服務(wù)驳规,主要用于定位運(yùn)行在AWS域中的中間層服務(wù)肴敛,以達(dá)到負(fù)載均衡和中間層服務(wù)故障轉(zhuǎn)移的目的。

Eureka包含兩個(gè)組件:Eureka Server和Eureka Client

  1. Eureka Server提供服務(wù)注冊(cè)服務(wù)吗购,各個(gè)節(jié)點(diǎn)啟動(dòng)后医男,會(huì)在Eureka Server中進(jìn)行注冊(cè),這樣Eureka Server中的服務(wù)注冊(cè)表中將會(huì)存儲(chǔ)所有可用服務(wù)節(jié)點(diǎn)的信息捻勉,服務(wù)節(jié)點(diǎn)的信息可以在界面中直觀的看到
  2. Eureka Client是一個(gè)java客戶端镀梭,用于簡(jiǎn)化與Eureka Server的交互,客戶端同時(shí)也就是一個(gè)內(nèi)置的踱启、使用輪詢(round-robin)負(fù)載算法的負(fù)載均衡器

在應(yīng)用啟動(dòng)后报账,將會(huì)向Eureka Server發(fā)送心跳,默認(rèn)周期為30秒埠偿,如果Eureka Server在多個(gè)心跳周期內(nèi)沒(méi)有接收到某個(gè)節(jié)點(diǎn)的心跳透罢,Eureka Server將會(huì)從服務(wù)注冊(cè)表中把這個(gè)服務(wù)節(jié)點(diǎn)移除(默認(rèn)90秒)。

Eureka Server之間通過(guò)復(fù)制的方式完成數(shù)據(jù)的同步冠蒋,Eureka還提供了客戶端緩存機(jī)制羽圃,即使所有的Eureka Server都掛掉,客戶端依然可以利用緩存中的信息消費(fèi)其他服務(wù)的API抖剿。

Eureka中的三個(gè)核心角色:

  1. 服務(wù)注冊(cè)中心:Eureka Server端统屈,提供服務(wù)注冊(cè)發(fā)現(xiàn)功能
  2. 服務(wù)提供者:Eureka Client端,向注冊(cè)中心注冊(cè)服務(wù)牙躺,對(duì)外提供Rest風(fēng)格服務(wù)
  3. 服務(wù)消費(fèi)者:Eureka Client端愁憔,從注冊(cè)中心發(fā)現(xiàn)服務(wù)

2.1 創(chuàng)建父工程

創(chuàng)建一個(gè)Maven父工程來(lái)管理依賴:

<packaging>pom</packaging>
<!--Spring Boot版本-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
</parent>
<!--Spring Cloud版本-->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2.2 創(chuàng)建eureka-server子工程

eureka-server本身就是一個(gè)微服務(wù)

eureka-server會(huì)把注冊(cè)來(lái)的服務(wù)信息保存在Map中

  1. 添加eureka-server的起步依賴:

    <dependencies>
        <!--eureka-server的起步依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>
    
  2. 創(chuàng)建啟動(dòng)類,并添加@EnableEurekaServer注解:

    package com.lhp;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    // 啟用EurekaServer
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(EurekaServerApplication.class, args);
        }
    }
    
  3. 在application.yml中配置:

    server:
      port: 7000
    eureka:
      client:
        # 是否將自己注冊(cè)到eureka-server中
        register-with-eureka: false
        # 是否從eureka-server中獲取服務(wù)信息
        fetch-registry: false
        service-url:
          # eureka-server的注冊(cè)地址
          defaultZone: http://localhost:7000/eureka
      server:
        # 每隔5秒檢查一次心跳孽拷,剔除沒(méi)有續(xù)約的失效服務(wù)
        eviction-interval-timer-in-ms: 5000
        # 關(guān)閉自我保護(hù)機(jī)制吨掌;自我保護(hù)模式下不會(huì)剔除任何服務(wù)實(shí)例
        enable-self-preservation: false
    spring:
      application:
        # 應(yīng)用名,會(huì)在eureka中作為serviceId
        name: eureka-server
    
  4. 啟動(dòng),瀏覽器訪問(wèn)查看:http://localhost:7000/

2.3 創(chuàng)建provider子工程

服務(wù)提供者注冊(cè)服務(wù)

服務(wù)在啟動(dòng)時(shí)膜宋,若檢測(cè)到有@EnableDiscoveryClient注解和配置信息窿侈,則會(huì)向注冊(cè)中心發(fā)起注冊(cè)請(qǐng)求,攜帶服務(wù)元數(shù)據(jù)信息(ip秋茫、port等)

服務(wù)注冊(cè)完后史简,服務(wù)提供者會(huì)維持一個(gè)心跳,保存服務(wù)處于存在狀態(tài)肛著,這個(gè)稱之為服務(wù)續(xù)約(renew)

當(dāng)服務(wù)正常關(guān)閉時(shí)會(huì)發(fā)送服務(wù)下線的REST請(qǐng)求給eureka-server圆兵,eureka-server接收到請(qǐng)求后將該服務(wù)置為下線狀態(tài)

  1. 添加eureka-client的起步依賴和web的起步依賴:

    <dependencies>
        <!--lombok的依賴-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--mybatis的起步依賴-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <!--mybatis驅(qū)動(dòng)-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    
        <!--需要添加web的起步依賴,不然client可能注冊(cè)不到eureka-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--eureka-client的起步依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
    
  2. 創(chuàng)建啟動(dòng)類枢贿,并添加@EnableDiscoveryClient注解或@EnableEurekaClient注解:

    package com.lhp;
    
    import org.mybatis.spring.annotation.MapperScan;
    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;
    
    // @EnableDiscoveryClient和@EnableEurekaClient任選其一即可
    // 開(kāi)啟客戶端的發(fā)現(xiàn)功能殉农,且注冊(cè)中心只能是Eureka
    // @EnableEurekaClient
    // 開(kāi)啟客戶端的發(fā)現(xiàn)功能
    @EnableDiscoveryClient
    // MapperScan會(huì)掃描指定包下的所有的接口,然后將接口的代理對(duì)象交給Spring容器
    @MapperScan(basePackages = "com.lhp.dao")
    @SpringBootApplication
    public class ProviderApplication {
        public static void main(String[] args) {
            SpringApplication.run(ProviderApplication.class, args);
        }
    }
    
  3. 在application.yml中配置:

    server:
      port: 7101
    eureka:
      client:
        service-url:
          # eureka-server的注冊(cè)地址
          defaultZone: http://localhost:7000/eureka
        # 獲取eureka-server中的服務(wù)列表(只讀備份)的間隔時(shí)間
        registry-fetch-interval-seconds: 30
      instance:
        # 指定自己的ip
        ip-address: 127.0.0.1
        # 啟用ip進(jìn)行注冊(cè)局荚,而不是hostname
        prefer-ip-address: true
        # 租約過(guò)期時(shí)間超凳,默認(rèn)90seconds
        lease-expiration-duration-in-seconds: 150
        # 租期續(xù)約間隔,默認(rèn)30seconds耀态;服務(wù)超過(guò)30秒沒(méi)有發(fā)生心跳轮傍,eureka-server會(huì)將服務(wù)從列表移除(前提是eureka-server關(guān)閉了自我保護(hù))
        lease-renewal-interval-in-seconds: 30
    spring:
      application:
        # 應(yīng)用名,會(huì)在eureka中作為serviceId
        name: provider
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db01?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8
        username: root
        password: 123456
    mybatis:
      # 配置mybatis映射文件的位置
      mapper-locations: classpath:com/lhp/dao/*Dao.xml
    
  4. 啟動(dòng)首装,瀏覽器訪問(wèn)查看:http://localhost:7000/

2.4 創(chuàng)建consumer子工程

服務(wù)消費(fèi)者發(fā)現(xiàn)服務(wù)

服務(wù)消費(fèi)者啟動(dòng)時(shí)创夜,會(huì)檢測(cè)是否獲取服務(wù)注冊(cè)信息配置;如果是簿盅,則會(huì)從eureka-server服務(wù)列表獲取只讀的備份挥下,緩存到本地

  1. 添加eureka-client的起步依賴和web的起步依賴:

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    
        <!--需要添加web的起步依賴,不然client可能注冊(cè)不到eureka-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--eureka-client的起步依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
    
  2. 創(chuàng)建啟動(dòng)類桨醋,并添加@EnableDiscoveryClient注解或@EnableEurekaClient注解:

    package com.lhp;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    // 開(kāi)啟客戶端的發(fā)現(xiàn)功能
    @EnableDiscoveryClient
    @SpringBootApplication
    public class ConsumerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }
    
        // 將RestTemplate對(duì)象放入到Spring容器
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
  3. 在application.yml中配置:

    server:
      port: 7201
    eureka:
      client:
        service-url:
          # eureka-server的注冊(cè)地址
          defaultZone: http://localhost:7000/eureka
        # 獲取eureka-server中的服務(wù)列表(只讀備份)的間隔時(shí)間
        registry-fetch-interval-seconds: 30
    spring:
      application:
        # 應(yīng)用名棚瘟,會(huì)在eureka中作為serviceId
        name: consumer
    
  4. 啟動(dòng),瀏覽器訪問(wèn)查看:http://localhost:7000/

2.5 consumer通過(guò)eureka-server訪問(wèn)provider

consumer在eureka-server中獲取provider的ip和port:

package com.lhp.controller;

import com.lhp.pojo.User;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RequestMapping("/movie")
public class MovieController {
    // RestTemplate是從Spring3.0開(kāi)始支持的一個(gè)HTTP請(qǐng)求工具喜最,它提供了常見(jiàn)的REST請(qǐng)求方案的模版
    @Autowired
    private RestTemplate restTemplate;
    // DiscoveryClient可以發(fā)現(xiàn)當(dāng)前注冊(cè)中心的服務(wù)
    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/see")
    public Object see() {
        // 獲取指定serviceId的實(shí)例集合
        List<ServiceInstance> instances = discoveryClient.getInstances("provider");
        // 獲取第0個(gè)實(shí)例
        ServiceInstance instance = instances.get(0);
        // 獲取實(shí)例的ip和port
        String instanceUrl = "http://" + instance.getHost() + ":" + instance.getPort() + "/user/1";

        // http://192.168.219.224:7001/user/1
        System.out.println(instanceUrl);
        User user = restTemplate.getForObject(instanceUrl, User.class);
        return user;
    }
}

3. Spring Cloud Netflix Ribbon

Ribbon是客戶端負(fù)載均衡器偎蘸,其負(fù)載均衡策略有(IRule實(shí)現(xiàn)類):

  1. com.netflix.loadbalancer.RoundRobinRule:輪詢
  2. com.netflix.loadbalancer.AvailabilityFilteringRule:根據(jù)可用性篩選
  3. com.netflix.loadbalancer.WeightedResponseTimeRule:根據(jù)加權(quán)響應(yīng)時(shí)間篩選
  4. com.netflix.loadbalancer.ZoneAvoidanceRule(默認(rèn)):根據(jù)區(qū)域和可用性篩選,使用區(qū)域(Zone)對(duì)服務(wù)器進(jìn)行分類
  5. com.netflix.loadbalancer.BestAvailableRule:忽略“短路”的服務(wù)器瞬内,并選擇并發(fā)數(shù)較低的服務(wù)器
  6. com.netflix.loadbalancer.RandomRule:隨機(jī)
  7. com.netflix.loadbalancer.RetryRule:重試

Ribbon使用:提前準(zhǔn)備一個(gè)服務(wù)集群(將provider復(fù)制一份充當(dāng)provider2迷雪,模擬時(shí)修改端口號(hào))

  1. Eureka的依賴中已經(jīng)有了Ribbon的依賴,因此無(wú)需再引入依賴虫蝶;使用Ribbon時(shí)章咧,只需在RestTemplate的@Bean方法上添加@LoadBalanced注解

    package com.lhp;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    // 開(kāi)啟客戶端的發(fā)現(xiàn)功能
    @EnableDiscoveryClient
    @SpringBootApplication
    public class ConsumerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }
    
        // 開(kāi)啟負(fù)載均衡
        @LoadBalanced
        // 將RestTemplate對(duì)象放入到Spring容器
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
  2. consumer通過(guò)serviceId訪問(wèn)provider

    package com.lhp.controller;
    
    import com.lhp.pojo.User;
    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.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.List;
    
    @RestController
    @RequestMapping("/movie")
    public class MovieController {
        // RestTemplate是從Spring3.0開(kāi)始支持的一個(gè)HTTP請(qǐng)求工具,它提供了常見(jiàn)的REST請(qǐng)求方案的模版
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping("/see")
        public Object see() {
            // 通過(guò)serviceId訪問(wèn)provider
            String serviceId = "provider";
            String instanceUrl = "http://" + serviceId + "/user/1";
            User user = restTemplate.getForObject(instanceUrl, User.class);
            return user;
        }
    }
    
  3. 修改對(duì)serviceId的負(fù)載均衡策略:

    server:
      port: 7201
    eureka:
      client:
        service-url:
          # eureka-server的注冊(cè)地址
          defaultZone: http://localhost:7000/eureka
        # 獲取eureka-server中的服務(wù)列表(只讀備份)的間隔時(shí)間
        registry-fetch-interval-seconds: 30
    spring:
      application:
        # 應(yīng)用名能真,會(huì)在eureka中作為serviceId
        name: consumer
    # 修改對(duì)serviceId的負(fù)載均衡策略赁严,默認(rèn)是輪詢
    # 格式:serviceId或default.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.類
    # 若為default扰柠,則對(duì)所有服務(wù)生效
    provider:
      ribbon:
        # 隨機(jī)策略
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    

負(fù)載均衡的過(guò)程:

跟蹤LoadBalancerInterceptor類的源碼:LoadBalancerInterceptor會(huì)對(duì)RestTemplate的請(qǐng)求進(jìn)行攔截

ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution)
// 獲取服務(wù)名
String serviceName = originalUri.getHost()

<T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
// 獲取負(fù)載均衡器,此時(shí)負(fù)載均衡器中有serviceId的服務(wù)列表
ILoadBalancer loadBalancer = getLoadBalancer(serviceId)
// 根據(jù)負(fù)載均衡器選擇出要使用的服務(wù)節(jié)點(diǎn)
Server server = getServer(loadBalancer, hint)

4. Spring Cloud Netflix Hystrix

服務(wù)雪崩效應(yīng)是一種因“服務(wù)提供者”不可用而導(dǎo)致“服務(wù)消費(fèi)者”不可用疼约,并將不可用逐漸放大的過(guò)程卤档;在微服務(wù)中,一個(gè)請(qǐng)求可能需要多個(gè)微服務(wù)才能實(shí)現(xiàn)程剥,會(huì)形成復(fù)雜的調(diào)用鏈路劝枣,若鏈路中的某個(gè)基礎(chǔ)服務(wù)故障,則會(huì)導(dǎo)致級(jí)聯(lián)故障织鲸,進(jìn)而造成整個(gè)系統(tǒng)不可用

熔斷機(jī)制是應(yīng)對(duì)服務(wù)雪崩效應(yīng)的一種微服務(wù)鏈路保護(hù)機(jī)制舔腾,熔斷器有3個(gè)狀態(tài):

  1. Closed:所有請(qǐng)求正常訪問(wèn);當(dāng)失敗的請(qǐng)求量達(dá)到閾值(默認(rèn)20)昙沦,且失敗的請(qǐng)求百分比達(dá)到閾值(默認(rèn)50%)時(shí)琢唾,熔斷器從Closed切換到Open
  2. Open:請(qǐng)求會(huì)直接失敗而不會(huì)發(fā)送給服務(wù)载荔;Open保持一段時(shí)間后(默認(rèn)5秒)會(huì)自動(dòng)切換到Half Open
  3. Half Open:判斷下一次請(qǐng)求的返回情況盾饮,如果請(qǐng)求成功,則熔斷器切換到Closed懒熙,否則切換到Open

Hystrix解決服務(wù)雪崩效應(yīng)的主要方式:

  1. 服務(wù)降級(jí):當(dāng)某個(gè)服務(wù)熔斷之后將不再被調(diào)用丘损,此時(shí)客戶端可以自己準(zhǔn)備一個(gè)本地的fallback回調(diào),返回一個(gè)缺省值工扎,這樣雖然服務(wù)水平下降徘钥,但好歹可用,比直接掛掉要強(qiáng)
  2. 線程隔離:為每個(gè)服務(wù)分配一個(gè)小的線程池肢娘,如果一個(gè)服務(wù)的線程發(fā)生阻塞呈础,調(diào)用該服務(wù)的過(guò)程中不會(huì)影響到其它服務(wù)

Hystrix使用:

  1. 在consumer中添加hystrix的起步依賴

    <!--hystrix的起步依賴-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    
  2. 在consumer的啟動(dòng)類上添加@EnableCircuitBreaker注解

  3. 在consumer的Controller中添加服務(wù)降級(jí)處理方法

    // 服務(wù)降級(jí)處理方法,方法名隨意橱健,返回值和形參需要和@HystrixCommand修飾的方法一致
    public Object fallback() {
        User user = new User();
        user.setUsername("服務(wù)降級(jí)而钞,默認(rèn)處理!");
        return user;
    }
    
  4. 局部熔斷:在consumer中可能發(fā)生問(wèn)題的方法上添加@HystrixCommand(fallbackMethod = "降級(jí)處理方法名")注解

    // 如果方法發(fā)生問(wèn)題拘荡,則調(diào)用降級(jí)處理方法臼节;僅對(duì)當(dāng)前方法生效
    @HystrixCommand(fallbackMethod = "fallback")
    @GetMapping("/see")
    public Object see() {
        // 通過(guò)serviceId訪問(wèn)provider
        String serviceId = "provider";
        String instanceUrl = "http://" + serviceId + "/user/1";
        User user = restTemplate.getForObject(instanceUrl, User.class);
        return user;
    }
    
  5. 全局熔斷:在consumer中可能發(fā)生問(wèn)題的方法上添加@HystrixCommand注解,并在類上添加@DefaultProperties(defaultFallback = "全局降級(jí)處理方法名")

    package com.lhp.controller;
    
    import com.lhp.pojo.User;
    import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
    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.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    // 全局服務(wù)降級(jí)處理珊皿,對(duì)類的所有方法生效
    @DefaultProperties(defaultFallback = "defaultFallback")
    @RestController
    @RequestMapping("/movie")
    public class MovieController {
        // RestTemplate是從Spring3.0開(kāi)始支持的一個(gè)HTTP請(qǐng)求工具网缝,它提供了常見(jiàn)的REST請(qǐng)求方案的模版
        @Autowired
        private RestTemplate restTemplate;
    
        // 如果方法發(fā)生問(wèn)題,則調(diào)用降級(jí)處理方法蟋定;僅對(duì)當(dāng)前方法生效
        // @HystrixCommand(fallbackMethod = "fallback")
        @HystrixCommand
        @GetMapping("/see")
        public Object see() {
            // 通過(guò)serviceId訪問(wèn)provider
            String serviceId = "provider";
            String instanceUrl = "http://" + serviceId + "/user/1";
            User user = restTemplate.getForObject(instanceUrl, User.class);
            return user;
        }
    
        // 全局服務(wù)降級(jí)處理方法:不能有形參粉臊,返回值應(yīng)與類中每個(gè)方法的返回值兼容
        public Object defaultFallback() {
            User user = new User();
            user.setUsername("服務(wù)降級(jí),全局默認(rèn)處理驶兜!");
            return user;
        }
    
        // 服務(wù)降級(jí)處理方法扼仲,方法名隨意果元,返回值和形參需要和@HystrixCommand修飾的方法一致
        public Object fallback() {
            User user = new User();
            user.setUsername("服務(wù)降級(jí),默認(rèn)處理犀盟!");
            return user;
        }
    }
    
  6. 在consumer的application.yml中追加其他熔斷策略的配置

    # 配置熔斷策略
    hystrix:
      command:
        default:
          circuitBreaker:
            # 是否強(qiáng)制打開(kāi)熔斷器
            forceOpen: false
            # 失敗的請(qǐng)求量閾值
            requestVolumeThreshold: 10
            # 失敗的請(qǐng)求百分比閾值
            errorThresholdPercentage: 50
            # Open到Half Open的時(shí)間
            sleepWindowInMilliseconds: 10000
          execution:
            isolation:
              thread:
                # 熔斷超時(shí)時(shí)間
                timeoutInMilliseconds: 2000
    

5. Spring Cloud OpenFeign

OpenFeign是一個(gè)聲明性的REST客戶端

OpenFeign已經(jīng)集成了Ribbon而晒、Hystrix、slf4j

使用OpenFeign替代RestTemplate發(fā)送Rest請(qǐng)求:

  1. 在consumer中添加openfeign的起步依賴:

    <!--openfeign的起步依賴-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. 在consumer的啟動(dòng)類上添加@EnableFeignClients注解

  3. 在consumer中創(chuàng)建POJOFeign接口

    package com.lhp.feign;
    
    import com.lhp.pojo.User;
    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.RequestMapping;
    
    /**
     * Feign會(huì)通過(guò)動(dòng)態(tài)代理生成實(shí)現(xiàn)類
     * FeignClient(name = "serviceId")注解聲明該接口是一個(gè)Feign的客戶端
     */
    @FeignClient(name = "provider")
    @RequestMapping("/user")
    public interface UserFeign {
        /**
         * Feign會(huì)根據(jù)注解生成URL地址
         * 方法名隨意
         * 返回值和形參應(yīng)和provider中的保持一致
         */
        @GetMapping("/{id}")
        User findById(@PathVariable(name = "id") Integer id);
    }
    
  4. 在consumer的Controller中注入并使用POJOFeign:

    package com.lhp.controller;
    
    import com.lhp.feign.UserFeign;
    import com.lhp.pojo.User;
    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;
    
    @RestController
    @RequestMapping("/feign")
    public class ConsumerController {
        @Autowired
        private UserFeign userFeign;
    
        // 使用Feign調(diào)用provider的方法
        @RequestMapping("/{id}")
        public User findById(@PathVariable(value = "id") Integer id) {
            return userFeign.findById(id);
        }
    }
    
  5. 瀏覽器訪問(wèn)測(cè)試:http://localhost:7201/feign/1

5.1 OpenFeign配置Ribbon

在consumer的application.yml中配置ribbon:

provider:
  ribbon:
    # 負(fù)載均衡策略
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
    # 連接超時(shí)時(shí)間
    ConnectTimeout: 10000
    # 請(qǐng)求處理超時(shí)時(shí)間
    ReadTimeout: 2000
    # 最大重試次數(shù)
    MaxAutoRetries: 1
    # 重試集群中下一個(gè)服務(wù)實(shí)例的最大次數(shù)
    MaxAutoRetriesNextServer: 0
    # 所有操作都重試
    OkToRetryOnAllOperations: false

5.2 OpenFeign配置Hystrix

  1. 在consumer的application.yml中啟用feign的hystrix:

    feign:
      hystrix:
        # 啟用feign的hystrix
        enabled: true
    
  2. 實(shí)現(xiàn)POJOFeign接口

    package com.lhp.feign.impl;
    
    import com.lhp.feign.UserFeign;
    import com.lhp.pojo.User;
    import org.springframework.stereotype.Component;
    
    /**
     * 重寫的每一個(gè)方法阅畴,即這個(gè)方法的降級(jí)處理方法
     */
    @Component
    public class UserFeignImpl implements UserFeign {
        @Override
        public User findById(Integer id) {
            User user = new User();
            user.setUsername("feign的hystrix:降級(jí)處理方法");
            return user;
        }
    }
    
  3. 在consumer中POJOFeign接口的@FeignClient注解中指定降級(jí)處理類倡怎,并去掉接口上的@RequestMapping注解

    package com.lhp.feign;
    
    import com.lhp.feign.impl.UserFeignImpl;
    import com.lhp.pojo.User;
    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.RequestMapping;
    
    /**
     * Feign會(huì)通過(guò)動(dòng)態(tài)代理生成實(shí)現(xiàn)類
     * FeignClient(name = "serviceId")注解聲明該接口是一個(gè)Feign的客戶端
     * fallback指定降級(jí)處理類
     */
    @FeignClient(name = "provider", fallback = UserFeignImpl.class, path = "/user")
    // 若用服務(wù)降級(jí),則不能在接口上使用@RequestMapping注解贱枣;要么在@FeignClient中配置path屬性监署,要么在方法上拼接全路徑
    // @RequestMapping("/user")
    public interface UserFeign {
        /**
         * Feign會(huì)根據(jù)注解生成URL地址
         * 方法名隨意
         * 返回值和形參應(yīng)和provider中的保持一致
         */
        @GetMapping("/{id}")
        User findById(@PathVariable(name = "id") Integer id);
    }
    

5.3 配置請(qǐng)求/響應(yīng)壓縮

OpenFeign可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行GZIP壓縮,以減少通信過(guò)程中的性能損耗

在consumer的application.yml中啟用feign的請(qǐng)求壓縮:

feign:
  compression:
    # 請(qǐng)求壓縮
    request:
      enabled: true
      # 對(duì)指定類型的數(shù)據(jù)進(jìn)行壓縮
      mime-types: text/html,application/xml,application/json
      # 觸發(fā)壓縮的下限
      min-request-size: 2048
    # 響應(yīng)壓縮
    response:
      enabled: true

5.4 配置日志

OpenFeign配置日志:

  1. 在consumer的application.yml中配置普通日志級(jí)別

    # com.lhp包下的日志級(jí)別都為debug
    logging:
      level:
        com.lhp: debug
    
  2. 在一個(gè)配置類中注入Logger.Level

    package com.lhp.feign.config;
    
    import feign.Logger;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class FeignLogger {
        @Bean
        public Logger.Level feignLoggerLevel() {
            /**
             * NONE:不記錄任何日志纽哥,默認(rèn)值
             * BASIC:僅記錄請(qǐng)求的方法钠乏、URL、狀態(tài)碼春塌、執(zhí)行時(shí)間
             * HEADERS:在BASIC基礎(chǔ)上記錄了請(qǐng)求和響應(yīng)的頭信息
             * FULL:記錄所有請(qǐng)求和響應(yīng)的明細(xì)
             */
            return Logger.Level.FULL;
        }
    }
    
  3. 在consumer中POJOFeign接口的@FeignClient注解中指定配置類

    @FeignClient(name = "provider", fallback = UserFeignImpl.class, path = "/user", configuration = FeignLogger.class)
    

6. Spring Cloud Gateway

Spring Cloud Gateway旨在提供一種簡(jiǎn)單而有效的方式來(lái)路由API

網(wǎng)關(guān)可以根據(jù)斷言的規(guī)則對(duì)請(qǐng)求進(jìn)行轉(zhuǎn)發(fā)(路由)晓避、過(guò)濾

6.1 Gateway基本使用

環(huán)境準(zhǔn)備:Gateway本身就是一個(gè)微服務(wù),先創(chuàng)建一個(gè)子工程Eureka Client端

  1. 創(chuàng)建gateway子工程只壳,添加gateway的起步依賴(不要加入web的起步依賴俏拱,沖突):

    <dependencies>
        <!--gateway起步依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--eureka-client起步依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
    
  2. 創(chuàng)建啟動(dòng)類

    package com.lhp;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    // 開(kāi)啟客戶端的發(fā)現(xiàn)功能
    @EnableDiscoveryClient
    @SpringBootApplication
    public class GatewayApplication {
        public static void main(String[] args) {
            SpringApplication.run(GatewayApplication.class, args);
        }
    }
    
  3. 在application.yml中配置路由規(guī)則:

    server:
      port: 7301
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:7000/eureka
    spring:
      application:
        name: gateway
      cloud:
        gateway:
          # 配置路由規(guī)則
          routes:
            - id: user-service-route # 路由id
              # 轉(zhuǎn)發(fā)到哪個(gè)uri
              # uri: http://localhost:7101
              # lb協(xié)議表示從eureka-server獲取服務(wù)請(qǐng)求地址,并且會(huì)通過(guò)Ribbon進(jìn)行負(fù)載均衡
              uri: lb://provider
              # 斷言:路由攔截的地址
              predicates:
                # 如:http://localhost:7301/user/1--轉(zhuǎn)發(fā)到-->http://localhost:7101/user/1
                - Path=/user/** # 將以/user/開(kāi)頭的請(qǐng)求都轉(zhuǎn)發(fā)到uri
              # 配置局部過(guò)濾器
              filters:
                # Param需要自定義ParamGatewayFilterFactory類
                #- Param=age, 21
    
                # 以下兩個(gè)規(guī)則相互抵消了
                # PrefixPath給請(qǐng)求添加前綴吼句,自帶的
                # 如:http://localhost:7301/1--轉(zhuǎn)發(fā)到-->http://localhost:7101/user/1
                - PrefixPath=/user
                # StripPrefix給請(qǐng)求去除前綴锅必,自帶的
                # 如去除第1個(gè)前綴:http://localhost:7301/user/1--轉(zhuǎn)發(fā)到-->http://localhost:7101/1
                - StripPrefix=1
          # 配置全局默認(rèn)過(guò)濾器
          default-filters:
            - AddResponseHeader=MyName, lhp # 添加響應(yīng)頭:字段名, 值
    

6.2 過(guò)濾器

過(guò)濾器分類:

  1. 全局過(guò)濾器:作用在所有路由上
  2. 局部過(guò)濾器:只作用在具體路由上
  3. 默認(rèn)過(guò)濾器
  4. 自定義過(guò)濾器

常見(jiàn)默認(rèn)過(guò)濾器:

  1. AddRequestHeader:添加請(qǐng)求頭
  2. AddRequestParameter:添加請(qǐng)求參數(shù)
  3. AddResponseHeader:添加響應(yīng)頭
  4. StripPrefix:去除前綴

自定義全局過(guò)濾器:創(chuàng)建GlobalFilter實(shí)現(xiàn)類,并交給Spring容器

package com.lhp.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class LoginGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 獲取請(qǐng)求參數(shù)
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        // 如果token為空惕艳,則沒(méi)登錄
        if (StringUtils.isEmpty(token)) {
            // 設(shè)置狀態(tài)碼為403
            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            // 結(jié)束請(qǐng)求
            return exchange.getResponse().setComplete();
        }
        // 放行
        return chain.filter(exchange);

        // 帶token訪問(wèn)測(cè)試:http://localhost:7301/user/1?token=1
        // 不帶token訪問(wèn)測(cè)試:http://localhost:7301/user/1
    }

    // 定義過(guò)濾器執(zhí)行順序搞隐;返回值越小,優(yōu)先級(jí)越高
    @Override
    public int getOrder() {
        return 0;
    }
}

自定義局部過(guò)濾器:創(chuàng)建AbstractNameValueGatewayFilterFactory子類远搪,并交給Spring容器

package com.lhp.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

// XxxGatewayFilterFactory:Xxx為yaml配置文件中filters的參數(shù)
// 需要在yaml配置文件中的filters添加:- Param=name, value
@Component
public class ParamGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 獲取yaml配置文件中filters的- Param=name, value
                System.out.println("nameInConfig=" + config.getName() + ", " + "valueInConfig=" + config.getValue());
                // 放行
                return chain.filter(exchange);
            }
        };
    }
}

7. Spring Cloud Config

創(chuàng)建一個(gè)遠(yuǎn)程倉(cāng)庫(kù)劣纲,在其中創(chuàng)建需要被統(tǒng)一配置管理的配置文件

配置文件命名規(guī)約:${application}-${profile}.yml/yaml/properties
${application}為應(yīng)用名稱
${profile}用于區(qū)分開(kāi)發(fā)環(huán)境dev、測(cè)試環(huán)境test终娃、生產(chǎn)環(huán)境pro等

例如:將provider的application.yml重命名為provider-dev.yml上傳到遠(yuǎn)程倉(cāng)庫(kù)

創(chuàng)建配置中心config-server子工程:

  1. 添加config-server的依賴:

    <dependencies>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--config-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
    </dependencies>
    
  2. 在啟動(dòng)類上添加@EnableConfigServer注解:

    package com.lhp;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.config.server.EnableConfigServer;
    
    // 開(kāi)啟配置服務(wù)功能
    @EnableConfigServer
    // 開(kāi)啟客戶端的發(fā)現(xiàn)功能
    @EnableDiscoveryClient
    @SpringBootApplication
    public class ConfigServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConfigServerApplication.class, args);
        }
    }
    
  3. 在application.yml中配置:

    server:
      port: 7401
    eureka:
      client:
        service-url:
          # eureka-server的注冊(cè)地址
          defaultZone: http://localhost:7000/eureka
    spring:
      application:
        name: config-server
      cloud:
        config:
          server:
            git:
              # 遠(yuǎn)程倉(cāng)庫(kù)的地址
              uri: https://gitee.com/liu-haopeng/config.git
    # com包下的日志級(jí)別都為debug
    logging:
      level:
        com: debug
    
  4. 啟動(dòng)味廊,瀏覽器訪問(wèn)查看文件:http://localhost:7401/provider-dev.yml

讓provider從配置中心獲取配置:

  1. 添加config的起步依賴:

    <!--config的起步依賴-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    
  2. 刪除原來(lái)的application.yml,創(chuàng)建bootstrap.yml:

    # bootstrap.yml是SpringBoot的默認(rèn)配置文件棠耕,其加載時(shí)間早于application.yml
    # bootstrap.yml一般用于系統(tǒng)級(jí)配置余佛,而application.yml一般用于應(yīng)用級(jí)配置
    eureka:
      client:
        service-url:
          # eureka-server的注冊(cè)地址
          defaultZone: http://localhost:7000/eureka
    spring:
      cloud:
        config:
          name: provider # 遠(yuǎn)程倉(cāng)庫(kù)中配置文件名的{application}
          profile: dev # 遠(yuǎn)程倉(cāng)庫(kù)中配置文件名的{profile}
          label: master # 遠(yuǎn)程倉(cāng)庫(kù)的分支
          discovery:
            # 啟用配置中心
            enabled: true
            # 配置中心的serviceId
            service-id: config-server
    
  3. 依次啟動(dòng)eureka-server、config-server窍荧、provider辉巡,看provider是否報(bào)錯(cuò)

以上存在的問(wèn)題:若修改遠(yuǎn)程倉(cāng)庫(kù)中的配置,config-server立即生效蕊退,而provider只有重啟后才會(huì)生效

8. Spring Cloud Bus

Spring Cloud Bus可以解決Spring Cloud Config修改遠(yuǎn)程倉(cāng)庫(kù)中的配置后郊楣,服務(wù)只有重啟后才會(huì)生效的問(wèn)題

Spring Cloud Bus默認(rèn)基于RabbitMQ憔恳,因此使用Spring Cloud Bus前需要先啟動(dòng)RabbitMQ

消息總線分發(fā)消息的過(guò)程:

  1. 訪問(wèn)配置中心的消息總線
  2. 消息總線接收到請(qǐng)求后向消息隊(duì)列發(fā)送消息
  3. provider微服務(wù)監(jiān)聽(tīng)到消息隊(duì)列中的消息后會(huì)重新從配置中心獲取最新配置信息

修改config-server子工程:

  1. 添加bus和stream-binder-rabbit的依賴:

    <!--bus-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-bus</artifactId>
    </dependency>
    <!--stream-binder-rabbit-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
    </dependency>
    
  2. 在application.yml中配置RabbitMQ并開(kāi)放bus-refresh接口:

    server:
      port: 7401
    eureka:
      client:
        service-url:
          # eureka-server的注冊(cè)地址
          defaultZone: http://localhost:7000/eureka
    spring:
      application:
        name: config-server
      cloud:
        config:
          server:
            git:
              # 遠(yuǎn)程倉(cāng)庫(kù)的地址
              uri: https://gitee.com/liu-haopeng/config.git
      # 配置RabbitMQ
      rabbitmq:
        host: localhost
        port: 5672
        virtual-host: /
        username: guest
        password: guest
    management:
      endpoints:
        web:
          exposure:
            # 開(kāi)放bus-refresh接口,以便配合bus實(shí)現(xiàn)配置動(dòng)態(tài)刷新
            include: bus-refresh
    # com包下的日志級(jí)別都為debug
    logging:
      level:
        com: debug
    

修改provider子工程:

  1. 添加bus和stream-binder-rabbit的依賴:

    <!--bus-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-bus</artifactId>
    </dependency>
    <!--stream-binder-rabbit-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
    </dependency>
    
  2. 在bootstrap.yml中配置RabbitMQ:

    # bootstrap.yml是SpringBoot的默認(rèn)配置文件净蚤,其加載時(shí)間早于application.yml
    # bootstrap.yml一般用于系統(tǒng)級(jí)配置钥组,而application.yml一般用于應(yīng)用級(jí)配置
    eureka:
      client:
        service-url:
          # eureka-server的注冊(cè)地址
          defaultZone: http://localhost:7000/eureka
    spring:
      cloud:
        config:
          name: provider # 遠(yuǎn)程倉(cāng)庫(kù)中配置文件名的{application}
          profile: dev # 遠(yuǎn)程倉(cāng)庫(kù)中配置文件名的{profile}
          label: master # 遠(yuǎn)程倉(cāng)庫(kù)的分支
          discovery:
            # 啟用配置中心
            enabled: true
            # 配置中心的serviceId
            service-id: config-server
      # 配置RabbitMQ
      rabbitmq:
        host: localhost
        port: 5672
        virtual-host: /
        username: guest
        password: guest
    
  3. @RefreshScope注解可以啟用刷新配置文件的信息:創(chuàng)建一個(gè)單獨(dú)的配置類存放yml中的數(shù)據(jù),然后在Controller中注入這個(gè)配置類今瀑,不然直接在Controller上添加@RefreshScope注解會(huì)導(dǎo)致獲取的配置為空

    package com.lhp.config;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.stereotype.Component;
    
    // 啟用刷新配置文件的信息
    @RefreshScope
    @Component
    public class ConfigData {
        @Value("${age}")
        private int age;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
  4. 修改遠(yuǎn)程倉(cāng)庫(kù)中的配置后程梦,以POST方式訪問(wèn)配置中心的消息總線http://localhost:7401/actuator/bus-refresh,actuator固定橘荠,bus-refresh為配置中心yml中的management.endpoints.web.exposure.include

以上組件的Spring Cloud總架構(gòu)圖:

9. Spring Cloud Alibaba Nacos

Spring Cloud Alibaba的組件版本關(guān)系:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

Nacos官網(wǎng):https://nacos.io/zh-cn/

Nacos可以代替Eureka做注冊(cè)中心屿附,也可以代替Config做配置中心

Nacos支持CP和AP模式的切換:

  1. 一致性C:在分布式系統(tǒng)中的所有數(shù)據(jù)備份,在同一時(shí)刻是否同樣的值哥童。(等同于所有節(jié)點(diǎn)訪問(wèn)同一份最新的數(shù)據(jù)副本)
  2. 可用性A:在集群中一部分節(jié)點(diǎn)故障后挺份,集群整體是否還能響應(yīng)客戶端的讀寫請(qǐng)求。(對(duì)數(shù)據(jù)更新具備高可用性)
  3. 分區(qū)容忍性P:分區(qū)相當(dāng)于對(duì)通信的時(shí)限要求贮懈,系統(tǒng)如果不能在時(shí)限內(nèi)達(dá)成數(shù)據(jù)一致性匀泊,就意味著發(fā)生了分區(qū)的情況,必須就當(dāng)前操作在C和A之間做出選擇错邦。
  • AP模式下只支持注冊(cè)臨時(shí)實(shí)例探赫,CP模式下支持注冊(cè)持久化實(shí)例

Nacos分為客戶端和服務(wù)端型宙,客戶端可以是個(gè)微服務(wù)撬呢,服務(wù)端需要單獨(dú)下載安裝:

  1. Nacos服務(wù)端下載(如:nacos-server-1.2.1.zip):https://github.com/alibaba/nacos/releases
  2. Nacos安裝啟動(dòng):解壓安裝包,Windows下啟動(dòng)bin目錄里的startup.cmd妆兑;Linux下執(zhí)行:sh startup.sh -m standalone
  3. 啟動(dòng)后瀏覽器訪問(wèn)魂拦,默認(rèn)賬號(hào)和密碼都是nacos:http://localhost:8848/nacos

9.1 創(chuàng)建父工程

創(chuàng)建一個(gè)Maven父工程來(lái)管理依賴:

<packaging>pom</packaging>
<!--Spring Boot版本-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
</parent>
<dependencyManagement>
    <dependencies>
        <!--Spring Cloud版本-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--Spring Cloud Alibaba版本-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.1.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

9.2 Nacos作為注冊(cè)中心

  1. 創(chuàng)建nacos-client子工程,導(dǎo)入依賴

    <dependencies>
        <!--nacos作為注冊(cè)中心客戶端的起步依賴-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--web起步依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    
  2. 創(chuàng)建啟動(dòng)類搁嗓,并添加@EnableDiscoveryClient注解

    package com.lhp;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class NacosClientApplication {
        public static void main(String[] args) {
            SpringApplication.run(NacosClientApplication.class, args);
        }
    }
    
  3. 在application.yml中配置:

    server:
      port: 9001
    spring:
      application:
        name: nacos-client
    
  4. 在bootstrap.yml中配置:

    spring:
      cloud:
        nacos:
          # 服務(wù)端地址
          server-addr: localhost:8848
          discovery:
            # 服務(wù)端的注冊(cè)地址
            server-addr: ${spring.cloud.nacos.server-addr}
      application:
        name: nacos-client
    
  5. 啟動(dòng)服務(wù)端和客戶端微服務(wù)芯勘,瀏覽器訪問(wèn)服務(wù)端查看:http://localhost:8848/nacos

9.2 Nacos作為配置中心

  1. 導(dǎo)入依賴:

    <!--nacos作為配置中心的起步依賴-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    
  2. 在application.yml中配置:

    server:
      port: 9001
    spring:
      application:
        name: nacos-client
    
  3. 在bootstrap.yml中配置spring.cloud.nacos.config:

    spring:
      cloud:
        nacos:
          # 服務(wù)端地址
          server-addr: localhost:8848
          discovery:
            # 服務(wù)端的注冊(cè)地址
            server-addr: ${spring.cloud.nacos.server-addr}
          config:
            # 配置中心的地址
            server-addr: ${spring.cloud.nacos.server-addr}
            # 配置文件的配置格式,默認(rèn)為properties
            file-extension: yaml
            # 命名空間ID
            namespace: public
            # Group
            group: DEFAULT_GROUP
      application:
        name: nacos-client
    
  4. 進(jìn)入Nacos服務(wù)端的配置管理-->配置列表-->點(diǎn)擊+號(hào)新建配置

    Data ID格式:${prefix}-${spring.profile.active}.${file-extension}
    prefix默認(rèn)為:spring.application.name腺逛,也可以通過(guò)spring.cloud.nacos.config.prefix來(lái)配置
    spring.profile.active為空時(shí)的格式:${prefix}.${file-extension}
    
    例如:nacos-client.yaml
    
    配置格式:選擇配置文件的格式
    配置內(nèi)容:application.yml的內(nèi)容
    

Nacos有不同的管理級(jí)別荷愕,可以進(jìn)行多環(huán)境管理:

  1. 配置集Data Id:一個(gè)配置文件通常就是一個(gè)配置集,配置文件中的每一項(xiàng)是配置項(xiàng)
  2. 配置分組group:配置分組是對(duì)配置集進(jìn)行分組
  3. 命名空間namespace:可用于進(jìn)行不同環(huán)境的配置隔離
  • 范圍大泄髅:Data Id \subset group \subset namespace
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末安疗,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子够委,更是在濱河造成了極大的恐慌荐类,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茁帽,死亡現(xiàn)場(chǎng)離奇詭異玉罐,居然都是意外死亡屈嗤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門吊输,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饶号,“玉大人,你說(shuō)我怎么就攤上這事季蚂√志拢” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵癣蟋,是天一觀的道長(zhǎng)透硝。 經(jīng)常有香客問(wèn)我,道長(zhǎng)疯搅,這世上最難降的妖魔是什么濒生? 我笑而不...
    開(kāi)封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮幔欧,結(jié)果婚禮上罪治,老公的妹妹穿的比我還像新娘。我一直安慰自己礁蔗,他們只是感情好觉义,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著浴井,像睡著了一般晒骇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磺浙,一...
    開(kāi)封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天洪囤,我揣著相機(jī)與錄音,去河邊找鬼撕氧。 笑死瘤缩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伦泥。 我是一名探鬼主播剥啤,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼不脯!你這毒婦竟也來(lái)了府怯?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤跨新,失蹤者是張志新(化名)和其女友劉穎富腊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體域帐,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赘被,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年是整,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片民假。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浮入,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出羊异,到底是詐尸還是另有隱情事秀,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布野舶,位于F島的核電站易迹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏平道。R本人自食惡果不足惜睹欲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望一屋。 院中可真熱鬧窘疮,春花似錦、人聲如沸冀墨。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诽嘉。三九已至蔚出,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間含懊,已是汗流浹背身冬。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留岔乔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓滚躯,卻偏偏與公主長(zhǎng)得像雏门,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掸掏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容