SpringCloud入门学习之Hystrix(二)

简介: SpringCloud入门学习之Hystrix(二)

熔断


熔断机制是应对雪崩效应的一种微服务链路保护机制。当服务调用者调用链路的某个服务不可用或响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。


在 SpringCloud 框架里熔断机制通过 Hystrix 实现,Hystrix 会监控微服务间调用的状况,当失败的调用满足一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是 HystrixCommand。


熔断机制是针对服务提供者 Provider 做的修改,我们根据 springcloud-provider-dept-8001 项目复制新增一个名为 springcloud-provider-dept-hystrix-8001 的项目,然后修改新项目。


1、导入  hystrix 依赖


<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
复制代码


2、修改 DeptServiceImpl


@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    DeptMapper deptMapper;
    @Override
    public boolean addDept(Dept dept) {
        return deptMapper.addDept(dept);
    }
    @HystrixCommand(fallbackMethod = "getDeptHystrix",
                    commandProperties = {
                        //默认 20 个;10s 内请求数大于 20 个时就启动熔断器,当请求符合熔断条件时将触发 getFallback()。
                        @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,
                                         value = "10"),
                        //请求错误率大于 50%时就熔断,然后 for 循环发起请求,当请求符合熔断条件时将触发 getFallback()。
                        @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,
                                         value = "50"),
                        //默认 5 秒;熔断多少秒后去尝试请求
                        @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,
                                         value = "5000"),
                    })
    @Override
    public Dept queryDept(long id) {
        Dept dept = deptMapper.queryDept(id);
        if (dept == null){
            throw new RuntimeException("id="+id+"=>没有对应的信息,null");
        }
        return dept;
    }
    public Dept getDeptHystrix(long id){
        return new Dept().setDeptId(id).
            setDpName("id="+id+"=>没有对应的信息,null").
            setDbSource("no database in MySQL");
    }
    @Override
    public List<Dept> queryAll() {
        return deptMapper.queryAll();
    }
}
复制代码


可以将@HystrixCommand 注解加在 Service 类上,也可以直接加在 controller 方法上,上述代码中我只加了关于 @GetMapping("/dept/get/{id}") 请求的熔断方法。


image.png


3、修改入口类


@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker       //开启熔断器
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class,args);
    }
}
复制代码


4、启动一个服务注册中心、本项目以及一个服务消费者即可,访问 http://localhost/consumer/dept/get/1 可以正常获取数据,但是访问 http://localhost/consumer/dept/get/10 页面显示内容为:


{"deptId":10,"dpName":"id=10=>没有对应的信息,null","dbSource":"no database in MySQL"}
复制代码


请求合并


没合并的请求与合并的请求


1.jpg

img


Hystrix中的请求合并,就是利用一个合并处理器,将对同一个服务发起的连续请求合并成一个请求进行处理(这些连续请求的时间窗默认为10ms),在这个过程中涉及到的一个核心类就是HystrixCollapser。


什么情况下使用请求合并


在微服务架构中会拆分成多个小的模块,各个模块之间进行调用是需要通信的,但是在并发量大的情况下会令大量线程处于等待状态,这就会增加响应时间,所以可以使用请求合并将多次请求合并为一次请求。


请求合并的代价是什么


在设置了请求合并以后,本来一次请求只需要5ms就搞定了,但是使用了请求合并后可能还需要再等待10ms,看看还有没有其他请求一起,这样一次请求就从5ms增加到了15ms。不过如果我们发起的命令本来就是一个高延迟的命令,那么这个时候就可以使用请求合并了,因为这个时候时间窗的时间消耗就显得微不足道了。

所以总结:请求合并适合在高延迟 + 大量并发的情况下使用。


服务提供者


我们根据 springcloud-provider-dept-8001 项目复制一个名为 springcloud-provider-dept-dgrade-8001 的新项目,作为服务提供者。


1、相关依赖不变


2、在 DeptService 接口上增加一个方法,用于多个 deptId 查询。


public interface DeptService {
    .....
    List<Dept> getDepts(List<Long> ids);
}
复制代码


3、同理修改 DeptMapper


@Mapper
@Repository
public interface DeptMapper {
    .....
    List<Dept> getDepts(List<Long> ids);
}
复制代码


4、修改 mapper 映射文件


<select id="getDepts" parameterType="list" resultType="Dept">
        select * from springcloud.dept
        <where>
            deptId in
            <foreach collection="list" item="id" open="(" close=")" separator=",">
                #{id}
            </foreach>
        </where>
    </select>
复制代码


5、DeptServiceImpl


@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    DeptMapper deptMapper;
    .....
    @Override
    public List<Dept> getDepts(List<Long> ids) {
        return deptMapper.getDepts(ids);
    }
}
复制代码


