美文网首页工作生活
SpringCloud之熔断器Hystrix

SpringCloud之熔断器Hystrix

作者: 架构师Javaspring | 来源:发表于2019-07-01 11:24 被阅读0次

    前言

      SpringCloud 是微服务中的翘楚,最佳的落地方案。

      在微服务架构中多层服务之间会相互调用,如果其中有一层服务故障了,可能会导致一层服务或者多层服务

      故障,从而导致整个系统故障。这种现象被称为服务雪崩效应。

      SpringCloud 中的 Hystrix 组件就可以解决此类问题,Hystrix 负责监控服务之间的调用情况,连续多次失败的

      情况进行熔断保护。保护的方法就是使用 Fallback,当调用的服务出现故障时,就可以使用 Fallback 方法的

      返回值;Hystrix 间隔时间会再次检查故障的服务,如果故障服务恢复,将继续使用服务。

    源码

      GitHub地址:https://github.com/intomylife/SpringCloud

    环境

    JDK 1.8.0 +

    Maven 3.0 +

    SpringBoot 2.0.3

    SpringCloud Finchley.RELEASE

    开发工具

    IntelliJ IDEA 

    正文

    commons 工程

    commons 工程 - POM 文件

    <?xml version="1.0" encoding="UTF-8"?>

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

        <modelVersion>4.0.0</modelVersion>

        <!-- 三坐标 -->

        <groupId>com.zwc</groupId>

        <artifactId>springcloud-hystrix-commons</artifactId>

        <version>1.0</version>

        <!-- 工程名称和描述 -->

        <name>springcloud-hystrix-commons</name>

        <description>公用工程</description>

        <!-- 打包方式 -->

        <packaging>jar</packaging>

        <!-- 在 properties下声明相应的版本信息,然后在dependency下引用的时候用 ${} 就可以引入该版本jar包了 -->

        <properties>

            <!-- 编码 -->

            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

            <!-- jdk -->

            <java.version>1.8</java.version>

            <!-- SpringBoot -->

            <platform-bom.version>Cairo-SR3</platform-bom.version>

            <!-- SpringCloud -->

            <spring-cloud-dependencies.version>Finchley.RELEASE</spring-cloud-dependencies.version>

        </properties>

        <!-- 加入依赖 -->

        <dependencies>

        </dependencies>

        <!-- 依赖 jar 包版本管理的管理器 -->

        <!-- 如果 dependencies 里的 dependency 自己没有声明 version 元素,那么 maven 就此处来找版本声明。 -->

        <!-- 如果有,就会继承它;如果没有就会报错,告诉你没有版本信息 -->

        <!-- 优先级:如果 dependencies 里的 dependency 已经声明了版本信息,就不会生效此处的版本信息了 -->

        <dependencyManagement>

            <dependencies>

                <!-- SpringBoot -->

                <dependency>

                    <groupId>io.spring.platform</groupId>

                    <artifactId>platform-bom</artifactId>

                    <version>${platform-bom.version}</version>

                    <type>pom</type>

                    <scope>import</scope>

                </dependency>

                <!-- SpringCloud -->

                <dependency>

                    <groupId>org.springframework.cloud</groupId>

                    <artifactId>spring-cloud-dependencies</artifactId>

                    <version>${spring-cloud-dependencies.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>

    </project>

    配置一些共用依赖

    commons 工程 - 项目结构

    service 工程

      ① 此工程下有四个模块:一个注册中心以及服务 A、B、C

      ② A 提供服务并且调用服务 B、B 提供服务并且调用服务 C 以及 C 提供服务

    registry-server(注册中心)

    registry-server - POM 文件

    <?xml version="1.0" encoding="UTF-8"?>

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

        <modelVersion>4.0.0</modelVersion>

        <!-- 继承父 -->

        <parent>

            <groupId>com.zwc</groupId>

            <artifactId>springcloud-hystrix-service</artifactId>

            <version>1.0</version>

        </parent>

        <!-- 三坐标 -->

        <groupId>com.zwc</groupId>

        <artifactId>springcloud-hystrix-registry-service</artifactId>

        <version>1.0</version>

        <!-- 工程名称描述 -->

        <name>springcloud-hystrix-registry-service</name>

        <description>注册中心</description>

        <!-- 打包方式 -->

        <packaging>jar</packaging>

        <!-- 在 properties下声明相应的版本信息,然后在dependency下引用的时候用 ${} 就可以引入该版本jar包了 -->

        <properties>

        </properties>

        <!-- 加入依赖 -->

        <dependencies>

            <!-- commons工程 依赖 -->

            <dependency>

                <groupId>com.zwc</groupId>

                <artifactId>springcloud-hystrix-commons</artifactId>

                <version>1.0</version>

            </dependency>

            <!-- 服务注册中心 -->

            <dependency>

                <groupId>org.springframework.cloud</groupId>

                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>

            </dependency>

        </dependencies>

        <!-- 插件依赖 -->

        <build>

            <plugins>

                <plugin>

                    <groupId>org.springframework.boot</groupId>

                    <artifactId>spring-boot-maven-plugin</artifactId>

                </plugin>

            </plugins>

        </build>

    </project>

    主要是加入 spring-cloud-starter-netflix-eureka-server 依赖

    registry-server - application.yml 配置文件

    # 端口

    server:

      port: 8761

    # 应用名称

    spring:

      application:

        name: eurka-server

    eureka:

      instance:

        # 使用 ip 代替实例名

        prefer-ip-address: true

        # 实例的主机名

        hostname: ${spring.cloud.client.ip-address}

        # 实例的 ID 规则

        instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}

      client:

        # 是否向注册中心注册自己

        registerWithEureka: false

        # 是否向注册中心获取注册信息

        fetchRegistry: false

        serviceUrl:

          # 注册中心地址

          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

    这里使用了默认的 8761 端口,当然也可以更改,不过在发现调用服务端的注册中心地址端口要与它一致

    registry-server - 启动类

    package com.zwc;

    import org.springframework.boot.SpringApplication;

    import org.springframework.boot.autoconfigure.SpringBootApplication;

    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

    @SpringBootApplication

    @EnableEurekaServer

    public class SpringcloudHystrixRegistryServiceApplication {

        public static void main(String[] args) {

            SpringApplication.run(SpringcloudHystrixRegistryServiceApplication.class, args);

        }

    }

    在启动类中添加 @EnableEurekaServer 注解表示此工程是注册中心

    registry-server - 启动项目

      1. 项目启动成功后访问 http://localhost:8761/ 即可看到 eureka-server 主页面

    服务工程 A(提供者和消费者)

    服务工程 A - POM 文件

    <?xml version="1.0" encoding="UTF-8"?>

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <!-- 继承父 -->

    <parent>

    <groupId>com.zwc</groupId>

    <artifactId>springcloud-hystrix-a-service</artifactId>

    <version>1.0</version>

    </parent>

    <!-- 三坐标 -->

    <groupId>com.zwc</groupId>

    <artifactId>springcloud-hystrix-a-service-core</artifactId>

    <version>1.0</version>

    <!-- 工程名称描述 -->

    <name>springcloud-hystrix-a-service-core</name>

    <description>服务工程 - A 核心</description>

    <!-- 打包方式 -->

    <packaging>jar</packaging>

    <!-- 在 properties下声明相应的版本信息,然后在dependency下引用的时候用 ${} 就可以引入该版本jar包了 -->

    <properties>

    </properties>

    <!-- 加入依赖 -->

    <dependencies>

    <!-- commons工程 依赖 -->

    <dependency>

    <groupId>com.zwc</groupId>

    <artifactId>springcloud-hystrix-commons</artifactId>

    <version>1.0</version>

    </dependency>

    <!-- api工程 依赖 -->

    <dependency>

    <groupId>com.zwc</groupId>

    <artifactId>springcloud-hystrix-a-service-api</artifactId>

    <version>1.0</version>

    </dependency>

    <!-- 提供者消费者 -->

    <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>

    <!-- 插件依赖 -->

    <build>

    <plugins>

    <plugin>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-maven-plugin</artifactId>

    </plugin>

    </plugins>

    </build>

    </project>

    加入 spring-cloud-starter-netflix-eureka-client 依赖

    还需要加入 Feign 的起步依赖 spring-cloud-starter-openfeign

    服务工程 A - application.yml 配置文件

    # 端口

    server:

      port: 8090

    # 应用名称

    spring:

      application:

        name: hystrix-a

    eureka:

      instance:

        # 使用 ip 代替实例名

        prefer-ip-address: true

        # 实例的主机名

        hostname: ${spring.cloud.client.ip-address}

        # 实例的 ID 规则

        instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}

      client:

        serviceUrl:

          # 注册中心地址

          defaultZone: http://${eureka.instance.hostname}:8761/eureka/

    注意此处配置注册中心地址的端口为 8761 也就是上面注册中心工程配置的端口

    spring.application.name:应用名称,被消费者调用时需要用到,它在消费的同时也可以被消费

    这里除注册中心工程以外的三个工程都与此配置相似,不一样的地方就是端口和应用名称,不再赘述

    服务工程 A -  application.properties(注意)

    # 开启断路器

    feign.hystrix.enabled=true

    断路器要主动开启,服务调用失败时才会熔断

    此处有一个坑,把此配置写到 yml 中熔断不会生效

    A 和 B 工程都有此配置,此次搭建中 C 工程只提供服务,没有消费服务。所以 C 工程不用该配置

    服务工程 A - controller 前端控制器(提供服务)

    package com.zwc.a.controller;

    import org.springframework.beans.factory.annotation.Value;

    import org.springframework.web.bind.annotation.RequestMapping;

    import org.springframework.web.bind.annotation.RestController;

    /*

    * @ClassName ASayHelloController

    * @Desc TODO  Say Hello

    * @Date 2019/5/20 23:24

    * @Version 1.0

    */

    @RestController

    public class ASayHelloController {

        /*

        * @ClassName ASayHelloController

        * @Desc TODO  读取配置文件中的端口

        * @Date 2019/5/20 23:24

        * @Version 1.0

        */

        @Value("${server.port}")

        private String port;

        /*

        * @ClassName ASayHelloController

        * @Desc TODO  Say Hello

        * @Date 2019/5/20 23:24

        * @Version 1.0

        */

        @RequestMapping("/a")

        public String a(){

            return "Hello!I'm a. port:" + port;

        }

    }

    提供一个服务:输出 Hello 和端口

    服务工程 A - 服务调用

    package com.zwc.a.api.feign;

    import com.zwc.a.api.impl.FeignApiFallBack;

    import org.springframework.cloud.openfeign.FeignClient;

    import org.springframework.web.bind.annotation.RequestMapping;

    /*

    * @ClassName FeignApi

    * @Desc TODO  使用 Feign 调用 b - 接口

    * @Date 2019/5/20 23:21

    * @Version 1.0

    */

    @FeignClient(value = "hystrix-b" , fallback = FeignApiFallBack.class)

    public interface FeignApi {

        /*

        * @ClassName FeignApi

        * @Desc TODO  通过 hystrix-b 服务名调用 b() 方法

        * @Date 2019/5/20 23:21

        * @Version 1.0

        */

        @RequestMapping("/b")

        String b();

    }

    通过 @FeignClient 注解中 value = "hystrix-b" 来指定调用哪个服务

    hystrix-b 就是提供者的 spring.application.name:应用名称

    通过 @FeignClient 注解中 fallback = FeignApiFallBack.class 来指定熔断时调用的方法

    FeignApiFallBack 就是此类(FeignApi)的实现类,对应的实现方法就是此类的熔断时调用的方法

    b():此方法是 B 工程中提供的服务,在这里定义成接口

    注意要与提供者具有相同返回值,相同方法名以及相同参数

    服务工程 A - Fallback(FeignApiFallBack)

    package com.zwc.a.api.impl;

    import com.zwc.a.api.feign.FeignApi;

    import org.springframework.stereotype.Component;

    /*

    * @ClassName FeignApi

    * @Desc TODO  fallback

    * @Date 2019/5/20 23:21

    * @Version 1.0

    */

    @Component

    public class FeignApiFallBack implements FeignApi {

        /*

        * @ClassName FeignApiFallBack

        * @Desc TODO  调用 hystrix-b 服务中的 b() 方法失败时执行

        * @Date 2019/5/20 23:31

        * @Version 1.0

        */

        @Override

        public String b() {

            return "Hello!aUseB fail";

        }

    }

    使用 @Component 注解把此类交给 Spring 管理

    实现了 FeignApi 接口,提供熔断时对应的方法

    服务工程 A - controller 前端控制器(消费服务)

    package com.zwc.a.controller;

    import com.zwc.a.api.feign.FeignApi;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.web.bind.annotation.RequestMapping;

    import org.springframework.web.bind.annotation.RestController;

    /*

    * @ClassName AUseBFeignController

    * @Desc TODO  使用 Feign 调用 b - 前端控制器

    * @Date 2019/5/20 23:23

    * @Version 1.0

    */

    @RestController

    public class AUseBFeignController {

        @Autowired(required = false)

        private FeignApi feignApi;

        /*

        * @ClassName FeignController

        * @Desc TODO  通过 hystrix-b 服务名调用 b() 方法

        * @Date 2019/5/20 23:13

        * @Version 1.0

        */

        @RequestMapping("/aUseB")

        public String aUseB(){

            return feignApi.b();

        }

    }

    使用 @Autowired 注解装配 Bean,通过此 Bean 中的方法调用服务

    此类对外暴露接口,调用的实则是提供者的服务

    服务工程 A - 启动类

    package com.zwc;

    import org.springframework.boot.SpringApplication;

    import org.springframework.boot.autoconfigure.SpringBootApplication;

    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

    import org.springframework.cloud.openfeign.EnableFeignClients;

    @SpringBootApplication

    @EnableEurekaClient

    @EnableFeignClients

    public class SpringcloudHystrixAServiceCoreApplication {

        public static void main(String[] args) {

            SpringApplication.run(SpringcloudHystrixAServiceCoreApplication.class, args);

        }

    }

    添加 @EnableEurekaClient 注解表示此工程可以提供服务或调用服务

    添加 @EnableFeignClients 注解表示开启 Feign 功能进行远程调用

    服务工程 A - 启动项目

      1. 项目启动成功后访问:http://localhost:8090/a (调用自己的服务)

      2. 输出内容:'Hello!I'm a. port:8090'

      3. 刷新 http://localhost:8761/(注册中心)可以看到服务已经被注册进来了

      4. 访问地址:http://localhost:8090/aUseB (调用 B 工程的服务)

      5. 输出内容:'Hello!aUseB fail' (此时因为 B 工程还未启动,所以调用了 fallback 中的方法)

      6. 启动服务工程 B,项目启动成功后再次访问:http://localhost:8090/aUseB (调用 B 工程的服务)

      7. 输出内容:'Hello!I'm b. port:8091' (如果还未调用成功,等待一会再刷新试试)

      8. 此时就证明熔断成功

      9. 访问地址:http://localhost:8091/b (调用自己的服务)

     10. 输出内容:'Hello!I'm b. port:8091'

     11. 再次刷新 http://localhost:8761/(注册中心),发现 B 工程服务也注册进来了

    service 工程 - 项目结构

    把多工程项目使用 IntelliJ IDEA  打开

    把项目从 GitHub 中下载到你的本地

    打开 IntelliJ IDEA 

    点击 File -> Open

    打开你下载到本地的项目目录

    springcloud-hystrix -> springcloud-hystrix-service(选择打开此工程)

    打开 service 工程后

    再次点击 File -> Project Structrue

    选择 Modules,点击 '+' 符号

    点击 Import  Module

    还是打开你下载到本地的项目目录

    springcloud-hystrix -> springcloud-hystrix-commons -> pom.xml

    点击 OK

    点击 Next,Finish

    点击 Apply,OK

    最后送福利了

    加群即可获取 群号:798891710 即可获取Java工程化、高性能及分布式、高性能、高架构、性能调优、Spring、MyBatis、Netty源码分析等多个知识点高级进阶干货的相关视频资料,等你来拿

    相关文章

      网友评论

        本文标题:SpringCloud之熔断器Hystrix

        本文链接:https://www.haomeiwen.com/subject/fcxgcctx.html