参考:https://blog.csdn.net/a13627210064/article/details/82348059
参考:https://blog.csdn.net/u010288264/article/details/55260237 (1 2 3 4)
依賴:
<repositories><!-- 代码库 --> <repository> <id>maven-ali</id> <url>http://maven.aliyun.com/nexus/content/groups/public//</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>always</updatePolicy> <checksumPolicy>fail</checksumPolicy> </snapshots> </repository> </repositories> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency> --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
yml 配置连接
spring: rabbitmq: # host: 192.168.18.129 addresses: 192.168.18.129:5672 username: guest password: guest # 支持发布确认 publisher-confirms: true # 支持发布返回 publisher-returns: true listener: simple: # 监听的最小线程数 concurrency: 4 # 监听的最大线程数 max-concurrency: 8 retry: enabled: true # ack应答改模式:auto-自动,manual-手动,none-无应答 acknowledge-mode: auto
生产者:发送消息
方式一:derict
配置:
package com.icil.config; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.QueueBuilder; import org.springframework.amqp.core.TopicExchange; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; //@Configuration public class RabbitConfig { /********************************direct****************************************/ // 交换机定义,这里我使用的是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,@Qualifier("directExchange") DirectExchange exchange) { return BindingBuilder.bind(queue).to(exchange).with("key"); } // // /********************************topic****************************************/ // // // @Bean // public TopicExchange topicExchange() { // /** // * TopicExchangeName // * 是否持久化 // * 是否自动删除 // */ // return new TopicExchange("topicExchange", true, false); // } // // /** // * 创建一个队列,指定一个Exchange // * @return // */ // @Bean(name = "topicQueue") // public Queue topicQueue() { // return QueueBuilder.durable("topicExchange").build(); // } // // // @Bean // public Binding bindingtopic(@Qualifier("topicQueue") Queue queue, DirectExchange exchange) { // return BindingBuilder.bind(queue).to(exchange).with("A.B.C"); } }
发送消息:
package com.icil.rabbitmq; import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.connection.CorrelationData; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback; import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @Component @Slf4j public class RabbitProducer implements ConfirmCallback , ReturnCallback{ private static Logger log =LoggerFactory.getLogger(RabbitProducer.class); @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 ---"+System.currentTimeMillis() ); } 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); log.error(message.getMessageProperties().getClusterId() + " send failed:error code " + replyCode + "mains:" + replyText); } /** * * 发送消息,供外部调用 * * ****** 重要 ******说明:发送时的方法选择 * * ****** 重要 ******convertAndSend属于不要求返回确认的 * * ****** 重要 ******convertSendAndReceive要求返回确认 * * ****** 重要 ******大家根据不同的业务场景进行选择, * * 不返回确认可以理解为全异步; * * 返回确认可以理解为异步处理,同步返回,存在一个生产者等待消费者的问题 * * 选择的原则一般为一致性要求较强的,要确认返回; * * 一致性不强的,使用不返回确认,加大处理效率,免去等待时间 */ public void sendSMSMessage(String msg){ // fanout类型的交换器不需要routingkey,我这里用的是direct所以指定了对应的routingkey this.rabbitTemplate.convertAndSend("name", "key", msg); // this.rabbitTemplate.convertSendAndReceive(msg); // this.rabbitTemplate.converandre } // public static void main(String[] args) { // RabbitProducer rabbitProducer = new RabbitProducer(); // rabbitProducer.sendSMSMessage("just for test"); // } }
方式二 : topic
配置:
package com.icil.config; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.QueueBuilder; import org.springframework.amqp.core.TopicExchange; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig2 { /********************************topic****************************************/ @Bean public TopicExchange topicExchange() { /** * TopicExchangeName * 是否持久化 * 是否自动删除 */ return new TopicExchange("topicExchange", true, false); } /** * 创建一个队列,指定一个Exchange * @return */ @Bean public Queue topicQueue01() { return QueueBuilder.durable("topicExchange01").build(); } @Bean public Queue topicQueue02() { return QueueBuilder.durable("topicExchange02").build(); } @Bean public Binding binding() { return BindingBuilder.bind(topicQueue01()).to(topicExchange()).with("A.#"); } @Bean public Binding binding2() { return BindingBuilder.bind(topicQueue02()).to(topicExchange()).with("#.B.#"); } // @Bean // public Binding bindingtopic(@Qualifier("topicQueue") Queue queue, DirectExchange exchange) { // return BindingBuilder.bind(queue).to(exchange).with("A.B.C"); } }
发送消息:
package com.icil.rabbitmq; import javax.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.connection.CorrelationData; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate.ConfirmCallback; import org.springframework.amqp.rabbit.core.RabbitTemplate.ReturnCallback; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; @Component @Slf4j public class RabbitProducer002 implements ConfirmCallback , ReturnCallback{ private static Logger log =LoggerFactory.getLogger(RabbitProducer002.class); @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################## 888---"+System.currentTimeMillis() ); log.info("cause is################## ---",cause ); } 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.info("$$$$$$$$$$$$$$$$$$$$$$$ message is {} $$$ replyCode is {} $$$ replyText is {} $$$ exchange is {} $$$ and routingKey is {}",message,replyCode, replyText,exchange,routingKey); // log.error(message.getMessageProperties().getCorrelationIdString() + " send failed:error code " + replyCode + "mains:" + replyText); log.error(message.getMessageProperties().getClusterId() + " send failed:error code " + replyCode + "mains:" + replyText); } /** * * 发送消息,供外部调用 * * ****** 重要 ******说明:发送时的方法选择 * * ****** 重要 ******convertAndSend属于不要求返回确认的 * * ****** 重要 ******convertSendAndReceive要求返回确认 * * ****** 重要 ******大家根据不同的业务场景进行选择, * * 不返回确认可以理解为全异步; * * 返回确认可以理解为异步处理,同步返回,存在一个生产者等待消费者的问题 * * 选择的原则一般为一致性要求较强的,要确认返回; * * 一致性不强的,使用不返回确认,加大处理效率,免去等待时间 */ public void sendSMSMessage(String msg){ // fanout类型的交换器不需要routingkey,我这里用的是direct所以指定了对应的routingkey this.rabbitTemplate.convertAndSend("topicExchange", "A.B.key", msg); } }
/*********************************************************************/
接受消息: (依赖与上面一样,无需其他配置)
配置yml
spring: rabbitmq: # host: 192.168.18.129 addresses: 192.168.18.129:5672 username: root password: root # 支持发布确认 publisher-confirms: true # 支持发布返回 publisher-returns: true listener: simple: # 监听的最小线程数 concurrency: 4 # 监听的最大线程数 max-concurrency: 8 retry: enabled: true # ack应答改模式:auto-自动,manual-手动,none-无应答 acknowledge-mode: auto
接收消息:(使用注解 --也可不用,此处用注解)
方式一:注解 可以参考 :https://www.jianshu.com/p/382d6f609697
package com.icil.rabbitmq; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import com.rabbitmq.client.Channel; @Component public class RabbitMQConsumer { private static Logger log =LoggerFactory.getLogger(RabbitMQConsumer.class); /** * * 消费者处理接收消息方法 * * <p> * * ****重要说明***** * 如果生产者是以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注解的queue和bindings不能同时指定,否则报错 // @RabbitListener(bindings ={@QueueBinding(value = @Queue(value = "q5",durable = "true"), // exchange =@Exchange(value = "zhihao.miao.exchange",durable = "true"),key = "welcome")}) // @RabbitListener(queues = "name") // @RabbitListener(queues = {"name","topicExchange01","topicExchange02"}) // 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); // } // } @RabbitListener(queues = {"topicExchange01"}) public void handler1(String msg, Message message, Channel channel) throws Exception { try { System.out.println("$$$$$$$$$$ topicExchange01 $$$$$$$$$$$$"+msg); } catch (Exception e) { log.error(e.toString(), e); channel.basicReject(message.getMessageProperties().getDeliveryTag(), false); } } @RabbitListener(queues = {"topicExchange02"}) public void handler2(String msg, Message message, Channel channel) throws Exception { try { System.out.println("$$$$$$$$$$ topicExchange02 $$$$$$$$$$$$"+msg); } catch (Exception e) { log.error(e.toString(), e); channel.basicReject(message.getMessageProperties().getDeliveryTag(), false); } } @RabbitListener(queues = {"name"}) public void handler3(String msg, Message message, Channel channel) throws Exception { try { System.out.println("$$$$$$$$$$ name $$$$$$$$$$$$"+msg); } catch (Exception e) { log.error(e.toString(), e); channel.basicReject(message.getMessageProperties().getDeliveryTag(), false); } } }
方式二: 手动配置
package com.icil.config; import org.springframework.amqp.core.AcknowledgeMode; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.Queue; import org.springframework.amqp.core.QueueBuilder; import org.springframework.amqp.core.TopicExchange; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.rabbitmq.client.Channel; @Configuration public class Rabbitconfing { @Bean public CachingConnectionFactory connectionFactory(){ CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setAddresses("192.168.18.129:5672"); connectionFactory.setUsername("guest"); connectionFactory.setPassword("guest"); connectionFactory.setVirtualHost("/"); connectionFactory.setPublisherConfirms(true); //必须要设置 return connectionFactory; } @Bean public TopicExchange topicExchange() { /** * TopicExchangeName * 是否持久化 * 是否自动删除 */ return new TopicExchange("topicExchange", true, false); } @Bean public Queue topicQueue01() { return QueueBuilder.durable("topicExchange01").build(); } @Bean public Queue topicQueue02() { return QueueBuilder.durable("topicExchange02").build(); } // @Bean // public Binding binding() { // return BindingBuilder.bind(topicQueue01()).to(topicExchange()).with("A.key"); // } // @Bean // public Binding binding2() { // return BindingBuilder.bind(topicQueue02()).to(topicExchange()).with("B.C"); // } // @Bean public SimpleMessageListenerContainer messageContainer() { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory()); container.setQueues(topicQueue01()); container.setExposeListenerChannel(true); container.setMaxConcurrentConsumers(1); container.setConcurrentConsumers(1); container.setAcknowledgeMode(AcknowledgeMode.AUTO); //设置确认模式手工确认 container.setMessageListener(new ChannelAwareMessageListener() { @Override public void onMessage(Message message, Channel channel) throws Exception { byte[] body = message.getBody(); System.out.println("¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥receive msg queue: " + new String(body)); // Thread.sleep(10000); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); //确认消息成功消费 } }); return container; } @Bean public SimpleMessageListenerContainer messageContainer2() { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory()); container.setQueues(topicQueue02()); container.setExposeListenerChannel(true); container.setMaxConcurrentConsumers(1); container.setConcurrentConsumers(1); container.setAcknowledgeMode(AcknowledgeMode.MANUAL); //设置确认模式手工确认 container.setMessageListener(new ChannelAwareMessageListener() { @Override public void onMessage(Message message, Channel channel) throws Exception { byte[] body = message.getBody(); System.out.println("¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥key: " + new String(body)); // Thread.sleep(10000); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); //确认消息成功消费 } }); return container; } }