版权声明:本文为博主原创文章,未经博主允许不得转载。 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);
}
}
实践证明:加了这句代码后,工作的快的,不会工作完后,就进入等待模式。这样就很好的避免了资源的一个浪费,场景:比如淘宝下一个订单,对应的不会只有一台订单服务器,会有多台,工作快的就会做点,工作满的就少分配点。