简体   繁体   English

如何在 Spring Boot 中使用阻塞队列?

[英]How to use blocking queue in Spring Boot?

I am trying to use BlockingQueue inside Spring Boot.我正在尝试在 Spring 引导中使用 BlockingQueue。 My design was like this: user submit request via a controller and controller in turn puts some objects onto a blocking queue.我的设计是这样的:用户通过 controller 提交请求,controller 依次将一些对象放入阻塞队列。 After that the consumer should be able to take the objects and process further.之后,消费者应该能够获取对象并进一步处理。

I have used Asnyc, ThreadPool and EventListener.我使用过 Asnyc、ThreadPool 和 EventListener。 However with my code below I found consumer class is not consuming objects.但是,通过下面的代码,我发现消费者 class 没有消耗对象。 Could you please help point out how to improve?你能帮忙指出如何改进吗?

Queue Configuration队列配置

@Bean
public BlockingQueue<MyObject> myQueue() {
    return new PriorityBlockingQueue<>();
}

@Bean
public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(3);
    executor.setMaxPoolSize(3);
    executor.setQueueCapacity(10);
    executor.setThreadNamePrefix("Test-");
    executor.initialize();
    return executor;
}

Rest Controller Rest Controller

@Autowired
BlockingQueue<MyObject> myQueue;

@RequestMapping(path = "/api/produce")
public void produce() {
    /* Do something */
    MyObject myObject = new MyObject();
    myQueue.put(myObject);
}

Consumer Class消费类 Class

@Autowired
private BlockingQueue<MyObject> myQueue;

@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
    consume();
}

@Async
public void consume() {
    while (true) {
        try {
            MyObject myObject = myQueue.take();
        }
        catch (Exception e) {
        }
    }
}

Your idea is using Queue to store messages, consumer listens to spring events and consume.你的想法是使用Queue来存储消息,消费者监听spring events并消费。 I didn't see your code have actually publish the event, just store them in queue .我没有看到您的代码实际上已经发布了事件,只是将它们存储在queue中。 If you want to use Spring Events, producers could like this:如果你想使用 Spring 事件,生产者可以这样:

@Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void doStuffAndPublishAnEvent(final String message) {
        System.out.println("Publishing custom event. ");
        CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
        applicationEventPublisher.publishEvent(customSpringEvent);
    }

check this doc检查这个文档

If you still want to use BlockingQueue , your consumer should be a running thread, continuously waiting for tasks in the queue, like:如果你仍然想使用BlockingQueue ,你的消费者应该是一个正在运行的线程,不断地等待队列中的任务,比如:

public class NumbersConsumer implements Runnable {
    private BlockingQueue<Integer> queue;
    private final int poisonPill;

    public NumbersConsumer(BlockingQueue<Integer> queue, int poisonPill) {
        this.queue = queue;
        this.poisonPill = poisonPill;
    }
    public void run() {
        try {
            while (true) {
                Integer number = queue.take(); // always waiting 
                if (number.equals(poisonPill)) {
                    return;
                }
                System.out.println(Thread.currentThread().getName() + " result: " + number);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

could check this code example可以检查这个代码示例

In the end I came up with this solution.最后我想出了这个解决方案。

Rest Controller Rest Controller

@Autowired
BlockingQueue<MyObject> myQueue;

@RequestMapping(path = "/api/produce")
public void produce() {
    /* Do something */
    MyObject myObject = new MyObject();
    myQueue.put(myObject);
    Consumer.consume();
}

It is a little bit weird because you have to first put the object on queue yourself then consume that object by yourself.这有点奇怪,因为您必须先自己将 object 放入队列,然后自己使用 object。 Any suggestions on improvement is highly appreciated.任何关于改进的建议都非常感谢。

@Async doesn't actually start a new thread if the target method is called from within the same object instance, this could be the problem in your case.如果从同一个@Async实例中调用目标方法,@Async 实际上不会启动新线程,这可能是您的问题。 Also note that you need to put @EnableAsync on a config class to enable the @Async annotation.另请注意,您需要将@EnableAsync放在配置 class 上以启用@Async注释。

See Spring documentation: https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#scheduling-annotation-support请参阅 Spring 文档: https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#scheduling

The default advice mode for processing @Async annotations is proxy which allows for interception of calls through the proxy only.处理 @Async 注释的默认建议模式是代理,它只允许通过代理拦截调用。 Local calls within the same class cannot get intercepted that way.同一个 class 内的本地呼叫不能以这种方式被拦截。 For a more advanced mode of interception, consider switching to aspectj mode in combination with compile-time or load-time weaving.对于更高级的拦截模式,请考虑结合编译时或加载时编织切换到 aspectj 模式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM