4. work模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16855077/article/details/81304878

1.生产者

package com.cloudtech.web.mq.work;

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

import com.cloudtech.web.util.RabbitmqUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
 * 生产者
* @ClassName: Sender  
* @Description:   
* @author wude  
* @date 2018年7月31日  
*
 */
public class Sender {
	private final static String QUEUE="testwork";
	
	public static void main(String[] args) {
		//获取连接
		try {
			Connection connection = RabbitmqUtils.getConnection();
			//创建通道
			Channel channel = connection.createChannel();
			//声明队列  说明队列存在则什么都不做  如果不存在则自动创建
			//参数1.队列名称
			//参数2 是否持久化队列,我们中的队列模式是指内存中的,如果rabbit重启会丢失,如果我们设置为true,则会保存到el-lang自带的数据库中重启后,会重新读取
			//参数3 是否排外,有两个作用,当我们连接关闭后,是否自动关闭队列,作用二 是否私用当天的队列,如果私有了,则其他队列不允许访问,如果为true,一般只适用于一个消费者的时候
			//参数4 是否自动删除
			//参数5我们的一些其他参数
			channel.queueDeclare(QUEUE,false,false,false,null);
			//发送内容
			for (int i = 0; i < 100; i++) {
				channel.basicPublish("", QUEUE, null, ("发送的消息"+i).getBytes());
			}
		
			//关闭连接
			channel.close();
			connection.close();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		}
	}
}

2消费者

   2.1消费者1

     

package com.cloudtech.web.mq.work;

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

import com.cloudtech.web.util.RabbitmqUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Rerver1 {
	private final static String QUEUE="testwork";
		
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection conn = RabbitmqUtils.getConnection();
		Channel channel = conn.createChannel();
		channel.queueDeclare(QUEUE, false, false, false, null);
		
		DefaultConsumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				//当我们收到消息时调用
				String s = new String(body);
				System.out.println("消费者1  收到的内容是:"+s);
				//确认
				//参数2 为false,确认收到消息  true为拒收到消息
				channel.basicAck(envelope.getDeliveryTag(), false);   
			}
		};
		//注册消费者    参数2 手动确认   代表我们收到消息后,需要手动告诉服务器,我收到消息了
		channel.basicConsume(QUEUE, false, consumer);
	}
}

   2.2 消费者2

    

package com.cloudtech.web.mq.work;

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

import com.cloudtech.web.util.RabbitmqUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Rerver2 {
	private final static String QUEUE="testwork";
		
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection conn = RabbitmqUtils.getConnection();
		Channel channel = conn.createChannel();
		channel.queueDeclare(QUEUE, false, false, false, null);
		
		DefaultConsumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				//当我们收到消息时调用
				String s = new String(body);
				System.out.println("消费者2   收到的内容是:"+s);
				//确认
				//参数2 为false,确认收到消息  true为拒收到消息
				channel.basicAck(envelope.getDeliveryTag(), false);   
			}
		};
		//注册消费者    参数2 手动确认   代表我们收到消息后,需要手动告诉服务器,我收到消息了
		channel.basicConsume(QUEUE, false, consumer);
	}
}

3测试结果

        为保证数据消费的公平性,先启动分别启动消费者后,再启动生产者

      消费者2测试结果:

      

消费者2   收到的内容是:发送的消息1
消费者2   收到的内容是:发送的消息3
消费者2   收到的内容是:发送的消息5
消费者2   收到的内容是:发送的消息7
消费者2   收到的内容是:发送的消息9
消费者2   收到的内容是:发送的消息11
消费者2   收到的内容是:发送的消息13
消费者2   收到的内容是:发送的消息15
消费者2   收到的内容是:发送的消息17
消费者2   收到的内容是:发送的消息19
消费者2   收到的内容是:发送的消息21
消费者2   收到的内容是:发送的消息23
消费者2   收到的内容是:发送的消息25
消费者2   收到的内容是:发送的消息27
消费者2   收到的内容是:发送的消息29
消费者2   收到的内容是:发送的消息31
消费者2   收到的内容是:发送的消息33
消费者2   收到的内容是:发送的消息35
消费者2   收到的内容是:发送的消息37
消费者2   收到的内容是:发送的消息39
消费者2   收到的内容是:发送的消息41
消费者2   收到的内容是:发送的消息43
消费者2   收到的内容是:发送的消息45
消费者2   收到的内容是:发送的消息47
消费者2   收到的内容是:发送的消息49
消费者2   收到的内容是:发送的消息51
消费者2   收到的内容是:发送的消息53
消费者2   收到的内容是:发送的消息55
消费者2   收到的内容是:发送的消息57
消费者2   收到的内容是:发送的消息59
消费者2   收到的内容是:发送的消息61
消费者2   收到的内容是:发送的消息63
消费者2   收到的内容是:发送的消息65
消费者2   收到的内容是:发送的消息67
消费者2   收到的内容是:发送的消息69
消费者2   收到的内容是:发送的消息71
消费者2   收到的内容是:发送的消息73
消费者2   收到的内容是:发送的消息75
消费者2   收到的内容是:发送的消息77
消费者2   收到的内容是:发送的消息79
消费者2   收到的内容是:发送的消息81
消费者2   收到的内容是:发送的消息83
消费者2   收到的内容是:发送的消息85
消费者2   收到的内容是:发送的消息87
消费者2   收到的内容是:发送的消息89
消费者2   收到的内容是:发送的消息91
消费者2   收到的内容是:发送的消息93
消费者2   收到的内容是:发送的消息95
消费者2   收到的内容是:发送的消息97
消费者2   收到的内容是:发送的消息99

     消费者1测试结果:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/D:/java/maven-local/org/slf4j/slf4j-simple/1.7.2/slf4j-simple-1.7.2.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/java/maven-local/org/slf4j/slf4j-log4j12/1.6.6/slf4j-log4j12-1.6.6.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.SimpleLoggerFactory]
