话不多说。一切以实际用处为出发点。可能没有那么详细-0-。
基础配置文件:
rabbitmq:
addresses: ip:端口
username: 账号
password: 密码
# 支持发布确认
publisher-confirms: true
# 支持发布返回
publisher-returns: true
listener:
simple:
# 监听的最小线程数
concurrency: 4
# 监听的最大纯种数
max-concurrency: 8
retry:
enabled: true
# ack应答改模式:auto-自动,manual-手动,none-无应答
acknowledge-mode: auto
首先是配置pom文件。
org.springframework.cloud
spring-cloud-starter-bus-amqp
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
下面是配置类。
@Configuration
public class RabbitConfig {
//交换机定义,这里我使用的是direct类型。大家可以根据自己的业务需求来指定对应的。下面会讲几种交换机的类型
//对应的3个参数1.交换机名称 2.持久性保持标识 3.是否自动删除标识
@Bean
public DirectExchange directExchange() {
return new DirectExchange(“name”, false, false);
}
//创建一个队列
@Bean(name = "queue")
public Queue queue() {
return QueueBuilder.durable(“name”).build();
}
//绑定队列到交换机上--with对应的是direct指定的具体key。
@Bean
public Binding binding(@Qualifier("queue") Queue queue, DirectExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("key");
}
下面是实际发送消息的地方了:
@Slf4j
@Component
public class RabbitProducer implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 初始化确认发送回调及发送返回回调
*/
@PostConstruct
public void init(){
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnCallback(this);
}
/**
* 实现消息发送到RabbitMQ交换器后接收ack回调
* @param correlationData
* @param ack
* @param cause
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack){ // 发送成功
log.info("trainLink message send success ---" + DateUtil.format(new Date()));
} else { // 发送失败
log.error("trainLink message send failed because ---" + cause);
}
}
/**
* 实现消息发送到RabbitMQ交换器,但无相应队列与交换器绑定时的回调
* @param message
* @param replyCode
* @param replyText
* @param exchange
* @param routingKey
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
log.error(message.getMessageProperties().getCorrelationIdString() + " send failed:error code " + replyCode + "mains:" + replyText);
}
/**
* 发送消息,供外部调用
* ****** 重要 ******说明:发送时的方法选择
* ****** 重要 ******convertAndSend属于不要求返回确认的
* ****** 重要 ******convertSendAndReceive要求返回确认
* ****** 重要 ******大家根据不同的业务场景进行选择,
* 不返回确认可以理解为全异步;
* 返回确认可以理解为异步处理,同步返回,存在一个生产者等待消费者的问题
* 选择的原则一般为一致性要求较强的,要确认返回;
* 一致性不强的,使用不返回确认,加大处理效率,免去等待时间
*/
public void sendSMSMessage(String msg){
// fanout类型的交换器不需要routingkey,我这里用的是direct所以指定了对应的routingkey
this.rabbitTemplate.convertAndSend(发送的交换机名称, 对应的routingkey, msg);
}
}
接收消息的消费者:
@Slf4j
@Component
public class RabbitMQConsumer {
/**
* 消费者处理接收消息方法
*
* ****重要说明*****
* 如果生产者是以convertSendAndReceive方法发送,则一定要手动给予返回,处理完后加入下面这一行:
* ack-true处理:channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
* 参数说明-------消息id,fasle代表不批量处理(批量是指将消息id小于当前id的都处理掉)
* ack-false处理:channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
* 参数说明-------消息id,fasle代表不批量处理(批量是指将消息id小于当前id的都处理掉),第二个false表示不重新入队(重新入队用true)
* 拒绝消息:channel.basicReject(message.getMessageProperties().getDeliveryTag(), false); 消息不会重新入队
* 参数说明-------消息id,fasle表示不重新入队(重新入队用true)
* 如果不手动返回,则该消息会一直认为没有被消费掉,会一直占用rabbitmq内存空间,时间一久,必然造成内存溢出,切记!!!
*
* @param msg
* @param message
* @param channel
* @throws Exception
*/
@RabbitListener(queues = 发送过来对应的队列名称)
public void handler(String msg, Message message, Channel channel) throws Exception {
try {
System.out.println(msg);
} catch (Exception e) {
log.error(e.toString(), e);
channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
}
}
}
科谱:
几种类型的交换机模式:
1、fanout模式
可以理解他是一个广播模式
不需要routing key它的消息发送时通过Exchange binding进行路由的~~在这个模式下routing key失去作用
这种模式需要提前将Exchange与Queue进行绑定,一个Exchange可以绑定多个Queue,一个Queue可以同多个Exchange进行绑定
如果接收到消息的Exchange没有与任何Queue绑定,则消息会被抛弃
2、Direct 模式
任何发送到Direct Exchange的消息都会被转发到routing_key中指定的Queue
一般情况可以使用rabbitMQ自带的Exchange:”” (该Exchange的名字为空字符串), 也可以自定义Exchange
可以将不同的routing_key与不同的queue进行绑定,不同的queue与不同exchange进行绑定
消息传递时需要一个“routing_key”
如果消息中不存在routing_key中绑定的队列名,则该消息会被抛弃
3、topic类型
前面讲到direct类型的Exchange路由规则是完全匹配binding key与routing key,但这种严格的匹配方式在很多情况下不能满足实际业务需求。
topic类型的Exchange在匹配规则上进行了扩展,它与direct类型的Exchage相似,也是将消息路由到binding key与routing key相匹配的Queue中,但这里的匹配规则有些不同,
它约定:
routing key为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
binding key与routing key一样也是句点号“. ”分隔的字符串
binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)
几种类型的效率对比:fanout>direct>>topic 根据自身业务极对效率的考量进行选择
除上述3种外,还有headers类型,目前我还没用到过,不大了解