RabbitMQ初学习

1.消息队列解决了什么问题?

异步处理
应用解耦
流量削峰
日志处理

2.rabbitMQ安装与配置

echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add - 
sudo apt-get update 
sudo apt-get install rabbitmq-server 
invoke-rc.d rabbitmq-server stop/start/etc我们看见进程是启动的
打开管理页面 
sudo rabbitmq-plugins enable rabbitmq_management
查看安装的插件 
sudo rabbitmqctl list_users
查看用户 
sudo rabbitmqctl list_users
新增管理员用户 
sudo rabbitmqctl add_user admin admin 
sudo rabbitmqctl set_user_tags admin administrator
用刚设置的账户登录管理页面 
http://127.0.0.1:15672

3.Virtual hosts 管理

相当于mysql 的 db

创建数据库一般以/开头

创建完成后需要授权

获取MQ连接

public static Connection getConnection() {
	//定义连接工厂类
	ConnectionFactory factory = new ConnectionFactory();
	//设置服务器地址
	factory.setHost("127.0.0.1");
	//AMQP端口 5672
	factory.setPort(5672);
	//vhost
	factory.setVirtualHost("/vhost_mmr");
	//用户名
	factory.setUsername("admin");
	//密码
	factory.setPassword("admin");

	try {
		return factory.newConnection();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (TimeoutException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return null;
}
  • 生产者发送消息
 private static final String QUEUE_NAME = "test_simple_queue";
    
    	public static void main(String[] args) {
    		//获取一个连接
    		Connection connection = ConnectionUtils.getConnection();
    		try {
    			//从连接中获取一个队列
    			Channel createChannel = connection.createChannel();
    			//创建队列声明
    			createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
    			//消息
    			String msg = "hello simple 3";
    			//发送消息
    			createChannel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
    
    			System.out.println("send msg " + msg);
    			try {
    				createChannel.close();
    				connection.close();
    			} catch (TimeoutException e) {
    				e.printStackTrace();
    			}
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
  • 消费者接受消息:老方法旧版本
	private static final String QUEUE_NAME = "test_simple_queue";

	public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {

		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//连接中获取队列
		Channel createChannel = connection.createChannel();
		//		createChannel.
		QueueingConsumer consumer = new QueueingConsumer(createChannel);
		createChannel.basicConsume(QUEUE_NAME, true, consumer);

		while (true) {
			Delivery nextDelivery = consumer.nextDelivery();
			String value = new String(nextDelivery.getBody());
			System.out.println("Recv = " + value);
		}
	}
  • 消费者接受消息:新方法
	//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//连接中获取队列
		Channel createChannel = connection.createChannel();
		//创建队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);

		//定义消费者
		DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {

			//获取到达的消息
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String value = new String(body);
				System.out.println("recv msg =" + value);
			}
		};

		//监听队列
		createChannel.basicConsume(QUEUE_NAME, true, defaultConsumer);

简单队列不足 耦合性高,生产者—对应消费者(如果有多个消费者想获取队列中的消息,就不行了)

5.work queues

为什么会出现工作队列,simple队列是一一对应的,而且我们实际开发中,生产者发送消息是毫不费力的,而消费者一般是要跟业务相结合的,消费者接收到消息之后就需要处理,可能会话费时间这个时候队列就会挤压很多消息.

  • 轮询分发
  • 生产者:发送消息
    private static final String QUEUE_NAME = "test_worker_queue";
    	public static void main(String[] args) throws IOException, InterruptedException, TimeoutException {
    		//获取连接
    		Connection connection = ConnectionUtils.getConnection();
    		//获取队列
    		Channel createChannel = connection.createChannel();
    		//声明队列
    		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
    		//发死你个数据
    		for (int i = 0; i < 50; i++) {
    			String msg = "hello" + i;
    			System.out.println("worker queue Send msg = " + msg);
    			createChannel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
    			Thread.sleep(i * 20);
    		}
    		createChannel.close();
    		connection.close();
    	}
  • 消费者1:接受消息
private static final String QUEUE_NAME = "test_worker_queue";

	public static void main(String[] args) throws IOException {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取队列
		Channel createChannel = connection.createChannel();
		//队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//等AMQP中数据时会出发handleDelivery方法
		DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String str = new String(body, "utf-8");
				System.out.println("{1} Recv query Msg = " + str);
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("{1} done");
				}
			}
		};
		boolean autoAck = true;
		createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
	}

  • 消费者2:接收消息
	private static final String QUEUE_NAME = "test_worker_queue";

	public static void main(String[] args) throws IOException {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取队列
		Channel createChannel = connection.createChannel();
		//队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//等AMQP中数据时会出发handleDelivery方法
		DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String str = new String(body, "utf-8");
				System.out.println("{2} Recv query Msg = " + str);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("{2} done");
				}
			}
		};
		boolean autoAck = true;
		createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
	}

