2. 消息队列
不考虑消息确认机制与持久化的话,BlockingQueue即可实现一个简单的消息队列;
其中包含三个角色是不可或缺的:
- Producer,生产消息
- Consumer,消费消息
- Broker,存储消息
2.1 AMQP
与JMS不同,AMQP是一个线路层wire-level协议,高级消息队列协议AMQP全称为Advanced Message Queueing Protocol,关于AMQP的介绍可以参考RabbitMQ与AMQP协议详解这篇博客
发布/订阅模式,以及与观察者模式的区别:
2.1.1 RabbitMQ
在线模拟器[RabbitMQ Simulator][tryrabbitmq.com/]
Exchange类型
- 当Exchange为Direct时
- 当Exchange类型为Fanout时
- 当Exchange类型为Topic时,主要有两个通配符:
- #代表任何零个或多个word,对应的正则表达式为:.*
- *匹配任何单个word,对应的正则表达式为:.
2.1.2 代码示例
启动RabbitMQ服务
rabbitmq-server.bat
RabbitMQ提供了Java客户端,其API详见:[Java Client API Guide][www.rabbitmq.com/api-guide.h…] ,[AMQP 0-9-1模型说明][www.rabbitmq.com/tutorials/a…]
rabbimq.client包中核心的类为:Connection与Channel,其中Connection由工厂模式创建,Channel提供了操纵Exchange,Queue的API;
本示例的结构如下图所示:
1.首先创建Publisher:
public class Publisher {
//定义队列名
static String QUEUE_NAME_1 = "queue1";
static String QUEUE_NAME_2 = "queue2";
static String QUEUE_NAME_3 = "queue3";
static String QUEUE_NAME_4 = "queue4";
public static String[] queueList = {QUEUE_NAME_1,QUEUE_NAME_2,QUEUE_NAME_3,QUEUE_NAME_4};
static String EXCHANGE_DIRECT = "direct";
static String EXCHANGE_FANOUT = "fanout";
static String EXCHANGE_TOPIC = "topic";
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = null;
Channel channel = null;
try {
//1.创建连接和通道
connection = factory.newConnection();
channel = connection.createChannel();
//声明三种类型的Exchange,两个boolean属性分别为durable,autoDelete
channel.exchangeDeclare(EXCHANGE_DIRECT,BuiltinExchangeType.DIRECT,false,false,null);
channel.exchangeDeclare(EXCHANGE_FANOUT,BuiltinExchangeType.FANOUT,false,false,null);
channel.exchangeDeclare(EXCHANGE_TOPIC,BuiltinExchangeType.TOPIC,false,false,null);
//声明队列,三个boolean属性分别为:durable,exclusive,autoDelete
channel.queueDeclare(QUEUE_NAME_1, false, false, false, null);
channel.queueDeclare(QUEUE_NAME_2, false, false, false, null);
channel.queueDeclare(QUEUE_NAME_3, false, false, false, null);
channel.queueDeclare(QUEUE_NAME_4, false, false, false, null);
//绑定关系
channel.queueBind(QUEUE_NAME_1,EXCHANGE_DIRECT,"direct");
channel.queueBind(QUEUE_NAME_2,EXCHANGE_FANOUT,"");
channel.queueBind(QUEUE_NAME_3,EXCHANGE_FANOUT,"");
channel.queueBind(QUEUE_NAME_3,EXCHANGE_TOPIC,"#.tiger");
channel.queueBind(QUEUE_NAME_4,EXCHANGE_TOPIC,"documentary.#");
//3.发布消息
Scanner scanner = new Scanner(System.in);
while(true) {
String command = scanner.nextLine();
System.out.println("指令:" + command);
if(command.indexOf("quit")!=-1){
break;
}
String[] subStr = command.split("\\|");
if(subStr.length==3){
String exchange = subStr[0];
String routingKey = subStr[1];
String msg = subStr[2];
channel.basicPublish(exchange,routingKey , null, msg.getBytes());
System.out.println("provider send a msg: " + msg);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
//4.关闭连接
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
然后在终端输入指令:
2.创建消费者Consumer,创建机制与线程类似:
public class MsgConsumer {
public static void main(String[] args) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = null;
Channel channel = null;
try {
// 1.创建连接和通道
connection = factory.newConnection();
channel = connection.createChannel();
System.out.println(" **** keep alive,waiting for messages, and then deal them **** ");
// 3.通过回调生成消费者
for(int i=0;i<Publisher.queueList.length;i++){
//4.消费消息
channel.basicConsume(Publisher.queueList[i], true, "队列"+i,new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
//获取消息内容然后处理
String msg = new String(body, "UTF-8");
System.out.println(String.format("consumerTag:%s,get message :[%s]",consumerTag,msg));
}
});
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
消费者端输出如下:
可以看到类型为Topic的交换机会根据相连的队列的绑定键bindingKey进行消息的路由;
-
路由键为documentary.lion的消息仅被发送到绑定键为**documentary.#**的队列
-
路由键为documentary.lion的消息仅被发送到绑定键为:documentary.#的队列,#.tiger的队列
2.1.3 可视化工具
RabbitMQ提供可视化插件,可以在sbin目录执行如下命令启动:
rabbitmq-plugins enable rabbitmq_management
打开:http://127.0.0.1:15672/#/即可访问
2.1.4 思维导图总结
高级消息队列协议AMQP:
RabbitMQ:
参考资料:
- [Learn Java J2EE with examples][tutorialflow.com/java/collec…]
- [深入理解AMQP协议][blog.csdn.net/weixin_3764…]
- [发布/订阅模式][hackernoon.com/observer-vs…]
- [RabbitMQ Hello][www.cnblogs.com/sam-uncle/p…]
- [RabbitMQ的Java API编程][www.cnblogs.com/wuzhenzhao/…]