6、DeptController


@RequestMapping("/dept/getList/{ids}")
    public List<Dept> getDepts2(@PathVariable("ids") String sid) throws ExecutionException, InterruptedException {
        List<Long> ids = Arrays.stream(sid.split(",")).map(Long::parseLong).collect(Collectors.toList());
        List<Dept> list = deptService.getDepts(ids);
        return list;
    }
复制代码


用户可以在 url 地址中输入多个 id,来查询多条数据。


7、启动该项目,在浏览器中输入 http://localhost:8001/dept/getList/1,2 ,则可以得到数据显示。


服务消费者


新建项目名为 springcloud-consumer-dept-80-syn。


1、导入依赖


<dependencies>
        <dependency>
            <groupId>com.msdn</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!--ribbon依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    </dependencies>
复制代码


2、application.yml


server:
  port: 80
#eureka配置
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/
复制代码


3、Myconfig


package com.msdn.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
 * @author hresh
 * @date 2020/4/28 21:27
 * @description
 */
@Configuration
public class MyConfig {
    //配置负载均衡实现RestTemplate
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
复制代码


4、DeptConsumerService 请求合并逻辑


@Service
public class DeptConsumerService {
    @Autowired
    RestTemplate restTemplate;
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
    //利用 hystrix 合并请求
    @HystrixCollapser(batchMethod = "batchDept", scope =
            com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL,
            collapserProperties = {
                    //请求时间间隔在 2s 之内的请求会被合并为一个请求,默认为 10ms
                    @HystrixProperty(name =
                            "timerDelayInMilliseconds", value = "2000"),
                    //设置触发批处理执行之前,在批处理中允许的最大请求数。
                    @HystrixProperty(name = "maxRequestsInBatch",
                            value = "200"),
            })
    public Future<Dept> getDept2(Long id){
        return null;
    }
    @HystrixCommand
    public List<Dept> batchDept(List<Long> ids){
        System.out.println("batchDept---------"+ids+"Thread.currentThread().getName():" + Thread.currentThread().getName());
        List<Dept> list = new ArrayList<>();
        String sid = Joiner.on(",").join(ids);
        Dept[] depts = restTemplate.getForObject(REST_URL_PREFIX+"/dept/getList/"+sid, Dept[].class);
        list = Arrays.asList(depts);
        return list;
    }
}
复制代码


关于@HystrixCollapser 注解的参数详解,可以参考下图:


1.jpg


5、DeptConsumerController


@RestController
public class DeptConsumerController {
    @Autowired
    DeptConsumerService service;
    @RequestMapping("/consumer/dept/get/{id}")
    public Dept getDept(@PathVariable("id") long id) throws ExecutionException, InterruptedException {
        Future<Dept> dept1 = service.getDept2(id);
        return dept1.get();
    }
}
复制代码


6、入口类


@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class DeptConsumerSyn_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumerSyn_80.class,args);
    }
}
复制代码


7、启动一个注册中心,以及上述的服务提供者,和本项目,注意服务消费者项目中需要进入 debug 状态,并在 controller 类中的 getDept 方法处打上断点,然后在浏览器中打开两个网页分别输入 http://localhost/consumer/dept/get/3  和 http://localhost/consumer/dept/get/2 ,然后来到 IDEA 中通过断点调试,查看控制台内容输出。


2.jpg


通过结果可知,两次请求会合并为一次请求,减少了对数据库的访问次数。本例中通过 debug 设置断点将两次请求拦截下来,合并为一次请求进行处理。让业务逻辑层把单个查询的 sql,改为批量查询的 sql。


疑惑:本来想通过 Jmeter 进行高并发测试的,不过没有得到想要的结果,可能是我应用场景处理的有问题,所以只能通过 debug 来实现。


基于Feign实现的负载均衡


降级


关于 Feign 的实现可以参考我的上一篇文章,其中主要修改了 springcloud-api 项目。


1、导入依赖


<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.6.RELEASE</version>
</dependency>
复制代码


2、添加 DeptServiceFallbackFactory


//降级
@Component
public class DeptServiceFallbackFactory implements FallbackFactory {
    @Override
    public DeptFeignService create(Throwable throwable) {
        return new DeptFeignService() {
            @Override
            public boolean addDept(Dept dept) {
                return false;
            }
            @Override
            public Dept queryDept(long id) {
                return new Dept().setDeptId(id).
                        setDpName("id="+id+"=>没有对应的信息,null").
                        setDbSource("服务降级处理");
            }
            @Override
            public List<Dept> queryAll() {
                return null;
            }
        };
    }
}
复制代码


3、修改 DeptFeignService


