SpringCloud学习笔记(五)——hystrix

一、服务雪崩的概念

 

 

 

 1.1 什么是服务雪崩

 

 

 

服务雪崩的本质:线程没有及时回收。
不管是调用成功还是失败,只要线程可以及时回收,就可以解决服务雪崩

1.2 服务雪崩怎么解决

1.修改调用时长

将服务间的调用超时时长改小,这样就可以让线程及时回收,保证服务可用
优点:非常简单,也可以有效的解决服务雪崩
缺点:不够灵活,有的服务需要更长的时间去处理(写库,整理数据)

2.设置拦截器

 

 

 二、Hystrix简介

熔断器,也叫断路器!(正常情况下 断路器是关的 只有出了问题才打开)用来保护微服务不雪崩的方法。思想和我们上面画的拦截器一样。
Hystrix 是 Netflix 公司开源的一个项目,它提供了熔断器功能,能够阻止分布式系统中出现联动故障。Hystrix 是通过隔离服务的访问点阻止联动故障的,并提供了故障的解决方案,从而提高了整个分布式系统的弹性。

 三、Hystrix快速入门

当有服务调用的时候,才会出现服务雪崩,所以 Hystrix 常和 OpenFeign,Ribbon 一起出现。

 3.1 首先我们正常写一个openFeign调用的程序

 1.先创建一个rent-car-service,修改其boot和cloud版本,然后添加配置文件,主类增加上相应注解,然后创建一个接口供调用即可,注册到eureka服务器上即可。

这里贴一下接口代码:

@RestController
public class TestController {

    @GetMapping("rentcar")
    public String rentcar() {
        return "租车成功!";
    }

}

2.再创建一个user-service,同样修改版本、添加配置文件,主类中除了增加eurekaClient注解外,还要加上feignClient注解,然后创建feign接口并且创建controller接口利用feign调用远程服务,最后注册到eureka服务器上。

feign接口代码如下:

@FeignClient("rent-car-service")
public interface UserRentCarFeign {

    @GetMapping("rentcar")
    public String rentcar();

}

controller类接口代码如下:

@RestController
public class TestController {

    @Autowired
    private UserRentCarFeign userRentCarFeign;

    @GetMapping("testHystrix")
    public String testHystrix() {
        String result = userRentCarFeign.rentcar();
        return result;
    }

}

3.测试

 

 

 3.2 现在我们给这个程序加上Hystrix熔断器

1.首先要在调用端即user-service这里增加一个feign接口的实现类,我们把这个实现类放在feign/hystrix包路径下,代码如下:

 

 

 代码如下:

@Component
public
class UserRentCarHystrix implements UserRentCarFeign { @Override public String rentcar() { return "我是hystrix实现类,是备份"; } }

2.在feignClient注解下增加一个备用类的属性

@FeignClient(value="rent-car-service",fallback= UserRentCarHystrix.class)

3.yml配置文件中开启hystrix

注:在F版本之前是不用开启的,默认开启,因为之前只有这一种熔断器,现在熔断器种类多了,所以需要手动控制开启

feign:
  hystrix:
    enabled: true #开启断路器的使用

4.然后开启服务进行测试

 

 

rent-car-service宕机以后,再访问就会到自己的实现类。

 

 

 3.3  在 Ribbon 中使用 Hystrix(了解)

四、手写熔断器

4.1 断路器的设计

 

 

 

 

断路器的状态说明以及状态转变:
关:服务正常调用 A---》B
开:在一段时间内,调用失败次数达到阀值(5s 内失败 3 次)(
5s 失败 30 次的)则断路器
打开,直接 return
半开:断路器打开后,过一段时间,让少许流量尝试调用 B 服务,如果成功则断路器关闭,

4.2 手写断路器(利用AOP切面编程)

 

五、Hystrix的一些配置

hystrix: #hystrix 的全局控制
    command:
      default: #default 是全局控制,也可以换成单个方法控制,把 default 换成方法名即可
      fallback:
        isolation:
          semaphore:
            maxConcurrentRequests: 1000 #信号量隔离级别最大并发数
      circuitBreaker:
        enabled: true #开启断路器
        requestVolumeThreshold: 3 #失败次数(阀值)
        sleepWindowInMilliseconds: 20000 #窗口时间
        errorThresholdPercentage: 60 #失败率
      execution:
        isolation:
          Strategy: thread #隔离方式 thread 线程隔离集合和 SEMAPHORE 信号量隔离    
级别
           thread:
             timeoutInMilliseconds: 3000 #调用超时时长

ribbon:
  ReadTimeout: 5000#要结合 feign 的底层 ribbon 调用的时长
  ConnectTimeout: 5000
#隔离方式 两种隔离方式 thread 线程池 按照 group(10 个线程)划分服务提供者,用户请求的线程
和做远程的线程不一样
# 好处 当 B 服务调用失败了 或者请求 B 服务的量太大了 不会对 C 服务造成影响 用户访问比较大的情
况下使用比较好 异步的方式
# 缺点 线程间切换开销大,对机器性能影响
# 应用场景 调用第三方服务 并发量大的情况下
# SEMAPHORE 信号量隔离 每次请进来 有一个原子计数器 做请求次数的++ 当请求完成以后 --
# 好处 对 cpu 开销小
# 缺点 并发请求不易太多 当请求过多 就会拒绝请求 做一个保护机制
# 场景 使用内部调用 ,并发小的情况下
# 源码入门 HystrixCommand AbstractCommand HystrixThreadPool

六、项目结构分析

组织结构方式一:

 

组织结构方式二:

 

 七、按组织结构方式二创建一个fegin项目

 7.1 项目结构

 

 domain中存放实体类,api中存放接口,这两个是不需要运行启动的,也不需要注册到eureka中,他俩的作用就是存放这些类,然后供其他服务调用的,就类似于jar包的作用。

