react 线程_React式服务中的线程本地状态可用性

react 线程

任何架构决策都需要权衡。 如果您决定采用React式,也没有什么不同,例如,一方面使用React式流实现几乎可以立即获得更好的资源利用率,但另一方面会使调试更加困难。 引入React式库也对您的域产生巨大影响,您的域将不再仅在PaymentOrderCustomer方面说话,React式术语将破解Flux<Payment>Flux<Order>Mono<Customer> (或Observable<Payment>Flowable<Order>Single<Customer>或您选择的库提供的任何Reactive Streams发布者)。 这种折衷很快就变得很明显,但是您可能会猜到并不是所有的折衷都会如此明显– 泄漏抽象定律保证了这一点。

React性库使更改线程上下文变得轻而易举。 您可以轻松地订阅一个调度程序,然后在另一个调度程序上执行部分操作员链,最后跳到完全不同的调度程序上。 只要您不涉及线程本地状态,就可以从一个线程跳到另一个线程-尽管您的服务支持服务的关键部分(例如安全性,交易),但通常不会每天处理这种状态, 多租户)。 当技术堆栈中隐藏良好的部分取决于线程局部状态时,更改线程上下文会导致棘手的错误定位。

让我通过一个简单的示例演示该问题:

private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final String SESSION_ID = "session-id";

@GetMapping("documents/{id}")
Mono<String> getDocument(@PathVariable("id") String documentId) {
    MDC.put(SESSION_ID, UUID.randomUUID().toString());
    LOG.info("Requested document[id={}]", documentId);
    return Mono.just("Lorem ipsum")
            .map(doc -> {
                LOG.debug("Sanitizing document[id={}]", documentId);
                return doc.trim();
            });
}

使用MDC.put(SESSION_ID, UUID.randomUUID().toString())我们将会话session-id放入基础日志库的映射诊断上下文中 ,以便稍后进行登录。

让我们以自动为我们记录session-id的方式配置记录模式:

logging.pattern.console=[%-28thread] [%-36mdc{session-id}] - %-5level - %msg%n

当我们通过请求( curl localhost:8080/documents/42 )访问公开的服务时,我们将看到session-id出现在日志条目中:

[reactor-http-server-epoll-10] [00c4b05f-a6ee-4a7d-9f92-d9d53dbbb9d0] - INFO  - Requested document[id=42]
[reactor-http-server-epoll-10] [00c4b05f-a6ee-4a7d-9f92-d9d53dbbb9d0] - DEBUG - Sanitizing document[id=42]

如果在将session-id放入MDC之后切换执行上下文(例如,通过预订不同的调度程序),情况将发生变化:

@GetMapping("documents/{id}")
Mono<String> getDocument(@PathVariable("id") String documentId) {
    MDC.put(SESSION_ID, UUID.randomUUID().toString());
    LOG.info("Requested document[id={}]", documentId);
    return Mono.just("Lorem ipsum")
            .map(doc -> {
                LOG.debug("Sanitizing document[id={}]", documentId);
                return doc.trim();
            })
            .subscribeOn(Schedulers.elastic()); // don't use schedulers with unbounded thread pool in production
}

在执行上下文更改之后,我们将注意到该调度程序调度的操作员记录的日志条目中缺少session-id

[reactor-http-server-epoll-10] [c2ceae03-593e-4fb3-bbfa-bc4970322e44] - INFO  - Requested document[id=42]
[elastic-2                   ] [                                    ] - DEBUG - Sanitizing document[id=42]

您可能会猜到,我们正在使用的日志记录库内部隐藏了一些ThreadLocal

一些Reactive Streams实现提供了允许将上下文数据提供给操作员的机制(例如Project Reactor提供订户上下文 ):

@GetMapping("documents/{id}")
Mono<String> getDocument4(@PathVariable("id") String documentId) {
    String sessionId = UUID.randomUUID().toString();
    MDC.put(SESSION_ID, sessionId);
    LOG.info("Requested document[id={}]", documentId);
    return Mono.just("Lorem ipsum")
            .zipWith(Mono.subscriberContext())
            .map(docAndCtxTuple -> {
                try(MDC.MDCCloseable mdc = MDC.putCloseable(SESSION_ID, docAndCtxTuple.getT2().get(SESSION_ID))) {
                    LOG.debug("Sanitizing document[id={}]", documentId);
                    return docAndCtxTuple.getT1().trim();
                }})
            .subscriberContext(Context.of(SESSION_ID, sessionId))
            .subscribeOn(Schedulers.elastic()); // don't use schedulers with unbounded thread pool in production
}

当然,使数据可用只是故事的一部分。 一旦我们提供了session-idsubscriberContext(Context.of(SESSION_ID, sessionId)) ),我们不仅必须检索它,还必须将其附加到线程上下文中,并且还记得要自己清理,因为调度程序可以自由地进行清理。重用线程。

提出的实现会带回session-id

[reactor-http-server-epoll-10] [24351524-f105-4746-8e06-b165036d02e6] - INFO  - Requested document[id=42]
[elastic-2                   ] [24351524-f105-4746-8e06-b165036d02e6] - DEBUG - Sanitizing document[id=42]

但是,使它起作用的代码太复杂,太具有侵入性,以致于在大多数代码库中都不用张开双臂来欢迎它,尤其是当它最终散布在整个代码库中时。

我很乐意通过为该问题提供一个简单的解决方案来结束本博客文章,但我还没有偶然发现这样的问题(现在,我们需要使用这样更复杂,更具侵入性的解决方案,同时还要尝试解决这种复杂性从以业务为中心的软件部分到基础设施部分,如果可能的话,还可以直接到库本身)。

翻译自: https://www.javacodegeeks.com/2018/09/thread-local-state-availability-in-reactive-services.html

react 线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值