SpringCloud04_服务降级熔断限流(Hystrix)

Hystrix断路器介绍

复杂的链路结构调用,在其中一个服务出现问题时,会导致与其协调的服务运行压力越来越大,引起雪崩效应。
Hystrix通过断路器的故障监控,向调用方返回一个符合预期的、可处理的备选响应,而不是长时间等待或抛出异常。避免故障蔓延。

  • 降级:服务不可用时,向调用方返回一个符合预期的,可处理的的备选响应(fallback),运行异常、超时、熔断、线程池满等会触发降级。
  • 熔断:达到最大访问量,拒绝访问,调用服务降级方法返回。
  • 限流:高并发操作时,有序进行。

1、模拟超时

1.1 支付模块模拟高并发访问(Eureka服务注册);
在这里插入图片描述
PaymentController .java

@RestController
@RequestMapping("/payment")
@Slf4j
@RequiredArgsConstructor
public class PaymentController {

    private final PaymentService paymentService  ;
    
    @GetMapping("/hy/ok/{id}")
    public String getOk(@PathVariable Integer id){
        return paymentService.getOk(id);
    }

    @GetMapping("/hy/timeout/{id}")
    public String getTimeOut(@PathVariable Integer id){
        return paymentService.getTimeOut(id);
    }

}

PaymentService .java

public interface PaymentService extends IService<Payment> {
    public String getOk(Integer id);

    public String getTimeOut(Integer id);

}

PaymentServiceImpl .java

@Service
public class PaymentServiceImpl extends ServiceImpl<PaymentMapper, Payment> implements PaymentService {

    @Override
    public String getOk(Integer id) {
        return Thread.currentThread().getName()+"、ok:"+id;
    }

    @Override
    public String getTimeOut(Integer id) {
        int time = 3000;
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Thread.currentThread().getName()+"、ok:"+id + "耗时:"+ time;
    }
}

访问在这里插入图片描述
采坑8:@PathVariable(“id”)使用过程中需带上参数,不然可能报错;

2022-03-12 22:02:17.580 ERROR 24720 --- [  restartedMain] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'paymentController': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.payment.FeignService.PaymentFeignService': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: PathVariable annotation was empty on param 0.
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:337) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879) ~[spring-beans-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) ~[spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) [spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [spring-boot-2.2.2.RELEASE.jar:2.2.2.RELEASE]
	at com.payment.Consumer4Application.main(Consumer4Application.java:12) [classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.2.2.RELEASE.jar:2.2.2.RELEASE]

1.2 订单消费者模块模拟高并发访问(openfeign服务调用);
在这里插入图片描述
PaymentController .java

package com.payment.Controller;

import com.commons.Entity.Payment;
import com.commons.Entity.Result;
import com.payment.FeignService.PaymentFeignService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/consumer4")
public class PaymentController {

    @Resource
    private PaymentFeignService paymentFeignService;
    
    @GetMapping("/hy/ok/{id}")
    public String getOk(@PathVariable("id") Integer id){
        return paymentFeignService.getOk(id);
    }

    @GetMapping("/hy/timeout/{id}")
    public String getTimeOut(@PathVariable("id") Integer id){
        return paymentFeignService.getTimeOut(id);
    }

}

PaymentFeignService.java

package com.payment.FeignService;

import com.commons.Entity.Payment;
import com.commons.Entity.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "PAYMENT") //值为注册的支付模块微服务application.name
@Component
public interface PaymentFeignService {

    @GetMapping("/payment/hy/ok/{id}")
    public String getOk(@PathVariable("id") Integer id);

    @GetMapping("/payment/hy/timeout/{id}")
    public String getTimeOut(@PathVariable("id") Integer id);

}

  • 主程序:@EnableFeignClients //激活
@SpringBootApplication(scanBasePackages = "com.payment")
@EnableFeignClients //激活
public class Consumer4Application {

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

}
  • application.yml
    采坑9:需要开启超时控制,否则3秒会返回超时异常