消费者1  收到的内容是:发送的消息0
消费者1  收到的内容是:发送的消息2
消费者1  收到的内容是:发送的消息4
消费者1  收到的内容是:发送的消息6
消费者1  收到的内容是:发送的消息8
消费者1  收到的内容是:发送的消息10
消费者1  收到的内容是:发送的消息12
消费者1  收到的内容是:发送的消息14
消费者1  收到的内容是:发送的消息16
消费者1  收到的内容是:发送的消息18
消费者1  收到的内容是:发送的消息20
消费者1  收到的内容是:发送的消息22
消费者1  收到的内容是:发送的消息24
消费者1  收到的内容是:发送的消息26
消费者1  收到的内容是:发送的消息28
消费者1  收到的内容是:发送的消息30
消费者1  收到的内容是:发送的消息32
消费者1  收到的内容是:发送的消息34
消费者1  收到的内容是:发送的消息36
消费者1  收到的内容是:发送的消息38
消费者1  收到的内容是:发送的消息40
消费者1  收到的内容是:发送的消息42
消费者1  收到的内容是:发送的消息44
消费者1  收到的内容是:发送的消息46
消费者1  收到的内容是:发送的消息48
消费者1  收到的内容是:发送的消息50
消费者1  收到的内容是:发送的消息52
消费者1  收到的内容是:发送的消息54
消费者1  收到的内容是:发送的消息56
消费者1  收到的内容是:发送的消息58
消费者1  收到的内容是:发送的消息60
消费者1  收到的内容是:发送的消息62
消费者1  收到的内容是:发送的消息64
消费者1  收到的内容是:发送的消息66
消费者1  收到的内容是:发送的消息68
消费者1  收到的内容是:发送的消息70
消费者1  收到的内容是:发送的消息72
消费者1  收到的内容是:发送的消息74
消费者1  收到的内容是:发送的消息76
消费者1  收到的内容是:发送的消息78
消费者1  收到的内容是:发送的消息80
消费者1  收到的内容是:发送的消息82
消费者1  收到的内容是:发送的消息84
消费者1  收到的内容是:发送的消息86
消费者1  收到的内容是:发送的消息88
消费者1  收到的内容是:发送的消息90
消费者1  收到的内容是:发送的消息92
消费者1  收到的内容是:发送的消息94
消费者1  收到的内容是:发送的消息96
消费者1  收到的内容是:发送的消息98

根据测试结果,说明生产者生产一条消费后,根据公平的规则,对应消费者平方对应的消息,只会被一个消费者消费到。

说到这里可能有些朋友会有一个疑问,毕竟多个消费者,肯定是部署在多条服务器上,服务器的配置有好有坏,那服务器的配置会不会对多消费者公平消费产生影响勒,下面通过一个线程延时测试模拟。

      模拟耗时测试

   把消费者1设置延迟300ms,消费者2设置延迟500ms,按照正常的逻辑,应该是消费者1应该工作的多些,大家觉得勒???

DefaultConsumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				//当我们收到消息时调用
				String s = new String(body);
				System.out.println("消费者1  收到的内容是:"+s);
				
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				//确认
				//参数2 为false,确认收到消息  true为拒收到消息
				channel.basicAck(envelope.getDeliveryTag(), false);   
			}
		};

实践证明:100条消息两个消费者平分,只是一个工作的快些,另一个工作得慢些。

通过上述的实践证明,又引生出一个问题,工作的快的一直在等待,造成资源的一个浪费,你觉得合理吗???

改造后代码

增加    channel.basicQos(1);  //告诉服务器,在我没有确定当前消息完成之前,不要给我发新的消息

package com.cloudtech.web.mq.work;

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

import com.cloudtech.web.util.RabbitmqUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Rerver1 {
	private final static String QUEUE="testwork";
		
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection conn = RabbitmqUtils.getConnection();
		Channel channel = conn.createChannel();
		channel.queueDeclare(QUEUE, false, false, false, null);
		channel.basicQos(1);  //告诉服务器,在我没有确定当前消息完成之前,不要给我发新的消息
		
		DefaultConsumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				//当我们收到消息时调用
				String s = new String(body);
				System.out.println("消费者1  收到的内容是:"+s);
				
				try {
					Thread.sleep(300);   //模拟耗时
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
				//确认
				//参数2 为false,确认收到消息  true为拒收到消息
				channel.basicAck(envelope.getDeliveryTag(), false);   
			}
		};
		//注册消费者    参数2 手动确认   代表我们收到消息后,需要手动告诉服务器,我收到消息了
		channel.basicConsume(QUEUE, false, consumer);
	}
}

实践证明:加了这句代码后,工作的快的,不会工作完后,就进入等待模式。这样就很好的避免了资源的一个浪费,场景:比如淘宝下一个订单,对应的不会只有一台订单服务器,会有多台,工作快的就会做点,工作满的就少分配点。

猜你喜欢

转载自blog.csdn.net/qq_16855077/article/details/81304878