RabbitMq ----> 学习笔记

环境:

centos6.8,jdk1.8.0_u172

1、环境搭建

官网:https://www.rabbitmq.com

准备:

erlang-21.0.7-1.el6.x86_64.rpm,rabbitmq-server-3.7.7-1.el6.noarch.rpm

先安装erlang,再安装rabbit-server

1 cd /home/rui/softwarerepo
2 
3 rpm -ivh erlang-21.0.7-1.el6.x86_64.rpm #安装erlang
4 
5 rpm -q erlang-21.0.7-1.el6.x86_64 #查询是否安装erlang
6 
7 rpm -qi erlang-21.0.7-1.el6.x86_64 #显示erlang的详细信息
8 
9 rpm -ql erlang-21.0.7-1.el6.x86_64 #查看erlang的安装位置和包含的文件,/usr/lib64和/usr/share/doc/

 

socat是一个网络工具,官网:http://www.dest-unreach.org/socat/

从http://www.dest-unreach.org/socat/download/下载socat,选择 socat-1.7.3.2.tar.gz 

1 tar –zxvf socat-1.7.3.2.tar.gz
2 
3 cd socat-1.7.3.2
4 
5 ./configure
6 
7 make && make install

 

安装rabbit-server

1 rpm -ivh rabbitmq-server-3.7.7-1.el6.noarch.rpm --nodeps
2
3 rpm -ql rabbitmq-server-3.7.7-1.el6.noarch #查看安装详情,/etc/logrotate.d/,/etc/profile.d/,/etc,/etc/rc.d/init.d/,/usr/lib/ocf/resource.d/,/usr/lib/,/usr/sbin/,/usr/share/doc/
4
5 cd /usr/share/doc/rabbitmq-server-3.7.7/ #拷贝配置文件到 /etc/rabbitmq
6
7 cp rabbitmq.config.example /etc/rabbitmq/rabbit.config

RabbitMq ----> 学习笔记_第1张图片

 启动rabbitmq

1 service rabbitmq-server start

 查看rabbitmq进程是否存在

1 ps -ef|grep rabbitmq

 rabbitmq命令

1 service rabbitmq-server   start #启动
2 
3 service rabbitmq-server   restart #重启动
4 
5 service rabbitmq-server   stop #关闭
6 
7 service rabbitmq-server   status #查看状态

配置rabbitmq的管理权限,通过rabbitmqctl命令行来配置,更多详细信息参考文档:https://www.rabbitmq.com/rabbitmqctl.8.html

 1 #配置用户管理
 2 rabbitmqctl add_user 目标用户名 目标用户密码
 3 
 4 rabbitmqctl set_user_tags 目标用户名 标签名(比如:administrator)
 5 
 6 #配置用户访问控制权限
 7 rabbitmqctl set_permissions -p /  目标用户名 “.*” “.*” “.*” #后面的.*分别表示针对所有资源拥有配置权限,写入权限,读取权限;/是指虚拟主机的名字,这是默认设置
 8 
 9 示例: 10 rabbitmqctl add_user admin admin 11 12 rabbitmqctl set_user_tags admin administrator 13 14 rabbitmqctl set_permissions -p / admin “.*” “.*” “.*” 15 16 rabbitmqctl list_users

RabbitMq ----> 学习笔记_第2张图片

通过web 浏览器的方式管理和查看rabbitmq,详细内容查看文档:https://www.rabbitmq.com/management.html

1)激活rabbitmq_management插件,不用重新启动rabbitmq

1 rabbitmq-plugins enable rabbitmq_management

RabbitMq ----> 学习笔记_第3张图片

2)开放防火墙端口15672,浏览器访问 http://192.168.0.102:15672/

RabbitMq ----> 学习笔记_第4张图片

1 -A INPUT -m state --state NEW -m tcp -p tcp --dport 15672 -j ACCEPT

RabbitMq ----> 学习笔记_第5张图片

------------------------------------------------------我是分割线-----------------------------------------------------------------

RabbitMq ----> 学习笔记_第6张图片