现象
虽然消费者1与消费者2 都设置了不同时间的延迟,但是消费者1 与消费者2 处理的数据消息是一样的,这种方式叫做轮询分发,不管谁类谁轻松,都不会给多给谁多一个或少一个,你一个我一个

work queues 公平分发

公平分发保证每次发送一条数据,当消费者处理完成一条消息后告诉队列发送下一条消息给消费者,手动确认消息

  • 生产者:发送消息
private static final String QUEUE_NAME = "test_worker_queue";

	public static void main(String[] args) throws IOException, InterruptedException, TimeoutException {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取队列
		Channel createChannel = connection.createChannel();
		//声明队列
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);

		/**
		 * 每个消费者发送确认消息之前,消息队列不发送下一条数据到消费者,一次只处理一个消息
		 * 
		 * 限制发送给同一个消费者不得吵过一条消息
		 */
		int prefetchCount = 1;
		createChannel.basicQos(prefetchCount);

		//发死你个数据
		for (int i = 0; i < 50; i++) {
			String msg = "hello" + i;
			System.out.println("worker queue Send msg = " + msg);
			createChannel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
			Thread.sleep(i * 20);
		}
		createChannel.close();
		connection.close();
	}
  • 消费者1
private static final String QUEUE_NAME = "test_worker_queue";

	public static void main(String[] args) throws IOException {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取队列
		final Channel createChannel = connection.createChannel();
		//队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);

		createChannel.basicQos(1); //保证每次发送一个

		//等AMQP中数据时会出发handleDelivery方法
		DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String str = new String(body, "utf-8");
				System.out.println("{1} Recv query Msg = " + str);
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("{1} done");
					createChannel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		boolean autoAck = false; //自动应答
		createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
	}
  • 消费者2
private static final String QUEUE_NAME = "test_worker_queue";

	public static void main(String[] args) throws IOException {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取队列
		final Channel createChannel = connection.createChannel();
		//队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);

		createChannel.basicQos(1);
		//等AMQP中数据时会出发handleDelivery方法
		DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String str = new String(body, "utf-8");
				System.out.println("{2} Recv query Msg = " + str);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("{2} done");
					createChannel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		boolean autoAck = false;
		createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
	}

6.消息应答与消息持久化

boolean autoAck = false;  
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);


boolean autoAck = true; //自动确认模式
一旦rabbitmq将消息发送给消费者,就会从内存将消息删除
这种情况下如果杀死正在执行的消费者,就会丢失正在处理的消息

boolean autoAck = false;  //手动确认模式

如果有一个消费者挂掉,就会交付给其他消费者rabbitmq支持消息应答,
消费者发送一个消息应答告诉rabbitmq这个消息我已经处理完成,你可以删了
然后rabbitmq就删除内存中的消息

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


message acknowkedgment

如果rabbitmq挂了,我们的消息仍然会丢失!!!

消息的持久化
Boolean durable =  false;
createChannel.queueDeclare(QUEUE_NAME, durable, false, false, null);

我们将程序中的 boolean durable = flase ;改成 true是不可以的尽管代码是正确的,他也不会运行成功!应为我们已经定义了一个test_worker_queue这个queue是未持久化的,rabbitmq不允许从新定义(不同参数)一个已存在的队列
想实现需要删除队列重新定义或者创建新的队列

7.publish / subscribe 发布订阅

