RabbitMQ总结-异步

866 阅读3分钟

这是我参与8月更文挑战的第31天,活动详情查看:8月更文挑战

异步

可以使用MQ来实现异步操作。

以餐厅订单为例:

如果是同步模式,餐厅接受

到订单后,通知厨师,厨师接收到订单请求,餐厅完成订单请求。 这样看来,需要等到厨师告诉餐厅接受到请求,餐厅才会去接收下一个订单请求。如果大量订单涌入,就会使订单大量堆积。

异步模式:餐厅接收到订单,向RabbitMQ发送一个消息后就完成订单请求。 厨师通过RabbitMQ接收到订单请求,返回确认响应ack,消息删除。

餐厅可以不断的接收请求,不需要等待厨师的确认响应。RabbitMQ中的消息可以看做已经完成的任务,只不过不是立即完成。

性能

RabbitMQ 轮询机制会平均发送请求,厨师服务器不够就增加厨师服务器,餐厅服务器不够就增加餐厅服务器,能够精确提高处理性能的瓶颈。

解耦

两个逻辑实现分离,餐厅逻辑不需要依赖厨师逻辑才能运行,成为两个独立的应用。

拓展性

如果我们还要增加其他逻辑(如开发票),根本不需要在原有的代码上修改逻辑,只要在发送消息时增加需要添加的信。通过RabbitMQ再增加其他的队列,另外编写发票接口,即可增加订单的开发票逻辑。

而且其他逻辑可以使用其他语言使用。

实现RPC

普通的RPC实现方式需要调用方和服务方的代码紧密联系,调用方需要等待服务方的返回结果才能继续响应。

使用RabbitMQ实现RPC,调用方只需要发布消息,通过绑定转发到合适的队列。服务方订阅队列消费服务即可完成业务实现。这其中,RabbitMQ自动完成了负载均衡,服务崩溃等方案,不需要开发者自己手动配置。

但是如何将服务方的结果返回给调用方?

AMQP是单向的,消费者如何返回给生产者?

使用消息来返回应答,即消费者也发送一个应答消息给生产者。

1.每个AMQP消息头中有个字段reply_to,生产者自己设置这个字段信息,预先创建应答队列,订阅该队列并等待应答消息。

2.消费者能够通过检查消息中的reply_to 字段,知道应答队列的名称,可以通过默认交换机,队列名把应答消息发布到应答队列中。

3.生产者从应答队列中消费应答消息。

实现

生产者

1.创建连接和信道

2.设置消息属性(reply_to字段信息)并发送消息

3.创建并订阅应答队列

public void RPC() throws IOException, TimeoutException {
        final String RPC_SERVICE_QUEUE = "RPCService";
        final String RPC_CLIENT_QUEUE = "RPCClient";

        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();

        // 设置消息头属性(reply字段)
        AMQP.BasicProperties prop = new AMQP.BasicProperties().builder().replyTo(RPC_CLIENT_QUEUE).correlationId("22").build();

        // 发布消息
        channel.basicPublish("",RPC_SERVICE_QUEUE,prop,"client".getBytes());
        // 创建并订阅应答队列
        channel.queueDeclare(RPC_CLIENT_QUEUE,false,false,false,null);
        com.rabbitmq.client.Consumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("corId"+properties.getCorrelationId());
                String s = new String(body, "UTF-8");
                System.out.println("message"+s);
                try {
                    // 消费者确认消息,队列删除消息
                    channel.basicAck(envelope.getDeliveryTag(), false);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        channel.basicConsume(RPC_CLIENT_QUEUE,false,consumer);
//        channel.close();
//        connection.close();
    }

消费者

1.创建连接和信道

2.创建并订阅队列

3.接收到消息时处理后发送到应答队列中

public void RPC() throws IOException, TimeoutException {
        final String RPC_SERVICE_QUEUE = "RPCService";
        final String RPC_CLIENT_QUEUE = "RPCClient";
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(RPC_SERVICE_QUEUE,false,false,false,null);
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String replyTo = properties.getReplyTo();
                AMQP.BasicProperties replyProps = new AMQP.BasicProperties().builder().correlationId("23").replyTo(replyTo).build();
                System.out.println("corId"+properties.getCorrelationId());
                String s = new String(body, "UTF-8");
                System.out.println("message"+s);

                try {
                    channel.basicPublish("",RPC_CLIENT_QUEUE,replyProps,(s+"->client").getBytes());
                    // 消费者确认消息,队列删除消息
                    channel.basicAck(envelope.getDeliveryTag(), false);
//                    channel.basicNack(envelope.getDeliveryTag(), false,true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };

        channel.basicConsume(RPC_SERVICE_QUEUE,true,consumer);
//        channel.close();
//        connection.close();
    }