2022-03-12 21:56:11.353 ERROR 25492 --- [nio-8085-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.RetryableException: Read timed out executing GET http://PAYMENT/payment/hy/timeout/1] with root cause

java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.8.0_181]
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) ~[na:1.8.0_181]
	at java.net.SocketInputStream.read(SocketInputStream.java:171) ~[na:1.8.0_181]
	at java.net.SocketInputStream.read(SocketInputStream.java:141) ~[na:1.8.0_181]
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246) ~[na:1.8.0_181]
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:286) ~[na:1.8.0_181]
	at java.io.BufferedInputStream.read(BufferedInputStream.java:345) ~[na:1.8.0_181]
	at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:735) ~[na:1.8.0_181]
	at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:678) ~[na:1.8.0_181]
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1587) ~[na:1.8.0_181]
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492) ~[na:1.8.0_181]
	at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) ~[na:1.8.0_181]
	at feign.Client$Default.convertResponse(Client.java:82) ~[feign-core-10.4.0.jar:na]
	at feign.Client$Default.execute(Client.java:78) ~[feign-core-10.4.0.jar:na]
	at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:93) ~[spring-cloud-openfeign-core-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:56) ~[spring-cloud-openfeign-core-2.2.1.RELEASE.jar:2.2.1.RELEASE]
	at com.netflix.client.AbstractLoadBalancerAwareClient$1.call(AbstractLoadBalancerAwareClient.java:104) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
	at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:303) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
	at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:287) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
	at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:231) ~[rxjava-1.3.8.jar:1.3.8]
	at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:228) ~[rxjava-1.3.8.jar:1.3.8]

server:
  port: 8085

eureka:
  client:
    register-with-eureka: false #是否要注册
    fetchRegistry: true #是否抓取注册信息
    service-url:
      #      defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001:7001/eureka #,http://eureka7002:7002/eureka


spring:
  application:
    name : consumer8085

  datasource:
    url: jdbc:mysql://localhost:3306/springboot-mybatisplus?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
    username: root
    password: 123456
    driver-class-name=com: mysql.cj.jdbc.Driver