2、基本概念 ,参考文档:https://www.rabbitmq.com/getstarted.html

producer:发送消息的用户应用程序。

queue:存储消息的缓存区。

consumer:接收消息的用户应用程序。

exchange:它接收来自生产者的消息,然后将它们推送到队列。exchange有四种类型:direct,topic,headers,fanout。

    1)fanout:将它收到的所有消息广播到它知道的所有队列。

    2)direct:消息的routing key和direct exchange与queue之间的binding key相匹配时,消息将被送到相匹配的队列。direct --> fanout :当全部queue和exchange direct之间的binding key相同时,此时direct exchange等价于fanout exchange。

    3)topic:使用特定routing key发送的消息将传递到使用匹配的binding key绑定的所有队列。routing key的规则:必须使用以点分隔开的字符串;大小不能超过255bytes;binding key的规则:可以使用符号“ * ”和“ # ”,“ * ”替代一个单词,“ # ”替代零个或者多个单词。topic exchange在某些情况下可以表现出direct exchange和fanout exchange的特性:topic --> direct:binding key不使用特殊字符“ * ”和“ # ”时;topic --> fanout:binding key只使用特殊字符“ # ”时。

binding:exchange和queue的关系称为绑定。绑定通常还会和routingKey参数一起使用,称为binding key。

temporary queue:具有随机名称的队列。它可以通过queueDeclare()方法生成,可以使用这个生成的随机对列名创建一个非持久,独立而且会自动删除的队列。

routing key:与producer发布的消息绑定的路由信息。

binding key:queue和exchange之间的绑定关系。

Round-robin dispatching:循环调度,默认地,rabbitmq会发送每一个消息给下一个消费者。按平均来算,每个消费者将会得到相同数量的消息。

Message acknowledgment:ack:消费者发回ack(nowledgement)告诉RabbitMQ已收到,处理了特定消息,RabbitMQ可以自由删除它。如果消费者死亡(其channal关闭,connection关闭或TCP连接丢失)而不发送ack,RabbitMQ将理解消息未完全处理并将重新排队。如果其他消费者同时在线,则会迅速将其重新发送给其他消费者。这样我们就可以确保没有消息丢失,即使worker(消费者)偶尔会死亡。

Message durability:通过设置durable=true确保rabbitmq的队列不会丢失,必须同时在生产者和消费者之间设置;接着通过设置 MessageProperties.PERSISTENT_TEXT_PLAIN保证消息持久化。

Fair dispatch:公平调度,通过设置prefetchCount=1实现一次发送一条信息给worker,直到worker处理并且确认之后再发送下一条消息。

deliveryMode:标记消息的发布模式,两种选择, persistent 或者 transient。

contentType:描述编码的lmine类型。比如指定为application/json.

replyTo:回调队列的名字。

correlationId:rpc中,响应和请求对应关系的关联纽带。每个请求的CorrelationId是唯一的。

3、框架 

1)Publish/Subscribe,图片来自:https://www.rabbitmq.com/tutorials/tutorial-three-java.html

RabbitMq ----> 学习笔记_第7张图片

2)topic,图片来自:https://www.rabbitmq.com/tutorials/tutorial-five-java.html

RabbitMq ----> 学习笔记_第8张图片

3)rpc,图片来自:https://www.rabbitmq.com/tutorials/tutorial-six-java.html

RabbitMq ----> 学习笔记_第9张图片

说明:在rpc中使用rabbitmq时,请求会发送replyTo和correlationId两个属性,replyTo,表示为请求创建匿名的独占队列,称为reply queue。当worker等到请求,完成了相关的工作后,worker把处理结果和correlationId发送到reply queue。客户端等到reply queue出现数据时,会检查correlationId是否是之前请求中的correlationId,如果不是,将丢弃消息;如果是一样的,则响应给应用程序。

4、支持的协议

AMQP 0-9-1

5、java操作 参考文档:https://www.rabbitmq.com/getstarted.html

maven依赖:

1  
2       com.rabbitmq
3       amqp-client
4       4.0.2
5  

