channel:
几乎所有的操作都在channel中进行,客户端可建立多个channel,每个channel代表一个会话任务
message:
由properties和body组成。properties可以对消息进行修饰,比如消息的优先级、延迟等高级特性;
body就是消息体的内容
virtual host:
逻辑隔离,一个virtual host可以有若干个exchange和queue,同一个virtual host不能有相同名称
的exchange和queue
exchange:
接受消息,根据路由键转发消息到绑定的队列
binding:
exchange和queue中的虚拟连接,binding中可以包含routing key
routing key:
queue:
amqp:5672
cluster:25672
management:15672
生产端
connectionfactory,setHost、port、virtualHost
connection
connection.createChannel
channel.basicPublish(exchange,routingKey,props,body)
channel.close
connection.close
消费端
// exclusive:独占,这个队列只有我一个channel可监听
// autoDelete:队列脱离exchange,是否自动删除
channel.queueDeclare(queue,durable,exclusive,autoDelete,arguments)
QueuingConsumer queueingConsumer = new QueueingConsumer(channel)
// autoAck:是否自动签收
channel.basicConsume(queueName,autoAck,queueingConsumer)
// 阻塞获取消息
Delivery delivery = queueingConsumer.nextDelivery()
delivery.getBody()
delivery.getProperties()
// Envelop envelop = delivery.getEnvelop();
exchange和queue之间连接关系,
exchange和exchange之间也可以
binding中可以包含routingKey或者参数
durability
auto delete:最后一个监听被移除,是否自动删除
properties和body组成,
可以自定义属性,使用headers,
常用属性:delivery model
其他属性:
content_Type、content_encoding、priority
correlation_id、
Map headsMap = new ...
headsMap.put("my1","111")
AMQP.BasicProperties = 链式编程
.deliveryMode(2) // 2为持久化消息
.contentEncoding("utf-8")
.expiration("15000") // 过期时间,15秒
.headers(headsMap) //自定义的属性
.build()
direct、topic、fanout、headers
channel.exchangeDeclare(exchangeName,exchangeType,...)
channel.queueBind(queueName,exchangeName,routingKey)
topicExchange:可以使用通配符进行模糊匹配
# :一个或多个词,
* :一个词
fanoutExchange:不处理任何路由键,只需简单地将队列绑定到交换机上
Topic类型:
消费者这边:
routingKey = "user.#"
channel.queueBind(queueName,exchangeName,routingKey)
生产者这边:
routingKey = "user.save"
channel.basicPublish(exchangeName,routingKey...,msg)
durability:是否需要持久化
autoDelete:当最后一个绑定到exchange的队列删除后,自动删除该exchange
什么是生产端的可靠性投递?
1.消息成功发出
2.MQ节点的成功接收
3.发送端收到broker的确认应答
4.完善的消息补偿机制
1.消息落库,对消息状态进行打标
2.消息延迟投递,二次确认,回调检查
消息落库,对消息状态进行打标:
1.消息先不发出去,先进行数据入库
2.发送消息到broker
3.broker confirm,消息队列返回确认消息
4.producer端异步监听第三步的返回确认消息
6.监听到消息成功消费,修改数据库中消息状态
可能出的状况:
producer5分钟都没收到确认:那么使用分布式定时任务进行一次重新发送
重新发送次数大于3次,那么我们定为消息发送失败,要人工进行去补偿,
消息延迟投递,二次确认,回调检查:
1.多了一个callbackservice
2.下游服务消费完消息后,向一个队列里发送确认,callback服务会监听这条消息
3.监听到后会将这条消息持久化到数据库
上游服务发送第一条消息之后,会接着发送第二条消息,这条消息用于询问callbackservice下游服务是否成功消费
5.callbackservice监听第二条询问消息,然后告诉上游服务,成功消费了
可能状况:
询问之后没有发现成功消费,那么重新再次发送两条消息,直到成功为止
channel.confirmSelect() // 消息投递模式:消息确认模式
channel.addConfirmListener(new ConfirmListener() {
HandlerNack...{
确认消息未成功送达
}
handlerack...{
确认消息成功送达
}
})
// 还有一种可能就是返回的确认丢失,需要一个分布式定时任务重新发送未成功的消息
mandatory:为true,监听器会接受到路由不可达消息,然后人工后续处理,为false,broker端自动删除该消息
channel.addReturnListener(new ReturnListener(){
HandlerReturn(...){
}
})
update count = count -1 ,version = version+1
where version = 1
解决方案:
1.数据库去重机制
2.Redis,判断是否已经存在
consumer.nextDelivery太low
channel.basicConsume(queueName,true,new MyConsumer(channel))
MyConsumer extends DefaultConsumer {
构造(channel)
handlerDelivery(consumerTag,Envelop,properties,body){
}
}
qos(服务质量保证):非自动确认消息的前提下,如果一定数目的消息未被确认前,不进行消费新的消息。
//prefetchSize:消息大小,消费端一般不做限制,生产端会做
//prefetchCount:一次最多处理多少条消息,一般设置为1,一旦有N个消息还没ack,该consumer将block住
//global:true在channel级别,false在consumer级别
// prefetchSize和global这两项,rabbitmq还没实现,prefetchCount在no_ask=false时生效,必须手工签收,而不是自动签收
void basicQoS(unit prefetchSize,prefetchCount,global)
channel.basicQos(0:不限制消息大小,prefetchCount=1,false:应用到consume级别)
// autoAck = false关闭自动签收
channel.basicConsume(...autoAck = false ...)
修改MyConsumer:
channel.basicAck(envelop.getDeliveryTag(),false:不批量签收)
NACK:让broker重新投递,达到次数后手工ack,业务异常进行日志记录,然后人工补偿。
若consumer宕机没有ack,那么broker会等consumer恢复之后重新投递消息,或给下一个consumer消费
// requeue是否重回队列,false,不重新投递,抛弃此条消息
channel.basicNAck(...requeue)
当一个消息,变成死信,它能重新被publish到另一个exchange,这个exchange就是DLX
消息被拒绝,并且requeue=false
ttl过期
队列达到最大长度
设置死信队列的exchange叫做dlx.exchange和queue
然后绑定
消费者指定死信队列:
arguments.put("x-dead-letter-exchange","dlx.exchange")
channel.queueDeclare(queueName,true,false,false,arguments)