paint-brush
如此调整 Spring WebFlux 软件应用过程 经历过@vladimirf
7,509 讀數
7,509 讀數

如何调试 Spring WebFlux 应用程序

通过 Vladimir Filipchenko7m2023/05/09
Read on Terminal Reader

太長; 讀書

调试 Spring WebFlux 应用程序可能是一项具有挑战性的任务,尤其是在处理复杂的反应流时。阻塞代码、并发问题和竞争条件等问题都可能导致难以诊断的细微错误。对于熟悉 Reactive 应用程序的人来说,根本原因很容易找到。但是,下面的一些做法可能仍然非常有助于修改。
featured image - 如何调试 Spring WebFlux 应用程序
Vladimir Filipchenko HackerNoon profile picture
0-item
1-item
操作子环节 Spring WebFlux APP子环节将会是这项具备有的挑战的任務,尤其要是在处理繁琐的发生不良反应流时。与过去的的闭塞APP子环节各种,在过去的的闭塞APP子环节中,堆栈追综还可以清理地显示疑问的完全的原因,发生不良反应式APP子环节将会更难操作子环节。闭塞码、消息队列疑问和行业條件等疑问都将会引起没办法初步判断的小小出错。


设想

在处置严重错误时,它并不常常与二维码相关的的困难。它可以是一种缘由,举列近期的抽象化、精英团队改变、硬期效等。在现实中日常中,终极处理好小型操作执行子程序的错误码是很通常的小事,那些操作执行子程序是由马上前搬出平台可你刚开始融入的人打造的。


对许多 各个领域和技术水平详细了解丝毫并会让您的活动更方便。


在下面的代碼范本中,想必美感的有缺陷的代碼针对近几天参加销售团队的人而言会是之类状态。


将调整此代码怎么用更好像那段之旅而是挑战。在熟练 Reactive 应运流程的人言之,实际情况很更容易寻找。只不过,底下的很多家常做法也许 已经非常的有助修正。


 @GetMapping("/greeting/{firstName}/{lastName}") public Mono<String> greeting(@PathVariable String firstName, @PathVariable String lastName) { return Flux.fromIterable(Arrays.asList(firstName, lastName)) .filter(this::wasWorkingNiceBeforeRefactoring) .transform(this::senselessTransformation) .collect(Collectors.joining()) .map(names -> "Hello, " + names); } private boolean wasWorkingNiceBeforeRefactoring(String aName) { // We don't want to greet with John, sorry return !aName.equals("John"); } private Flux<String> senselessTransformation(Flux<String> flux) { return flux .single() .flux() .subscribeOn(Schedulers.parallel()); }


所以,你们的编码所做的是:它在看作基本参数提供了的分类前插入“Hello,”。您的老同事 John 问他您在他的学习苹果笔记本PC上任何事物正常人。它是真正:


 > curl localhost:8080/greeting/John/Doe > Hello, Doe


但是当你像curl localhost:8080/greeting/Mick/Jagger一样运行它时,你会看到下一个堆栈跟踪:


 java.lang.IndexOutOfBoundsException: Source emitted more than one item at reactor.core.publisher.MonoSingle$SingleSubscriber.onNext(MonoSingle.java:134) ~[reactor-core-3.5.5.jar:3.5.5] Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Error has been observed at the following site(s): *__checkpoint ⇢ Handler com.example.demo.controller.GreetingController#greeting(String, String) [DispatcherHandler] *__checkpoint ⇢ HTTP GET "/greeting/Mick/Jagger" [ExceptionHandlingWebHandler] Original Stack Trace: <18 internal lines> at java.base/java.util.concurrent.FutureTask.run(FutureTask.java) ~[na:na] (4 internal lines)


非常好的,两条线案件线索都就不会会造成下面的编号实例。


它所揭示的只是 1) 它发生在GreetingController#greeting方法中,以及 2) 客户端执行了一个`HTTP GET "/greeting/Mick/Jagger

.doOnError()


