java-rabbitmq实例

安装rabbitmq见上一篇写的文章。
rabbitmq服务是随Windows一起启动的,和mysql一样,所以只要安装了即可。

消息队列有两种模式
生产者-消费者:生产者发一个消息到队列里,只有一个消费者能获得,谁先到谁先得。
发布者-订阅者:生产者发一个消息到队列里,所有消费者都能获得这个消息

需要的jar包:rabbitmq-client.jar
MqSender.java 生产者、发布者 角色

package test;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

/**
 * 主要流程:
 * (1):创建ConnectionFactory,并且设置一些参数,比如hostname,portNumber等等
 * (2):利用ConnectionFactory创建一个Connection连接 
 * (3):利用Connection创建一个Channel通道
 * (4):创建queue并且和Channel进行绑定 
 * (5):创建消息,并且发送到队列中
 * (6):关闭连接和通道
 * 
 * @author yulisao
 * @createDate 2019-9-11
 * @description
 */
public class MqSender {
	// 队列名称
	private final static String QUEUE_NAME = "MyQueue1";
	
	//交换机名称
	private final static String EXCHANGE_NAME = "MyExchangeFanout";
	
	private static ConnectionFactory factory;
	private static Connection connection;
	private static Channel channel;

	public static void main(String[] args) 
			throws IOException, TimeoutException {
		System.out.println("当前队列名:"+QUEUE_NAME);
		System.out.println("请输入需要发送的消息内容");
		Scanner scanner = new Scanner(System.in);
		while (scanner.hasNext()) {
			//consumeSend(scanner.next()); // 生产者
			readSend(scanner.next()); // 发布者
		}
		
	}
	
	/**
	 * 连接初始化
	 *
	 * @author yulisao
	 * @createDate 2019-9-11
	 * @description 
	 * @throws IOException
	 */
	public static void init() throws IOException {
		factory = new ConnectionFactory(); // 创建工厂
		factory.setHost("localhost"); // 设置IP.端口,账户和密码,客户端访问需要的
		factory.setPort(5672);
		factory.setUsername("guest");
		factory.setPassword("guest");		
		connection = factory.newConnection();// 创建连接
		channel = connection.createChannel();// 创建一个通道
	}

	/**
	 * 消费者没模式
	 *
	 * @author yulisao
	 * @createDate 2019-9-11
	 * @description 
	 * @throws IOException
	 */
	public static void consumeSend(String messageText) throws IOException, TimeoutException {
		init(); // 初始化
		
		/* 消费者模式
         * 声明(创建)队列
         * 参数1:队列名称
         * 参数2:为true时server重启队列不会消失
         * 参数3:队列是否是独占的,如果为true只能被一个connection使用,其他连接建立时会抛出异常
         * 参数4:队列不再使用时是否自动删除(没有连接,并且没有未处理的消息)
         * 参数5:建立队列时的其他参数
         */
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        
		String message = messageText; // 消息主题内容
		/*
         * 向server发布一条消息
         * 参数1:交换机exchange的名称,若为空则使用默认的exchange
         * 参数2:队列映射的路由key
         * 参数3:其他的属性
         * 参数4:消息体
         * RabbitMQ默认有一个exchange,叫default exchange,它用一个空字符串表示,它是direct exchange类型,
         * 任何发往这个exchange的消息都会被路由到routing key的名字对应的队列上,如果没有对应的队列,则消息会被丢弃
         */
		channel.basicPublish("", QUEUE_NAME,  null, message.getBytes("UTF-8")); 
		System.out.println("[生产者][消费模式]已经发送消息:" + message);

		// 关闭资源
		channel.close();
		connection.close();
	
	}
	
	/**
	 * 订阅者模式
	 *
	 * @author yulisao
	 * @createDate 2019-9-11
	 * @description 
	 * @throws IOException
	 */
	public static void readSend(String messageText) throws IOException {
		init(); // 初始化
		
		/* 订阅者模式
         * 声明exchange(交换机)
         * 参数1:交换机名称
         * 参数2:交换机类型 : direct 、fanout 、topic
         *   direct:
         *   fanout: 下发到所有绑在该交换机下的队列
         *   topic:
         * 参数3:交换机持久性,如果为true则服务器重启时不会丢失
         * 参数4:交换机在不被使用时是否删除
         * 参数5:交换机的其他属性
         */
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout",true,true,null);
        
        String message = messageText; // 消息主题内容
        // 第一个和第二个参数 位置相反。 第二个参数字符串B是路由key名称,在订阅者中与绑定队列channel.queueBind的第三个参数需保持一致,不然订阅者接收不到
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
        System.out.println("[生产者][订阅模式]已经发送消息:" + message);

		// 关闭资源
		channel.close();
		connection.close();
	}
}

main是主方法, init是创建连接的。consumeSend是消费者模式, readSend是发布者模式,需要测试哪种模式就放开main里面的哪种调用。

MqReceiver.java 消费者、订阅者如下

package test;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.ShutdownSignalException;

/**
 * 主要流程:
 * (1):创建ConnectionFactory 
 * (2):创建一个Connection连接 
 * (3):创建一个Channel通道
 * (4):将queue和Channel进行绑定,注意这里的queue名字要和前面producer创建的queue一致
 * (5):创建消费者Consumer来接收消息,同时将消费者和queue进行绑定
 * 
 * @author yulisao
 * @createDate 2019-9-11
 * @description
 */
