SpringCloud 基礎

SpringCloud 基礎

1、概述

  1. 微服務:將傳統的一站式應用,拆分紅一個個的服務,完全去耦合,一個微服務就是單功能業務,只作一件事。
  2. 微服務是一種架構模式或者一種架構風格,提倡將單一應用程序劃分紅一組小的服務獨立部署服務之間相互配合、相互協調,每一個服務運行於本身的進程中。服務與服務間採用輕量級通信,如HTTP的RESTful API等避免統一的、集中式的服務管理機制 。
  3. 微服務優勢
    • 每一個服務足夠內聚,足夠小,比較容易聚焦。
    • 開發簡單且效率高,一個服務只作一件事情。
    • 微服務能用不一樣的語言開發
    • 易於和第三方集成,微服務容許容易且靈活的自動集成部署(持續集成工具備Jenkins,Hudson,bamboo等)
    • 微服務易於被開發人員理解,修改和維護。
    • 微服務只是業務邏輯的代碼,不會和HTML,CSS或其餘界面組件融合
    • 每一個微服務均可以有本身的存儲能力,數據庫可自有也能夠統一,十分靈活
  4. 微服務缺點
    • 開發人員要處理分佈式系統的複雜性
    • 多服務運維難度,隨着服務的增長,運維的壓力也會增大
    • 依賴系統部署
    • 服務間通信的成本
    • 性能監控的難度大
    • 系統集成測試難度大
    • 數據的一致性維護比較困難
  5. Spring Cloud是一個基於Spring Boot實現的雲原生應用開發工具,它爲基於JVM的雲原生應用開發中涉及的配置管理、服務發現、熔斷器、智能路由、微代理、控制總線、分佈式會話和集羣狀態管理等操做提供了一種簡單的開發方式。
  6. SpringCloud 核心子項目
    • Spring Cloud Netflix:核心組件,能夠對多個Netflix OSS開源套件進行整合,包括如下幾個組件:
      • Eureka:服務治理組件,包含服務註冊與發現
      • Hystrix:容錯管理組件,實現了熔斷器
      • Ribbon:客戶端負載均衡的服務調用組件
      • 基於Ribbon和Hystrix的聲明式服務調用組件
      • Zuul:網關組件,提供智能路由、訪問過濾等功能
      • Archaius:外部化配置組件
    • Spring Cloud Config:配置管理工具,實現應用配置的外部化存儲,支持客戶端配置信息刷新、加密/解密配置內容等。

2、服務發現組件 Eureka

1. 介紹

  1. Eureka是Spring Cloud Netflix微服務套件中的一部分,是一套成熟的服務註冊和發現組件,能夠與Springboot構建的微服務很容易的整合起來。Eureka包含了服務器端和客戶端組件。Eureka客戶端是一個java客戶端,用來簡化與服務器的交互、做爲輪詢負載均衡器,並提供服務的故障切換支持。

2. 搭建 Maven 父工程

  • pom.xmlmysql

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <groupId>com.offcn</groupId>
    <artifactId>APartenProject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    
    <properties>
        <java.version>1.8</java.version>
    </properties>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

3. 建立 Eureka 集羣

  1. 新建子模塊 eureka-server01git

    1. pom.xmlgithub

      <parent>
          <artifactId>APartenProject</artifactId>
          <groupId>com.offcn</groupId>
          <version>1.0-SNAPSHOT</version>
      </parent>
      <groupId>com.offcn</groupId>
      <artifactId>eureka-server01</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <name>eureka-server01</name>
      <description>Demo project for Spring Boot</description>
      
      <properties>
          <java.version>1.8</java.version>
          <spring-cloud.version>Greenwich.SR2</spring-cloud.version>
      </properties>
      
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
          </dependency>
      </dependencies>
      
      <dependencyManagement>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-dependencies</artifactId>
                  <version>${spring-cloud.version}</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
          </dependencies>
      </dependencyManagement>
      
      <build>
          <plugins>
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
              </plugin>
          </plugins>
      </build>
    2. 配置 application.ymlweb

      #內置的tomcat服務啓動監聽端口號
      server:
        # port: 8888
        port: 10086
      
      #應用名稱
      spring:
        application:
          name: eureka-server
      
      #EurekaServer配置
      eureka:
        client:
          # register-with-eureka: false #此EurekaServer再也不註冊到其餘的註冊中心
          # fetch-registry: false       #再也不從其餘中心中心拉取服務器信息
          service-url:
            #defaultZone: http://localhost:${server.port}/eureka #註冊中心訪問地址
            defaultZone: http://localhost:10087/eureka #指向另一臺Eureka服務器
        server:
          enable-self-preservation: false   # Eureka開啓自動保護模式
          eviction-interval-timer-in-ms: 4000
    3. 爲啓動類添加註解spring

      @SpringBootApplication
      // 開啓 EurekaServer
      @EnableEurekaServer
      public class EurekaServer01Application {
      
          public static void main(String[] args) {
              SpringApplication.run(EurekaServer01Application.class, args);
          }}
  2. 和 eureka-server01 同樣,新建一個子模塊eureka-server02,只改一下配置文件 application.yml,其它保持一致。sql

    server:
      port: 10087
    
    spring:
      application:
        name: eureka-server
    
    eureka:
      server:
        enable-self-preservation: false
        eviction-interval-timer-in-ms: 4000
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka
  3. 運行兩個服務器 http://localhost:10086/http://localhost:10087/ 均可以數據庫

