SpringCloud之Hystrix熔断降级(六)

前言

  Hystrix是一个用于处理分布式系统的延迟容错和开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能保证在一个依赖出现问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性

  “断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障 监控(类似熔断保险丝),向调用方返回一个符合预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

服务熔断

  熔断机制是应对雪崩效应的一种微服务 链路保护机制;

  当扇出链路的某个服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回“错误”的响应信息,当检测到该节点微服务调用响应正常后 恢复调用链路,在springcloud框架里熔断机制通过Hystrix实现,Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒20次调用失败就会启动熔断机制,熔断机制的注解是@HystrixCommand

服务降级

  整体资源快不够用了,忍痛将某些服务先关掉,待度过难关,在开启回来。

  所谓降级,就是一般是从整体符合考虑,就是当某个服务熔断之后,服务器将不再被调用,此刻客户端可以自己准备一个本地的fallback回调,返回一个缺省值,这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强。

服务熔断环境搭建

1. 创建springcloud-provider-dept-hystrix-8001服务提供者

① 修改pom依赖,和springcloud-provider-dept-8001中的pom相同,复制过来 然后添加Hystrix依赖

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

② 编写application.ym配置文件

server:
  port: 8001                                         # 当前微服务的端口

mybatis:
  config-location: classpath:mybatis/mybatis-config.xml # mybatis配置文件所在路径
  type-aliases-package: com.common.springcloud.entity    # 所有饿entity实体类所在包
  mapper-locations:
    - classpath:mybatis/mapper/*.xml                # mapper映射文件

spring:
  application:
    name: springcloud-provider-dept-hystrix            # 对外暴露的微服务的名字(很重要)
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource      # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver        # mysql驱动包
    url: jdbc:mysql://localhost:3306/db01?useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=UTF-8&useSSL=false        # 数据库名称
    username: root
    password: 123456
  druid:
    # 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个,
    #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串
    test-on-borrow: false
    # 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.
    #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串
    test-while-idle: true
    # 指明是否在归还到池中前进行检验,注意: 设置为true后如果要生效,
    #validationQuery参数必须设置为非空字符串
    test-on-return: false
    # SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.
    #如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录
    validation-query: select 1

eureka:
  client:   #客户端注册进eureka服务列表内
    service-url:
      defaultZone: http://eureka1:7001/eureka/,http://eureka2:7002/eureka/,http://eureka3:7003/eureka/      #去注册中心的地址
      #defaultZone:7001,7002,7003 去注册中心的地址
  instance:
    instance-id: springcloud-dept-hystrix8001   #自定义hystrix相关的服务名称信息
    prefer-ip-address: true                         #访问路径可以显示ip地址

info:
  app.name: wj-springcloud
  company.name: www.wj.com
  build.artifactId: $project.artifactId$
  build.version: $project.version$                   

③ 在相关类添加注解

Controller类

package com.yt.springcloud.controller;
import java.util.List;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.yt.springcloud.entity.Dept;
import com.yt.springcloud.service.impl.DeptService;

@RestController//整合了responceBody+Controller
public class DeptController {

    @Autowired
    private DeptService service = null;

    @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
    //一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
    @HystrixCommand(fallbackMethod = "processHystrix_Get")//发生异常的时候,会调用这个fallbackmethod方法,去处理
    public Dept get(@PathVariable("id") Long id){

        Dept dept = this.service.get(id);
        
        if (null == dept) {
            throw new RuntimeException("该ID:" + id + "没有没有对应的信息");
        }
        
        return dept;
    }

    /**
     * 发生异常的时候,会调用这个方法来处理,还是返回一个dept对象,但是里面的信息是我们自定义的
   * 下面的链路赋值形式:new ObjectTest().setXX().setXX().....; 需要导入lombok依赖即可 *
@param id * @return */ public Dept processHystrix_Get(@PathVariable("id") Long id){ return new Dept().setDeptno(id).setDname("该ID:" + id + "没有没有对应的信息,null--@HystrixCommand") .setDb_source("no this database in MySQL"); } }

ApplicationBoot启动类

package com.yt.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker//对hystrixR熔断机制的支持!!!
public class DeptProvider8001_Hystrix_App
{
    public static void main(String[] args)
    {
        SpringApplication.run(DeptProvider8001_Hystrix_App.class, args);
    }
}
 

测试

启动Eureka服务注册服务,如果搭建了集群,全部启动也可。

启动Hystrix服务提供者服务

启动服务消费者


 打开注册Eureka页面:http://localhost:7001/  #打开自己设置URL即可

进行接口调用

测试id为1,数据是OK的

当的id不存在的时候,会进入容错的方法,返回刚刚设置的返回值


服务降级环境搭建

  服务降级处理是在客户端完成的,与服务端没有关系

  修改springcloud-api工程,根据已经有的DeptClientService接口新建一个实现了FallbackFactory接口的类DeptClientServiceFallbackFactory

package com.yt.springcloud.service;
import java.util.List;
import org.springframework.stereotype.Component;
import com.yt.springcloud.entity.Dept;
import feign.hystrix.FallbackFactory;
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService>
{
    @Override
    public DeptClientService create(Throwable throwable)
    {
        return new DeptClientService() {
            @Override
            public Dept get(long id)
            {
                return new Dept().setDeptno(id).setDname("该ID:" + id + "没有没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭")
                        .setDb_source("no this database in MySQL");
            }

            @Override
            public List<Dept> list()
            {
                return null;
            }

            @Override
            public boolean add(Dept dept)
            {
                return false;
            }
        };
    }
}

修改springcloud-api工程,DeptClientService接口在注解@FeignClient

import com.common.springcloud.pojo.Dept;
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.PostMapping;
import java.util.List;
//@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")//针对于更细粒化的控制,针对哪一个微服务进行面向接口的feign的
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT", fallback = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
    @GetMapping("/dept/get/{id}")
    public Dept queryId(@PathVariable("id") Long id);

    @GetMapping("/dept/list")
    public List<Dept> queryAll();

    @PostMapping("/dept/list")
    public boolean addDept(Dept dept);
}

修改springcloud-consumer-dept-feign工程的application.yml配置文件

测试

1. 启动3个Eureka

2. 微服务springcloud-provider-dept-8001启动

3. springcloud-consumer-dept-feign启动

4. 正常访问测试: http://localhost/consumer/dept/get/1

5. 故意关闭微服务springcloud-provider-dept-8001

6. 客户端(消费者)自己调用提示

 

此时服务端provider已经down了,但是我们做了服务降级处理,让客户端不可用时也会获得提示信息而不会挂起耗死服务器。

可以看做是服务提供者的一个应急策略,返回服务坏掉的信息。

posted @ 2019-12-08 18:58  王大军  阅读(437)  评论(0编辑  收藏  举报