Spring cloud @HystrixCommand不代理在CompletableFuture.supplyAsync中调用的方法

时间:2018-01-03 20:08:43

标签: spring java-8 spring-cloud java.util.concurrent hystrix

我有一个spring组件bean,它包含由@HystrixCommand和fallbackMethod定义的方法 methodA 。 bean有另一种方法 methodB 通过CompletableFuture.supplyAsync(...)调用 methodA 。我希望Hystrix javanica会在 methodA 上编织方面,但是当我调试它时,我没有看到hystrix方面被编织。

以下是一些主要的sudo代码,

TestApplication

package com.my.own.test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.my.own.test")
public class TestApplication {
    public static void main(final String[] args) throws Exception {
        SpringApplication.run(TestApplication.class, args);
    }
}

ApplicationConfiguration

package com.my.own.test;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableConfigurationProperties
@EnableCircuitBreaker
public class ApplicationConfig {

}

AsyncConfig

package com.my.own.test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncConfig {

   @Value("${spring.threadpool.executor.core:10}")
   private int corePoolSize;

   @Value("${spring.threadpool.executor.max:20}")
   private int maxPoolSize;

   @Value("${spring.threadpool.executor.queue:1000}")
   private int queueCapacity;

   @Value("${spring.threadpool.executor.timeout:true}")
   private boolean coreThreadTimeOut;

   @Value("${spring.threadpool.executor.keepalive:30000}")
   private int keepAlive;

   @Value("${spring.threadpool.executor.prefix:ThreadPoolTaskExecutor}")
   private String threadNamePrefix;

   @Bean("taskExecutor")
   public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
       final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
       executor.setCorePoolSize(corePoolSize);
       executor.setMaxPoolSize(maxPoolSize);
       executor.setQueueCapacity(queueCapacity);
       executor.setAllowCoreThreadTimeOut(coreThreadTimeOut);
       executor.setKeepAliveSeconds(keepAlive);
       executor.setThreadNamePrefix(threadNamePrefix + "-");
       executor.initialize();

       return executor;
   }
}

的TestController

package com.my.own.test.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.my.own.test.core.TestProcessor;

@RestController
public class TestController {

    private static Logger logger = LoggerFactory.getLogger(TestController.class);

    @Autowired
    TestProcessor tester;

    @RequestMapping(value = "/test", method = { RequestMethod.POST })
    public void test() {
        tester.methodB();
    }
}

TestProcessor

package com.my.own.test.core;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Component
public class TestProcessor {

    @Autowired
    private ThreadPoolTaskExecutor executor;

    public void methodB() {
        final List<CompletableFuture<String>> a = new ArrayList<>();
        a.add(CompletableFuture.supplyAsync(() -> methodA(), executor));
        CompletableFuture.allOf(a.toArray(new CompletableFuture[a.size()])).join();
    }

    @HystrixCommand(fallbackMethod = "deferPushdown")
    public String methodA() {
        if (true) {
            throw new RuntimeException();
        } else {
            return "methodA";
        }
    }

    public String deferMethodA() {
        return "deferMethodA";
    }
}

运行输出

Jan 03, 2018 2:55:55 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.util.concurrent.CompletionException: java.lang.RuntimeException] with root cause
java.lang.RuntimeException
    at com.my.own.test.core.TestProcessor.methodA(TestProcessor.java:40)
    at com.my.own.test.core.TestProcessor.lambda$0(TestProcessor.java:33)
    at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
    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)

P.S。 从输出中,methodA不是从hystrix方面调用的,而是由lambda直接调用的。这是Hystrix或javanica的问题吗?如果您知道解决方案,请分享。我很感激。

1 个答案:

答案 0 :(得分:1)

除非你正在使用aspectj编织(我认为需要特殊处理编译步骤),spring默认使用interface / cglib编织,它只适用于从类外部调用的第一个方法,如{{ 3}}。

总之,如果调用methodB,则不应用任何方面,并且从methodB到methodA的调用不适用于方面拦截。 要激活方面,您必须直接从TestProcessor类外部调用methodB。