4. 建立服務提供方集羣

  1. 新建子模塊 userprovider01bootstrap

    1. pom.xml後端

      <dependencies>
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-data-jpa</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-jdbc</artifactId>
              </dependency>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
              </dependency>
          </dependencies>
    2. application.yml

      server:
        port: 8001
      
      spring:
        application:
          name: userprovider
        datasource:
          url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
        jpa:
          hibernate:
            ddl-auto: update
          show-sql: true
      #必定注意eureka與spring屬於平級 注意格式
      eureka:
        client:
          service-url:
            defaultZone: http://localhost:10086/eureka/,http://localhost:10087/eureka/
      
      # 數據
      ProviderVersion: UserProvider:0.02V
    3. 建立實體類User

      @Entity
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class User {
          @Id
          @GeneratedValue
          private Long id;
      
          @Column(name="name",nullable = true,length = 200)
          private String name;
      
          @Column(name = "age",nullable = true,length = 4)
          private Integer age;
      }
    4. UserDao 實現 JpaRepository<User, Long>,建立UserService、UserServiceImpl、UserController(RESTful風格)

      // UserController
      @RestController
      @RequestMapping("/user")
      public class UserController {
          @Autowired
          UserService userService;
      
          @Value("${ProviderVersion}")
          private String ProviderVersion;
      
          @GetMapping("/getall")
          @ApiOperation(value = "獲取所有用戶信息", notes = "獲取所有用戶信息")
          public Map<String,Object> getUsers() {
              Map<String,Object> map=new HashMap<>();
              List<User> list = userService.getUserList();
              map.put("list", list);
              map.put("ProviderVersion", ProviderVersion);
              return map;
          }   
          ......
      }
    5. 啓動類添加註解 @EnableDiscoveryClient

  2. 新建子模塊 userprovider02,除了 application.yml 和 UserController的getAll方法有些差異外,其它所有同樣

    server:
      port: 8002
    
    spring:
      application:
        name: userprovider
      datasource:
        url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
        username: root
        password: 123456
        driver-class-name: com.mysql.cj.jdbc.Driver
      jpa:
        hibernate:
          ddl-auto: update
        show-sql: true
    #必定注意eureka與spring屬於平級 注意格式
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka/,http://localhost:10087/eureka/
    // UserController 中的getUser方法中 ProviderVersion 的值與 UserProvider01項目有所區別(ProviderVersion: UserProvider:0.02V),這是爲了後面負載均衡的時候看出差異。
    @GetMapping("/getall")
        public Map<String,Object> getUsers() {
            Map<String,Object> map=new HashMap<>();
            List<User> list = userService.getUserList();
            map.put("list", list);
            String ProviderVersion="用戶服務UserProvdier002:0.01V";
            map.put("ProviderVersion", ProviderVersion);
            return map;
        }
  3. 運行兩個提供者

