点击蓝色“程序猿DD”关注我哟
有多少人从未在生产环境中遇到系统崩溃或故障?当然,你们每个人迟早都会经历它。如果我们无法避免失败,那么解决方案似乎是将我们的系统维持在永久性故障状态。Chaos Monkey - 这个概念是Netflix发明的工具,用于测试其IT基础架构的弹性。几天前,我根据Netflix工具背后的想法找到了解决方案,该工具旨在测试Spring Boot应用程序。Codecentric已经实现了这样的库。到目前为止,我认为它们只是作为Spring Boot生态系统专用的其他有趣解决方案的作者 - Spring Boot Admin。我已经在之前的一篇文章中描述了这个库使用 Monitoring Microservices With Spring Boot Admin。
今天我将向您展示如何在Spring Boot应用程序中包含Codecentric的Chaos Monkey,然后在一些微服务示例系统中实现混沌工程。Chaos Monkey库可以与Spring Boot 2.0一起使用,它的当前发行版本是1.0.1。但是,我将使用2.0.0-SNAPSHOT版本实现示例,因为它具有此库的早期版本中不能用的一些新的有趣功能。为了能够下载Codecentric的Chaos Monkey库的SNAPSHOT版本,您必须记住包含Maven存储库https://oss.sonatype.org/content/repositories/snapshots到您的pom.xml存储库中。
为Spring Boot应用程序启用Chaos Monkey 有两个必需的步骤。首先,让我们将chaos-monkey-spring-boot库添加到项目的依赖项中。
然后,我们应该在应用程序启动时激活chaos-monkey文件。
$ java -jar target/order-service-1.0-SNAPSHOT.jar --spring.profiles.active=chaos-monkey
我们的示例系统由三个微服务组成,每个微服务都在两个实例中启动,还有一个服务发现服务器。微服务将自己注册到发现服务器,并通过HTTP API相互通信。Chaos Monkey库包含在所有正在运行的微服务的每个实例中,但不包含在发现服务器中。下面是用于说明示例系统架构的图表。
样本应用程序的源代码可以在GitHub的仓库 sample-spring-chaosmonkey中找到。克隆此存储库并使用mvn clean install命令构建它之后,您应该首先运行discovery-service。然后通过-Dserver.port使用适当的数字设置属性,在不同端口上运行每个微服务的两个实例。下面是我的一组运行命令。
$ java -jar target/discovery-service-1.0-SNAPSHOT.jar
$ java -jar target/order-service-1.0-SNAPSHOT.jar --spring.profiles.active=chaos-monkey
$ java -jar -Dserver.port=9091 target/order-service-1.0-SNAPSHOT.jar --spring.profiles.active=chaos-monkey
$ java -jar target/product-service-1.0-SNAPSHOT.jar --spring.profiles.active=chaos-monkey
$ java -jar -Dserver.port=9092 target/product-service-1.0-SNAPSHOT.jar --spring.profiles.active=chaos-monkey
$ java -jar target/customer-service-1.0-SNAPSHOT.jar --spring.profiles.active=chaos-monkey
$ java -jar -Dserver.port=9093 target/customer-service-1.0-SNAPSHOT.jar --spring.profiles.active=chaos-monkey
在版本2.0.0-SNAPSHOT的chaos-monkey-spring-boot库中,Chaos Monkey默认启用它包含的应用程序。您可以使用chaos.monkey.enabled属性禁用它。但是,默认情况下启用的唯一攻击是延迟。这种类型的攻击会通过属性chaos.monkey.assaults.latencyRangeStart和chaos.monkey.assaults.latencyRangeEnd确定的范围为应用程序处理的请求添加随机延迟。受攻击请求的数量取决于属性chaos.monkey.assaults.level,其中1表示每个请求,10表示每第10个请求。我们还可以为我们的应用程序启用异常和appKiller攻击。为简单起见,我为所有微服务设置了配置。我们来看看application.yml文件中提供的设置。
chaos:
monkey:
assaults:
level: 8
latencyRangeStart: 1000
latencyRangeEnd: 10000
exceptionsActive: true
killApplicationActive: true
watcher:
repository: true
restController: true
理论上,上面显示的配置应该启用所有三种可用类型的攻击。但是,如果启用latency和exceptions,killApplication永远不会发生。此外,如果同时启用latency和exceptions,则无论使用chaos.monkey.assaults.level属性设置哪个级别,发送给应用程序的所有请求都将受到攻击。记住激活restController观察者很重要,默认情况下禁用观察者。
Codecentric在其Chaos Monkey库的2.0版本中实现了一项新功能 - Spring Boot Actuator的端点。要为我们的应用程序启用它,我们必须按照执行程序约定激活它 - 通过将属性management.endpoint.chaosmonkey.enabled设置为true。此外,从Spring Boot 2.0版开始,我们必须公开该HTTP端点在应用程序启动后可用。
management:
endpoint:
chaosmonkey:
enabled: true
endpoints:
web:
exposure:
include: health,info,chaosmonkey
chaos-monkey-spring-boot提供了几个端点,允许你查看和修改配置。你可以使用GET /chaosmonkey方法来获取库的整个配置。你也可以在启动应用程序后,通过调用/chaosmonkey/disable方法,禁用chaos monkey。此处列出了可用端点的完整列表:https://codecentric.github.io/chaos-monkey-spring-boot/2.0.0-SNAPSHOT/#endpoints。
所有示例微服务都在MySQL中存储数据。因此,第一步是使用Docker镜像在本地运行MySQL数据库。下面显示的Docker命令还会创建具有密码的数据库和用户。
$ docker run -d --name mysql -e MYSQL_DATABASE=chaos -e MYSQL_USER=chaos -e MYSQL_PASSWORD=chaos123 -e MYSQL_ROOT_PASSWORD=123456 -p 33306:3306 mysql
在运行所有示例应用程序之后,所有微服务都在两个实例中使用不同端口,我们的环境如下图所示。
应用程序启动期间,您将在日志中看到以下信息。
我们可以通过调用以下执行器端点来检查每个运行的应用程序实例的Chaos Monkey配置设置。
出于测试目的,我使用了流行的性能测试库--Gatling。它创建了20个并发线程,每个线程通过order-service 的API网关公开调用500次POST /orders和GET /order/{id}方法。
class ApiGatlingSimulationTest extends Simulation {
val scn = scenario("AddAndFindOrders").repeat(500, "n") {
exec(
http("AddOrder-API")
.post("http://localhost:8090/order-service/orders")
.header("Content-Type", "application/json")
.body(StringBody("""{"productId":""" + Random.nextInt(20) + ""","customerId":""" + Random.nextInt(20) + ""","productsCount":1,"price":1000,"status":"NEW"}"""))
.check(status.is(200), jsonPath("$.id").saveAs("orderId"))
).pause(Duration.apply(5, TimeUnit.MILLISECONDS))
.
exec(
http("GetOrder-API")
.get("http://localhost:8090/order-service/orders/${orderId}")
.check(status.is(200))
)
}
setUp(scn.inject(atOnceUsers(20))).maxDuration(FiniteDuration.apply(10, "minutes"))
}
POST端点在OrderController 的 add(...)方法内部实现。它调用由OpenFeign客户端公开的customer-service和product-service使用的查找方法。如果客户有足够的资金且库存中仍有产品,则它接受订单并使用PUT方法对客户和产品进行更改。下面是Gatling性能测试测试的两种方法的实现。
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
OrderRepository repository;
@Autowired
CustomerClient customerClient;
@Autowired
ProductClient productClient;
@PostMapping
public Order add(@RequestBody Order order) {
Product product = productClient.findById(order.getProductId());
Customer customer = customerClient.findById(order.getCustomerId());
int totalPrice = order.getProductsCount() * product.getPrice();
if (customer != null && customer.getAvailableFunds() >= totalPrice && product.getCount() >= order.getProductsCount()) {
order.setPrice(totalPrice);
order.setStatus(OrderStatus.ACCEPTED);
product.setCount(product.getCount() - order.getProductsCount());
productClient.update(product);
customer.setAvailableFunds(customer.getAvailableFunds() - totalPrice);
customerClient.update(customer);
} else {
order.setStatus(OrderStatus.REJECTED);
}
return repository.save(order);
}
@GetMapping("/{id}")
public Order findById(@PathVariable("id") Integer id) {
Optional order = repository.findById(id);
if (order.isPresent()) {
Order o = order.get();
Product product = productClient.findById(o.getProductId());
o.setProductName(product.getName());
Customer customer = customerClient.findById(o.getCustomerId());
o.setCustomerName(customer.getName());
return o;
} else {
return null;
}
}
// ...
}
Chaos Monkey将随机延迟设置在1000到10000毫秒之间(如步骤3所示)。 在开始测试之前更改Feign和Ribbon客户端的默认超时非常重要。我决定设置readTimeout为5000毫秒。这将导致一些延迟的请求超时,而一些将请求成功(约50%-50%)。下面是Feign客户端的超时配置。
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
hystrix:
enabled: false
这是API网关的功能区客户端超时配置。我们还更改了Hystrix的设置以禁用Zuul的断路器。
ribbon:
ConnectTimeout: 5000
ReadTimeout: 5000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 15000
fallback:
enabled: false
circuitBreaker:
enabled: false
要启动Gatling性能测试,请转到performance-test目录和运行gradle loadTest命令。这是针对设置延迟攻击生成的结果。当然,我们可以通过设置Chaos Monkey的延迟值或Ribbon和Feign超时值来更改此结果。
这是具有平均响应时间的Gatling图。结果看起来不太好。但是,我们应该记住从order-service的单个POST方法调用product-service的两个方法和customer-service的两个方法。
下面是另一个Gatling结果图 - 这次它说明了时间轴上的错误和成功响应。Gatling在性能测试期间生成的所有HTML报告都可以在performance-test/build/gatling-results目录下找到
原文链接:https://piotrminkowski.wordpress.com/2018/05/23/chaos-monkey-for-spring-boot-microservices/
作者:Piotr Mińkowski
译者:xuli
号外:最近整理了之前编写的一系列内容做成了PDF,关注我并回复相应口令获取:
- 001 :领取《Spring Boot基础教程》
- 002 :领取《Spring Cloud基础教程》
更多内容陆续奉上,敬请期待
- END -
近期热文:
2019
我在星球与大家聊聊技术人的斜杠生活
释放焦虑、拥抱自由
看完,赶紧点个“好看”鸭
点鸭点鸭
↓↓↓↓