解读
1.一个生产着多个消费者
2.每一个消费者都有自己对应的队列
3.生产者没有直接发送到队列,而是发送到交换机,转发器 exchange
4.每一个队列都要绑定到交换机上
5.生产者发送的消息 经过交换机 到达队列 就能实现一个消息被多个消费者消费
注册 → 邮件 → 短信

  • 生产者
	private static final String EXCHANGE_NAME = "test_exchange_fanout";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUtils.getConnection();
		Channel channel = connection.createChannel();

		//声明交换机
		channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); //分发

		//发送消息
		String msg = "hello ps";
		channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());

		System.out.println("send msg = " + msg);

		channel.close();
		connection.close();

	}

消息那去了呢???
丢失了,应为交换机没有储存能力,rabbitmq里面只有队列有存储能力.因为这个时候没有队列绑定到这个交换机所以数据丢失了

  • 消费者1
	private static final String EXCHANGE_NAME = "test_exchange_fanout";
	private static final String QUEUE_NAME = "test_queue_fanout_email";

	public static void main(String[] args) throws IOException {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取队列
		final Channel createChannel = connection.createChannel();
		//队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);

		//队列绑定交换机
		createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

		createChannel.basicQos(1);

		//等AMQP中数据时会出发handleDelivery方法
		DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String str = new String(body, "utf-8");
				System.out.println("{1} Recv query Msg = " + str);
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("{1} done");
					createChannel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		boolean autoAck = false;
		createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
	}
  • 消费者2
private static final String EXCHANGE_NAME = "test_exchange_fanout";
	private static final String QUEUE_NAME = "test_queue_fanout_note";

	public static void main(String[] args) throws IOException {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取队列
		final Channel createChannel = connection.createChannel();
		//队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//队列绑定交换机
		createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

		createChannel.basicQos(1);

		//等AMQP中数据时会出发handleDelivery方法
		DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String str = new String(body, "utf-8");
				System.out.println("{2} Recv query Msg = " + str);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("{2} done");
					createChannel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		boolean autoAck = false;
		createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
	}


8.Exchange(交换机,转发器)

一方面是接受生产者的消息,另一方面是想队列推送消息
匿名转发 “”
fanout : 不处理路由键交换机收到消息就会将消息传给跟他绑定的队列
Direct : 处理路由键,发送数据是需要路由Key,队列也要绑定路由Key 如果队列KEy与他相同,将数据交给他

9.routing路由模式

  • 生产者
	private static final String EXCHANGE_NAME = "test_exchange_direct";

	public static void main(String[] args) throws IOException, TimeoutException {

		Connection connection = ConnectionUtils.getConnection();
		Channel channel = connection.createChannel();

		//声明交换机
		channel.exchangeDeclare(EXCHANGE_NAME, "direct");

		String msg = "hello direct"; //消息
		System.out.println("send msg =" + msg);
		String routingKey = "info"; //路由key
		channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());

		channel.close();
		connection.close();

	}

  • 消费者1
	private static final String EXCHANGE_NAME = "test_exchange_direct";
	private static final String QUEUE_NAME = "test_queue_direct_error";

	public static void main(String[] args) throws IOException {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取队列
		final Channel createChannel = connection.createChannel();
		//队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);

		//队列绑定交换机
		createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");

		createChannel.basicQos(1);

		//等AMQP中数据时会出发handleDelivery方法
		DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String str = new String(body, "utf-8");
				System.out.println("{1} Recv query Msg = " + str);
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("{1} done");
					createChannel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		boolean autoAck = false;
		createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
	}

  • 消费者2
private static final String EXCHANGE_NAME = "test_exchange_direct";
	private static final String QUEUE_NAME = "test_queue_direct_all";

	public static void main(String[] args) throws IOException {
		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//获取队列
		final Channel createChannel = connection.createChannel();
		//队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//队列绑定交换机
		createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");
		createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "info");
		createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

		createChannel.basicQos(1);

		//等AMQP中数据时会出发handleDelivery方法
		DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String str = new String(body, "utf-8");
				System.out.println("{2} Recv query Msg = " + str);
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("{2} done");
					createChannel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		boolean autoAck = false;
		createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
	}

