目录
一、服务熔断介绍
二、商品服务端熔断案例
1、引入依赖
2、开启熔断,在入口类加入注解
3、指定熔断后的备选方案
4、注册服务
5、进行测试
三、订单服务端熔断案例
1、引入依赖
2、开启openfeign,在入口类加入注解
3、OpenFeign远端调用接口加入注解
4、熔断备选方案
5、调用 openfeign 接口
6、开启 hystrix 熔断服务
7、进行测试
商品服务 宕机情况:
商品服务 异常情况
商品服务熔断
总结:
服务熔断作用:就是防止服务雪崩现象出现。
服务熔断:正常情况出现超时、异常、宕机等情况就是服务降级,只有当默认10秒内超过20个请求次数 或者 默认10内超过50%的请求失数 的情况,才是熔断,此时才开启熔断器。
服务熔断机制:所有微服务都需要引入Hystrix组件,即每个服务都有自己的监控器。当某个微服务请求达到阈值,则开启断路器,进行服务熔断。具体如下:
此案例是以 商品服务 和 订单服务为基础,先演示商品服务的熔断。在演示订单服务的熔断。
商品服务端熔断,我们通过浏览器(来模拟订单服务)访问的商品服务,看 商品服务容端的过程。
springcloudbase
com.hwadee.springcloud2022
0.0.1-SNAPSHOT
4.0.0
com.hwadee.springcloud
productServer9001
0.0.1-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
org.springframework.boot
spring-boot-starter-web
com.hwadee.springcloud
springcloud-api
0.0.1-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.projectlombok
lombok
开启熔断,在入口类使用注解 @EnableCircuitBreaker 激活 Hystrix 熔断服务,代码如下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient// 启动 eureka 客户端
@EnableCircuitBreaker // 主启动类激活 Hystrix
public class ProductServerApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServerApplication.class, args);
}
}
本案例中以第一种方式为案例。
在异常方法上使用注解 @HystrixCommand(),并指定备选方案(即降级方法)。
@RequestMapping(value = "/select/{id}")
// @HystrixCommand 中指定备选方案
@HystrixCommand(fallbackMethod= "selectHystrixBreakerFallback")
public Product selectHystrixBreaker(@PathVariable Long id) { ....... }
// 备选方案 即 降级方法public Product selectHystrixBreakerFallback(Long id) { ..... }
完整代码:
import com.hwadee.springcloud.entity.Product; import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; @RestController @RequestMapping("/product") public class ProductController { //方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip @Value("${server.port}") private String port; @Value("${spring.cloud.client.ip-address}") private String ip; @RequestMapping(value = "/select/{id}") @HystrixCommand(fallbackMethod = "selectHystrixBreakerFallback", commandProperties = { @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器 @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),//请求次数 @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期 @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),//失败率达到多少后跳闸 }) public Product selectHystrixBreaker(@PathVariable Long id) { // 程序异常 测试异常熔断 if (id<=0) { throw new RuntimeException("id无效"); } // 程序正常执行 Product product = new Product(); product.setId(id); // 后面需要测试负载均衡,所以返回 ip 地址及端口号 product.setName("当前访问服务地址:" + ip + ":" + port + " " + "从购物车删除订单,订单号:" + id); product.setPrice(new BigDecimal(10000.0)); System.out.println(product); //测试超时熔断 try { //测试并发熔断 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return product; } public Product selectHystrixBreakerFallback(Long id) { Product product = new Product(); product.setId(id); product.setName("当前访问服务地址:" + ip + ":" + port + " " + "查询订单异常,通过注解 @HystrixCommand()指定的备选方案进行服务熔断"); product.setPrice(new BigDecimal(10000.0)); return product; } }
在异常方法上使用注解 @HystrixCommand(),使用默认备选方案(即全局降级方法)。
@RequestMapping(value = "/select/{id}")
// @HystrixCommand 中指定备选方案@HystrixCommand(defaultFallback= "selectHystrixFallback")
public Product selectHystrixBreaker(@PathVariable Long id) { ....... }
// 备选方案 即 全局降级方法,无参数public Product selectHystrixFallback() { ..... }
完整代码
package com.hwadee.springcloud.controller; import com.hwadee.springcloud.entity.Product; import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; @RestController @RequestMapping("/product") public class ProductController { //方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip @Value("${server.port}") private String port; @Value("${spring.cloud.client.ip-address}") private String ip; @RequestMapping(value = "/select/{id}") @HystrixCommand(defaultFallback = "selectHystrixFallback") public Product selectHystrixBreaker(@PathVariable Long id) { // 程序异常 测试异常熔断 if (id<=0) { throw new RuntimeException("id无效"); } //测试超时熔断 try { //测试并发熔断 Thread.sleep(900); } catch (InterruptedException e) { e.printStackTrace(); } // 程序正常执行 Product product = new Product(); product.setId(id); // 后面需要测试负载均衡,所以返回 ip 地址及端口号 product.setName("当前访问服务地址:" + ip + ":" + port + " " + "从购物车删除订单,订单号:" + id); product.setPrice(new BigDecimal(10000.0)); System.out.println(product); return product; } public Product selectHystrixFallback() { Product product = new Product(); product.setName("当前访问服务地址:" + ip + ":" + port + " " + "查询订单异常,通过注解 @HystrixCommand()指定的默认备选方案进行服务熔断"); product.setPrice(new BigDecimal(10000.0)); System.out.println(product); return product; } }
在Controller类上使用注解 @DefaultProperties(),使用默认备选方案(即全局降级方法)。
@RestController
@RequestMapping("/product")
@DefaultProperties(defaultFallback = "hystrixFallback") // 中指定备选方案
public class ProductController {
@RequestMapping(value = "/select/{id}")
@HystrixCommand
public Product selectHystrixBreaker(@PathVariable Long id) { ....... }
// 备选方案 即 全局降级方法,无参数public Product hystrixFallback() { ..... }
}
完整代码
package com.hwadee.springcloud.controller; import com.hwadee.springcloud.entity.Product; import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; @RestController @RequestMapping("/product") @DefaultProperties(defaultFallback = "hystrixFallback") public class ProductController { //方便后面讲负载均衡,查看ip,此处获取配置中的端口号和ip @Value("${server.port}") private String port; @Value("${spring.cloud.client.ip-address}") private String ip; @RequestMapping(value = "/select/{id}") @HystrixCommand public Product selectHystrixBreaker(@PathVariable Long id) { // 程序异常 测试异常熔断 if (id<=0) { throw new RuntimeException("id无效"); } //测试超时熔断 try { //测试并发熔断 Thread.sleep(900); } catch (InterruptedException e) { e.printStackTrace(); } // 程序正常执行 Product product = new Product(); product.setId(id); // 后面需要测试负载均衡,所以返回 ip 地址及端口号 product.setName("当前访问服务地址:" + ip + ":" + port + " " + "从购物车删除订单,订单号:" + id); product.setPrice(new BigDecimal(10000.0)); System.out.println(product); return product; } public Product hystrixFallback() { Product product = new Product(); product.setName("当前访问服务地址:" + ip + ":" + port + " " + "查询订单异常,通过注解 @HystrixCommand()指定默认的全局备选方案进行服务熔断"); product.setPrice(new BigDecimal(10000.0)); return product; } }
server: port: 9001 spring: application: name: product-service # 为当前商品服务命名 eureka: client: service-url: # 配置服务注册地址,与 eureka-server 中暴露地址保持一致 defaultZone: http://localhost:8000/eureka instance: prefer-ip-address: true # 是否使用 IP 地址注册,默认 false # instance-id: product-service # 实例 id,服务的唯一标识 instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置 lease-renewal-interval-in-seconds: 5 # 发送心跳的间隔,单位秒,默认 30 lease-expiration-duration-in-seconds: 10 # 续约到期时间,单位秒,默认90
在浏览器发送 商品服务 错误请求:http://localhost:9001/product/select/0 20次
迅速发送正确请求:http://localhost:9001/product/select/1
持续5s发送正确请求:http://localhost:9001/product/select/1
观察结果发现,当在浏览器发送20个错误请求后,错误率已经超过50%,断路器开启,再次访问正确请求也会进行服务降级,此时断路器处于开启状态。持续发送正确请求5s后,则会发先可以正常访问。
订单服务端熔断,我们通过浏览器访问的订单服务,通过订单服务的接口访问商品服务容端的熔断过程(相当于上面讲的浏览器直接访问商品服务)。在订单服务端的熔断,使用openfeign接口的方式。
因为使用 openfeign 接口远端调用,所以引入 spring-cloud-starter-openfeign 依赖,其中集成了 hysrix,所以不需要额外引入 hysrix 依赖。
springcloudbase
com.hwadee.springcloud2022
0.0.1-SNAPSHOT
4.0.0
com.hwadee.springcloud
orderServer9000
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-starter-web
com.hwadee.springcloud
springcloud-api
0.0.1-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.projectlombok
lombok
开启 openfeign,在入口类加入注解 @EnableFeignClients 。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient// 启动 eureka 客户端
@EnableFeignClients // 启动 feign
public class OrderServerApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServerApplication.class, args);
}
}
创建 OpenFeign 远端调用接口,在接口加入注解@FeignClient ,通过 @FeignClient 指定熔断后的备选方案。
import com.hwadee.springcloud.entity.Product;
import com.hwadee.springcloud.service.impl.OrderFeignServiceFallBack;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Component // 让 spring 可以识别,不加也行,但是在注入的时候 IDEA 会报错,不会影响运行,但有条红线让自己不舒服
@FeignClient(value = "PRODUCT-SERVICE",fallback = OrderFeignServiceFallBack.class)
public interface IOrderFeignService {
@RequestMapping(value = "/product/select/{id}")
Product selectOrderById(@PathVariable Long id);
}
因为 订单服务 是通过 接口调用 商品服务,因此,只需在 OpenFeign 接口的实现类中完成备选方案,将所有的降级方法提取到 OpenFeign 接口的实现类中。当OpenFeign 接口访问有异常 或 超时 或 宕机 时,则使用 OpenFeign 接口中对应的方法实现作为降级或熔断。
@Component
public class OrderFeignServiceFallBack implements IOrderFeignService {
@Override
public Product selectOrderById(Long id) {
Product product = new Product();
product.setId(id);
product.setName("当前订单服务访问/order/select/1 请求服务熔断,进行降级:"+id);
return product;
}
}
import com.hwadee.springcloud.entity.Product;
import com.hwadee.springcloud.service.IOrderFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
IOrderFeignService orderFeignService;
@RequestMapping(value = "/select/{id}")
public Product selectOrderById(@PathVariable Long id) {
Product product = orderFeignService.selectOrderById(id);
return product;
}
}
开启 openfeign 调用过程中对 hystrix 服务的支持。
server:
port: 9000
spring:
application:
name: order-service # 为当前订单服务命名为 order-service
# 配置eureka客户端信息
eureka:
client:
service-url:
# 配置eureka客户端服务order-service的注册地址,与 eureka-server 中暴露地址要保持一致
defaultZone: http://localhost:8000/eureka/
instance:
prefer-ip-address: true # 是否使用 IP 地址注册,默认 false
# instance-id: order-service # 实例 id,服务的唯一标识,会自动的找到order-service的ip和端口
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 如果想在控制页面看到服务地址与端口,可以将 instance-id 这样配置
# 开启 openfeign 的 hystrix 服务
feign:
hystrix:
enabled: true
此处测试分三种情况:服务宕机、服务异常和服务熔断
关闭 商品服务 ,依次访问 商品服务和 订单服务 两个网址,查看效果。
商品服务 http://localhost:9001/product/select/0
订单服务 http://localhost:9000/order/select/1
通过观察发现,如果 商品服务 宕机,直接访问会显示错误页面,但是 订单服务 使用了降级熔断,访问时候会在 订单服务 端进行服务降级熔断。
依次访问 订单服务 的两个网址 ,查看效果。
http://localhost:9000/order/select/0
http://localhost:9000/order/select/1
通过观察发现:
当访问 订单服务 的 http://localhost:9000/order/select/0 时候,会在 商品服务 端发生异常,商品服务 进行服务降级,返回降级的内容。
当访问 订单服务 的 http://localhost:9000/order/select/1 时候,在 商品服务 端不发生异常,商品服务 正常返回。
快速访问 订单服务错误请求 http://localhost:9000/order/select/0 20次
接着快速访问 订单服务正确 http://localhost:9000/order/select/1
5s后再访问 订单服务 http://localhost:9000/order/select/1
通过观察发现:
快速访问 订单服务错误请求 http://localhost:9000/order/select/0 20次后触发商品服务熔断机制,熔断断路器开启后其他请求如: http://localhost:9000/order/select/1 使用 商品服务 的备选方案。默认5s后可以尝试发送第一个请求 http://localhost:9000/order/select/1 成功,此时熔断器关闭。
简单理解,服务熔断在服务超时、异常、宕机情况下,就是服务降级。当默认10秒内超过20个请求次数 或者 默认10内超过50%的请求失数 的情况,才是熔断。
第九章:Hystrix断路器详解+服务降级之全局解耦
第十一章:GetAway服务网关详解