先是也是最简简单单的试过是将 .doOnError() 调整增多到祝愿语链的律师。


 @GetMapping("/greeting/{firstName}/{lastName}") public Mono<String> greeting(@PathVariable String firstName, @PathVariable String lastName) { return Flux.fromIterable(Arrays.asList(firstName, lastName)) // <...> .doOnError(e -> logger.error("Error while greeting", e)); }


挺好的测试,但运行日志不会提示 丝毫改良。


一直以来尽管,Reactor 的内壁堆栈跟踪定位:



以下是doOnError在调试过程中可以/不能提供帮助的一些方法:

  1. 日志记录:您可以使用doOnError来记录错误消息并提供有关反应流中出错的更多上下文。这在调试具有许多运算符的复杂流中的问题时特别有用。

  2. 恢复doOnError也可用于从错误中恢复并继续处理流。例如,您可以使用onErrorResume在出现错误时提供回退值或流。

  3. 调试doOnError很可能不会提供任何更好的堆栈跟踪,除了您已经在日志中看到的内容。不要依赖它作为一个好的故障排除程序。


日志()


下一站是用log()方法调用替换之前添加的doOnError() 。越简单越好。默认情况下log()会观察所有 Reactive Streams 信号并将它们跟踪到 INFO 级别的日志中。


让我们看看我们现在在日志中看到的附加信息:


我们可以看到调用了哪些 Reactive 方法( onSubscriberequestonError )。此外,了解从哪些线程(池)中调用了这些方法可能是非常有用的信息。但是,它与我们的案例无关。


关于线程池


线程名称ctor-http-nio-2代表reactor-http-nio-2 。响应式方法onSubscribe()request()在 IO 线程池(调度程序)上执行。这些任务在提交它们的线程上立即执行。


通过在senselessTransformation中使用.subscribeOn(Schedulers.parallel())我们指示 Reactor 在另一个线程池上订阅更多元素。这就是为什么onErrorparallel-1线程上执行的原因。


您可以在本文中阅读有关线程池的更多信息。


log()方法允许您将日志记录语句添加到流中,从而更轻松地跟踪数据流和诊断问题。如果我们有更复杂的数据流,比如 flatMap、子链、阻塞调用等,我们将从将它们全部记录下来受益匪浅。对于日常使用来说,这是一件非常简单和美好的事情。但是,我们仍然不知道根本原因。


Hooks.onOperatorDebug() 方法


指令Hooks.onOperatorDebug()告诉 Reactor 为反应流中的所有操作符启用调试模式,允许更详细的错误消息和堆栈跟踪。


根据官方文档:

稍后观查到有误时,将的使用简单说明书原史零件线堆栈的抑制性失败来充裕植物的根。应该在现实的加载制作者(举列 Flux.map、Mono.fromCallable)刚刚加载以过滤恰当的堆栈相关信息。


该指令表应在所有加载时取用单次。最好的选择的好地方之中是配置单或主类。就人们的用例,它将是:


 public Mono<String> greeting(@PathVariable String firstName, @PathVariable String lastName) { Hooks.onOperatorDebug(); return // <...> }


通过添加Hooks.onOperatorDebug()我们终于可以在调查中取得进展。 Stacktrace 更有用:



在第 42 行,我们有single()调用。


不要向上滚动,接下来是senselessTransformation

 private Flux<String> senselessTransformation(Flux<String> flux) { return flux .single() // line 42 .flux() .subscribeOn(Schedulers.parallel()); }


这可是几乎缘故。


single()从 Flux 源发出一项,或为具有多个元素的源发出IndexOutOfBoundsException信号。这意味着该方法中的通量会发出不止一项。通过在调用层次结构中往上走,我们看到最初有一个包含两个元素的 Flux Flux.fromIterable(Arrays.asList(firstName, lastName))


过滤方法wasWorkingNiceBeforeRefactoring在它等于John时从 flux 中删除一个项目。这就是代码适用于名为 John 的大学的原因。嗯。


Hooks.onOperatorDebug()在调试复杂的反应流时特别有用,因为它提供了有关如何处理流的更多详细信息。但是,启用调试模式会影响应用程序的性能(由于填充的堆栈跟踪),因此它应该只在开发和调试期间使用,而不应在生产中使用。


检查点


为了以最小的性能影响实现与Hooks.onOperatorDebug()几乎相同的效果,有一个特殊的checkpoint()运算符。它将为流的该部分启用调试模式,同时不影响流的其余部分。


让我们在过滤后和转换后添加两个检查点:


 public Mono<String> greeting(@PathVariable String firstName, @PathVariable String lastName) { return Flux.fromIterable(Arrays.asList(firstName, lastName)) .filter(this::wasWorkingNiceBeforeRefactoring) /* new */ .checkpoint("After filtering") .transform(this::senselessTransformation) /* new */ .checkpoint("After transformation") .collect(Collectors.joining()) .map(names -> "Hello, " + names); }


看看日志:


这个检查点故障告诉我们,在我们描述为After transformation 的第二个检查点之后观察到了错误。这并不意味着在执行过程中没有到达第一个检查点。是的,但是错误仅在第二个之后才开始出现。这就是为什么我们看不到After filtering 的原因。


您还能够 找到层级中说的其他5个檢查点,产自和 。用户是在我们大家设有的,抵达的,长期到加载要素结构设计。


除了描述之外,您还可以通过将true作为第二个参数添加到checkpoint()方法来强制 Reactor 为您的检查点生成堆栈跟踪。请务必注意,生成的堆栈跟踪将引导您找到带有检查点的行。它不会为原始异常填充堆栈跟踪。所以它没有多大意义,因为您可以通过提供描述轻松找到检查点。


结论


按照遵守那些较佳实践操作,您还可以简易化复位环节并快速的识别系统和彻底解决 Spring WebFlux APP步骤中的疑问。不管怎样您是充实经验充实的制作人還是才刚准备崩溃式编程序,那些方式 都将作用您增强编码的质量管理和正规性,而且为您的访客展示 更有效的体验性。


바카라사이트 바카라사이트 온라인바카라