5. 建立服務消費方

  1. 新建子模塊 userweb01

  2. pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>4.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
  3. 配置 application.yml

    server:
      port: 9001
    
    spring:
      thymeleaf:
        cache: false
      application:
        name: userweb01
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka/,http://localhost:10087/eureka/
  4. 修改啓動類

    @SpringBootApplication
    @EnableDiscoveryClient
    public class Userweb01Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Userweb01Application.class, args);
        }
    
        @Bean
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
  5. 新建 User,UserController,UserService,UserSerivceImpl

    // UserController.java
    // 由於使用了 thymeleaf,因此須要轉發到相應模版,使用 Model 攜帶數據
    @Controller
    public class UserController {
        @Autowired
        UserService userService;
    
        @GetMapping("/")
        public String getUserList(Model model){
            Map map = userService.getUserMap();
            List<User> list=(List<User>) map.get("list");
            model.addAttribute("page", list);
            model.addAttribute("ProviderVersion", map.get("ProviderVersion"));
    
            return "user/list";
        }
     ......
    }
    // UserSerivceImpl.java
    @Service
    public class UserServiceImpl implements UserService {
        //遠程服務調用客戶端
        @Autowired
        RestTemplate restTemplate;
        //Eureka客戶端
        @Autowired
        DiscoveryClient discoveryClient;
    
        /***
         * 經過客戶端負載均衡器獲取生產者服務器基礎地址
         * @return
         */
        public String getServerUrl() {
            //經過客戶端調用器查找指定服務
            List<ServiceInstance> instList = discoveryClient.getInstances("USERPROVIDER");
            //獲取第一個服務器
            ServiceInstance inst = instList.get(0);
            //獲取服務提供者服務器ip、端口號
            String ip = inst.getHost();
            int port = inst.getPort();
            //拼接調用地址
            String url="http://"+ip+":"+port+"/user";
            return url;
        }
    
        @Override
        public Map getUserMap() {
            Map map = restTemplate.getForObject(getServerUrl()+"/getall", Map.class);
            return map;
        }
    
        @Override
        public void createUser(User user) {
    
            restTemplate.postForObject(getServerUrl()+"/save", user,String.class);
    
        }
    
        @Override
        public User getUser(Long id) {
    
            return restTemplate.getForObject(getServerUrl()+"/get/"+id, User.class);
        }
    
        @Override
        public void updateUser(Long id, User user) {
            restTemplate.put(getServerUrl()+"/update/"+id, user);
    
        }
    
        @Override
        public void deleteUser(Long id) {
            restTemplate.delete(getServerUrl()+"/delete/"+id);
        }
    }
  6. 啓動服務,跳轉地址 http://localhost:9001/

3、服務調用組件

 SpringColud中已經幫咱們集成了一系列負載均衡組件:LoadBalancerClient、Ribbon(緞帶)、Feign(裝做),簡單修改代碼便可使用。

1. 調用服務基於 LoadBalancerClient

  1. 新建子模塊 userweb02

  2. pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>4.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
  3. 配置文件 application.yml

    server:
      port: 9002
    
    spring:
      thymeleaf:
        cache: false
      application:
        name: userweb02
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka/,http://localhost:10087/eureka/
  4. 啓動類、Bean、UserController、UserService 還和 UserWeb01 項目保持一直,只有UserServiceImpl 有一些變化

    // UserServiceImpl.java
    @Service
    public class UserServiceImpl implements UserService {
        //遠程服務調用客戶端
        @Autowired
        RestTemplate restTemplate;
        //支持負載均衡的調用客戶端
        @Autowired
        LoadBalancerClient loadBalancerClient;
    
        /***
         * 經過客戶端負載均衡器獲取生產者服務器基礎地址
         * @return
         */
        public String getServerUrl() {
            //經過客戶端調用器查找指定服務,只有這裏發生了變化。
            ServiceInstance inst  = loadBalancerClient.choose("USERPROVIDER");
            //獲取服務提供者服務器ip、端口號
            String ip = inst.getHost();
            int port = inst.getPort();
            //拼接調用地址
            String url="http://"+ip+":"+port+"/user";
            return url;
        }
        ......
       // 方法還和之前保持一致     
    }
  5. 運行服務,http://localhost:9002/

