自己实现hystrix

简介: 自己实现hystrix

在这里插入图片描述

一、功能设计

拦截远程调用,当远程服务异常,则拦截,直接返回;
若远程服务正常,则放行调用。

二、窗口滑动

在一个时间窗口
11:01->11:02->11:03->11:04->11:05
在一个窗口时间内,若调用失败次数达到一个值,做什么事情

在这里插入图片描述

三、熔断器的状态 HystrixStatus

/**
* 断路器的状态
* @author WHX
*/
public enum HystrixStatus {
   
   
    OPEN(0),
    CLOSE(1),
    HALF_OPEN(2); // 半开是关的一种策略 半开: 使用少许请求测试提供者是否活了

    // 半开是: 开(多一点) + 关
    HystrixStatus(int code) {
   
   
    }
}

四、断路器接口设计 HystixCmd

/**
 * 断路器功能设计
 * @author WHX
 */
public interface HystixCmd {
   
   
    /**
     * 拦截请求
     *
     * @param request
     * @return
     */
    Object interceptor(ProceedingJoinPoint request);

    /**
     * 通过请求
     *
     * @return
     */
    Object pass(ProceedingJoinPoint request);

    /**
     * 测试请求
     *
     * @param request
     * @return
     */
    Object test(ProceedingJoinPoint request);

    /**
     * 修改拦截器的状态
     *
     * @param status
     */
    void changeStatus(HystrixStatus status);

    /**
     * 获取自己的状态
     *
     * @return
     */
    HystrixStatus getStatus();
}

五、切面拦截远程请求的方法 HystixAspect

@Aspect
@Component
public class HystixAspect {
   
   
    /**
     * key: 服务的id
     * value:该服务对应的拦截器
     */
    private Map<String, HystixCmd> hystixCmds = new HashMap<String, HystixCmd>();

    {
   
   
//注入一个此服务的拦截器
        hystixCmds.put("server-id", new HystixCmdImpl());
    }

    @Around("@annotation(com.sxt.feign.core.anno.HystixCmdRpc)")
    public Object interceptor(ProceedingJoinPoint point) {
   
   
// 从容器里面取自己的拦截器
        HystixCmd hystixCmd = hystixCmds.get("server-id");
        switch (hystixCmd.getStatus()) {
   
   
            case OPEN:
                return hystixCmd.interceptor(point);
            case CLOSE:
                return hystixCmd.pass(point);
            case HALF_OPEN:
// 有结果,没有抛异常
                System.out.println("半开,我用3%的几率访问一下,看提供者活了没有");
                Object result = hystixCmd.test(point);
                return result;
            default:
                break;
        }
        return null; // 没有符合的条件
    }
}

六、拦截器的实现HystixCmdImpl 实现上面的断路器接口

public class HystixCmdImpl implements HystixCmd {
   
   
    /**
     * 一个窗口的时间是5
     */
    private static Long WINDOW_SLIDE_TIME = 5000L;
    /**
     * 在一个窗口内失败10 次就代表远程服务异常
     */
    private static Integer MAX_FAIL_COUNT = 3;
    /**
     * 锁对象
     */
    private Object lock = new Object();
    /**
     * 默认是关的
     */
    private HystrixStatus status = HystrixStatus.CLOSE;
    /**
     * 随机数
     */
    private static Random RDM = new Random();
    /**
     * 在一个窗口怎么统计次数
     */
    private AtomicInteger currentFallCount = new AtomicInteger(0);

    {
   
   
// 定时任务做啥事情, 若一个窗口里面它没有达到失败的阈值,但是我们不能让该值去影响下个窗口,就需要清空该窗口的值
        new Thread(() -> {
   
   
            while (true) {
   
   
                try {
   
   
                    Thread.sleep(WINDOW_SLIDE_TIME); // 5s 执行一次检查
                } catch (InterruptedException e1) {
   
   
                    e1.printStackTrace();
                }
                if (this.getStatus() == HystrixStatus.CLOSE) {
   
    // 断路器关闭
                    currentFallCount.set(0);
                } else {
   
    // 断路器打开了 或半开
                    System.out.println("断路器打开了,我统计失败次数,没有任何意义,我先死一会");
                    synchronized (lock) {
   
   
                        try {
   
   
//少许流量测试通过后再唤醒我
                            lock.wait();
                            System.out.println("远程服务正常了,我需要在启动清空窗口数据了");
                            currentFallCount.set(0);
                        } catch (InterruptedException e) {
   
   
                            e.printStackTrace();
                        }
                    }

                }
            }
        }).start();
    }