@Service
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptServiceFallbackFactory.class)
public interface DeptFeignService {
    @PostMapping("/dept/add")
    boolean addDept(@RequestBody Dept dept);
    @GetMapping("/dept/get/{id}")
    Dept queryDept(@PathVariable("id") long id);
    @GetMapping("/dept/list")
    List<Dept> queryAll();
}
复制代码


接下来我们修改服务消费者 springcloud-consumer-dept-80-feign 项目。


1、修改配置文件


server:
  port: 80
#feign默认是不开启hystrix,默认值为false
feign:
  hystrix:
    enabled: true
#eureka配置
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001:7001/eureka/,http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/
复制代码


2、修改 DeptConsumerController


@RestController
public class DeptConsumerController {
    @Autowired
    DeptFeignService service;
    @RequestMapping("/consumer/dept/get/{id}")
    public Dept getDept(@PathVariable("id") long id) {
        return service.queryDept(id);
    }
    @RequestMapping("/consumer/dept/list")
    public List<Dept> queryAll() {
        return service.queryAll();
    }
    @RequestMapping(name = "/consumer/dept/add")
    public boolean addDept(Dept dept) {
        return service.addDept(dept);
    }
}
复制代码


3、启动一个注册中心和一个服务提供者,以及本项目,然后访问 http://localhost/consumer/dept/get/3,页面正常显示数据,当关闭服务提供者,再次访问上述网址,页面会显示如下内容:


{"deptId":3,"dpName":"id=3=>没有对应的信息,null","dbSource":"服务降级处理"}
复制代码


Hystrix-dashboard


Hystrix-dashboard 是一款针对 Hystrix 进行实时监控的工具,通过 Hystrix-dashboard 我们可以直观的看到各 Hystrix Command 的请求响应时间,请求成功率等数据。

新建一个名为 springcloud-consumer-hystrix-dashboard 的普通 maven 项目,当作监控中心。


1、导入相关依赖


<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    </dependencies>
复制代码


2、新增配置文件,设置端口号


server:
  port: 9001
复制代码


3、新增启动类


package com.msdn;
@SpringBootApplication
@EnableHystrixDashboard
@EnableHystrix
public class DeptConsumerDashboard {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumerDashboard.class);
    }
}
复制代码


4、启动该项目,访问 http://localhost:9001/hystrix,页面内容如下:


3.jpg


监控项目


对 springcloud-provider-dept-hystrix-8001 项目进行扩增。


在入口类 DeptProviderHystrix_8001 中增加 ServletRegistrationBean 注入。


@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker       //开启熔断器
public class DeptProviderHystrix_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderHystrix_8001.class,args);
    }
    @Bean
    public ServletRegistrationBean getServletRegistrationBean(){
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        servletRegistrationBean.addUrlMappings("/actuator/hystrix.stream");
        return servletRegistrationBean;
    }
}
复制代码


启动一个注册中心,并启动该监控项目,在监控中心页面输入上述图片中内容,然后点击按钮跳转。


1.jpg


当你不断访问 http://localhost:8001/dept/get/1,可以观察上述页面变化。


1.jpg


详情代码访问: https://github.com/Acorn2/springcloud-eureka ,如有疑惑可联系我。



相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
3天前
|
监控 Java API
Spring cloud Hystrix 、Dashboard、API(zuul)相关报错
Spring cloud Hystrix 、Dashboard、API(zuul)相关报错
18 2
|
3天前
|
SpringCloudAlibaba Java 持续交付
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
【构建一套Spring Cloud项目的大概步骤】&【Springcloud Alibaba微服务分布式架构学习资料】
204 0
|
3天前
|
Java Nacos 微服务
全面学习SpringCloud框架指南
V 哥建议:如果你正在学习SpringCloud,你可以把这篇文章作为学习指南,如果你已经学过了,可以查漏补缺,因为 V 哥这篇文章全面介绍了SpringCloud的方方面面,当然你还需要结合官方文档的细节和源码部分去学习,成功拿下SpringCloud不是问题。加油兄弟!
|
3天前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
3天前
|
监控 Java 微服务
第八章 Spring Cloud 之 Hystrix
第八章 Spring Cloud 之 Hystrix
14 0
|
3天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
3天前
|
Dubbo Java 应用服务中间件
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
|
3天前
Springcloud-ribbon和hystrix配置
Springcloud-ribbon和hystrix配置
10 0
|
3天前
|
SpringCloudAlibaba Java 测试技术
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(六)Hystrix(豪猪哥)的使用
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(六)Hystrix(豪猪哥)的使用
50 1
|
6月前
|
缓存 运维 监控
微服务技术系列教程(22) - SpringCloud- 服务保护机制Hystrix
微服务技术系列教程(22) - SpringCloud- 服务保护机制Hystrix
60 0