哪些情况会出现降级
1.程序运行异常
2.超时
3.线程池/信号量打满也会导致服务降级
等等
服务超时案例
我们让方法延时5秒运行 但是我们服务端只能接受3秒的等待
cloud-provider-hystrix-paymen8001 项目
启动类
@EnableEurekaClient
@EnableCircuitBreaker
pom配置
server:
port: 8001
spring:
application:
name: cloud-provider-hystrix-payment #这个很重要 消费者要指定 集群同理
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka #注册中心
service
package com.hexu.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* @Classname PaymentService
* @Description TODO
* @Author 86176
* @Date 2021-12-31 11:14
* @Version 1.0
**/
@Service
public class PaymentService
{
/**
* 正常访问,一切OK
* @param id
* @return
*/
public String paymentInfo_OK(Integer id)
{
return "线程池:"+Thread.currentThread().getName()+"paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O";
}
/**
* 超时访问,演示降级
* fallbackMethod 表示一个兜底的方法 程序运行过程中出现问题会执行这个方法
* value 表示只能等待多少毫秒 超过及降级处理
*/
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value="3000")
})
public String paymentInfo_TimeOut(Integer id)
{
//延时5面说明程序出问题了
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O,耗费5秒";
}
/**
* 兜底方法 方法名要与 fallbackMethod 里的对应
* @param id
* @return
*/
public String paymentInfo_TimeOutHandler(Integer id){
return "/(ㄒoㄒ)/调用支付接口超时或异常:\t"+ "\t当前线程池名字" + Thread.currentThread().getName();
}
}
controller
package com.hexu.controller;
import com.hexu.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @Classname PaymentController
* @Description TODO
* @Author 86176
* @Date 2021-12-31 11:14
* @Version 1.0
**/
@RestController
@Slf4j
public class PaymentController
{
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
/**
* OK的方法
* @param id
* @return
*/
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id)
{
String result = paymentService.paymentInfo_OK(id);
log.info("****result: "+result);
return result;
}
/**
* 演示降级处理的方法
* @param id
* @return
* @throws InterruptedException
*/
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException
{
String result = paymentService.paymentInfo_TimeOut(id);
log.info("****result: "+result);
return result;
}
}
可以看见 与兜底方法结果一致
消费者不嫌事大 继续调用提供者
cloud-consumer-feign-hystrix-order80 项目
启动类
@EnableFeignClients
@EnableHystrix
pom
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
feign:
circuitbreaker:
enabled: true
service
service接口
package com.hexu.service;
import org.springframework.cloud.openfeign.EnableFeignClients;
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;
/**
* @Classname PaymentHystrixService
* @Description TODO
* @Author 86176
* @Date 2021-12-31 15:35
* @Version 1.0
**/
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfo_OK(@PathVariable("id") Integer id);
/**
* @GetMapping("/payment/hystrix/ok/{id}")这个就相当于调用了提供者服务
* @param id
* @return
*/
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
service实现类
package com.hexu.service;
import org.springframework.stereotype.Component;
/**
* @Classname PaymentFallbackService
* @Description TODO 统一为接口里面的方法进行异常处理
* @Author 86176
* @Date 2022-01-01 16:17
* @Version 1.0
**/
@Component
public class PaymentFallbackService implements PaymentHystrixService{
@Override
public String paymentInfo_OK(Integer id) {
return "服务调用失败,提示来自:cloud-consumer-feign-order80";
}
@Override
public String paymentInfo_TimeOut(Integer id) {
return "服务调用失败,提示来自:cloud-consumer-feign-order80";
}
}
controller
package com.hexu.controller;
import com.hexu.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @Classname OrderHystirxController
* @Description TODO
* @Author 86176
* @Date 2021-12-31 15:40
* @Version 1.0
**/
@RestController
@Slf4j
public class OrderHystirxController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
/**
* @param id
* @return
*/
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
}
消费端调用提供方
这时我们把提供方服务关了
服务熔断
一句话就是家里的保险丝 当错误达到一定数量时拉闸
多次错误,然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行
1.熔断打开 : 请求不再进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态
2.熔断关闭 : 熔断关闭不会对服务进行熔断
3.熔断半开 : 部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断
修改cloud-provider-hystrix-paymen8001 项目
service增加
//=========服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
//当达到多少数量时开启
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
//在多少时间类
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
//错误的百分率
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
if (id < 0) {
throw new RuntimeException("******id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " + id;
}
controller调用
/**
* 服务熔断测试
* @param id
* @return
*/
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id)
{
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result: "+result);
return result;
}
服务监控hystrixDashboard
HystrixDashboardMain9001项目+新注解@EnableHystrixDashboard
pom配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
yml
server:
port: 9001
hystrix:
dashboard:
proxy-stream-allow-list: "*"
继续修改cloud-provider-hystrix-paymen8001 项目
启动类增加
/**
*此配置是为了服务监控而配置,与服务容错本身无关,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;
}
测试8001端口