利用空的queue名字("")让rabbitMQ生成一个唯一的队列名称,同时指定队列是:临时的(auto-delete)、私有的(exclusive)。

在发送的RPC调用消息里设置消息的属性(com.rabbitmq.client.AMQP.BasicProperties)的reply_to字段来传递上面那个随机的队列名称。

基本流程如下:

代码如下:

服务端RpcServer.java

 package com.yzl.test4;

 import java.io.IOException;

 import org.apache.log4j.Logger;

 import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope; /**
* 基于rabbitMQ的RPC服务的服务提供者
* 消息消费者角色,被动接收消息,然后回复消息
* @author: yzl
* @date: 2016-10-23
*/
public class RpcServer {
//交换器名称
private static final String EXCHANGE_NAME = "rpcDirectExchange";
//client调用的消息存储queue
private static final String QUEUE_NAME = "rpcQueue";
//服务提供方的路由key,实际使用场景会是应用的appid或者appName
private static final String SERVER_ROUTING_KEY = "rpc_server1";
private static final Logger logger = Logger.getLogger(RpcServer.class); /**
* @param args
* @throws
* @throws IOException
*/
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("guest");
factory.setPassword("guest"); Connection connection = factory.newConnection();
final Channel channel = connection.createChannel(); //定义RPC的direct的交换器
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//声明接收client调动请求的队列
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//绑定队列
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, SERVER_ROUTING_KEY); logger.info("服务启动完成,等待接收请求....."); //接收client端的请求
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
//回复ack响应
channel.basicAck(envelope.getDeliveryTag(), false); logger.info("收到client调用请求-----------");
String msg = new String(body);
logger.info("收到的消息如下:" + msg);
String resp = null; //模拟解析需要调用的方法
if(msg.indexOf("add") != -1){
String parameter = msg.substring(msg.indexOf("(")+1, msg.indexOf(")"));
String[] args = parameter.split(",");
resp = String.valueOf(add(Integer.valueOf(args[0]), Integer.valueOf(args[1])));
}else{
resp = "method is not found!!";
}
logger.info("需要回调的client的queueName:" + properties.getReplyTo());
try {
Integer time = new java.util.Random().nextInt(5000);
logger.info("休眠" + time + "毫秒");
//随即休眠,模拟服务调用方法正常业务方法耗时
Thread.sleep(time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//发送给默认的direct交换器,路由键为发送方的queueName,默认交换机会把消息转发给此路由键名称的queue
channel.basicPublish("", properties.getReplyTo(), properties, (msg + ", resp is:" + resp).getBytes());
logger.info("回复clent完成");
}
});
} //模拟的server中的方法
private static int add(int num1, int num2){
logger.info("call add method, para num1 is :" + num1 + ",num2 is :" + num2);
return num1 + num2;
}
}

