RabbitMQ的理解和搭配springboot的简单使用

名词解释

Producer(生产者):消息队列生产者,向消息队列发布消息,产生需求的。
Consumer(消费者):消息队列的消费者,从消息队列里获取消息,处理需求的。
Broker(代理):接受消息,保存消息,分配消息的,rabbitMQ server就是一个broker。
  上面这三个是分布式标准的三个角色。rabbitmq扮演的就是Broker这个角色。
  rabbitmq实现的是AMQP标准,在这个标准中定义了几个功能要求:

  1. exchange:交换机。作用和交换机一样,生产者将消息发送到交换机,然后交换机负责分发消息到绑定的消息队列。这里的绑定就是下面的binding,在bingding这个动作中定义了queue和exchange绑定的规则。exchange在rabbitmq中对应的类就是各种xxxxExchange(TopicExchange,DirectExchange…)
  2. binding:定义了队列和交换机的绑定规则,一个交换机可以绑定多个队列。
  3. queue:也就是我们所说的队列。消费者从队列中取出消息。
      在上述说道,exchange有不同的类型。类型如下:
  4. DirectExchange.java,这个交换机为直连交换机,是默认类型的交换机,也是最简单的模式.即创建消息队列的时候,指定一个routingKey(一个字符串).当发送者发送消息的时候,指定对应的Key(也是一个字符串).当Key和消息队列的routingKey一致的时候,消息将会被发送到该消息队列中。
  5. TopicExchange.java,这个交换机从翻译上为主题交换机。这个交换机可以在和消息队列绑定时的routingKey使用通配符,例如(user.#)。我们可以在发送时的routingKey填成user.save或者user.add,都会匹配到这个队列。
  6. FanoutExchange.java,这个交换机为广播队列,在这个队列routingKey没用。只要发送到这个交换机,那么所有队列都会加入这条消息。
  7. HeadExchange.java,这个交换机翻译上为头交换机,不能直接看出这个交换机的作用。这个交换机使用略少,在使用头交换机时,需要设置一些键值对为参数,例如:usefor=update,type=user。这些在绑定时需要放入map数组作为参数。

在springboot中使用RabbitMQ:

Producer(发送消费到队列的)
  1. 导包。pom.xml:
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>

导包就是这么简单。
2. java的配置类。
  配置队列的

	// topicExchange用的
	@Bean("user")
	public Queue userQueue() {
		return new Queue("user");
	}

	@Bean("other.err")
	public Queue otherErrQueue() {
		return new Queue("other.err");
	}
	// 下两个dirctExchange用的
	@Bean("log.err")
	public Queue logErrQueue() {
		return new Queue("log.err");
	}

	@Bean("log.info")
	public Queue logInfoQueue() {
		return new Queue("log.info");
	}

配置交换机

	@Bean
	public DirectExchange directExchange() {
		// 参数1为交换机的名称,在发送时需要指定交换机名字
		return new DirectExchange("directExchange");
	}

	@Bean
	public TopicExchange topicExchange() {
		return new TopicExchange("topicExchange");
	}

	@Bean
	public FanoutExchange fanoutExchange() {
		return new FanoutExchange("fanoutExchange");
	}

	@Bean
	public HeadersExchange headersExchange() {
		return new HeadersExchange("headExchange");
	}

配置绑定规则,即Binding

@Bean
	public Binding bindingLogErrExchangeDirect(@Qualifier("log.err") Queue logErrQueue, DirectExchange directExchange) {
		// 将logErrQueue和directExchange绑定,并指定routingKey为log.err
		return BindingBuilder.bind(logErrQueue).to(directExchange).with("log.err");
	}

	@Bean
	public Binding bindingLogInfoExchangeDirect(@Qualifier("log.info") Queue logInfoQueue,
			DirectExchange directExchange) {
		return BindingBuilder.bind(logInfoQueue).to(directExchange).with("log.info");
	}
	
	@Bean
	public Binding bindingOtherErrExchangeDirect(@Qualifier("other.err") Queue logErrQueue, TopicExchange topicExchange) {
		// 将otherErrQueue和topicExchange绑定,并指定routingKey为log.err
		return BindingBuilder.bind(logErrQueue).to(topicExchange).with("other.#");
	}

	@Bean
	public Binding bindingExchangeMessages(@Qualifier("user") Queue userQueue, TopicExchange topicExchange) {
		return BindingBuilder.bind(userQueue).to(topicExchange).with("user.#");
	}

还需要配置一些其他的东西,比如确认消息是否到了exchange的回调。还有消息失败返回,比如路由不到队列时触发回调。

@Bean
	public RabbitTemplate rabbitTemplate() {
		// 若使用confirm-callback或return-callback,必须要配置publisherConfirms或publisherReturns为true
		// 每个rabbitTemplate只能有一个confirm-callback和return-callback,如果这里配置了,那么写生产者的时候不能再写confirm-callback和return-callback
		// 使用return-callback时必须设置mandatory为true,或者在配置中设置mandatory-expression的值为true
		RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
		rabbitTemplate.setMandatory(true);
        /**
         * 如果消息没有到exchange,则confirm回调,ack=false
         * 如果消息到达exchange,则confirm回调,ack=true
         */
		rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
			@Override
			public void confirm(CorrelationData correlationData, boolean ack, String cause) {
				
			}
		});
		//         * exchange到queue成功,则不回调return
		//         * exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)
		rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
			@Override
			public void returnedMessage(Message message, int replyCode, String replyText, String exchange,
					String routingKey) {
			}
		});
		return rabbitTemplate;
	}