    /**
     * 拦截请求
     */
    @Override
    public Object interceptor(ProceedingJoinPoint request) {
   
    // 不调用正常的值,使用备胎值

        MethodSignature methodSignature = (MethodSignature) request.getSignature();
        Method method = methodSignature.getMethod(); // 这是正常的实现类方法,我们需要调用备胎里面的方法
// 现在没法得到接口和实现类
        Object object = getFallCallback(method);
        Method callBack = null;
        try {
   
   
            callBack = object.getClass().getMethod(method.getName(), method.getParameterTypes());
        } catch (NoSuchMethodException | SecurityException e1) {
   
   
            e1.printStackTrace();
        }
        Object fallBackResult = null;
        try {
   
   
            fallBackResult = callBack.invoke(object, request.getArgs()); // 直接调用备胎里面的方法
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
   
   
            e.printStackTrace();
        }
        return fallBackResult;
    }

    /**
     * 正常调用
     */
    @Override
    public Object pass(ProceedingJoinPoint request) {
   
   
        Object result = null;
        try {
   
   
            result = request.proceed(request.getArgs());
        } catch (Throwable e) {
   
    // 有异常了,若异常为超时异常,代表失败了 一段时间失败n 次
// e.printStackTrace();
// 若失败了,我把失败次数++
            currentFallCount.getAndIncrement();
            if (currentFallCount.get() >= MAX_FAIL_COUNT) {
   
    // 失败超过阈值
                System.out.println("当前的窗口里面已经达到失败的阈值了,我把断路器打开");
                this.changeStatus(HystrixStatus.OPEN); // 打开断路器,一段时间后,把断路器改为半开
                new Thread(() -> {
   
   
                    try {
   
   
                        Thread.sleep(5000L);
                        System.out.println("过了一段时间,我把断路器该为半开");
                        this.changeStatus(HystrixStatus.HALF_OPEN);
                    } catch (InterruptedException e1) {
   
   
                        e1.printStackTrace();
                    }
                }).start();
            }
// 失败了,我们也不直接报错,而是返回备胎的结果
            result = interceptor(request);
        }
        return result;
    }

    @Override
    public Object test(ProceedingJoinPoint request) {
   
   
// 断路器半开,使用少许的流量测试
// 3% 100 3
        int num = RDM.nextInt(100) + 1; //1 100
        Object result = null;
        if (num <= 3) {
   
    // 做测试
            System.out.println("test中奖,去测试提供者是否活了");
            try {
   
   
                result = request.proceed(request.getArgs());
// 测试通过了,关闭断路器
                this.changeStatus(HystrixStatus.CLOSE);
// 开启窗口滑动来清空计数
                synchronized (lock) {
   
   
                    System.out.println("远程调用的测试已经通过,远程服务正常");
//唤醒窗口线程
                    lock.notifyAll();
                }
            } catch (Exception e) {
   
   
// 进来,就没有通过
                System.out.println("测试没有通过1");
// e.printStackTrace();
                return interceptor(request);
            } catch (Throwable throwable) {
   
   
// 进来,就没有通过
                System.out.println("测试没有通过2");
            }
        } else {
   
   
            System.out.println("未进测试,直接返回备胎结果");
            return interceptor(request);
        }
        return result;
    }

    @Override
    public void changeStatus(HystrixStatus status) {
   
   
        this.status = status;
    }

    @Override
    public HystrixStatus getStatus() {
   
   
        return this.status;
    }

    private Object getFallCallback(Method method) {
   
   
        HystixCmdRpc annotation = method.getAnnotation(HystixCmdRpc.class);
        Class<?> callback = annotation.callback();
        try {
   
   
            return callback.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
   
   
            e.printStackTrace();
        }
        return null;
    }
}

七、使用注解来实现接口的获取 @HystixCmdRpc

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HystixCmdRpc {
   
   
    /**
     * 该方法调用失败了,我做啥
     *
     * @return 是备胎的类型
     */
    Class<?> callback() default Object.class;
}

八、测试:远程调用的接口 RpcService

远程调用的接口 RpcService

public interface RpcService {
   
   
    /**
     * 执行远程调用
     * @return
     */
    public String rpc();
}

正常调用的实现类

@Service
public class RpcServiceImpl implements RpcService {
   
   
    @Autowired
    private RestTemplate rest;

