如何在REST API中拥有订阅者?

时间:2016-08-29 16:19:56

标签: java rest publish-subscribe

考虑进入Web服务器的请求,调用REST API。该请求正在为服务器创建一个耗时的工作。因此,Web服务器代码会将作业委派给其他服务器以提供可扩展的解决方案。此方案通常使用Publisher-Subscriber模式实现。

到目前为止一切都那么好,但是如果我想保持结果的请求呢?我可以反过来转动Publisher-Subscriber,所以现在我的REST API代码将等待一些发布者在结果准备好时得到通知。

这种情况的问题是订阅通知是以异步方式完成的(这本身就是完美的)。因此,在Spring中实现REST API等待RabbitMQ的通知将如下所示:

@RequestMapping(value = "/url", method = RequestMethod.GET)
public ResponseEntity url()
{
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("localhost");
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();
    String command = "Job Description";
    channel.basicPublish("", "Heavy Worker", null, command.getBytes());

    Consumer consumer = new DefaultConsumer(channel)
    {
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
            throws IOException
        {
            String result = new String(body, "UTF-8");
            //return the result as the HTTP response
        }
    };
    channel.basicConsume("Heavy Job Result", true, consumer);
}

我试图在上面的代码片段中说明的问题是,在channel.basicConsume("Heavy Job Result", true, consumer);之后,响应将返回给客户端,Java将不会等待"Heavy Job Result"准备好。< / p>

是否有一种已知的做法如何以非轮询方式保留请求的挂起?

1 个答案:

答案 0 :(得分:3)

解决方案取决于繁重工作完成所需的时间。如果它在比服务器超时配置的时间更短的时间内完成,那么您只需返回CompletableFutureListentableFuture就可以阻止您的Web IO:

@RequestMapping(value = "/url", method = RequestMethod.GET)
public CompletableFuture<ResponseEntity> url() {
  return CompletableFuture.supplyAsync(() -> {
    // do your computations here
  });
}

Spring会为你处理剩下的事情。

如果花费的时间超过服务器超时,则可以使用WebSockets在作业完成时处理从服务器向客户端发送消息。 查看Spring WebSocket Support。流程将是:

  1. 接收请求
  2. 将其交给新线程中长时间运行的处理器(例如CompletableFuture.runAsync())
  3. 在开始这个新线程后立即返回响应
  4. 一旦作业完成,您就会向前端发送一条消息,表明它已完成,包含您想要包含的任何数据