配置消息对象序列化和反序列话时的策略

	/**
	 * 修改默认的消息转换器
	 * @return
	 */
	@Bean
    public MessageConverter messageConverter(){
          return new Jackson2JsonMessageConverter();
    }

还需要在项目中配置(application.yml)一些东西

spring:
  rabbitmq:
  	#和mysql一样,配置地址,端口,用户名和密码
    host: localhost
    port: 35672
    username: admin
    password: 123456
    #两个回调用的
    publisher-confirms: true
    publisher-returns: true
    listener:
      simple:
        #工作队列有两种工作方式:轮询分发(默认)、公平分发即当某个消费者没有消费完成之前不用再分发消息。
        # 消费者每次从队列获取的消息数量。此属性当不设置时为:轮询分发,设置为1为:公平分发
        prefetch: 1
        #设置消费者应答方式为手动
        #acknowledge-mode: manual
        retry:
          #开启重试,重试次数为3
          enabled: true
          max-attempts: 3

发送消息类

//获取到rabbitTemplate
//可以在这里再次设置消息转化器
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.convertAndSend("topicExchange", "user.save", 消息内容);
consumer(获取并处理消息的)

导包:

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>

yml配置文件

#有些配置不知道该不该要
spring:
  rabbitmq:
    host: localhost
    port: 35672
    username: admin
    password: 123456
    listener:
      simple:
        #工作队列有两种工作方式:轮询分发(默认)、公平分发即当某个消费者没有消费完成之前不用再分发消息。
        # 消费者每次从队列获取的消息数量。此属性当不设置时为:轮询分发,设置为1为:公平分发
        prefetch: 1
        #设置消费者应答方式为手动
        acknowledge-mode: manual

处理代码

	//绑定队列的名字
	@RabbitListener(queues = { "log.err" })
	public void saveErrLog(Message message, Channel channel) throws IOException {
		System.out.println("save err log :" + new String(message.getBody()) + ". success !");
		// 手动应答,false标识当前consumer处理成功,true为所有consumer处理成功
		channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

		//判断消息是否为重复入队的消息,如果是重复入队的消息再次处理失败就不再入队
		 /*if (message.getMessageProperties().getRedelivered()) {
             System.out.println("消息已重复处理失败,拒绝再次接收!");
             // 拒绝消息,requeue=false 表示不再重新入队,如果配置了死信队列则进入死信队列
             channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
         } else {
             System.out.println("消息即将再次返回队列处理!");
             // requeue为是否重新回到队列,true重新入队
             channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
         }*/
		
	}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值