1.声明
当前内容主要用于本人学习和复习之用,内容包括探讨一般集群模式下的测试,发现其中的问题
节点名称 | 节点ip |
---|---|
node1 | 192.168.1.110 |
node2 | 192.168.1.108 |
node3 | 192.168.1.102 |
启动后的页面
2.测试集群消费
1.创建队列和交换exchange(在node)
public class ClusterNode2MsgSender {
private static String host = "192.168.1.108";
private static int port = 5672;
private static String username = "root";
private static String password = "root";
// private static String vhost = "/";
public static void main(String[] args) throws IOException {
RabbitMqUtils mqUtils = new RabbitMqUtils(host, port, username, password);
String exchangeName = "node2Exchange";
String queueName = "node2Queue";
try (Connection connection = mqUtils.getConnection(); Channel channel = connection.createChannel();) {
DeclareOk declareOk = channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC, true, false, null);
System.out.println("创建【" + exchangeName + "】的exchange成功==>" + declareOk);
com.rabbitmq.client.AMQP.Queue.DeclareOk declareOk2 = channel.queueDeclare(queueName, true, false, false,
null);
System.out.println("创建【" + queueName + "】的queu成功==>" + declareOk2);
BindOk bindOk = channel.queueBind(queueName, exchangeName, "test");
System.out.println("绑定queue到exchange成功==>" + bindOk);
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:
所有的ui界面结果
2.创建生产者(分别向node1、node2、node3中的node3Exchange中发送消息)
node1的生产者
public class ClusterNode1MsgSender {
private static String host = "192.168.1.110";
private static int port = 5672;
private static String username = "root";
private static String password = "root";
// private static String vhost = "/";
public static void main(String[] args) throws IOException {
RabbitMqUtils mqUtils = new RabbitMqUtils(host, port, username, password);
String exchangeName = "node2Exchange";
try (Connection connection = mqUtils.getConnection();
Channel channel = connection.createChannel();) {
BasicProperties.Builder builder=new BasicProperties.Builder();
builder.deliveryMode(2);
BasicProperties props = builder.build();
for (int i = 0; i < 10; i++) {
channel.basicPublish(exchangeName, "test", props, ("【" + (i + 1) + "】node1,你好集群").getBytes());
}
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
node2生产者
public class ClusterNode2MsgSender {
private static String host = "192.168.1.108";
private static int port = 5672;
private static String username = "root";
private static String password = "root";
// private static String vhost = "/";
public static void main(String[] args) throws IOException {
RabbitMqUtils mqUtils = new RabbitMqUtils(host, port, username, password);
String exchangeName = "node2Exchange";
try (Connection connection = mqUtils.getConnection(); Channel channel = connection.createChannel();) {
BasicProperties.Builder builder = new BasicProperties.Builder();
builder.deliveryMode(2);
BasicProperties props = builder.build();
for (int i = 0; i < 10; i++) {
channel.basicPublish(exchangeName, "test", props, ("【" + (i + 1) + "】node2,你好集群").getBytes());
}
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
node3生产者
public class ClusterNode3MsgSender {
private static String host = "192.168.1.102";
private static int port = 5672;
private static String username = "root";
private static String password = "root";
// private static String vhost = "/";
public static void main(String[] args) throws IOException {
RabbitMqUtils mqUtils = new RabbitMqUtils(host, port, username, password);
String exchangeName = "node2Exchange";
try (Connection connection = mqUtils.getConnection();
Channel channel = connection.createChannel();) {
BasicProperties.Builder builder=new BasicProperties.Builder();
builder.deliveryMode(2);
BasicProperties props = builder.build();
for (int i = 0; i < 10; i++) {
channel.basicPublish(exchangeName, "test", props, ("【" + (i + 1) + "】node3,你好集群").getBytes());
}
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3.创建node2节点的node2Queue的消费者
public class ClusterNode2MsgComsumer {
private static String host = "192.168.1.108";
private static int port = 5672;
private static String username = "root";
private static String password = "root";
public static void main(String[] args) throws IOException {
RabbitMqUtils mqUtils = new RabbitMqUtils(host, port, username, password);
String queueName = "node2Queue";
Connection connection = mqUtils.getConnection();
Channel channel = connection.createChannel();
channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
System.out.println("consumerTag==>" + consumerTag);
System.out.println("envelope==>" + envelope);
System.out.println("props==>" + properties);
System.out.println("消费者node2接收消息==>【" + StringUtils.toString(body) + "】");
}
});
}
}
4.分别启动所有的生产者
5.启动一个node2消费者
测试成功
3.查看和测试node2Queue
分析发现node2Queue属于node2节点的
直接将该节点node2直接stop_app
再次启动生产者
此时发现除了node2的消息发送直接出现错误,其他的node1和node2都不会出现报错
再次启动消费者
修改消费者ip为其他的node3的ip的时候,前面一样直接报错
private static String host = "192.168.1.102";
发现一旦当前的主要存储消息的node2节点宕机,那么其他的节点接收到同样发向node2的节点的时候不会有任何问题(不报错),然而直接发向node2节点的直接报错
再次启动node2节点,查看消息是否丢失
发现此时消息都丢失了,这说明如果出现无法连接到写入的节点的时候,其他节点接收的消息直接丢弃(存在消息丢失的问题)
并且发现此时可以通过node3连接到node2Queue节点!
4.分析集群中存在的问题
-
集群中正常的时候
1.1. 任何客户端都可以通过任何的节点去连接一个节点中的队列,此时不会出现问题
1.2. 向任何节点中的Exchange推送消息的时候,都会被转发到指定路由节点的队列中 -
当集群中节点(存放数据的节点)宕机的时候
1.1 该消费者不能通过任何节点去连接该宕机节点的队列,此时连接会报错的
1.2 向宕机节点推送数据直接报错,无法推送数据;向其他没有宕机节点发送指向宕机节点的数据时候,不会出现报错,而是出现直接丢弃数据(可以设置返回)
所以集群中的模式应该是这样的
1.(集群正常)发布数据到node2Queue中
2.(集群中node2出现宕机)发布数据到node2Queue中
所以无法向node2中发送数据(连接都报错了),而连接node1、node3却可以
3.(集群正常)拉取消息
4.(集群中node2出现宕机)拉取消息
所以一旦拉取宕机节点的队列的数据的时候,所有的其他的node都是同样的报错
5.总结
1.集群环境下的rabbitmq带来了好处,就是可以通过任意节点获取指定的队列中的数据(但是需要指定节点)
2.集群环境下的rabbitmq也同样带来了坏处,如果一个节点宕机,那么不能通过任何一个节点访问该宕机节点的队列,但是可以向没有宕机的节点访问该宕机节点的队列
3.集群中数据实际是存放在一个节点中的,该实际存放数据的节点可能导致其他的问题
4.只要能解决上面的问题:数据节点宕机问题,保证消息可达,保证消息能正常在集群中消费等问题那么集群就不存在问题
5.为什么不能通过一个代理例如:eureka注册中心方式直接注册rabbitmq,通过一个节点就可操作了,为什么还要指定ip,每次创建queue的时候都直接和节点绑定了,不利于分布式
以上纯属个人见解,如有问题请联本人!