repeatWhenEmpty 运算符在 RSocket websocket 请求的上下文中不起作用,但在 HTTP 调用中起作用
repeatWhenEmpty operator doesn't work in the context of an RSocket websocket request but it does in HTTP calls
我遇到了 repeatWhenEmpty
运算符的奇怪行为,这对我来说没有任何意义。我在 Github 上上传了一个包含最小可重现样本的存储库:https://github.com/codependent/rsocket-rating-service.
考虑这个控制器,它提供两个端点(HTTP @GetMapping("/requestRating")
- WebSocket @MessageMapping("request-rating")
。注意这个调用 .repeatWhenEmpty(Repeat.onlyIf<Any> { true }.backoff(Backoff.fixed(Duration.ofSeconds(1))))
在 generateRating()
链的中间:
@RestController
class RatingServiceRestController {
private val FAIL_RATE = 0
private val logger = LoggerFactory.getLogger(javaClass)
@GetMapping("/requestRating")
fun getRatingHttp(ratingRequest: RatingRequest): Mono<Rating> {
return generateRating(ratingRequest)
}
@MessageMapping("request-rating")
fun getRatingWebSocket(ratingRequest: RatingRequest): Mono<Rating> {
return generateRating(ratingRequest)
}
private fun generateRating(ratingRequest: RatingRequest): Mono<Rating> {
return doGenerateRating(ratingRequest)
.doOnNext {
logger.info("Next1 {}", it)
}
.doOnCancel {
logger.info("Cancel1")
}
.doOnSuccess {
logger.info("Success1 {}", it)
}
.doOnError { throwable ->
logger.error("Error1 {}", throwable)
}
.doOnTerminate {
logger.info("Terminate1")
}
.repeatWhenEmpty(Repeat.onlyIf<Any> { true }.backoff(Backoff.fixed(Duration.ofSeconds(1))))
.doOnNext {
logger.info("Next2 {}", it)
}
.doOnCancel {
logger.info("Cancel2")
}
.doOnSuccess {
logger.info("Success2 {}", it)
}
.doOnError { throwable ->
logger.error("Error2 {}", throwable)
}
.doOnTerminate {
logger.info("Terminate2")
}
}
private fun doGenerateRating(ratingRequest: RatingRequest): Mono<Rating> {
return Mono.defer {
val random = (0..100).random()
if (random <= FAIL_RATE) {
Mono.empty()
} else {
Mono.just(Rating(ratingRequest.songId, (0..10).random()))
}
}
}
}
启动应用程序后,我可以调用 http://localhost:8080/requestRating?songId=1234
并得到 returns 结果,按预期显示这些日志:
RatingServiceRestController : Next1 Rating(songId=1234, value=1)
RatingServiceRestController : Success1 Rating(songId=1234, value=1)
RatingServiceRestController : Terminate1
RatingServiceRestController : Cancel1
RatingServiceRestController : Next2 Rating(songId=1234, value=1)
RatingServiceRestController : Success2 Rating(songId=1234, value=1)
RatingServiceRestController : Terminate2
当我从 Websocket 调用相同的逻辑时:
- 访问
http://localhost:8080/index.html
- 填写任意字符串并推送
奇怪的是,这些是我看到的所有日志:
RatingServiceRestController : Next1 Rating(songId=asfdasf, value=2)
RatingServiceRestController : Success1 Rating(songId=asfdasf, value=2)
RatingServiceRestController : Terminate1
大约三分钟后出现:
RatingServiceRestController : Cancel2
RatingServiceRestController : Cancel1
尽管已生成如 Next1
中所示的元素,但在 repeatWhenEmpty
之后未调用 doOnXXX
运算符。客户端显然也得不到结果。
这是怎么回事?我如何在 RSocket websocket 的上下文中使用 repeatWhenEmpty
?
更新:
我添加了一个 log()
运算符以获取更多信息。
HTTP 请求:
10:37:01.957 INFO 5202 --- [ctor-http-nio-2] reactor.Mono.Defer.1 : onSubscribe(MonoNext.NextSubscriber)
10:37:01.959 INFO 5202 --- [ctor-http-nio-2] reactor.Mono.Defer.1 : request(unbounded)
10:37:01.967 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Next1 Rating(songId=1234, value=0)
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Success1 Rating(songId=1234, value=0)
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Terminate1
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Cancel1
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] reactor.Mono.Defer.1 : onNext(Rating(songId=1234, value=0))
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Next2 Rating(songId=1234, value=0)
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Success2 Rating(songId=1234, value=0)
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Terminate2
10:37:01.976 INFO 5202 --- [ctor-http-nio-2] reactor.Mono.Defer.1 : onComplete()
RSocket 请求:
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] reactor.Mono.Defer.2 : onContextUpdate(Context1{reactor.onDiscard.local=reactor.core.publisher.Operators$$Lambda0/0x0000000800597c40@6dfdbeee})
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] reactor.Mono.Defer.2 : onSubscribe(MonoNext.NextSubscriber)
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] reactor.Mono.Defer.2 : request(1)
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] c.c.r.r.c.RatingServiceRestController : Next1 Rating(songId=asdf, value=0)
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] c.c.r.r.c.RatingServiceRestController : Success1 Rating(songId=asdf, value=0)
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] c.c.r.r.c.RatingServiceRestController : Terminate1
三分钟后:
10:40:27.802 INFO 5202 --- [ parallel-1] c.c.r.r.c.RatingServiceRestController : Cancel2
10:40:27.802 INFO 5202 --- [ parallel-1] reactor.Mono.Defer.2 : cancel()
10:40:27.802 INFO 5202 --- [ parallel-1] c.c.r.r.c.RatingServiceRestController : Cancel1
如您所见,存在一些差异
- 在 RSocket 请求中有一个
onContextUpdate
调用。
- RSocket 请求 1 个元素,HTTP 无界
- 尽管发出了一个元素 (onNext),但 RSocket 执行似乎正在重试或做某事。 CPU 卡在 6% 处做一些工作,这在 HTTP 调用中没有发生,如下图所示:
更新 2:
我一直在调试,发现在第一次取消期间的执行中存在这种差异,特别是在 class Operators
:
public static void onDiscardMultiple(@Nullable Iterator<?> multiple, boolean knownToBeFinite, Context context) {
if (multiple == null) return;
if (!knownToBeFinite) return;
Consumer<Object> hook = context.getOrDefault(Hooks.KEY_ON_DISCARD, null);
if (hook != null) {
try {
multiple.forEachRemaining(o -> {
if (o != null) {
try {
hook.accept(o);
}
catch (Throwable t) {
log.warn("Error while discarding element from an Iterator, continuing with next element", t);
}
}
});
}
catch (Throwable t) {
log.warn("Error while discarding Iterator, stopping", t);
}
}
}
HTTP:
上下文:
Consumer<Object> hook = context.getOrDefault(Hooks.KEY_ON_DISCARD, null);
hook
为空,因此不执行:
if (hook != null) {
try {
multiple.forEachRemaining(o -> {...
RSocket:
上下文:
在这种情况下挂钩是:
它在 forEachRemaining
块中无限循环:
if (hook != null) {
try {
multiple.forEachRemaining(o -> {
if (o != null) {
try {
hook.accept(o);
}
catch (Throwable t) {
log.warn("Error while discarding element from an Iterator, continuing with next element", t);
}
}
});
}
如 https://github.com/rsocket/rsocket-java/issues/860 中所示,Project Reactor 中似乎存在问题。
我按照建议改成了下面的操作符,解决了问题:
.repeatWhenEmpty(30) { longFlux ->
longFlux.delayElements(Duration.ofSeconds(1))
.doOnNext { logger.info("Repeating {}", it) }
}
我遇到了 repeatWhenEmpty
运算符的奇怪行为,这对我来说没有任何意义。我在 Github 上上传了一个包含最小可重现样本的存储库:https://github.com/codependent/rsocket-rating-service.
考虑这个控制器,它提供两个端点(HTTP @GetMapping("/requestRating")
- WebSocket @MessageMapping("request-rating")
。注意这个调用 .repeatWhenEmpty(Repeat.onlyIf<Any> { true }.backoff(Backoff.fixed(Duration.ofSeconds(1))))
在 generateRating()
链的中间:
@RestController
class RatingServiceRestController {
private val FAIL_RATE = 0
private val logger = LoggerFactory.getLogger(javaClass)
@GetMapping("/requestRating")
fun getRatingHttp(ratingRequest: RatingRequest): Mono<Rating> {
return generateRating(ratingRequest)
}
@MessageMapping("request-rating")
fun getRatingWebSocket(ratingRequest: RatingRequest): Mono<Rating> {
return generateRating(ratingRequest)
}
private fun generateRating(ratingRequest: RatingRequest): Mono<Rating> {
return doGenerateRating(ratingRequest)
.doOnNext {
logger.info("Next1 {}", it)
}
.doOnCancel {
logger.info("Cancel1")
}
.doOnSuccess {
logger.info("Success1 {}", it)
}
.doOnError { throwable ->
logger.error("Error1 {}", throwable)
}
.doOnTerminate {
logger.info("Terminate1")
}
.repeatWhenEmpty(Repeat.onlyIf<Any> { true }.backoff(Backoff.fixed(Duration.ofSeconds(1))))
.doOnNext {
logger.info("Next2 {}", it)
}
.doOnCancel {
logger.info("Cancel2")
}
.doOnSuccess {
logger.info("Success2 {}", it)
}
.doOnError { throwable ->
logger.error("Error2 {}", throwable)
}
.doOnTerminate {
logger.info("Terminate2")
}
}
private fun doGenerateRating(ratingRequest: RatingRequest): Mono<Rating> {
return Mono.defer {
val random = (0..100).random()
if (random <= FAIL_RATE) {
Mono.empty()
} else {
Mono.just(Rating(ratingRequest.songId, (0..10).random()))
}
}
}
}
启动应用程序后,我可以调用 http://localhost:8080/requestRating?songId=1234
并得到 returns 结果,按预期显示这些日志:
RatingServiceRestController : Next1 Rating(songId=1234, value=1)
RatingServiceRestController : Success1 Rating(songId=1234, value=1)
RatingServiceRestController : Terminate1
RatingServiceRestController : Cancel1
RatingServiceRestController : Next2 Rating(songId=1234, value=1)
RatingServiceRestController : Success2 Rating(songId=1234, value=1)
RatingServiceRestController : Terminate2
当我从 Websocket 调用相同的逻辑时:
- 访问
http://localhost:8080/index.html
- 填写任意字符串并推送
奇怪的是,这些是我看到的所有日志:
RatingServiceRestController : Next1 Rating(songId=asfdasf, value=2)
RatingServiceRestController : Success1 Rating(songId=asfdasf, value=2)
RatingServiceRestController : Terminate1
大约三分钟后出现:
RatingServiceRestController : Cancel2
RatingServiceRestController : Cancel1
尽管已生成如 Next1
中所示的元素,但在 repeatWhenEmpty
之后未调用 doOnXXX
运算符。客户端显然也得不到结果。
这是怎么回事?我如何在 RSocket websocket 的上下文中使用 repeatWhenEmpty
?
更新:
我添加了一个 log()
运算符以获取更多信息。
HTTP 请求:
10:37:01.957 INFO 5202 --- [ctor-http-nio-2] reactor.Mono.Defer.1 : onSubscribe(MonoNext.NextSubscriber)
10:37:01.959 INFO 5202 --- [ctor-http-nio-2] reactor.Mono.Defer.1 : request(unbounded)
10:37:01.967 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Next1 Rating(songId=1234, value=0)
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Success1 Rating(songId=1234, value=0)
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Terminate1
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Cancel1
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] reactor.Mono.Defer.1 : onNext(Rating(songId=1234, value=0))
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Next2 Rating(songId=1234, value=0)
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Success2 Rating(songId=1234, value=0)
10:37:01.968 INFO 5202 --- [ctor-http-nio-2] c.c.r.r.c.RatingServiceRestController : Terminate2
10:37:01.976 INFO 5202 --- [ctor-http-nio-2] reactor.Mono.Defer.1 : onComplete()
RSocket 请求:
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] reactor.Mono.Defer.2 : onContextUpdate(Context1{reactor.onDiscard.local=reactor.core.publisher.Operators$$Lambda0/0x0000000800597c40@6dfdbeee})
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] reactor.Mono.Defer.2 : onSubscribe(MonoNext.NextSubscriber)
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] reactor.Mono.Defer.2 : request(1)
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] c.c.r.r.c.RatingServiceRestController : Next1 Rating(songId=asdf, value=0)
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] c.c.r.r.c.RatingServiceRestController : Success1 Rating(songId=asdf, value=0)
10:37:29.143 INFO 5202 --- [ctor-http-nio-6] c.c.r.r.c.RatingServiceRestController : Terminate1
三分钟后:
10:40:27.802 INFO 5202 --- [ parallel-1] c.c.r.r.c.RatingServiceRestController : Cancel2
10:40:27.802 INFO 5202 --- [ parallel-1] reactor.Mono.Defer.2 : cancel()
10:40:27.802 INFO 5202 --- [ parallel-1] c.c.r.r.c.RatingServiceRestController : Cancel1
如您所见,存在一些差异
- 在 RSocket 请求中有一个
onContextUpdate
调用。 - RSocket 请求 1 个元素,HTTP 无界
- 尽管发出了一个元素 (onNext),但 RSocket 执行似乎正在重试或做某事。 CPU 卡在 6% 处做一些工作,这在 HTTP 调用中没有发生,如下图所示:
更新 2:
我一直在调试,发现在第一次取消期间的执行中存在这种差异,特别是在 class Operators
:
public static void onDiscardMultiple(@Nullable Iterator<?> multiple, boolean knownToBeFinite, Context context) {
if (multiple == null) return;
if (!knownToBeFinite) return;
Consumer<Object> hook = context.getOrDefault(Hooks.KEY_ON_DISCARD, null);
if (hook != null) {
try {
multiple.forEachRemaining(o -> {
if (o != null) {
try {
hook.accept(o);
}
catch (Throwable t) {
log.warn("Error while discarding element from an Iterator, continuing with next element", t);
}
}
});
}
catch (Throwable t) {
log.warn("Error while discarding Iterator, stopping", t);
}
}
}
HTTP:
上下文:
Consumer<Object> hook = context.getOrDefault(Hooks.KEY_ON_DISCARD, null);
hook
为空,因此不执行:
if (hook != null) {
try {
multiple.forEachRemaining(o -> {...
RSocket:
上下文:
在这种情况下挂钩是:
它在 forEachRemaining
块中无限循环:
if (hook != null) {
try {
multiple.forEachRemaining(o -> {
if (o != null) {
try {
hook.accept(o);
}
catch (Throwable t) {
log.warn("Error while discarding element from an Iterator, continuing with next element", t);
}
}
});
}
如 https://github.com/rsocket/rsocket-java/issues/860 中所示,Project Reactor 中似乎存在问题。
我按照建议改成了下面的操作符,解决了问题:
.repeatWhenEmpty(30) { longFlux ->
longFlux.delayElements(Duration.ofSeconds(1))
.doOnNext { logger.info("Repeating {}", it) }
}