Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/neo4j/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 意外的异步行为:Springs';s@Async vs RxJava_Multithreading_Spring Mvc_Asynchronous_Rx Java - Fatal编程技术网

Multithreading 意外的异步行为:Springs';s@Async vs RxJava

Multithreading 意外的异步行为:Springs';s@Async vs RxJava,multithreading,spring-mvc,asynchronous,rx-java,Multithreading,Spring Mvc,Asynchronous,Rx Java,我正在玩Spring、RxJava和非阻塞数据处理。在我的测试应用程序中,我希望实现以下测试工作流程: [RT]接收请求 [RT]在工作线程中异步启动处理 [WT]执行一些(昂贵的)初始化工作 [WT]异步调用远程系统以获取值 [HT]执行对远程系统的请求 [HT]将响应结果转发给工作线程 [WT]使用远程系统的结果进行更多(昂贵)工作 [WT]返回最终结果 RT:请求线程(Tomcat NIO) WT:工作线程(固定大小为1、队列大小为5的线程池) HT:Hystrix线程(具有默认设置的Hy

我正在玩Spring、RxJava和非阻塞数据处理。在我的测试应用程序中,我希望实现以下测试工作流程:

  • [RT]接收请求
  • [RT]在工作线程中异步启动处理
  • [WT]执行一些(昂贵的)初始化工作
  • [WT]异步调用远程系统以获取值
  • [HT]执行对远程系统的请求
  • [HT]将响应结果转发给工作线程
  • [WT]使用远程系统的结果进行更多(昂贵)工作
  • [WT]返回最终结果
  • RT:请求线程(Tomcat NIO)

    WT:工作线程(固定大小为1、队列大小为5的线程池)

    HT:Hystrix线程(具有默认设置的Hystrix线程池)

    (这只是一个结合对远程资源的依赖性来模拟昂贵数据处理的示例)

    我有两种代码变体:

  • 使用
    @Async
    调用WT(步骤2)和Rx的
    可观察的
    ,其余(
    http://localhost:9001/value
  • 仅使用Rx的观测值(
    http://localhost:9001/value-rx
  • http://localhost:9002/value
    是远程资源)

    变量2运行得很好,但是变量1(使用
    @Async
    )遇到了一些问题。通过分析异常、线程转储、线程池状态和日志文件,看起来
    ListenableFuture
    (由步骤2中的
    @Async
    服务方法返回)正在无限地阻塞线程池,线程本身正在等待。因此,RxJava无法在给定的线程池中运行所需的回调代码(步骤6)。30秒后抛出异常,整个进程失败,因为线程池仍然被阻塞,我不明白为什么

    如果我多次使用variant 1,第二个(以及下面的所有请求)在步骤2中(而不是6)失败,因为线程池(size=1)仍然被
    ListenableFuture
    (下面的堆栈跟踪)阻塞

    变量2能够“同时”处理多个请求,直到队列已满,即使只有1个请求线程和1个工作线程,也不会出现问题

    • 在这两种情况下,我都在使用的修改版本将
      Observable
      的实例映射到
      ListenableFuture
    • 我向控制器和服务类添加了额外的日志记录。这使得更容易看到代码部分在哪个线程中执行
    为什么
    @Async
    会导致此问题,我如何修复此问题?

    代码如下:

    应用程序1控制器

    @ResponseBody
    @RequestMapping("/value")
    public ListenableFuture<String> value() {
        final SettableListenableFuture<String> future;
        this.app1Service.value(future);
        return future;
    }
    
    application.properties:

    server.port=9001
    server.tomcat.max-threads=1
    hystrix.command.app2.fallback.enabled=false
    hystrix.command.app2.execution.isolation.thread.timeoutInMilliseconds=15000
    
    变量1的日志输出(第一次调用)

    变量2的日志输出(第一次调用)

    WT的线程转储(使用变体1后)

    服务

    @Async
    public void value(final SettableListenableFuture<String> future) {
        this.doSomeStuff();
        this.valueFromApp2Service().subscribe(future::set, future::setException);
    }
    
    @Async
    公共无效值(最终设置可列出未来){
    这个。doSomeStuff();
    this.valueFromApp2Service().subscribe(future::set,future::setException);
    }
    
    我想你已经回答了你的问题(或者我遗漏了什么):

    如果我多次使用变体1,则第二个(以及以下所有 由于线程 池(大小=1)仍被ListenableFuture(堆栈)阻止 跟踪如下)

    您的
    TaskExecutor
    只有一个可用线程,用于
    @Async
    。然后,从该线程中,您希望再次使用您的
    TaskExecutor
    作为可观察的:

        this.app2Service.value().observeOn(Schedulers.from(this.taskExecutor)).subscribe(
    
    但是,池中没有更多可用线程。如果增加coreSize,或者为RxJava文件定义不同的TaskExecutor,它应该可以工作

    编辑

    如果确实需要异步执行
    app1Service.value()
    ,可以从中删除@Asynch,并将任务显式提交给taskExecutor,这样就可以向
    ListenableFuture
    添加回调。如果将结果类型更改为
    DeferredResult
    ,则可以在执行
    ListenableFuture
    的回调时设置其结果:

    @Autowired
    private TaskExecutor taskExecutor;
    
    @ResponseBody
    @RequestMapping("/value")
    public DeferredResult<String> value() {
    
        final DeferredResult<String> dr = new DeferredResult<String>();
        taskExecutor.execute(() -> {
            final ListenableFuture<String> future = app1Service.value();
            future.addCallback(new ListenableFutureCallback<String>() {
    
                @Override
                public void onSuccess(String result) {
                    dr.setResult(result);
                }
    
                @Override
                public void onFailure(Throwable ex) {
                    dr.setErrorResult(ex);
                }
            });
        });
        return dr;
    }
    
    @Autowired
    私有任务执行器任务执行器;
    @应答器
    @请求映射(“/value”)
    公共延迟结果值(){
    最终递延结果dr=新递延结果();
    taskExecutor.execute(()->{
    final ListenableFuture=app1Service.value();
    future.addCallback(新ListenableFutureCallback(){
    @凌驾
    成功时的公共void(字符串结果){
    setResult博士(结果);
    }
    @凌驾
    失效时的公共无效(可丢弃的ex){
    setErrorResult博士(ex);
    }
    });
    });
    返回dr;
    }
    

    当然,也可以使用更好的解决方案。

    我还介绍了变体2在相同的条件下工作,并且可以同时处理更多的请求。简单的解决方案是:我不能将ListenableFuture用作@Async方法的返回类型。Spring只是在使用将来的功能,并调用阻塞“get”方法。Spring在
    AsyncExecutionInterceptor
    中调用
    future.get()
    ,因为这是它唯一能做的事情,它没有其他方法来获取
    app1Service.value()的计算结果,从而分派请求。事实上,我认为您根本不需要
    @Async
    ,因为您已经在使用RxJava了。最好将导入添加到代码示例中。。。。
    16:06:31.871 [nio-9001-exec-1] before invoke 'app1Service'
    16:06:31.879 [nio-9001-exec-1] after invoke 'app1Service'
    16:06:31.887 [       worker-1] before start processing
    16:06:31.888 [       worker-1] do some expensive stuff
    16:06:32.890 [       worker-1] finish some expensive stuff
    16:06:32.891 [       worker-1] before invoke 'app2Service'
    16:06:33.135 [x-App2Service-1] before invoke remote service
    16:06:33.136 [x-App2Service-1] after invoke remote service
    16:06:33.137 [x-App2Service-1] invoke
    16:06:33.167 [       worker-1] after invoke 'app2Service'
    16:06:33.172 [       worker-1] after start processing
    16:07:02.816 [nio-9001-exec-1] Exception Processing ErrorPage[errorCode=0, location=/error]
    
    java.lang.IllegalStateException: Cannot forward after response has been committed
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:328)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:318)
        at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:439)
        at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:305)
        at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:399)
        at org.apache.catalina.core.AsyncContextImpl.setErrorState(AsyncContextImpl.java:438)
        at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:291)
        at org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1709)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:649)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
    
    16:07:54.465 [nio-9001-exec-1] before invoke 'app1Service'
    16:07:54.472 [nio-9001-exec-1] before start processing
    16:07:54.500 [nio-9001-exec-1] after start processing
    16:07:54.500 [nio-9001-exec-1] after invoke 'app1Service'
    16:07:54.517 [       worker-1] do some expensive stuff
    16:07:55.522 [       worker-1] finish some expensive stuff
    16:07:55.522 [       worker-1] before invoke 'app2Service'
    16:07:55.684 [x-App2Service-1] before invoke remote service
    16:07:55.685 [x-App2Service-1] after invoke remote service
    16:07:55.686 [x-App2Service-1] invoke
    16:07:55.717 [       worker-1] after invoke 'app2Service'
    16:08:05.786 [       worker-1] next (from 'app2Service')
    16:08:05.786 [       worker-1] do some more expensive stuff with 'value from app2 service'
    16:08:07.791 [       worker-1] finish some more expensive stuff with 'value from app2 service'
    16:08:07.791 [       worker-1] completed (from 'app2Service')
    16:08:07.791 [       worker-1] next (processing)
    16:08:07.792 [       worker-1] completed (processing)
    
    "worker-1" #24 prio=5 os_prio=31 tid=0x00007fe2be8cf000 nid=0x5e03 waiting on condition [0x0000000123413000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006c0d68fb0> (a org.springframework.util.concurrent.ListenableFutureTask)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429)
        at java.util.concurrent.FutureTask.get(FutureTask.java:191)
        at org.springframework.util.concurrent.SettableListenableFuture.get(SettableListenableFuture.java:122)
        at org.springframework.aop.interceptor.AsyncExecutionInterceptor$1.call(AsyncExecutionInterceptor.java:110)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    
       Locked ownable synchronizers:
        - <0x00000006c0d68170> (a java.util.concurrent.ThreadPoolExecutor$Worker)
    
    "worker-1" #24 prio=5 os_prio=31 tid=0x00007fc6136dd800 nid=0x5207 waiting on condition [0x000000012d638000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006c02f5388> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    
       Locked ownable synchronizers:
        - None
    
    @ResponseBody
    @RequestMapping("/value")
    public ListenableFuture<String> value() {
        final SettableListenableFuture<String> future;
        this.app1Service.value(future);
        return future;
    }
    
    @Async
    public void value(final SettableListenableFuture<String> future) {
        this.doSomeStuff();
        this.valueFromApp2Service().subscribe(future::set, future::setException);
    }
    
        this.app2Service.value().observeOn(Schedulers.from(this.taskExecutor)).subscribe(
    
    @Autowired
    private TaskExecutor taskExecutor;
    
    @ResponseBody
    @RequestMapping("/value")
    public DeferredResult<String> value() {
    
        final DeferredResult<String> dr = new DeferredResult<String>();
        taskExecutor.execute(() -> {
            final ListenableFuture<String> future = app1Service.value();
            future.addCallback(new ListenableFutureCallback<String>() {
    
                @Override
                public void onSuccess(String result) {
                    dr.setResult(result);
                }
    
                @Override
                public void onFailure(Throwable ex) {
                    dr.setErrorResult(ex);
                }
            });
        });
        return dr;
    }