客户端

 package com.yzl.test4;

 import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import org.apache.log4j.Logger; import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.AMQP.Queue;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery; /**
* 基于rabbitMQ的RPC服务的服务调用者
* 消费生产者角色,发送调用请求,然后读取回复
* 发送的交换器 和 接收回应的交换器是不同的
* @author: yzl
* @date: 2016-10-23
*/
public class RpcClient {
//交换器名称
private static final String EXCHANGE_NAME = "rpcDirectExchange";
//服务提供方的路由key
private static final String SERVER_ROUTING_KEY = "rpc_server1"; private static final Logger logger = Logger.getLogger(RpcClient.class);
/**
* @param args
*/
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("guest");
factory.setPassword("guest"); Connection connection = factory.newConnection();
final Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "direct"); int threadCount = 1; //使用CountDownLatch控制10个线程一起运行
final CountDownLatch cdl = new CountDownLatch(threadCount); //生成10个线程同时访问服务
ExecutorService pool = Executors.newFixedThreadPool(threadCount); for(int i=0; i<threadCount; i++){
final int index = i;
pool.submit(new Runnable() {
@Override
public void run() {
try {
cdl.await(); //Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments)
//默认direct exchange 匿名的、 私有、自动删除
Queue.DeclareOk queue = channel.queueDeclare("", false, true, true, null);
//获取rabbit帮我们生成的随即的队列名称,来接收服务端返回的数据
String queueName = queue.getQueue(); final String messageId = UUID.randomUUID().toString();
//定义replyTo属性 和 消息ID
BasicProperties props = new BasicProperties.Builder().replyTo(queueName).correlationId(messageId).build(); logger.info("发送rpc调用请求,消息index:" + index); //发送RPC方法调用
channel.basicPublish(EXCHANGE_NAME, SERVER_ROUTING_KEY, props, ("add(" + index + "," + (index+1) + ")").getBytes()); logger.info("等待服务器响应"); //定义队列式的消费者处理器,之前是用的DefaultConsumer
QueueingConsumer consumer = new QueueingConsumer(channel); //把消费者处理器和队列对照起来
channel.basicConsume(queueName, consumer); //这里会堵塞,直到取到值或者超时
logger.info("尝试从consumer里取返回的值");
Delivery delivery = consumer.nextDelivery();
//Delivery delivery = consumer.nextDelivery(5000);
logger.info("成功取到消息,开始处理");
if(delivery.getProperties().getCorrelationId().equals(messageId)){
logger.info("收到服务器回复-----------");
String msg = new String(delivery.getBody());
logger.info("回复消息id:" + delivery.getProperties().getCorrelationId() + "内容:" + msg);
} /*
//该取消息的方式是异步的,不会堵塞,会导致后面的logger.info先于handleDelivery执行
channel.basicConsume(queueName, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
BasicProperties properties, byte[] body) throws IOException {
if(properties.getCorrelationId().equals(messageId)){
logger.info("收到服务器回复-----------");
String msg = new String(body);
logger.info("回复消息id:" + properties.getCorrelationId() + "内容:" + msg);
}
}
});
*/ logger.info("消息处理完成");
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
for(int i=0; i<threadCount; i++){
cdl.countDown();
}
}
}

先运行server后运行client,输入如下:

 2016-10-23 21:22:38,961 [com.yzl.test4.RpcServer]-[INFO] 服务启动完成,等待接收请求.....
2016-10-23 21:22:42,414 [com.yzl.test4.RpcServer]-[INFO] 收到client调用请求-----------
2016-10-23 21:22:42,414 [com.yzl.test4.RpcServer]-[INFO] 收到的消息如下:add(0,1)
2016-10-23 21:22:42,414 [com.yzl.test4.RpcServer]-[INFO] call add method, para num1 is :0,num2 is :1
2016-10-23 21:22:42,414 [com.yzl.test4.RpcServer]-[INFO] 需要回调的client的queueName:amq.gen-iek4oYqWiqQ-HU7-i2g6mA
2016-10-23 21:22:42,414 [com.yzl.test4.RpcServer]-[INFO] 休眠3999毫秒
2016-10-23 21:22:46,415 [com.yzl.test4.RpcServer]-[INFO] 回复clent完成
 2016-10-23 21:22:42,398 [com.yzl.test4.RpcClient]-[INFO] 发送rpc调用请求,消息index:0
2016-10-23 21:22:42,414 [com.yzl.test4.RpcClient]-[INFO] 等待服务器响应
2016-10-23 21:22:42,414 [com.yzl.test4.RpcClient]-[INFO] 尝试从consumer里取返回的值
2016-10-23 21:22:46,415 [com.yzl.test4.RpcClient]-[INFO] 成功取到消息,开始处理
2016-10-23 21:22:46,415 [com.yzl.test4.RpcClient]-[INFO] 收到服务器回复-----------
2016-10-23 21:22:46,415 [com.yzl.test4.RpcClient]-[INFO] 回复消息id:1e4b7fb3-1728-41e2-8cac-b13fe88d5a20内容:add(0,1), resp is:1
2016-10-23 21:22:46,415 [com.yzl.test4.RpcClient]-[INFO] 消息处理完成

RabbitMQ学习笔记5-简单的RPC调用的更多相关文章

  1. Hadoop源码学习笔记(4) ——Socket到RPC调用

    Hadoop源码学习笔记(4) ——Socket到RPC调用 Hadoop是一个分布式程序,分布在多台机器上运行,事必会涉及到网络编程.那这里如何让网络编程变得简单.透明的呢? 网络编程中,首先我们要 ...

  2. RabbitMQ学习笔记(六) RPC

    什么RPC? 这一段是从度娘摘抄的. RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的 ...

  3. rabbitMQ学习笔记(二) 简单的发送与接收消息 HelloWorld

    首先要下载rabbitmq的javaClient库,然后加入到项目中,下载地址为:http://www.rabbitmq.com/releases/rabbitmq-java-client/v3.1. ...

  4. Memcached 学习笔记(二)——ruby调用

    Memcached 学习笔记(二)——ruby调用 上一节我们讲述了怎样安装memcached及memcached常用命令.这一节我们将通过ruby来调用memcached相关操作. 第一步,安装ru ...

  5. RabbitMQ学习笔记(五) Topic

    更多的问题 Direct Exchange帮助我们解决了分类发布与订阅消息的问题,但是Direct Exchange的问题是,它所使用的routingKey是一个简单字符串,这决定了它只能按照一个条件 ...

  6. RabbitMQ学习笔记1-hello world

    安装过程略过,一搜一大把. rabbitmq管理控制台:http://localhost:15672/   默认账户:guest/guest RabbitMQ默认监听端口:5672 JAVA API地 ...

  7. Spark学习笔记0——简单了解和技术架构

    目录 Spark学习笔记0--简单了解和技术架构 什么是Spark 技术架构和软件栈 Spark Core Spark SQL Spark Streaming MLlib GraphX 集群管理器 受 ...

  8. 官网英文版学习——RabbitMQ学习笔记(十)RabbitMQ集群

    在第二节我们进行了RabbitMQ的安装,现在我们就RabbitMQ进行集群的搭建进行学习,参考官网地址是:http://www.rabbitmq.com/clustering.html 首先我们来看 ...

  9. 官网英文版学习——RabbitMQ学习笔记(一)认识RabbitMQ

    鉴于目前中文的RabbitMQ教程很缺,本博主虽然买了一本rabbitMQ的书,遗憾的是该书的代码用的不是java语言,看起来也有些不爽,且网友们不同人学习所写不同,本博主看的有些地方不太理想,为此本 ...

随机推荐

  1. 【03_136】Single Number

    感谢:http://www.cnblogs.com/changchengxiao/p/3413294.html Single Number Total Accepted: 103007 Total S ...

  2. struts2--表单重复提交

    1.表单重复提交: --使用struts2的Token或TokenSession拦截器避免重复提交: > 上述两个拦截器均不在默认的拦截器栈中,需要在struts.xml中配置: > To ...

  3. 《理解 ES6》阅读整理:块绑定(Block Binding)

    变量声明一直是JavaScript中一个需要技巧的部分.在大多数基于C的编程语言中,变量(更正式地说是绑定)在声明的时候创建,然而在JavaScript中并不是这样.在JavaScript中,变量在何 ...

  4. 简单sql操作

    ----------------------------- 数据库的有关SQL语句 -------------------------1.数据库 创建 create database data_nam ...

  5. SQLSERVER truncate table之后是否会重置表的自增值

    SQLSERVER truncate table之后是否会重置表的自增值 今天清理业务库数据的时候,开发人员说可以使用truncate table把两个表的所有数据清理掉 这两个表都有自增ID,都做了 ...

  6. linux下卸载mysql

    卸载mysql rpm -qa|grep -i mysql rpm -ev MySQL-server-4.0.14-0 MySQL-client-4.0.14-0 卸载后/var/lib/mysql中 ...

  7. C#通过反射获取上层调用方法信息

    System.Diagnostics.StackFrame frame = ); System.Reflection.MethodBase method = frame.GetMethod(); st ...

  8. EmberJs之Ember-Data

    写在前面 最近比较忙,换了新工作还要学习很多全新的技术栈,并给自己找了很多借口来不去坚持写博客.常常具有讽刺意味的是,更多剩下的时间并没有利用而更多的是白白浪费,也许这就是青春吧,挥霍吧,这不是我想要 ...

  9. UML中关联(Association)、聚合(Aggregation)和合成(Composition)之间的区别

    本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载. 现在,我们需要设计一个项目管理系统,目前我们收集到了如下这些需求: REQ1:一个项目内有多名项目成 ...

  10. windowsXP用户被禁用导致不能网站登录

    1.查看系统事件,发现弹出如下的错误 2.根据上面的错误,我们很容易就可以判断是禁用了账户引起的 2.1后面进入计算机管理,再进入用户管理 2.2双击点开Internet来宾用于,发现此用户已经停用了 ...