7.2 创建项目

7.2.1 创建父类项目

因为要整明白项目之间的父子关系,所以不能直接创建springboot项目,需要创建一个maven项目,然后自己添加依赖,并指定父项目(我感觉直接创建springboot项目好像也可以?)。

 

刚创建出来的配置文件是啥都没有的:

<?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>org.example</groupId>
    <artifactId>project-openfegin</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

然后修改其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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>org.example</groupId>
    <artifactId>project-openfegin</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--指明打包方式,这个是不用写,因为一旦创建子模块就会自动生成 -->
    <packaging>pom</packaging>

    <!-- 用来定义公共的版本号控制-->
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
    </properties>

    <!-- 这里的依赖,所有的子模块都会有-->
    <dependencies>
        <dependency>
            <!-- 注意:这里不用写版本号,因为父模块中已经定义好了默认的了 -->
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <!-- 加在这里的依赖不会被真的引进项目,只是做一个版本控制-->
    <dependencyManagement>
        <!-- 控制springcloud的版本-->
        <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>

    </build>

</project>

7.2.2 创建子项目

首先创建project-domain

 

 创建其余子项目:

 

 然后我们按照设计的图里搞明白输入关系,即api依赖domain,其余客户端依赖api,需要在对应的pom文件中注入依赖,这样调用各个模块的类时才能调用到

api中加入domain的依赖:

    <dependencies>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>project-domain</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

其余客户端加入api的依赖:

    <dependencies>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>common-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

接下来首先完成domain模块的编写:

首先创建包,然后在包中增加需要用到的实体类,注意每个模块的报名要保持一致,这里我们所有模块的报名统一全部使用com.example.

Order类代码如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Order {

    private Integer id;
    private String name;
    private Double price;

}

接下来完成order-center模块的编写

首先要补充依赖。springweb和eureka的依赖,增加依赖如下:

     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- eureka的客户端的依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

因为这个模块最后需要被打成jar包,所以需要配置上打包插件:

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

然后因为是创建的maven项目,所以需要在这个模块下自己创建一个新的主类:

@SpringBootApplication
@EnableEurekaClient
public class OrderCenterApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderCenterApplication.class, args);
    }

}

然后新建controller,注意包名保持一致。然后本来我们是要创建一个接口方法的,但是因为这个方法和fegin中的接口是一致的,这个方法只是个实现类而已。所以我们要把这个接口抽象到commom-api这个模块内,所以这里建议先看完commom-api模块的编写后,在继续看这一部分。

因为我们在api模块已经实现了fegin接口,所以在这个controller中我们只要实现api的接口就可以了。

public class OrderController implements UserOrderFeign {

    @Override
    public Order getOrderById(Integer id) {
        System.out.println("得到了输入的id:"+id);
        Order order = Order.builder()
                .id(100)
                .name("猪肉炖粉条").price(15.5).build();
        return order;
    }
}

最后别忘了创建yml文件,并将其注册到eureka服务器上。

server:
  port: 8081
spring:
  application:
    name: order-center
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
  instance:
    instance-id: ${spring.application.name}:${server.port}
    prefer-ip-address: true

然后把这个模块运行起来就好了。

接下来完成commom-api模块的编写

 首先是在pom配置文件中添加上feign的依赖:

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

然后新建一个feign包,下边新建一个UserOrderFeign接口(只要是user调用order的方法都写到这个接口下,调用其它模块的再新建接口),写上对应抽象方法,代码如下:

@FeignClient(value = "order-center")
public interface UserOrderFeign {

    @GetMapping("/order/getOrderById")
    Order getOrderById(@RequestParam Integer Id);

}

接下来完成user模块的编写

和order模块一样,同样需要添加依赖、配置打包插件、添加yml配置文件,添加主类。

搞完上述这些以后,创建一个controller类,在里边写接口调用order-center。

@RestController
public class UserController {

    @Autowired
    public UserOrderFeign userOrderFeign;

    @GetMapping("find")
    public Order findOrder(Integer id) {
        return userOrderFeign.getOrderById(id);
    }

}

注意:这里不同模块的包名一定得保持一致,否则自动注入会失败,因为他可能会扫描不到feign接口,也就没法把它加入到IOC容器中。

注意:不要忘记在主类中将当前模块注册为feign的客户端

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

这里才踩了一个坑,实现接口的时候,把形参的Id改成了id,这导致了访问失败,就不要多手,什么东西能保持一致就尽量保持一致。

然后在浏览器中输入http://localhost:8082/find,可以发现访问成功。

 

 

 然后我们在当前的结构上增加hystrix熔断器的配置

 其实熔断器配置在api模块或者user模块都是可以的。这里我们选择将hystrix配置在api模块里。

首先创建fegin的hystrix实现类:

 

 

@Component
public class UserOrderHystrix implements UserOrderFeign {
    @Override
    public Order getOrderById(Integer id) {
        Order order = Order.builder().id(1).name("备份").price(0.0).build();
        return order;
    }
}

然后在FeignClient注解下增加回调函数:

@FeignClient(value = "order-center",fallback = UserOrderHystrix.class)
public interface UserOrderFeign {

    @GetMapping("/order/getOrderById")
    Order getOrderById(@RequestParam Integer id);

}

最后在user模块的yml配置文件中将hystrix的配置开启:

feign:
  hystrix:
    enabled: true #开启断路器的使用

然后开启两个客户端测试:

然后关掉order模块的测试端再测试:

 

posted @ 2022-10-26 14:41  一直学习的程序小白  阅读(176)  评论(0编辑  收藏  举报