public class MqReceiver {
	
	// 队列名称。消费者模式下各个消费端的队列名称要一致,订阅者模式下各个订阅者端不要相同
	private final static String QUEUE_NAME = "MyQueue";
	
	//交换机名称
	private final static String EXCHANGE_NAME = "MyExchangeFanout";
	
	private static ConnectionFactory factory;
	private static Connection connection;
	private static Channel channel;

	public static void main(String[] args) throws IOException,
			TimeoutException, ShutdownSignalException,
			ConsumerCancelledException, InterruptedException {
		System.out.println("当前队列名:"+QUEUE_NAME);
		System.out.println("exeing.... ");
		//consumeReceive(); // 消费者
		readReceive(); // 订阅者
	}
	
	/**
	 * 连接初始化
	 *
	 * @author yulisao
	 * @createDate 2019-9-11
	 * @description 
	 * @throws IOException
	 */
	public static void init() throws IOException {
		factory = new ConnectionFactory();
		factory.setHost("localhost");
		connection = factory.newConnection();
		channel = connection.createChannel();
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
	}

	/**
	 * 消费者模式
	 *
	 * @author yulisao
	 * @createDate 2019-9-11
	 * @description 
	 * @throws IOException
	 */
	public static void consumeReceive() throws IOException, TimeoutException,
			ShutdownSignalException, ConsumerCancelledException,
			InterruptedException {
		init(); // 初始化

		/*
		 * 这行表示同一时刻服务器只会发一条消息给消费者 
		 * 注释表示平均轮流模式:N个消费者,从第1到N个消费者轮流接一个消息,如此往复循环
		 * 放开表示能者多劳模式:N个消费者,谁处理的快,效率高,谁接的消息会多一些
		 */
		// channel.basicQos(1);

		// 定义队列的消费者
		QueueingConsumer consumer = new QueueingConsumer(channel);
		/*
		 * 监听队列 
		 * 参数1: 队列名称 
		 * 参数2: 是否自动回复。 true:自动 false:手动 
		 * 参数3:消费者
		 */
		channel.basicConsume(QUEUE_NAME, true, consumer);

		// 获取消息
		while (true) { // 这个循环能保证一直在监听,不然执行一次整个main程序就执行完毕了。
			QueueingConsumer.Delivery delivery = consumer.nextDelivery();
			String message = new String(delivery.getBody());
			System.out.println("[消费者]接收到消息是:" + message);

			// 当channel.basicConsume里面第二个参数是false时,需要下面这行手动回复
			// channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);

			// channel.basicReject(); channel.basicNack();
			//可以通过这两个函数拒绝消息,可以指定消息在服务器删除还是继续投递给其他消费者
		}

	}
	
	/**
	 * 订阅者模式
	 *
	 * @author yulisao
	 * @createDate 2019-9-11
	 * @description 
	 * @throws IOException
	 * @throws InterruptedException 
	 * @throws ConsumerCancelledException 
	 * @throws ShutdownSignalException 
	 */
	public static void readReceive() throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
		init(); // 初始化
		
		/*
         * 绑定队列到交换机(交换机名称一定要和生产者交换机名称相同)
         * 参数1:队列的名称
         * 参数2:交换机的名称
         * 参数3:Routing Key , 需与发布者的 channel.basicPublish 第二个参数一致 才能接收到消息
         * 
         */
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "A");
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "B");
		channel.basicQos(1); //同一时刻服务器只会发一条消息给订阅者
		
		QueueingConsumer consumer = new QueueingConsumer(channel);
		// 监听队列,手动返回完成
		channel.basicConsume(QUEUE_NAME, false, consumer);
		
		 // 获取消息
		while (true) {
			QueueingConsumer.Delivery delivery = consumer.nextDelivery();
			String message = new String(delivery.getBody());
			System.err.println("[订阅者]接收到消息是:" + message);
			// 手动回复
			channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
		}
	}
}

将MqReceiver打jar运行测试。
消费者模式下:
生产者发了7个消息出去。当前队列名是MyQueue ,消费者的队列名也必须都是MyQueue,不然接收不到消息
生产者发布消息
打包好消费者后,同时运行了三个,也就是三个消费者,三个消费者的代码都是一样的。理论上他们三个是轮流来一人一次的接收一次消息,如下图。
在这里插入图片描述
但如果加了channel.basicQos(1); 这行代码,则是能者多劳模式,谁处理的快谁就可以去优先竞争下一个消息。没加channel.basicQos(1); 这行就是轮流平均模式,这行表示同一时刻服务器只会发一条消息给消费者。 当然轮流平均只是理论上(比如消费者收到了消息但回复生产者的时候没成功或者网络不畅,生产者则会以为他没收到从而把这个消息另给一个消费者)。
redis里面的list类型的左添加lpush和右取出rpop也是一种队列机制,但是没有回复的功能,而rabbitmq则有自动回复和手动回复的功能优点在此,对队列安全严格重视的用rabbitmq比较好,不是很重视用redis也成,轻量级。

订阅者模式:
发布者和订阅者的交换机名称MyExchangeFanout必须一致,队列名字不用相同随便取名字,只要将取好的队列名绑到交换机MyExchangeFanout上即可收到发布者推送的全部消息。
在这里插入图片描述
运行三个订阅者,他们的队列名分别是queue1,queue2,queue3,服务的发布的消息他们三者都可以收到无需竞争。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值