4

我们试图在我们的应用程序的多线程部分和数据库访问之间实现一种保证,以便数据库不会受到太多线程(客户要求)的影响,同时保持系统的其他部分充分利用有必要数量的线程。

设计似乎可以工作(弹簧批处理分区+使用 ThreadPoolTask​​Executor 处理数据访问),但问题在于测试设计(基于http://helenaedelson.com/?p=432)。

现在,我必须将 Thread.sleep(4000) 添加到我的单元测试中,以确保在生成的额外线程发生更改以完成其工作并返回返回值之前,Spring 上下文不会从测试中被杀死到主线程。

有人会对如何使这个测试实现更智能有一些更好的想法吗?

测试者:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:partitionJdbcJob.xml" })
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public class TaskTests {
protected static final Logger logger = LoggerFactory.getLogger(TaskTests.class);

@Autowired
private OrderServiceImpl orderService;

@Test
public void testExecution() {
    logger.info("Starting execution thread...");

    for (int i = 0; i < 8; i++) {
        orderService.dispatch();
    }

    try {
        // So that spring context is not destroyed from under the multi-threaded runnables
        Thread.sleep(4000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

测试服务:

@Service("orderServiceImpl")
public class OrderServiceImpl {
protected static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);

@Resource(name = "beanTaskExecutor")
private TaskExecutor taskExecutor;
// private AsyncTaskExecutor taskExecutor;

CompletionService completionService;

@Autowired
public void OrderServiceImpl(DataSource dataSource) {
    completionService = new ExecutorCompletionService(taskExecutor);
}

public void dispatch(final RetailPriceOptimization order) {
    logger.info("Starting dispatch execution...");

    if (this.taskExecutor != null) {
        logger.info("taskExecutor found...");
        this.taskExecutor.execute(new Runnable() {
            public void run() {
                withExecutor(order);
            }
        });
    }

    try {
        Object future1 = completionService.take().get();
        Object future2 = completionService.take().get();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    logger.info("Completed dispatch execution...");
}

private void withExecutor(final RetailPriceOptimization order) {
    logger.info("Starting withExecutor execution...");

    Object result1 = completionService.submit(new Callable<String>() {
        public String call() {
            return findById("0000dd2gsl1u1546");
        }
    });
    Object result2 = completionService.submit(new Callable() {
        public Object call() {
            return orderDao.find(new Long("16"));
        }
    });
}

}

4

2 回答 2

0

现在您的测试似乎是同步的,因为您completionService .take()在方法内部调用了两次dispatch(...)。将take()等到作业完成。所以当我读到它时,你根本不需要你的线程睡眠。

另外,我认为根本不需要CompletionService。您应该只保留 2 个期货,然后get(...)一次调用它们。似乎您无论如何都在等待 2 个调用完成,它们将同时运行。 CompletionService只有当您可以立即开始处理其中一个结果时,它们才是好的。

于 2015-09-13T18:57:25.440 回答
0

你不尝试重构你的服务。

@Service
public class ConcurrentService{

    public Map<String, Object> createList(){
       this.asynCall();
    }

    @Async("taskExecutor")
    private Future<Map<String, Object>> asynCall(){
            //I will use submit instead of execute
            return this.taskExecutor.submit(new Callable(){
                 //Override the proper logic following interface
             })
    .....
    }

}

对于您要并行执行的所有进程,请在 @Async 后面使用一个新方法,这使其异步执行,在您用于合并响应的方法或仅用于启动并行进程的方法中,使用 Future API 等到作业完成。

boolean isDone()如果此任务完成,则返回 true。完成可能是由于正常终止、异常或取消——在所有这些情况下,此方法都将返回 true。

这将等待所有期货完成

while(future1.isDone() future2.isDone()){
//Do Something in the meanwhile
}
//Code will start the execution once both process are completed

您可以构建一个 wrap 方法以更轻松、更动态地等待完成。

要做到这一点的 Spring 配置是:

<task:annotation-driven executor="taskExecutor"  mode="aspectj" />

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="50" />
    <property name="maxPoolSize" value="300" />
    <property name="queueCapacity" value="30" />
</bean>

对我来说,这是集成测试,因为您使用的是真实的对象和服务,上面的配置将导致测试正确执行所有业务逻辑,并将等待所有流程完成,因此您不需要线程。睡眠,在您的示例中的方法执行之后>

orderService.dispatch();

您将能够创建断言列表以验证过程是否按预期工作,不要忘记这是测试目的的一部分。

于 2015-09-13T19:20:05.960 回答