10. topics 主题

将路由键和某个模式匹配

通配符
“#” : 匹配一个或者多个
“.” : 匹配一个
商品 : 发布 删除 修改 查询

  • 生产者
	private static final String EXCHANGE_NAME = "test_exchange_topics";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUtils.getConnection();
		Channel channel = connection.createChannel();

		channel.exchangeDeclare(EXCHANGE_NAME, "topic");
		String msg = "添加商品2";
		channel.basicPublish(EXCHANGE_NAME, "Good.2", null, msg.getBytes());
		System.out.println(" send  add  " + msg);
		channel.close();
		connection.close();
	}
  • 消费者
	private static final String EXCHANGE_NAME = "test_exchange_topics";
	private static final String QUEUE_NAME = "test_queue_topics";

	public static void main(String[] args) throws IOException {

		Connection connection = ConnectionUtils.getConnection();
		Channel channel = connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//队列与交换机结合
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "Good.*");

		DefaultConsumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String msg = new String(body, "utf-8");
				System.out.println(msg);
			}
		};
		//等待数据
		boolean autoAck = true;//自动确认
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}

  • 11.RabbitMQ的消息确认机制(事物+confirm)
在RabbitMQ中我们可以通过持久化数据解决rabbitmq服务器异常的数据丢失问题

问题
      1.生产者将消息发送出去以后到底有没有到达RabbitMQ服务器

默认情况下是不知道的,
 
 两种方式
      1,AMQP 实现了事务机制
      2.Confirm模式



事务机制

txSelect
   用户当前channel设置成为transation模式
txCommit 
   提交事务
txRollback
    回滚事务
  • 生产者
	private static final String QUEUE_NAME = "test_queue_tx";

	public static void main(String[] args) throws Exception {

		Connection connection = ConnectionUtils.getConnection();
		Channel channel = connection.createChannel();

		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		String msg = "";

		try {
			channel.txSelect();
			channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
			channel.txCommit();
		} catch (Exception e) {
			System.out.println("send msg rollback");
			channel.txRollback();
		}
		channel.close();
		connection.close();
	}

  • 消费者
private static final String QUEUE_NAME = "test_queue_tx";

	public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {

		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//连接中获取队列
		Channel createChannel = connection.createChannel();
		//创建队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);

		//监听队列
		createChannel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(createChannel) {

			//获取到达的消息
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				String value = new String(body);
				System.out.println("recv msg =" + value);
			}
		});
	}

模式弊
降低消息的吞吐量,走的通信过多,大量的请求,降低吞吐量

Confirm模式

生产者端confirm模式的实现原理
生产者将信道设置为confirm模式,一旦进入Confirm模式,所有在该信道上发布的消息都会被指派一个唯一ID(从1开始),一旦消息被投递到所有匹配的队列以后,broker就会发送一个确认消息给生产者,(包含消息的唯一ID),这使得生产者知道消息已经正确到达目的队列,如果消息和队列是持久化的,那么确认消息会将消息写入磁盘后发送出,broker回传给生产者的确认消息中delicer-tag域包含了确认消息的序列号,此外broker也可以设置basicack的nultiple域,表示到这个之前所有消息都已经得到处理
Confirm 模式最大的好处在于它是异步的
如果自身出现了自身奔溃,消息丢失就会发送一条Nack信息

开启confirm模式串行

   Channel.confirmSelect();

编程模式
普通

    发送一条 waitForConfirms();

批量

  发松一批消息 waitForConfirms();

异步

    Confirm模式支持异步
  • 普通生产者
	channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//生产者调用confirmSelect 将channel设置为Confirm模式
		channel.confirmSelect();

		String msg = "1";
		channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

		if (!channel.waitForConfirms()) {
			System.out.println("message send  to defeated");
		} else {
			System.out.println("message send  to ok");
		}
  • 普通消费者