1)简单的producer-queue-consumer

Send.java

 0     private final static String QUEUE_NAME = "hello"; //声明一个队列的名称
 1     ConnectionFactory factory = new ConnectionFactory(); //创建连接工厂
 2     factory.setHost("localhost"); // 表示连接本地的rabbitmq,如果是远程,必须指定ip
 3     Connection connection = factory.newConnection(); //创建与rabbitmq server的连接
 4     Channel channel = connection.createChannel(); //创建用于发送消息的通道
 5 
 6     channel.queueDeclare(QUEUE_NAME, false, false, false, null);//声明一个队列,名称指定为“hello”
 7     String message = "Hello World!";
 8     channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));//将消息编码,发送消息到指定名称的队列
 9     System.out.println(" [x] Sent '" + message + "'");
10 
11     channel.close(); //关闭通道
12     connection.close(); //关闭连接

 Rec.java

 0     private final static String QUEUE_NAME = "hello"; //声明队列名称
1 ConnectionFactory factory = new ConnectionFactory(); 2 factory.setHost("localhost"); 3 Connection connection = factory.newConnection(); 4 Channel channel = connection.createChannel(); 5 6 channel.queueDeclare(QUEUE_NAME, false, false, false, null); //上面代码作用见Send.java 7 System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); 8 9 Consumer consumer = new DefaultConsumer(channel) { //异步发送消息,以对象的形式提供回调方法,缓冲消息,直到我们准备使用消息 10 @Override 11 public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) 12 throws IOException { 13 String message = new String(body, "UTF-8"); //消息的内容 14 System.out.println(" [x] Received '" + message + "'"); 15 } 16 }; 17 channel.basicConsume(QUEUE_NAME, true, consumer); //指定消费者消费指定的队列

 2)wokers queues

NewTask.java

 1  private static final String TASK_QUEUE_NAME = "task_queue";//声明队列名称
 2 
 3 //同Send.java:创建连接工厂,设置主机,创建连接,创建通道,声明队列
 4 
 5 boolean durable = true; //声明队列为持久化,producer和consumer必须同时声明
 6 channel.queueDeclare(TASK_QUEUE_NAME , durable, false, false, null);
 7 
 8 //dosomething
 9 
10 channel.basicPublish("", TASK_QUEUE_NAME ,
11         MessageProperties.PERSISTENT_TEXT_PLAIN, //将消息设置为可以持久化
12         message.getBytes("UTF-8"));
13 
14 
15     //关闭通道,关闭连接

Worker.java

 1 private static final String TASK_QUEUE_NAME = "task_queue";//声明队列的名称
 2 
 3 //同Rec.java:创建连接工厂,设置主机,创建连接,创建通道,声明队列
 4  
 5  boolean durable = true; //声明队列为持久化,producer和consumer必须同时声明
 6  channel.queueDeclare(TASK_QUEUE_NAME , durable, false, false, null);
 7  
 8 int prefetchCount = 1;
 9 channel.basicQos(prefetchCount); //一次只接受一个未确认的消息,实现公平调度
10 
11  
12  final Consumer consumer = new DefaultConsumer(channel) {
13   //....
14  
15     try{
16     //dosomething
17     }finally{
18      channel.basicAck(envelope.getDeliveryTag(), false);//确认消息,通过提供的标签
19  } 20  }; 21 22 boolean autoAck = false; //设置worker,即consumer可发送ack给rabbitmq,表示消息已被接收和处理,rabbitmq可以任意删除消息 23 channel.basicConsume(TASK_QUEUE_NAME , autoAck, consumer);

 3)Publish/Subscribe

 EmitLog.java

 1 private static final String EXCHANGE_NAME = "logs";//exchange的名称
 2 
 3 //同Send.java:创建连接工厂,设置主机,创建连接,创建通道
 4 
 5 channel.exchangeDeclare(EXCHANGE_NAME , BuiltinExchangeType.FANOUT);//声明exchange,类型为fanout
 6 
 7 //dosomething
 8 
 9  channel.basicPublish(EXCHANGE_NAME , "", null, message.getBytes("UTF-8"));  // 发布消息到exchange