2. 調度服務基於 Ribbon

 Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。它是一個基於HTTP和TCP的客戶端負載均衡器。

  1. 新建子模塊 UserWeb03

  2. pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>4.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
    </dependencies>
  3. 配置文件 application.yml

    • 負載均衡策略:
    • com.netflix.loadbalancer.RoundRobinRule:輪詢
    • com.netflix.loadbalancer.RandomRule:隨機
    • com.netflix.loadbalancer.RetryRule:重試
    • com.netflix.loadbalancer.WeightedResponseTimeRule:根據響應時間權重分配一個weight,響應時間越長,weight越小,被選中的可能性越低。
    • com.netflix.loadbalancer.BestAvailableRule:選擇一個最小的併發請求的server
    • AvailabilityFilteringRule:過濾掉那些由於一直鏈接失敗的被標記爲circuit tripped的後端server,並過濾掉那些高併發的的後端server(active connections 超過配置的閾值)
    • ZoneAvoidanceRule:複合判斷server所在區域的性能和server的可用性選擇server
    server:
      port: 9003
    
    spring:
      thymeleaf:
        cache: false
      application:
        name: userweb03
        #開啓Spring Cloud的重試功能
      cloud:
        loadbalancer:
          retry:
            enabled: true
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka/,http://localhost:10087/eureka/
    
    USERPROVIDER:
      ribbon: 
        # 配置指定服務的負載均衡策略
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
        # Ribbon的鏈接超時時間
        ConnectTimeout: 250
        # Ribbon的數據讀取超時時間
        ReadTimeout: 250
        # 是否對全部操做都進行重試
        OkToRetryOnAllOperations: true
        # 切換實例的重試次數
        MaxAutoRetriesNextServer: 1
        # 對當前實例的重試次數
        MaxAutoRetries: 1
  4. 啓動類修改

    @SpringBootApplication
    @EnableDiscoveryClient
    public class Userweb03Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Userweb03Application.class, args);
        }
    
        @Bean
    //    開啓 ribbon
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
  5. User、UserController、UserService都和上一個保持一致,只有UserServiceImpl有所不一樣

    @Service
    public class UserServiceImpl implements UserService {
        //遠程服務調用客戶端
        @Autowired
        RestTemplate restTemplate;
    
        //開啓Ribbon後,RestTemplate直接使用服務名就能夠發起調用
        String url="http://USERPROVIDER";
        ......
            其它代碼一致,取消了getUrl方法
    }

5.運行服務 http://localhost:9003/

3. 調度基於 Feign

 Feign是一個聲明性的web服務客戶端,使用Feign建立接口並對其進行註釋,就能夠經過該接口調用生產者提供的服務。Spring Cloud對Feign進行了加強,使得Feign支持了Spring MVC註解。

  • Feign採用的是接口加註解;
  • Feign 整合了ribbon
  1. 建立子模塊 UserWeb04

  2. pom.xml

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
            <dependency>
                <groupId>org.webjars</groupId>
                <artifactId>bootstrap</artifactId>
                <version>4.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.retry</groupId>
                <artifactId>spring-retry</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </dependency>
        </dependencies>
  3. 配置文件 application.yml

    server:
      port: 9004
    
    spring:
      thymeleaf:
        cache: false
      application:
        name: userweb04
        #開啓Spring Cloud的重試功能
      cloud:
        loadbalancer:
          retry:
            enabled: true
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka/,http://localhost:10087/eureka/
    
    USERPROVIDER:
      ribbon:
        # 配置指定服務的負載均衡策略
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
        # Ribbon的鏈接超時時間
        ConnectTimeout: 250
        # Ribbon的數據讀取超時時間
        ReadTimeout: 250
        # 是否對全部操做都進行重試
        OkToRetryOnAllOperations: true
        # 切換實例的重試次數
        MaxAutoRetriesNextServer: 1
        # 對當前實例的重試次數
        MaxAutoRetries: 1
    
    # 設置對應包的日誌級別
    logging.level.com.offcn.userweb04: debug
  4. 編寫配置類,定義日誌級別,Feign支持4種級別:

    • NONE:不記錄任何日誌信息,這是默認值。
    • BASIC:僅記錄請求的方法,URL以及響應狀態碼和執行時間
    • HEADERS:在BASIC的基礎上,額外記錄了請求和響應的頭信息
    • FULL:記錄全部請求和響應的明細,包括頭信息、請求體、元數據。
    @Configuration
    public class FeignConfig {
        @Bean
        public Logger.Level getFeignlogger(){
            return Logger.Level.FULL;
        }
    }
  5. 啓動類添加註解 @EnableFeignClients

  6. User、UserController與其它 UserWeb 項目保持一致,可是要刪除UserServiceImpl,修改UserService

    @FeignClient(value = "USERPROVIDER", configuration = FeignConfig.class)
    public interface UserService {
        @GetMapping("/user/getall")
        public Map<String, Object> getUserMap();
        @PostMapping("/user/save")
        public void createUser(User user);
        @GetMapping("/user/get/{id}")
        public User getUser(@RequestParam("id") Long id);
        @PutMapping("/user/update/{id}")
        public void updateUser(@RequestParam("id") Long id, @RequestBody User user);
        @DeleteMapping("/user/delete/{id}")
        public void deleteUser(@RequestParam("id") Long id);
    }
  7. 運行服務 <>

