RabbitMQ:RPC调用的模拟分析

1.声明

当前内容主要用于本人学习和复习之用,当前内容为使用RabbitMQ的生产者、消费者、队列实现RPC调用

当前内容来源:RabbitMQ官方文档

分析RPC调用

  1. 客户端发送参数请求服务端
  2. 服务端处理发送的请求并得到结果
  3. 服务端将结果返回到客户端

通过上面的RPC分析可以发现

  1. 数据信息可以看作消息
  2. 客户端即是消费者又是生产者(获得了消息,发送了消息)
  3. 服务端即是消费者又是生产者(处理了消息,返回了消息)

2.创建客户端和服务端

客户端(为它服务发送和接收消息的能力)

public class RPCMClientain {
	public static void main(String[] args) throws IOException, TimeoutException {
		ConnectionFactory factory=new ConnectionFactory();
		Connection conn = factory.newConnection();
		Channel channel = conn.createChannel();
		 UUID randomUUID = UUID.randomUUID(); 
		BasicProperties props = new BasicProperties
                .Builder()
                .correlationId(randomUUID.toString())
                .replyTo("rpc_queue")
                .build();
		channel.basicPublish("", "rpc_queue", props, ("10").getBytes());
		
		System.out.println("发送消息成功===>");
		/*
		 * try { Thread.sleep(5000l); } catch (InterruptedException e) { // TODO
		 * Auto-generated catch block e.printStackTrace(); }
		 */
		System.out.println("开始获取结果===>");
		channel.basicConsume("rpc_queue",true,new DefaultConsumer(channel) {

			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				System.out.println("获取结果===>"+StringUtils.toString(body));
				System.out.println(consumerTag);
				System.out.println(properties);
				System.out.println(new String(body,"utf-8"));
				//conn.close();
			}
			
		});

	}
}


服务端(使用官方的例子)

public class RPCServer {

	private static final String RPC_QUEUE_NAME = "rpc_queue";

	private static int doubleNum(int n) {
		return n*2;
	}

	public static void main(String[] argv) throws Exception {
		ConnectionFactory factory = new ConnectionFactory();
		factory.setHost("localhost");

		try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) {

			channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
			//channel.queuePurge(RPC_QUEUE_NAME);

			channel.basicQos(1);

			System.out.println(" [x] Awaiting RPC requests");

			Object monitor = new Object();
			DeliverCallback deliverCallback = (consumerTag, delivery) -> {
				AMQP.BasicProperties replyProps = new AMQP.BasicProperties.Builder()
						.correlationId(delivery.getProperties().getCorrelationId()).build();

				String response = "";

				try {
					String message = new String(delivery.getBody(), "UTF-8");
					int n = Integer.parseInt(message);

					System.out.println(" [.] fib(" + message + ")");
					response += doubleNum(n);
					System.out.println("执行完毕后的结果为===>" + response);
				} catch (RuntimeException e) {
					e.printStackTrace();
					System.out.println(" [.] " + e.toString());
				} finally {
					channel.basicPublish("", delivery.getProperties().getReplyTo(), replyProps,
							response.getBytes("UTF-8"));
					channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
					// RabbitMq consumer worker thread notifies the RPC server owner thread
					System.out.println("唤醒工作线程====>");
					synchronized (monitor) {
						monitor.notify();
					}
				}
			};

			channel.basicConsume(RPC_QUEUE_NAME, false, deliverCallback, (consumerTag -> {
			}));
			// Wait and be prepared to consume the message from RPC client.
			while (true) {
				synchronized (monitor) {
					try {
						System.out.println("rpcserver开始阻塞工作线程====>");
						monitor.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

3.测试RPC

先启动客户端然后启动服务端并查看结果

客户端执行的结果
在这里插入图片描述
服务端执行的结果
在这里插入图片描述

执行操作成功

4.分析该RPC操作

1.由于两个都是用了""这个默认的exchange,该exchange类型为Direct类型,如果出现两个相同的客户端默认使用轮询方式,这也是为什么发送消息的时候会发送给对方的原因

2.客户端发送了消息到server,那么server对结果进行处理(一条一条的处理)basicQos(1),最后将结果通过basicPublish发送给replyTo的queue中(即“rpc-queue”里面),然后进行应答,表示该消息处理完毕

3.由于消息发送给了“rpc-queue”这个queue中,那么通过Direct的特性,这个消息会发送给另外一个消费者,从而完成消息回应

注意:

由于上面的这个方法使用了相同的队列,所以会导致一个问题,如果客户端发起多个请求,那么这个回调的结果就是错误的

解决办法:直接将replayTo的结果放入另外一个队列中,然后客户端直接消费replayTo队列的消息即可完成

5.总结

1.使用消费者和生产者模拟RPC调用的时候其实实现起来还是很容易的

2.只需要在发送的时候指定replayTo为一个队列,然后服务器将结果发送的replayTo中就可以完成RPC调用

以上纯属个人见解,如有问题请联本人!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值