10 
11 
12 //关闭通道,关闭连接

ReceiveLogs.java

 1 private static final String EXCHANGE_NAME = "logs";//exchange的名称
 2 
 3 //同Rec.java:创建连接工厂,设置主机,创建连接,创建通道
 4  
 5 channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);//创建exchange,类型为fanout
 6 String queueName = channel.queueDeclare().getQueue();//声明一个有名称的队列,这个队列非持久,独占,可自动删除
 7 channel.queueBind(queueName, EXCHANGE_NAME, "");//队列和exchange进行绑定,exchange会将消息附加到队列
 8 
 9  
10  final Consumer consumer = new DefaultConsumer(channel) {
11  
12     //dosomething
13     
14  };
15  
16  channel.basicConsume(queueName, true, consumer);

 4)routing

EmitLogDirect.java

 1 private static final String EXCHANGE_NAME = "direct_logs";//exchange的名称
 2 
 3 //同Send.java:创建连接工厂,设置主机,创建连接,创建通道
 4 
 5 channel.exchangeDeclare(EXCHANGE_NAME , BuiltinExchangeType.DIRECT);//声明exchange,类型为direct
 6 
 7 //dosomething
 8 
 9 String routingKey = "...";
10 
11  channel.basicPublish(EXCHANGE_NAME , routingKey, null, message.getBytes("UTF-8"));  // 发布消息到exchange,消息上绑定有routingKey
12 
13 
14 //关闭通道,关闭连接

ReceiveLogsDirect.java

 1 private static final String EXCHANGE_NAME = "direct_logs";//exchange的名称
 2 
 3 //同Rec.java:创建连接工厂,设置主机,创建连接,创建通道
 4  
 5 channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);//创建exchange,类型为direct
 6 String queueName = channel.queueDeclare().getQueue();//声明一个有名称的队列,这个队列非持久,独占,可自动删除
 7 
 8 String routingKey = "..."
 9 
10 channel.queueBind(queueName, EXCHANGE_NAME, routingKey);//队列和exchange进行绑定,exchange会将消息附加到队列,暂且把routingKey称为 binding key,在exchange和queue增加binding key进行绑定
11 
12  
13  final Consumer consumer = new DefaultConsumer(channel) {
14  
15     //dosomething
16     
17  };
18  
19  channel.basicConsume(queueName, true, consumer);

 5)topic

EmitLogTopic.java

 1 private static final String EXCHANGE_NAME = "topic_logs";//exchange的名称
 2 
 3 //同Send.java:创建连接工厂,设置主机,创建连接,创建通道
 4 
 5 channel.exchangeDeclare(EXCHANGE_NAME , BuiltinExchangeType.TOPIC);//声明exchange,类型为TOPIC
 6 
 7 //dosomething
 8 
 9 String routingKey = "...";
10 
11  channel.basicPublish(EXCHANGE_NAME , routingKey, null, message.getBytes("UTF-8"));  // 发布消息到exchange,消息上绑定有routingKey
12 
13 
14 //关闭通道,关闭连接

ReceiveLogsTopic.java

private static final String EXCHANGE_NAME = "topic_logs";//exchange的名称

//同Rec.java:创建连接工厂,设置主机,创建连接,创建通道
 
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);//创建exchange,类型为topic
String queueName = channel.queueDeclare().getQueue();//声明一个有名称的队列,这个队列非持久,独占,可自动删除

String routingKey = "..."; // 把routingkey命名成bindingKey 

channel.queueBind(queueName, EXCHANGE_NAME, routingKey);//队列和exchange进行绑定,exchange会将消息附加到队列,暂且把routingKey称为 binding key,在exchange和queue增加binding key进行绑定

 
 final Consumer consumer = new DefaultConsumer(channel) {
 
    //dosomething
    
 };
 
 channel.basicConsume(queueName, true, consumer);

6)rpc