4、熔斷器組件 Hystrix

1. 介紹

 Hystrix是Netflix開源的一個延遲和容錯庫,用於隔離訪問遠程服務、第三方庫,防止出現級聯失敗。在分佈式系統中應用這一模式以後,服務調用方能夠本身進行判斷某些服務反應慢或者存在大量超時的狀況時,可以主動熔斷,防止總體系統被拖垮。不一樣於電路熔斷只能斷不能自動重連,Hystrix能夠實現彈性容錯,當狀況好轉以後,能夠自動重連。

2. Ribbon使用Hystrix

  1. 修改子模塊 UserWeb03,引入依賴

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
  2. 修改配置文件

    # 添加Hystrix熔斷超時時間,要求熔斷超時 > ribbon 讀取超時
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 700
  3. 在啓動類添加註解 @EnableCircuitBreaker

  4. 修改 UserServiceImpl

    @Override
    @HystrixCommand(fallbackMethod="getUserMapFallbackMethod")
    public Map getUserMap() {
        long beginTime = System.currentTimeMillis();
        Map map = restTemplate.getForObject(url+"/user/getall", Map.class);
        long endTime=System.currentTimeMillis();
        System.out.println("程序執行時間:"+(endTime-beginTime));
        return map;
    }
    
    // 熔斷超時,就會執行該方法
    public Map<String, Object> getUserMapFallbackMethod() {
        Map map = new HashMap();
        map.put("list", new ArrayList<>());
        map.put("ProviderVersion", "獲取遠程調用失敗");
        return map;
    }
  5. 修改 UserProvider01,模擬超時狀況

    @GetMapping("/getall")
        @ApiOperation(value = "獲取所有用戶信息", notes = "獲取所有用戶信息")
        public Map<String,Object> getUsers() {
            Map<String,Object> map=new HashMap<>();
            List<User> list = userService.getUserList();
            map.put("list", list);
            map.put("ProviderVersion", ProviderVersion);
            // 模擬超時
            try {
                Thread.sleep(900);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return map;
        }

7.運行服務 http://localhost:9003/

https://note.youdao.com/yws/public/resource/834f2a6b7c87213a5ccaf3e8e6a9e576/xmlnote/1FD79BAC363B471AB8A283EF8800B14D/6574

https://note.youdao.com/yws/public/resource/834f2a6b7c87213a5ccaf3e8e6a9e576/xmlnote/AEC849A417A543D78DF78D6B689A9BB5/6576

3. Feign使用Hystrix

  1. pom 引入依賴

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
  2. Feign默認也有對Hystrix的集成,只不過,默認狀況下是關閉的。咱們須要經過下面的參數來開啓,修改UserWeb04 模塊的配置文件:

    feign:
      hystrix:
        enabled: true
    #設定Hystrix熔斷超時時間
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 700
  3. 添加類 UserServiceImpl

    @Service
    public class UserServiceImpl implements UserService {
    
        @Override
        public Map<String, Object> getUserMap() {
            Map map = new HashMap();
            map.put("list", new ArrayList<>());
            map.put("ProviderVersion", "獲取遠程調用失敗");
            return map;
        }
    
        @Override
        public void createUser(User user) {
            System.out.println("建立用戶失敗:"+user);
    
        }
    
        @Override
        public User getUser(Long id) {
            System.out.println("獲取id:"+id+" 的用戶失敗");
            return null;
        }
    
        @Override
        public void updateUser(Long id, User user) {
            System.out.println("更新id:"+id+"的用戶失敗");
    
        }
    
        @Override
        public void deleteUser(Long id) {
            System.out.println("刪除id爲:"+id+"的用戶失敗");
    
        }
    }
  4. 在 UserService中,使用註解@FeignClient聲明熔斷調用實現類 @FeignClient(value = "USERPROVIDER", configuration = FeignConfig.class, fallback = UserServiceImpl.class)。熔斷超時,就會到該指定類執行對應的方法。

  5. 運行服務 http://localhost:9004/

4. Hystrix監控服務器

1. 搭建Hystrix Dashboard管理控制中心

  1. 新建子模塊 hystrix-dashboard,編輯pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
    </dependencies>
  2. 修改配置文件 application.yml

    spring:
      application:
        name: hystrix-dashboard
    server:
      port: 1301
  3. 啓動類添加註解 @EnableHystrixDashboard

  4. 運行服務 http://localhost:1301/hystrix

    https://note.youdao.com/yws/public/resource/834f2a6b7c87213a5ccaf3e8e6a9e576/xmlnote/4342E51A819B4F07A4BBE372DFDF8501/6578

2. 啓用客戶端Hystrix監控

  1. pom 引入依賴

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  2. 修改 UserWeb04 啓動類,在服務實例的主類中已經使用@EnableCircuitBreaker或@EnableHystrix註解,開啓斷路器功能。同時增長監控路徑訪問地址定義/hystrix.stream能夠訪問。

    @SpringBootApplication
    @EnableDiscoveryClient
    // 開啓假裝客戶端
    @EnableFeignClients
    // 開啓斷路器功能
    @EnableHystrix
    public class Userweb04Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Userweb04Application.class, args);
        }
    
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    
        @Bean
        public ServletRegistrationBean getServlet(){
            HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
            ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
            registrationBean.setLoadOnStartup(1);  //系統啓動時加載順序
            registrationBean.addUrlMappings("/hystrix.stream");//路徑
            registrationBean.setName("HystrixMetricsStreamServlet");
            return registrationBean;
        }
    }
  3. 啓動服務,先執行請求後,而後查看 http://localhost:9004/hystrix.stream

  4. 使用Hystrix Dashboard對Hystrix監控數據進行圖形化監控。在Hystrix Dashboard的首頁輸入http://localhost:9004/hystrix.stream,點擊「Monitor Stream」按鈕。

