消息队列

一、RabbitMQ

1、解决问题:异步消息、应用解耦、流量削峰、日志处理

2、RabbitMQ安装与配置

3、java操作RabbitMQ 

1.1 简单队列

P:消息生产者

红色:队列

C:消费者

获取连接

(1)定义一个连接工厂new ConnectionFactory

(2)设置服务地址factory.setHost("127.0.0.1")

(3)AMQP协议factory.setPort(5672)

(4)vhost factory.setVirtualHost("/name")

(5)用户名和密码 factory.setUsername()  factory.setPassword()

(6)获取连接factory.newConnection()

生产消息

(1)获取一个连接 connection

(2)从连接中获取一个通道connection.createChannel()

(3)创建队列声明 channel.queueDeclare(arg)

(4)发送消息channel.basicPublish(arg,msg)

(5)关闭通道和连接 channel.close  connection.close

消费者获取消息---3.4以前

(1)获取链接connection

(2)创建频道connection.createChannel()

(3)定义队列的消费者new QueueingConsumer(channel)

(4)监听队列channel.basicConsumer(arg)

while(true){

   Delivery delivery = consumer .nextDelivery()

   msgByte =   delivery.getBody()

}

消费者获取消息---新版

(1)获取链接connection

(2)创建频道connection.createChannel()

(3)队列声明 channel.queueDeclare(arg)

new DefaultConsumer(channel){

   获取到达的消息

   handleDelivery(){      ----回调方法,一旦有消息就被触发

      拿到消息

   }

}

(4)监听队列 channel.basicConsumer(arg)

简单队列不足:耦合性高,生产者一一对应消费者,无法满足多个消费者获取队列中的消息,队列名变更,这时候需要同时变更

1.2 Work Queue 工作队列

生产者--同简单队列

消费者--new 多个消费者,设置处理等待时间 

1.2.1 轮询分发

1.2.2 公平分发

(1)生产者 增加channel.basicQos(x)

每个消费者发送确认消息之前,消息队列只发送x条消息给消费者,一次只处理一条消息

限制发送给同一个消费者不得超过一条

(2)消费者 增加channel.basicQos(x),手动应答 channel.basicAck(arg)  自动应答ack=false

1.3 消息应答和持久化

autoAck = true 自动确认模式,一旦rabbit将消息分发给消费者,就会从内存中删除,一旦杀死正在执行的消费者,就会丢失正在处理的消息

autoAck = false 手动确认模式,如果有一个消费者没有反应,则会将消息发送给其他消费者

消息应答默认是打开的,false

消息的持久化

channel.queueDeclare(queue_name,durable,)

durable=false 已经定义的队列无法修改durable,rabbitMQ不允许重新定义一个已经存在的队列,否则会报错

1.4 订阅模式

一个生产者,多个消费者,每一个消费者都有自己的队列,生产者没有直接把消息发送给队列,而是发到交换机上(转换器),每个队列都要绑定到交换机上,生产者发送的消息经过交换机到达队列,就能实现一个消息被多个消费者收到

生产者

(1)获取一个连接 connection

(2)从连接中获取一个通道connection.createChannel()

(3)声明交换机channel.exchangeDeclare(arg) 

(4)发送消息给交换机 channel.basicPublish(arg)

(5)rabbitMQ中只有队列具有存储能力

消费者

(1)获取链接connection

(2)创建频道connection.createChannel()

(3)队列声明 channel.queueDeclare(arg)

new DefaultConsumer(channel){

   获取到达的消息

   handleDelivery(){      ----回调方法,一旦有消息就被触发

      拿到消息

   }

}

(4)绑定队列到交换机  channel.queeuBind(arg)

(5)监听队列 channel.basicConsumer(arg)

交换机(转发器)一方面接受生产者的消息,一方面将接受到的消息转发给绑定的队列

路由模式Exchange  Routing

交换机类型 fandout 不处理路由  direct 处理路由

生产者发送消息携带routingKey

消费者绑定队列时携带routingKey,只有两者的路由键匹配上才能接收到消息

主题Exchange  Topics

将路由键和某模式匹配

交换机类型 topic

#匹配一个或者多个

*匹配一个

RabbitMQ消息确认机制(事务+confirm)

AMQP 实现了事务机制

Confirm模式

(1)事务机制--对生产者的操作

txSelect 用于将channel设置为transation事务模式即开启事务模式

txCommit 用提交事务  

txRollback  回滚事务

这种模式比较耗时,降低了RabbitMQ的吞吐量

(2)confirm模式--异步

生产者将channel设置为confirm模式,一旦channel进入confirm模式,所有在该channel上面发布的消息都会被指派一个唯一的id(从1开始),一旦消息被投递到所有匹配的队列上之后,rabbit服务器就会发送一个确认给生产者(包含消息的id),这就使得生产者知道消息已经到达正确的队列 。

channel.confirmSelect()  开始confirm模式

编程模式:

1、普通  发送一条 waitForConfirms;

send msg  发送一条消息

