简体   繁体   中英

Spring boot and asynchronous RabbitMQ RPC

I'm trying to implement RabbitMQ RPC pattern (Request/Response).

It's totally new technology to me. So I am having a hard time.

It's a web app, built in spring boot.

Structure:

  • A user fills a form with some information and submits form, that invokes processing controller eg @{/processUser}

  • Object with that information from the form is sent to RabbitMQ queue

  • Response part is happening in other spring project service, that gets request, build response and sends it back.

  • Building response should do another User on that other spring project in given time frame, if not, generic response is sent back.

So I assumed that code for response, because it needs to wait on one thread whole time for request and I need main thread for spring boot application to run, should be on separated thread in background. Because I need it to be asynchronous.

This code works "asynchronously" as I want, but I have feeling that there is much better way that I just don't know. I don't know how this anonymous thread will behave with multiple users using web app. It doesn't need to be perfect, but acceptable:)

Code below is not finished and NOT doing the whole thing (sending object, making a response dynamically...) It's just test phase.

Code for Request:

public  String call(String message) throws Exception{
          final String corrID = UUID.randomUUID().toString();

        String replayQueueName = channel.queueDeclare().getQueue();
        AMQP.BasicProperties props = new AMQP.BasicProperties.Builder().correlationId(corrID).replyTo(replayQueueName).build();

        channel.basicPublish("", requestQueueName,props,message.getBytes());

        final BlockingQueue<String> response = new ArrayBlockingQueue<>(1);
        String ctag = channel.basicConsume(replayQueueName, true, (consumerTag, delivery) -> {
            if (delivery.getProperties().getCorrelationId().equals(corrID)) {
                response.offer(new String(delivery.getBody(), "UTF-8"));
            }
        }, consumerTag -> {
        });
        String result  = response.take();
        channel.basicCancel(ctag);
        return result;

And this method is called in that processing controller:

try(Connection connection = factory.newConnection()){
            channel = connection.createChannel();
            System.out.println("Sending request...");

            String response = call("Test_Message");
            System.out.println(response);

        }catch (Exception e){
            e.printStackTrace();
        }

Code for response:

@Bean
    public ConnectionFactory startFactory(){
        return new ConnectionFactory();
    }

    @Bean
    public Connection startCon(ConnectionFactory factory) throws Exception{
        return  factory.newConnection();
    }

    @Bean
    public void reciver(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{

                    Channel channel = connection.createChannel();
                    channel.queueDeclare(RPC_QUEUE_NAME,false,false,false,null);
                    channel.queuePurge(RPC_QUEUE_NAME);

                    channel.basicQos(1);

                    System.out.println("Awaiting rpc requests");

                    Object monitor = new Object();
                    DeliverCallback deliverCallback = (consumerTag, delivery) ->{
                        AMQP.BasicProperties replayProps = new AMQP.BasicProperties.Builder()
                                .correlationId(delivery.getProperties().getCorrelationId())
                                .build();
                        String response = "RESPONSE_TESTING";
                        String message = new String(delivery.getBody(),"UTF-8");
                        System.out.println(message);
                        channel.basicPublish("",delivery.getProperties().getReplyTo(), replayProps, response.getBytes());
                        channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
                        synchronized (monitor){
                            monitor.notify();
                        }

                    };
                    channel.basicConsume(RPC_QUEUE_NAME, false, deliverCallback, (consumerTag -> {}));
                    while(true){
                        synchronized (monitor){
                            try{
                                monitor.wait();
                            }catch (InterruptedException e){
                                e.printStackTrace();
                            }
                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }

            }
        }).start();



    }

I am not really sure what you are trying to achieve here, but given the code you provided it seems that you are trying to re-invent the wheel. Since you are using spring-boot , there is no need to create a whole infrastructure capable of running async to consume messages.

This is already offered by Spring as part of the spring-amqp-starter out of the box. For example in that case you won't have to manually poll the queue in order to get back response, since this action is handled by a declated RabbitListener .

I suggest you do through the documentation as well as the various examples in order to get a better feel on how to use Rabbit. You can check those here and here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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