5、分佈式配置中心組件Spring Cloud Config

1. 介紹

  • 在SpringBoot應用中,配置內容寫在application.yml,也可使用 application-{profile}.yml 的形式設置,可是在微服務架構中,配置文件的分散並不利於系統的管理和維護。
  • 微服務對配置管理有更高的要求:
    • 集中管理:服務須要集中管理配置,不然維護困難、容易出錯。
    • 運行期動態調整:某些參數須要在應用運行時動態調整,而且調整時不中止服務。
    • 自動更新配置:微服務可以在配置發生變化是自動更新配置。
  • Spring Cloud Config主要是爲了分佈式系統的外部配置提供了服務器端和客戶端的支持,只要體現爲Config Server和Config Client兩部分。
    • Config Server: 是一個看橫向擴展的,集中式的配置服務器,它用於集中管理應用程序各個環境下配置,默認使用Git存儲配置內容。
    • Config Client: 是一個Config Server的客戶端,用於操做存儲在Config Server上的配置屬性,全部微服務都指向Config Server,啓動的時候會請求它獲取所須要的配置屬性,而後緩存這些屬性以提升性能。

2. 構建Config Server

  1. 新建子模塊 ConfigServer001,修改pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
  2. 修改配置文件 application.yml

    server:
      port: 7001
    
    spring:
      application:
        name: config-server
      cloud:
        config:
          server:
            git:
            # 根據本身的狀況進行配置
              uri: https://github.com/username/repositoryname
              search-paths: src/main/resources
              username: username
              password: password
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka,http://localhost:10087/eureka
  3. 修改UserProvide01的配置文件 application.yml 爲 UserProvider01-test.yml 並上傳,運行服務執行 http://localhost:7001/UserProvider01-test.yml

3. 構建Config Client

  1. 將 UserProvider0一、UserProvider0二、UserWeb0一、UserWeb0二、UserWeb0三、UserWeb04 的配置文件 application.yml 修改成 application-dev.yml 並上傳到 git 遠程倉庫,項目自己的配置文件不修改,只是上傳git的時候須要更名。

  2. 修改以上子模塊

  3. pom.xml 引入config

    <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
  4. 新建配置文件 bootstrap.yml

    spring:
      application:
      # 根據本身上傳的 yml 文件來定
        name: UserProvider01
      cloud:
        config:
          discovery:
            enabled: true
            # 根據 configserver001 的 application.name 來定。 
            service-id: config-server
          #   根據本身上傳的 yml 文件來定
          profile: dev
          label: master
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka,http://localhost:10087/eureka
  5. 將原有的 application.yml 清空,加上新的內容,能夠在配置文件中的內容有改動的時候,不用重啓服務,動態刷新,須要在相應的調用類上加新的註解 @RefreshScope

    management:
      endpoints:
        web:
          exposure:
            include: refresh,health,info
  6. 運行服務,而後啓動任意一個客戶端,查看運行狀況

    https://note.youdao.com/yws/public/resource/834f2a6b7c87213a5ccaf3e8e6a9e576/xmlnote/4A42410AF62F42D8A8049289CF22282F/6586

