这是我参与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();
}