RPCClient.java

 1  private String requestQueueName = "rpc_queue";//定义一个routing key,本质是队列的名称
 2 
 3  public RPCClient() throws IOException, TimeoutException {
 4      //同Rec.java:创建连接工厂,设置主机,创建连接,创建通道
 5 }
 6 
 7 
 8 public String call(String message) throws IOException, InterruptedException {
 9 
10   final String corrId = UUID.randomUUID().toString();//用来关联rpc的响应和请求,必须是唯一的
11 
12     String replyQueueName = channel.queueDeclare().getQueue(); // 回调队列的名称,使用默认的queue,是用来接收worker的响应
13     AMQP.BasicProperties props = new AMQP.BasicProperties //这是消息额外的属性
14             .Builder()
15             .correlationId(corrId)  // 设置correlationId属性到消息体中
16             .replyTo(replyQueueName) //设置replyTo属性到消息体中
17             .build();
18 
19     channel.basicPublish("", requestQueueName, props, message.getBytes("UTF-8"));  //发送消息(包括了额外的属性props)到名称为“rpc_queue”的队列
20 
21     final BlockingQueue response = new ArrayBlockingQueue(1); 22 23 String ctag = channel.basicConsume(replyQueueName, true, new DefaultConsumer(channel) { //consumer对象的回调接口 24  @Override 25 public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { 26 if (properties.getCorrelationId().equals(corrId)) { //判断响应的信息中是否corrId和请求的corrId相等,如果相等,放到response队列中 27 response.offer(new String(body, "UTF-8")); 28  } 29  } 30  }); 31 32 String result = response.take(); 33 channel.basicCancel(ctag); // 取消消费者,一旦取消消费者,回调消费者的接口方法Consumer#handleCancelOk取出取处理 34 //ctag 是和消费者相关联的消费者标签ConsumerTag 35 return result; 36 } 37 38 //关闭通道,关闭连接

RPCServer.java

 1 private static final String RPC_QUEUE_NAME = "rpc_queue";
 2 
 3 //同Rec.java:创建连接工厂,设置主机,创建连接,创建通道
 4 
 5 
 6 channel.queueDeclare(RPC_QUEUE_NAME, false, false, false, null);
 7 channel.queuePurge(RPC_QUEUE_NAME); //清除队列rpc_queue的内容。
 8 
 9 channel.basicQos(1); //一次只接受一个未确认的消息,实现公平调度
10 
11 Consumer consumer = new DefaultConsumer(channel) {
12         @Override
13         public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
14           AMQP.BasicProperties replyProps = new AMQP.BasicProperties 15  .Builder() 16 .correlationId(properties.getCorrelationId()) //取出CorrId,构建消息的额外属性 17  .build(); 18 19 String response = ""; 20 21 try{ 22 23 }catch{ 24 25 }finally{ 26 channel.basicPublish( "", properties.getReplyTo(), replyProps, response.getBytes("UTF-8")); //发送消息给回调队列,消息内容包括response和replyProps中的CorrId 27 channel.basicAck(envelope.getDeliveryTag(), false);//确认消息,通过提供的标签 28 // RabbitMq consumer worker thread notifies the RPC server owner thread 29 synchronized(this) { 30 this.notify(); 31  } 32  } 33  } 34 }; 35 36 channel.basicConsume(RPC_QUEUE_NAME, false, consumer); 37 // 等待并准备消费RPC client发送过来的消息 38 while (true) { 39 synchronized(consumer) { 40 try { 41  consumer.wait(); 42 } catch (InterruptedException e) { 43  e.printStackTrace(); 44  } 45  } 46  } 47 48 //关闭通道,关闭连接

3、RabbitMQ使用场景 参考下面博文

CSDN https://blog.csdn.net/whoamiyang/article/details/54954780

知乎 https://www.zhihu.com/question/34243607

http://www.cnblogs.com/luxiaoxun/p/3918054.html

4、RabbitMQ工作机制的理解 参考下面文章

https://www.2cto.com/kf/201612/575219.html

转载于:https://www.cnblogs.com/mrray1105/p/8934177.html

你可能感兴趣的:(RabbitMq ----> 学习笔记)