if   ! channel.waitForConfirms()  发送成功

else 发送失败

2、批量 发送一批 waitForConfirms;

for send msg  批量发送消息

if   ! channel.waitForConfirms()  发送成功

else 发送失败

3、异步 提供一个回调方法

channel对象提供的ConfirmListener()回调方法只包含deliveryTag(当前channel发出的消息序列号),我们需要自己为每一个channel维护一个unconfirm的消息序号集合,没publish一条消息,集合元素加1,每回调一次handleAck方法,unconfirm集合删除相应的一条(multiple=false)或多条(multiple=true)记录

// 获取连接,开启channel
....
// 开启confirm模式
channel.confirmSelect();

// 未确认消息标识
final SortedSet confirmSet = Collecitons.synchronizedSortedSet(new TreeSet)

channel.addConfirmListener(new ConfirmListener(){
    
    //handleNack 消息回执有问题
    public void handleNack(long deliverytag, boolean multiple){
        if(multiple){
            confirmSet.headSet(deliveryTag + 1).clear();
        }else{
            confirmSet.remove(deliveryTag)
        }
    }

    // handleAck 消息回执没问题
    public void handleAck(long deliverytag, boolean multiple){
        if(multiple){
            confirmSet.headSet(deliveryTag + 1).clear();
        }else{
            confirmSet.remove(deliveryTag)
        }
    }
    
});

 String msg = "1111"
    while(true){
    
        long seqNo = channel.getNextPublishSeqNo();
        channel.basicPublish("",queue_name,null,msg.getBytes());
        confirmSet.add(seqNo);
        
    }

Spring 集成 RabbitMQ

配置文件

定义RabbitMQ的连接工厂

定义rabbit模板,指定连接工厂及定义exchange

MQ的管理包括队列,交换器声明等

定义队列、自动声明

定义交换器、自动声明

队列监听

消费者

二、ActiveMQ-tcp协议

JMS模型

p2p(point对point)模型

pub/sub(publish/subscribe)发布/订阅模型

默认本地连接地址:"tcp://localhost:61616"

Session接口中定义的几个常亮

AUTO_ACKNOWLEDGE = 1 自动确认

CLIENT_ACKNOWLEDGE = 2 客户端手动确认

DUPS_OK_ACKNOWLEDGE = 3 自动批量确认

SESSION_TRANSACTED = 4 事务提交并确认

消息发送者

1、创建连接工厂 new ActiveMQConnectionFactory("tcp://localhost:61616")

2、从工厂获取连接对象 connectionFactory.createConnection()

3、连接服务connection.start()

4、获取session对象 connection.createSession(transacted(boolean 是否需要事务), acknowledgeMode(session常量))

5、创建主题 session.createTopic("topic_name")

6、创建消息发送者 session.createProducer(topic)

7、创建消息对象 session.createTextMessage("str")

8、发送消息 producer.send(message)

9、关闭相关资源 producer.close() session.close() connection.close()

消息消费者

1、创建连接工厂 new ActiveMQConnectionFactory("tcp://localhost:61616")

2、从工厂获取连接对象 connectionFactory.createConnection()

3、连接服务connection.start()

4、获取session对象 connection.createSession(transacted(boolean 是否需要事务), acknowledgeMode(session常量))

5、订阅主题 session.createTopic("topic_name")

6、创建消息消费者 session.createConsumer()

7、指定消息监听器 consumer.setMessageListener(newMessageListener(){

          public  void onMessage(Message message){

                    获取到消息

             }

    })

8、无需关闭资源,时刻处于监听状态

第4步改为手动应答session.CLIENT_ACKNOWLEDGE,接收到消息之后需要手动应答message.acknowledge()

若消息处理失败,调用session.recover()实现失败消息重发(最多重发6次)

消息持久化订阅

持久化到文件-默认

修改消息发送方

producer.send(1,2,3,4)

1:message, 

2:deliveryMode, DeliveryMode.PERSISTENT 持久化

3:priority,  优先级 1

4:timetoLive 持久化时间 单位ms

修改消息接受方

connection.setClientID("字符串") 设置客户端id

创建消费者对象时 session.createDurableSubscribe(topic,"字符串")

持久化到数据库

1、将数据库驱动复制到activeMQ的lib目录下

2、修改activemq.xml配置文件

3、在配置文件中配置数据源

4、重启activeMQ服务

三、kafka

1、点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)

通常是一个基于拉取或者轮询的消息传送模型,这种模型从队列中请求信息,而不是将消息推送到客户端。这个模型的特点是发送到队列的消息被一个且有且只有一个接受者接受处理,即使有多个消息监听者也是如此

2、发布/订阅模式(一对多,数据产生后,推送给所有订阅者)

发布订阅模型是一个基于推送的消息传送模型。发布订阅模型可以有多个不同的订阅者,临时订阅者只有在主动监听主题的时候才接受消息,而持久订阅则监听主题的所有消息,即使订阅者不可用,处于离线状态

 

你可能感兴趣的:(消息队列)