#2、mybatis-plus配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config :
    db-config:
      logic-delete-value: 1
      logic-not-delete-value: 0
  mapper-locations: classpath:mapper/*.xml

logging:
  level:
     com.payment.FeignService: debug

feign:
  client:
    config:
      default:
        #简历连接所用的时间,适用于网络状况正常的情况下,两端连接所需要的时间
        ConnectTimeOut: 5000
        #指建立连接后从服务端读取到可用资源所用的时间
        ReadTimeOut: 10000

访问:在这里插入图片描述
此时如果在高并发情况下访问,就会造成服务延迟等待,会出现超时错误,此时就需要服务降级。

2、服务降级

2.1 降级配置

@HystrixCommand设置自身调用超时时间峰值,峰值内正常运行,超过峰值,作为服务降级fallback;

  • 2.1.1payment8001服务提供者-支付模块

pom.xml依赖

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

PaymentServiceImpl 设置@HystrixCommand

@Service
public class PaymentServiceImpl extends ServiceImpl<PaymentMapper, Payment> implements PaymentService {

    @Override
    @Transactional
    public boolean saveEntity(Payment payment) {
        baseMapper.insert(payment);
        return true;
    }

    @Override
    public Payment getPayment(Integer id) {
        Payment payment = baseMapper.selectById(id);
        return payment;
    }

    @Override
    public String getOk(Integer id) {
        return Thread.currentThread().getName()+"、ok:"+id;
    }

    @HystrixCommand(fallbackMethod = "TimeOutHandler",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    }) //如果该方法执行异常,执行TimeOutHandler方法
    @Override
    public String getTimeOut(Integer id) {
        int time = 5000;
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return Thread.currentThread().getName()+"、ok:"+id + "耗时:"+ time;
    }

    public String TimeOutHandler(Integer id){
        return Thread.currentThread().getName()+"执行异常";
    }

}

主启动类:@EnableCircuitBreaker

@SpringBootApplication(scanBasePackages = "com.payment")
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentApplication {

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

}

采坑10:fallbackMethod方法参数一定要跟本方法一致

There was an unexpected error (type=Internal Server Error, status=500).
fallback method wasn't found: TimeOutHandler([class java.lang.Integer])
com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException: fallback method wasn't found: TimeOutHandler([class java.lang.Integer])
	at com.netflix.hystrix.contrib.javanica.utils.MethodProvider$FallbackMethodFinder.doFind(MethodProvider.java:190)
	at com.netflix.hystrix.contrib.javanica.utils.MethodProvider$FallbackMethodFinder.find(MethodProvider.java:159)
	at com.netflix.hystrix.contrib.javanica.utils.MethodProvider.getFallbackMethod(MethodProvider.java:73)
	at com.netflix.hystrix.contrib.javanica.utils.MethodProvider.getFallbackMethod(MethodProvider.java:59)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.setFallbackMethod(HystrixCommandAspect.java:331)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.access$300(HystrixCommandAspect.java:64)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect$MetaHolderFactory.metaHolderBuilder(HystrixCommandAspect.java:176)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect$CommandMetaHolderFactory.create(HystrixCommandAspect.java:258)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect$MetaHolderFactory.create(HystrixCommandAspect.java:166)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.methodsAnnotatedWithHystrixCommand(HystrixCommandAspect.java:93)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
	at com.payment.Service.impl.PaymentServiceImpl$$EnhancerBySpringCGLIB$$5a388c27.getTimeOut(<generated>)
	at com.payment.Controller.PaymentController.getTimeOut(PaymentController.java:51)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)

访问:
在这里插入图片描述
除了在支付-服务提供者模块降级以外,一般推荐在订单-服务消费者模块使用降低

  • 2.1.2 cunsumer服务提供者-支付模块

pom.xml:与订单模块相同

application.yml开启

feign:
  hystrix:
    enabled: true

主启动类:@EnableHystrix激活

@SpringBootApplication(scanBasePackages = "com.payment")
@EnableFeignClients //激活
@EnableHystrix
public class Consumer4Application {

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

}

PaymentController .java

@RestController
@RequestMapping("/consumer4")
public class PaymentController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping("/payment/{id}")
    public Result getById (@PathVariable("id") Integer id){
        return paymentFeignService.getEntity(id);
    }

    @GetMapping("/hy/ok/{id}")
    public String getOk(@PathVariable("id") Integer id){
        return paymentFeignService.getOk(id);
    }

    @HystrixCommand(fallbackMethod = "TimeOutHandler",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    }) //如果该方法执行异常,执行TimeOutHandler方法
    @GetMapping("/hy/timeout/{id}")
    public String getTimeOut(@PathVariable("id") Integer id){
        return paymentFeignService.getTimeOut(id);
    }

    public String TimeOutHandler(Integer id){
        return Thread.currentThread().getName()+"执行异常";
    }
}

在这里插入图片描述
访问:
在这里插入图片描述

以上服务降级每个方法都需要配置@HystrixCommand降级方法,导致业务方法和逻辑方法混乱,也会导致代码膨胀。此时需要DefaultProperties。

2.2 DefaultProperties全局服务降级

@DefaultProperties加在类上面,当该类中的方法没有指定@HystrixCommand配置的fallbackMethod 时,使用该注解指定的降级方法。

采坑11:使用全局降级方法需要不带参数

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sat Mar 12 23:39:11 CST 2022
There was an unexpected error (type=Internal Server Error, status=500).
fallback method wasn't found: TimeOutHandler([])
com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException: fallback method wasn't found: TimeOutHandler([])
	at com.netflix.hystrix.contrib.javanica.utils.MethodProvider$FallbackMethodFinder.doFind(MethodProvider.java:190)
	at com.netflix.hystrix.contrib.javanica.utils.MethodProvider$FallbackMethodFinder.find(MethodProvider.java:159)
	at com.netflix.hystrix.contrib.javanica.utils.MethodProvider$FallbackMethodFinder.find(MethodProvider.java:161)
	at com.netflix.hystrix.contrib.javanica.utils.MethodProvider.getFallbackMethod(MethodProvider.java:73)
	at com.netflix.hystrix.contrib.javanica.utils.MethodProvider.getFallbackMethod(MethodProvider.java:59)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.setFallbackMethod(HystrixCommandAspect.java:331)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.access$300(HystrixCommandAspect.java:64)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect$MetaHolderFactory.metaHolderBuilder(HystrixCommandAspect.java:176)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect$CommandMetaHolderFactory.create(HystrixCommandAspect.java:258)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect$MetaHolderFactory.create(HystrixCommandAspect.java:166)
	at com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect.methodsAnnotatedWithHystrixCommand(HystrixCommandAspect.java:93)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)

以消费者-订单模块PaymentController 位例

@RestController
@DefaultProperties(defaultFallback = "TimeOutHandler",commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
}) //如果该方法执行异常,执行TimeOutHandler方法
@RequestMapping("/consumer4")
public class PaymentController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping("/payment/{id}")
    public Result getById (@PathVariable("id") Integer id){
        return paymentFeignService.getEntity(id);
    }

    @GetMapping("/hy/ok/{id}")
    public String getOk(@PathVariable("id") Integer id){
        return paymentFeignService.getOk(id);
    }

    @HystrixCommand
    @GetMapping("/hy/timeout/{id}")
    public String getTimeOut(@PathVariable("id") Integer id){
        return paymentFeignService.getTimeOut(id);
    }

    public String TimeOutHandler(){ //不带参数
        return Thread.currentThread().getName()+"执行异常";
    }
}

以上操作虽然简化了配置,但是降级逻辑和业务逻辑还是混合,耦合性较高,需要FenignFallback解耦。

2.3 FenignFallback通配服务降级
定义一个类,实现之前的业务调用接口,为该接口统一异常处理。

  • PaymentFeignServiceIml 实现类
package com.payment.FeignService;

import com.commons.Entity.Payment;
import com.commons.Entity.Result;
import org.springframework.stereotype.Component;

@Component
public class PaymentFeignServiceIml implements PaymentFeignService{

    @Override
    public Result<Payment> getEntity(Integer id) {
        return null;
    }

    @Override
    public String getOk(Integer id) {
        return Thread.currentThread().getName()+"ok";
    }

    @Override
    public String getTimeOut(Integer id) {
        return Thread.currentThread().getName()+"timeout";
    }
}

  • PaymentFeignService 接口添加@FeignClient(value = "PAYMENT" , fallback = PaymentFeignServiceIml.class)
//值为注册的支付模块微服务application.name: PAYMENT
//PaymentFeignServiceIml为错误处理实现类
@FeignClient(value = "PAYMENT" , fallback = PaymentFeignServiceIml.class)
@Component
public interface PaymentFeignService {

    @GetMapping("/payment/{id}") //对应支付模块该接口的地址
    public Result<Payment> getEntity(@PathVariable("id") Integer id);

    @GetMapping("/payment/hy/ok/{id}")
    public String getOk(@PathVariable("id") Integer id);

    @GetMapping("/payment/hy/timeout/{id}")
    public String getTimeOut(@PathVariable("id") Integer id);

}
  • PaymentController 去掉原降级方法
package com.payment.Controller;

import com.commons.Entity.Result;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.payment.FeignService.PaymentFeignService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@DefaultProperties(commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
}) //如果该方法执行异常,执行TimeOutHandler方法
@RequestMapping("/consumer4")
public class PaymentController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping("/payment/{id}")
    public Result getById (@PathVariable("id") Integer id){
        return paymentFeignService.getEntity(id);
    }

    @GetMapping("/hy/ok/{id}")
    public String getOk(@PathVariable("id") Integer id){
        return paymentFeignService.getOk(id);
    }

    @HystrixCommand
    @GetMapping("/hy/timeout/{id}")
    public String getTimeOut(@PathVariable("id") Integer id){
        return paymentFeignService.getTimeOut(id);
    }

//    public String TimeOutHandler(){
//        return Thread.currentThread().getName()+"执行异常";
//    }
}

  • application.yml 添加注解:feign.hystrix.enabled: true
server:
  port: 8085

eureka:
  client:
    register-with-eureka: false #是否要注册
    fetchRegistry: true #是否抓取注册信息
    service-url:
      #      defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001:7001/eureka #,http://eureka7002:7002/eureka

#ribbon:
#  ReadTimeOut: 5000 #请求连接时间
#  ConnectT888imeout: 5000 #资源处理时间

spring:
  application:
    name : consumer8085

  datasource:
    url: jdbc:mysql://localhost:3306/springboot-mybatisplus?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
    username: root
    password: 123456
    driver-class-name=com: mysql.cj.jdbc.Driver

#2、mybatis-plus配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config :
    db-config:
      logic-delete-value: 1
      logic-not-delete-value: 0
  mapper-locations: classpath:mapper/*.xml

logging:
  level:
     com.payment.FeignService: debug

#feign:
#  client:
#    config:
#      default:
#        #简历连接所用的时间,适用于网络状况正常的情况下,两端连接所需要的时间
#        ConnectTimeOut: 5000
#        #指建立连接后从服务端读取到可用资源所用的时间
#        ReadTimeOut: 10000

feign:
  hystrix:
    enabled: true

目录:
在这里插入图片描述
访问:
在这里插入图片描述

3、服务熔断

当扇出链路的某个服务出错不可用或者响应时间过长,会进行服务降级,熔断该微服务节点,快速返回响应。当检测到该节点正常以后,恢复该链路

  • PaymentController
@HystrixCommand(fallbackMethod = "circuitBreaker", commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "1000"),//时间窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")//失败率达到多少
package com.payment.Controller;

import com.commons.Entity.Result;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.payment.FeignService.PaymentFeignService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@DefaultProperties(commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
}) //如果该方法执行异常,执行TimeOutHandler方法
@RequestMapping("/consumer4")
public class PaymentController {

    @Resource
    private PaymentFeignService paymentFeignService;

    @GetMapping("/payment/{id}")
    public Result getById(@PathVariable("id") Integer id) {
        return paymentFeignService.getEntity(id);
    }

    @GetMapping("/hy/ok/{id}")
    public String getOk(@PathVariable("id") Integer id) {
        return paymentFeignService.getOk(id);
    }

    @HystrixCommand
    @GetMapping("/hy/timeout/{id}")
    public String getTimeOut(@PathVariable("id") Integer id) {
        return paymentFeignService.getTimeOut(id);
    }


    @HystrixCommand(fallbackMethod = "circuitBreaker", commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "1000"),//时间窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")//失败率达到多少
    })
    @GetMapping("get/hy/{id}")
    public String getCircuitBreaker(@PathVariable("id") Integer id) {
        if(id < 0){
            throw new RuntimeException("id不能为负数");
        }
        return paymentFeignService.getTimeOut(id);
    }

    public String circuitBreaker(Integer id) {
        return Thread.currentThread().getName() + "熔断";
    }

//    public String TimeOutHandler(){
//        return Thread.currentThread().getName()+"执行异常";
//    }
}

其他内容相同

  • 目录
    在这里插入图片描述
  • 访问在这里插入图片描述
    当满足配置条件后就会熔断,但是正常之后不会立即恢复,会在正确率等条件满足条之后慢慢恢复。

熔断器有3个重要参数:

  • 快照时间窗:是否熔断需要统计一些数据,统计的时间范围叫做快照时间窗,默认为最近10秒;
  • 请求总数阈值:快照时间窗内,请求数达到一定阈值才有资格熔断,意味着10秒内,调用次数达到阈值数才会熔断;
  • 错误百分比:在快照时间窗内,达到请求总数阈值,如在10秒内,调用30次,15次异常,达到50%错误百分比,就会熔断。

熔断器开启或者关闭条件:

  • 快照时间窗内,请求次数要达到请求总数阈值,错误百分比要达到设置值,就会熔断开启;
  • 一段时间后(默认5秒),处于半开状态,会让一个请求进行转发,如果成功,在熔断关闭,如果失败,继续开启,等待下一个5秒。

4、图形化Dashboard搭建

Hystrix提供了准实时的调用监控,持续记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户。

(1)监控方搭建

  • 监控方依赖
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>
  • pom.xml
<?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">
    <parent>
        <artifactId>cloud2022</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>hystrix</groupId>
    <artifactId>hystrix</artifactId>

    <dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>
        <dependency>
            <groupId>com.commons</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>
  • application.yml
server:
  port: 9001

spring:
  application:
    name : hystrix9001

  datasource:
    url: jdbc:mysql://localhost:3306/springboot-mybatisplus?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
    username: root
    password: 123456
    driver-class-name=com: mysql.cj.jdbc.Driver

  • 主启动类:@EnableHystrixDashboard
package com.hystrix;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@SpringBootApplication(scanBasePackages = "com.hystrix")
@EnableHystrixDashboard
public class HyStrixApplication {

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

}

采坑11

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class


Action:

Consider the following:
	If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
	If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).


Process finished with exit code 0

缺少数据库配置:https://blog.csdn.net/qq_45498460/article/details/112860809

  • 目录:

在这里插入图片描述

  • 访问:http://localhost:9001/hystrix
    在这里插入图片描述

(2)被监控方搭建

  • 被监控方依赖
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

在原来服务熔断-消费者订单模块的例子上修改主启动类,其他熔断内容不变;

  • 主启动类:
package com.payment;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;

@SpringBootApplication(scanBasePackages = "com.payment")
@EnableFeignClients //激活
@EnableHystrix
public class Consumer4Application {

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

    /**
     *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
     *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
     *只要在自己的项目里配置上下面的servlet就可以了
     */
    @Bean
    public ServletRegistrationBean getServlet() {
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}

  • 访问:http://localhost:9001/hystrix
  • 填写被监控方信息
    在这里插入图片描述
    访问监控熔断测试:
    在这里插入图片描述
    监控结果:
    在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值