MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。其中较为成熟的MQ产品有IBM WEBSPHERE MQ等等。
RabbitMQ 内部结构
1. Message
消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。
2. Publisher
消息的生产者,也是一个向交换器发布消息的客户端应用程序。
3. Exchange
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
4. Binding
绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
5. Queue
消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
6. Connection
网络连接,比如一个TCP连接。
7. Channel
信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内地虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
8. Consumer
消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
9. Virtual Host
虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。
10. Broker
表示消息队列服务器实体。
引入
implementation 'com.rabbitmq:amqp-client:4.0.0'
//arr的封装
package com.fszn.rabbitmqarr;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
/**
* 类 名: RabbitMQArr
* 创 建 者: 张华
* 创建时间: 2018/7/4 15:28
* 描 述:
* Exchange:交换机,决定了消息路由规则;
* Queue:消息队列;
* Channel:进行消息读写的通道;
* Bind:绑定了Queue和Exchange,意即为符合什么样路由规则的消息,将会放置入哪一个消息队列;
* <p>
* 因为你发送消息的时候,需要指定 交换机的名称 和 路由键名称;
* 而F创建的队列需要通过路由键和交换机进行绑定,
* 那么你Android端发布的消息就会发送到F创建的队列中(你的交换机名称和路由键肯定要和F创建的队列相关联的交换机名称和路由键相同。)
* <p>
* 交换机的名称 : TestRabbitmq.Message:TestRabbitmq
* 路由键 : ftf
* 队列名称:
* <p>
* http://www.rabbitmq.com/tutorials/tutorial-one-java.html
* https://my.oschina.net/xiaozhutefannao/blog/1838876
*/
public class RabbitMQArr extends AppCompatActivity {
private static final String TAG = "RabbitMQArr";
private ConnectionFactory factory;
private Connection connection = null;
private Channel channel = null;
public RabbitMQArr(String host, int port, String username, String password, String virtualHostname) {
factory = new ConnectionFactory();
setUpConnectionFactory(host, port, username, password, virtualHostname);// 连接设置
}
private void setUpConnectionFactory(String host, int port, String username, String password, String virtualHostname) {
factory.setHost(host);//主机地址:192.168.8.23
factory.setPort(port);// 端口号:5672
factory.setUsername(username);// 用户名 admin
factory.setPassword(password);// 密码 123456
factory.setVirtualHost(virtualHostname);
factory.setAutomaticRecoveryEnabled(true);//设置网络异常重连
factory.setRequestedHeartbeat(1);//是否断网
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate()");
}
/***
* 发布信息
* @param exchangeName 交换机名称
* @param routingKey 路由键 关键字
* @param msg 发布的信息
*/
public void publishMessage(String exchangeName, String routingKey, String msg) {
publishToAMPQ(exchangeName, routingKey);
publishMessage(msg);
}
/**
* 接收信息
*
* @param queueName 队列名称
* @param exchangeName 交换机名称
* @param routingKey 路由 关键字
*/
Thread subscribeThread; //用于从线程中获取数据,更新ui
public void subscribe(final Handler handler, final String queueName, final String exchangeName, final String routingKey, final String exchangeType) {
subscribeThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Log.d("subscribe", "111111111111111111111");
if (connection == null) {
connection = factory.newConnection(); //使用之前的设置,建立连接
Log.d("subscribe", "22222222222222222222222" + connection);
}
Log.d("subscribe", "========================================");
//创建一个通道
channel = connection.createChannel(2);
Log.d("subscribe", "33333333333333333333333333" + channel);
//这里的创建队列,是为了防止 消费 在 生产 之前
channel.queueDeclare(queueName, true, false, false, null);
Log.d("subscribe", "444444444444444444444444444");
//绑定队列
channel.exchangeDeclare(exchangeName, exchangeType, true, false, null);
Log.d("subscribe", "55555555555555555555555555555");
//绑定队列
channel.queueBind(queueName, exchangeName, routingKey, null);
Log.d("subscribe", "666666666666666666666666666666");
//创建消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
Log.d("subscribe", "7777777777777777777777777777777" + consumer);
//一次只发送一个,处理完成一个再获取下一个
channel.basicQos(1);
Log.d("subscribe", "8888888888888888888888888888888888");
//将队列绑定到消息交换机exchange上
channel.basicConsume(queueName, false, consumer);
Log.d("subscribe", "99999999999999999999999999999999999999999");
//channel.basicNack(1000, false, false);
while (true) {
Log.d("subscribe", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa " + consumer);
//wait for the next message delivery and return it.
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
Log.d("subscribe", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
channel.basicNack(delivery.getEnvelope().getDeliveryTag(), true, false);
String message = new String(delivery.getBody());
Log.d("subscribe", "cccccccccccccccccccccccccccccccccc");
//从message池中获取msg对象更高效
Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("msg", message);
msg.setData(bundle);
handler.sendMessage(msg);
Log.d("subscribe", "ddddddddddddddddddddddddddddddddddddd");
}
} catch (InterruptedException e) {
Log.d("subscribe", "0000000000000000000000000000000000000000000");
break;
} catch (Exception e1) {
Log.d(TAG, "Connection broken: " + e1.getClass().getName());
Log.d("subscribe", "99999999999999999999999999999999999999");
SystemClock.sleep(5 * 1000); //sleep and then try again
}
}
}
});
subscribeThread.start();
}
/**
* @param exchangeName 交换机名称
* @param routingKey 路由关键字
*/
private BlockingDeque<String> queue = new LinkedBlockingDeque<>(); // 创建内部消息队列,供消费者发布消息用
Thread publishThread;
public void publishToAMPQ(final String exchangeName, final String routingKey) {//Constant.MQ_ROUTINGKEY_MAP_REQUEST
publishThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
// 创建连接
if (connection == null)
connection = factory.newConnection();
// 创建通道
if (channel == null)
channel = connection.createChannel();
channel.confirmSelect();
while (true) {
String message = queue.takeFirst();
try {
// 发布消息 exchange routingKey
channel.basicPublish(exchangeName, routingKey, null, message.getBytes());
channel.waitForConfirmsOrDie();
Log.d(TAG, "发布消息..........");
} catch (Exception e) {
queue.putFirst(message);
throw e;
}
}
} catch (InterruptedException e) {
break;
} catch (Exception e) {
Log.d("TAG_Publish", "Connection broken: " + e.getClass().getName());
SystemClock.sleep(5 * 1000); //sleep and then try again
}
}
}
});
publishThread.start();
}
public void publishMessage(String message) {
try { //向内部阻塞队列添加一条消息
queue.putLast(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
常量类:
public class Constant {
public static final String host = "192.168.8.23"; //IP
public static final int port = 5672; //端口
public static final String username = "admin";//用户名
public static final String password = "123456";//密码
public static final String exchangename = "TestRabbitmq.Message:TestRabbitmq"; // 交换机名
public static final String routingKey = "ftf"; //路由 关键字
public static final String queuename = "TestRabbitmq.Message:TestRabbitmq_ftf"; //队列名
public static final String exchangeType = "topic";// exchange type : fanout、direct、topic、headers
public static final String virtualHostname = "/"; //虚拟机名
}
使用
mRabbitMQArr = new RabbitMQArr(Constant.host, Constant.port, Constant.username, Constant.password,Constant.virtualHostname);
mRabbitMQArr.subscribe(mHandler, Constant.queuename, Constant.exchangename, Constant.routingKey, Constant.exchangeType);
RabbitMq Android接收端 断网数据完整
最新推荐文章于 2022-04-20 17:33:07 发布