    /**
     * 正常的调用getForObject
     * 不正常走RpcFallCallback 里面的rpc 方法
     */
    @Override
    @HystixCmdRpc(callback = RpcFallCallback.class)
    public String rpc() {
   
   
        String result = rest.getForObject("http://localhost:8081/info", String.class);
        return result + "-------";
    }
}

失败的(备胎)实现类

public class RpcFallCallback implements RpcService{
   
   
    @Override
    public String rpc() {
   
   
        return "我是备胎";
    }
}

yml

server:
  port: 8087
spring:
  application:
    name: feign-client
eureka:
  client:
    service-url:
      defaultZone: http://nnhx.top:8761/eureka/,http://nnhx.top:8762/eureka/,http://nnhx.top:8763/eureka/

启动类:

@SpringBootApplication
@RestController
public class FeignClientApplication {
   
   
    @Autowired
    private RpcService rpcService;
    public static void main(String[] args) {
   
   
        SpringApplication.run(FeignClientApplication.class, args);
    }
    @Bean
    public RestTemplate rest() {
   
   
        return new RestTemplate();
    }
    @GetMapping("/rpc/testwu")
    public String sdfds() {
   
   
        int i=0;
        while(i<1000000000){
   
   
            String rpc = rpcService.rpc();
            System.out.println(rpc);
            i++;
        }
        return "rpcOK";
    }
}

启动提供者,在启动此消费者,访问http://localhost:8087/rpc/testwu查看消费者控制台--》关掉提供者,观察消费者控制台--》再重启提供者,观察消费者控制台;
在这里插入图片描述
在这里插入图片描述

相关文章
|
6月前
服务熔断器-Hystrix
服务熔断器-Hystrix
59 2
|
5月前
springCloud之服务降级熔断Hystrix、OpenFeign
springCloud之服务降级熔断Hystrix、OpenFeign
349 0
|
6月前
|
监控 Java Sentinel
springcloud4-服务熔断hystrix及sentinel
springcloud4-服务熔断hystrix及sentinel
69 0
|
监控 API 开发者
13SpringCloud - 断路器(Hystrix)
13SpringCloud - 断路器(Hystrix)
60 0
|
监控 Java Apache
详解Hystrix
1.微服务中的容错 1.1.服务雪崩 要说容错的话,肯定是有多种维度的。横向维度上来说,分布式架构,天然就带有分区容错性,多节点部署相同的服务就是为了容错,保证其中某些节点挂掉后,其它节点任然能提供该类服务。微服务种更需要考虑的是纵向维度上的容错机制,防止服务雪崩。 所谓的服务雪崩,指的是服务间存在着纵向的链路式的调用关系: 服务A调用服务B,服务B调用服务C。
145 1
|
存储 缓存 监控
SpringCloud极简入门-熔断器Hystrix
在电影里面经常出现的场景,在冰山雪地不要大声呼喊,因为声音的震动会导致雪球的滑落,然后引起连锁反应导致整个雪山的崩塌这就是生活中的雪崩。在微服务里面也是一样,服务的调用非常复杂的 ,一个请求往往需要很多的微服务共同完成,可能会形成很长的服务调用链,在整个服务调用链中,某一个服务发生故障会导致调用它的服务跟着异常,然后导致整个调用链调用的异常,甚至导致整个微服务瘫痪 , — 这就是雪崩效应。
289 0
|
消息中间件 缓存 监控
Spring Cloud Hystrix 断路器
在微服务架构中,我们将系统拆分成了若干弱小的单元,单元与单元之间通过HTTP或者TCP等方式相互访问,各单元的应用间通过服务注册与订阅的方式相互依赖。由于每个单元都在不同的进程中运行,依赖远程调用的方式执行,这样就可能引起因为网速变慢或者网络故障导致请求变慢或超时,若此时调用方的请求在不断增加,最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪。
242 0
Spring Cloud Hystrix 断路器
|
监控 Java 微服务
Hystrix讲解与应用
Hystrix讲解与应用
101 0
Hystrix讲解与应用
|
监控 数据可视化 Java
SpringCloud之Hystrix断路器
SpringCloud之Hystrix断路器
120 0
SpringCloud之Hystrix断路器
|
负载均衡 监控 安全
Hystrix 断路器是什么|学习笔记
快速学习 Hystrix 断路器是什么
Hystrix 断路器是什么|学习笔记
下一篇
无影云桌面