private static final String QUEUE_NAME = "test_queue_confirm_common";

	public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {

		//获取连接
		Connection connection = ConnectionUtils.getConnection();
		//连接中获取队列
		Channel createChannel = connection.createChannel();
		//创建队列声明
		createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);

		//监听队列
		createChannel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(createChannel) {

			//获取到达的消息
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
				System.out.println("recv msg =" + new String(body));
			}
		});
	}
  • 批量生产者
	channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//生产者调用confirmSelect 将channel设置为Confirm模式
		channel.confirmSelect();

		String msg = "1";
		for (int i = 0; i < 10; i++) {
			msg = msg + i;
			channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
		}

		if (!channel.waitForConfirms()) {
			System.out.println("message send  to defeated");
		} else {
			System.out.println("message send  to ok");
		}

批量的消费者与普通消费者一致,没有变化

confirm模式异步

Channel 对象提供的ConfirmListener()回调方法只包含deliveryTag(当前Chanel发出的消息序号),我们需要自己为每一个Channel维护一个UNconfirm的消息序号集合,每publish一条数据,集合中元素加1,没回掉一次handleAck方法,UNconfirm集合删除相应的一条(multiple=false) 或多条(multiple = true) 记录,从程序运行效率上看,这个unconfirm集合采用有序集合sotedSet存储结构

异步生产者

	channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		channel.confirmSelect();

		//未确认的消息标识
		final SortedSet<Long> confirm = Collections.synchronizedNavigableSet(new TreeSet<Long>());

		channel.addConfirmListener(new ConfirmListener() {

			//handleNack
public void handleNack(long arg0, boolean arg1) throws IOException {
	if (arg1) {
		System.out.println("-----handleNack---multiple");
		confirm.headSet(arg0 + 1).clear();
	} else {
		System.out.println("-----handleNack---multiple  false");
		confirm.remove(arg0);
	}
}

//没有问题的handleAck
public void handleAck(long arg0, boolean arg1) throws IOException {
	if (arg1) {
		System.out.println("-----handleAck---multiple");
		confirm.headSet(arg0 + 1).clear();
	} else {
		System.out.println("-----handleAck---multiple  false");
		confirm.remove(arg0);
				}
			}
		});

		String msg = "sss";
		while (true) {
			long nextPublishSeqNo = channel.getNextPublishSeqNo();
			channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
			confirm.add(nextPublishSeqNo);
		}

异步消费者与普通消费者一致,没有变化

初级Spring-AMQP使用

  • 在pom.xml中使用
    <dependency>
    		<groupId>org.springframework.amqp</groupId>
    		<artifactId>spring-rabbit</artifactId>
    		<version>1.6.5.RELEASE</version>
    	</dependency>
  • XML配置
 `  <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:rabbit="http://www.springframework.org/schema/rabbit"
    	xsi:schemaLocation="http://www.springframework.org/schema/rabbit
               http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
               http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    	<rabbit:connection-factory
    		virtual-host="/vhost_mmr" host="127.0.0.1" port="5672"
    		username="admin" password="admin" id="connectionFactory" />
    
    	<rabbit:template id="amqpTemplate"
    		connection-factory="connectionFactory" exchange="fanoutExchange" />
    
    	<rabbit:admin connection-factory="connectionFactory" />
    
    	<rabbit:queue name="myqueue" auto-declare="true"
    		durable="true" />
    
    	<rabbit:fanout-exchange name="fanoutExchange"
    		auto-declare="true">
    		<rabbit:bindings>
    			<rabbit:binding queue="myqueue"></rabbit:binding>
    		</rabbit:bindings>
    	</rabbit:fanout-exchange>
    
    	<rabbit:listener-container
    		connection-factory="connectionFactory">
    		<rabbit:listener ref="foo" method="listen"
    			queue-names="myqueue" />
    	</rabbit:listener-container>
    	<bean id="foo" class="com.oldkuo.rabbitmqDome.spring.RecvOne"></bean>
    </beans>

生产者

AbstractApplicationContext context = new ClassPathXmlApplicationContext("classpath:context.xml");
RabbitTemplate template = context.getBean(RabbitTemplate.class);
template.convertAndSend("Hello world");
Thread.sleep(1000);
context.destroy();

消费者

public class RecvOne {

	public void listen(String foo) {
		System.out.println("消费者: " + foo);
	}
}

简单使用SpringXML方式的Dome

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值