消息队列解决的问题:
异步处理
应用解耦
流量削峰
日志处理
AMQP:Advanced Message Queuing Protocol ,一个通过统一的消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计
安装步骤
官网地址:https://www.rabbitmq.com/install-windows.html 下载对应版本,rabbitmq是用erlang开发的所以要装erlang开发环境,地址https://www.erlang.org/downloads,下载直接next就可以了,需要配置一下环境变量
做完之后,打开cmd 运行命令rabbitmq-plugins enable rabbitmq_management 开启web管理插件,通过浏览器访问http://localhost:15672 默认用户guest 密码一致
https://www.cnblogs.com/t-ae/p/10137449.html
我开始安装报错,不是内部文件,可以先管理员方式运行rabbitmq-start 进入sbin目录,在输入上面的命令,在关闭以管理员运行
Virtual hosts 管理
虚拟主机,每一个虚拟主机中包含所有的AMQP基本组件,用户、队里、交换器等都是在虚拟主机里面创建。典型的用法是,如果公司的多个产品只想用一个服务器,就可以把他们划分到不同的虚拟主机中,里面的任何信息都是独立存在,互不干扰。一般以 / 开头
队列模型
简单模型
simple 模式
点对点消息模型,开启mq服务,开启进程p输出在向mq写消息,进程消费者进行监听
java连接测试一下
依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.0.2</version>
</dependency>
public class ConnectionUtils {
public static Connection getConnection() throws IOException, TimeoutException{
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置服务地址
connectionFactory.setHost("127.0.0.1");
//AMQP
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/tan");
connectionFactory.setUsername("tan");
connectionFactory.setPassword("123");
return connectionFactory.newConnection();
}
Connection connection = ConnectionUtils.getConnection();
Channel createChannel = connection.createChannel();
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msg="Hello simple";
createChannel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
createChannel.close();
connection.close();
Connection connection = ConnectionUtils.getConnection();
Channel createChannel = connection.createChannel();
createChannel.queueDeclare("test_simple_queue",false,false,false,null);
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String string = new String(body,"utf-8");
System.out.println(string);
}
};
createChannel.basicConsume("test_simple_queue", true,defaultConsumer);
不足
耦合性高。生成者和消费者一一对应
WordQueues
工作队列
出现工作队列原因:simple队列是一对一的,而且实际开发,生产者发送消息毫不费力,而消息者一般是和业务结合的,消费者接受到消息之后就需要处理,可能需要时间
一个生产者,多个消费者,每个消费者获取到的消息唯一,多个消费者只有一个队列
消费者1
Connection connection = ConnectionUtils.getConnection();
Channel createChannel = connection.createChannel();
createChannel.queueDeclare(QUEUE_NAME,false,false,false,null);
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg=new String (body,"utf-8");
System.out.println("消费者1"+msg);
try {
Thread.sleep(2);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
boolean autoAck=true;
createChannel.basicConsume(QUEUE_NAME, autoAck,defaultConsumer);
}
消费者2
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel createChannel = connection.createChannel();
createChannel.queueDeclare(QUEUE_NAME,false,false,false,null);
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg=new String (body,"utf-8");
System.out.println("消费者2"+msg);
try {
Thread.sleep(1);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
boolean autoAck=true;
createChannel.basicConsume(QUEUE_NAME, autoAck,defaultConsumer);
}
轮询分发
** 公平分发 **
必须改变自动应答 ack 改为手动
send 生产者 开启 createChannel.basicQos(1);
消费者 createChannel.basicAck 加createChannel.basicAck(envelope.getDeliveryTag(), false); 反馈
消息应答与消息持久化
boolean autoAck=false
channel.basicConsumer(QUEUE_NAME,autoACK,consumer)
当autoAck为true时,为自动确认模式开启,一旦rabbitmq把消息给到消费者,该消息会从内存中间删除,这种情况下,如果杀死正在执行的消费者,就会丢失正在处理的消息
当
autoack为false时为手动模式,如果有一个消费者挂掉,就会交付给其他消费者,rabbitmq 支持消息应答,消费者发送一个消息应答,告诉rabbitmq,这个消息已经处理完成可以删除
消息应答默认是打开的
如果rabbitmq 挂了,我们任务依然会丢
boolean durable=false
channel.queueDeclare(QUEUE_NAME,durable,false,false,null)
durable 就是持久化
队列一旦声明是不可以更改的
publish_subscribe 订阅模式
一个生产者多个消费者,每一个消费者都有自己的队列,生产者没有吧消息直接发送给队列,而是发送给了交换机,每一个队列都必须绑定到交换机上面,生产者发送的消息经过交换机,到达队列,就可以实现一个消息被多个消费者消费
代码
生产者
Connection connection = ConnectionUtils.getConnection();
Channel createChannel = connection.createChannel();
//声明交换机
createChannel.exchangeDeclare(EXCHANG_NAME, "fanout"); //fanout 类型为分发
String meg="hello";
createChannel.basicPublish(EXCHANG_NAME, "", null, meg.getBytes());
System.out.println("send"+meg);
createChannel.close();
connection.close();
交换机是没有存储能力的,在rabbitmq里面只有队列有存储能力
Connection connection = ConnectionUtils.getConnection();
final Channel createChannel = connection.createChannel();
createChannel.queueDeclare("test_queue_fanput_emai",false,false,false,null);
//绑定到交换机
createChannel.queueBind("test_queue_fanput_emai", "TEST_EXCHANG_FAN", "");
createChannel.basicQos(1);
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String string = new String(body);
System.out.println("消费者1"+string);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
createChannel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
createChannel.basicConsume("test_queue_fanput_emai", false,defaultConsumer);
}
交换机又叫转发器,一方面是接受生产者的信息,一方面又是向队列推送消息
channel.basicPulish("",queue_name,null,msg)
写空字符串匿名转发,
fanout 不处理路由键
direct 处理路由键
路由模式
生产者
在这里插入代码片