6、服務網關組件Netflix Zuul

1. 介紹

  • Zuul是Netflix開源的微服務網關,和Eureka,Ribbon,Hystrix等組件配合使用。Zuul組件的核心是一系列的過濾器,這些過濾器能夠完成如下功能:
    • 認證和安全 :對每個resource進行身份認證
    • 追蹤和監控 :實時觀察後端微服務的TPS、響應時間,失敗數量等準確的信息
    • 日誌 :記錄全部請求的訪問日誌數據,能夠爲日誌分析和查詢提供統一支持
    • 動態路由 : 動態的將request路由到後端的服務上去
    • 壓力測試 :逐漸的增長訪問集羣的壓力,來測試集羣的性能
    • 負載分配:爲每一種負載類型分配對應容量,並棄用超出限定值的請求
    • 靜態響應 :直接在網關返回一些響應,而不是經過內部的服務返回響應

2.Zuul服務網關搭建

  1. 新建子模塊 ZuulGateWay,修改 pom.xml

    <dependencies>
        <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>
    </dependencies>
  2. 修改配置文件:application.yml

    spring:
      application:
        name: zull-gateway
    
    server:
      port: 80
    
    # 經過 url 直接映射
    #zuul:
    #  routes:
    #    userprovider001:
    #      # userprovider001 部分爲路由的名字,能夠任意定義,可是一組映射關係的path和url要相同
    #      path: /userprovider001/**
    #      url: http://localhost:8001/
    #    userprovider002:
    #      path: /userprovider002/**
    #      url: http://localhost:8002/
    
    
    # 經過 serviceId的映射方式支持了斷路器,對於服務故障的狀況下,能夠有效的防止故障蔓延到服務網關上而影響整個系統的對外服務。
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:10086/eureka,http://localhost:10087/eureka
    
    
    zuul:
      routes:
        userprovider:
          path: /service/**
          service-id: USERPROVIDER
          # 就是將web中的配置 到zuul中 這樣多個web都用熔斷 僅須要寫一次 而沒必要要每一個web 都配置。
      retryable: true   #打開重試
    
    ribbon:
      NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
      ConnectionTimeOut: 250
      ReadTimeout: 1000
      OkToRetryOnAllOperations: true
      MaxAutoRetriesNextServer: 1
      MaxAutoRetries: 1
    
    #設定Hystrix熔斷超時時間
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 2000
  3. 啓動類

    @EnableZuulProxy
    // 包含了 @SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker
    @SpringCloudApplication
    public class ZuulgatewayApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ZuulgatewayApplication.class, args);
        }
    }
  4. Zuul服務網關過濾器

    public class AccessFilter extends ZuulFilter {
    
        /**
         * 四種不一樣生命週期
         * pre:能夠在請求被路由以前調用
         * routing:在路由請求時候被調用
         * post:在routing和error過濾器以後被調用
         * error:處理請求時發生錯誤時被調用
         * @return
         */
        @Override
        public String filterType() {
            return "pre";
        }
    
        /**
         * 過濾器的執行順序
         * @return
         */
        @Override
        public int filterOrder() {
            return 0;
        }
    
        /**
         * 過濾器是否要執行
         * @return
         */
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
            RequestContext ctx= RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            String token = request.getParameter("accesstoken");
            if(token == null) {
                // 過濾該請求,不對其進行路由
                ctx.setSendZuulResponse(false);
                // 返回的錯誤碼。也能夠經過ctx.setResponseBody(body)對返回
                ctx.setResponseStatusCode(401);
                return null;
            }
            return null;
        }
    }
  5. 在啓動類中實例化該過濾器

    @Bean
    public AccessFilter accessFilter() {
        return new AccessFilter();
    }
  6. 啓動服務,進行測試

相關文章
相關標籤/搜索