一、Virtual Hosts

每一个 RabbitMQ 服务器都能创建虚拟的消息服务器,我们称之为虚拟主机 (virtual host) ,简称为vhost。每一个 vhost 本质上是一个独立的小型 RabbitMQ 服务器,拥有自己独立的队列、交换器及绑定关系等,井且它拥有自己独立的权限。vhost 就像是虚拟机与物理服务器一样,它们在各个实例间提供逻辑上的分离,为不同程序安全保密地运行数据,它既能将同一个RabbitMQ 中的众多客户区分开,又可以避免队列和交换器等命名冲突。vhost 之间是绝对隔离的,无法将 vhostl 中的交换器与 vhost2 中的队列进行绑定,这样既保证了安全性,又可以确保可移植性。如果在使用 RabbitMQ 达到一定规模的时候,建议用户对业务功能、场景进行归类区分,并为之分配独立的 vhost。

1.1、Virtual Hosts 的功能说明

vhost可以限制最大连接数和最大队列数,并且可以设置vhost下的用户资源权限和Topic权限,具体权限见下方说明。
  • 在 Admin -> Limits 页面可以设置vhost的最大连接数和最大队列数,达到限制后,继续创建,将会报错。
AMQP 0-9-1 Operation   configure write read
exchange.declare (passive=false) exchange    
exchange.declare (passive=true)      
exchange.declare (with [AE](ae.html)) exchange exchange (AE) exchange
exchange.delete   exchange    
queue.declare (passive=false) queue    
queue.declare (passive=true)      
queue.declare (with [DLX](dlx.html)) queue exchange (DLX) queue
queue.delete   queue    
exchange.bind     exchange (destination) exchange (source)
exchange.unbind     exchange (destination) exchange (source)
queue.bind     queue exchange
queue.unbind     queue exchange
basic.publish     exchange  
basic.get       queue
basic.consume       queue
queue.purge       queue
 
举例说明:
    • 比如创建队列时,会调用 queue.declare 方法,此时会使用到 configure 权限,会校验队列名是否与 configure 的表达式匹配。
    • 比如队列绑定交换器时,会调用 queue.bind 方法,此时会用到 write 和 read 权限,会检验队列名是否与 write 的表达式匹配,交换器名是否与 read 的表达式匹配。
    • Topic权限是RabbitMQ 针对STOMP和MQTT等协议实现的一种权限。由于这类协议都是基于Topic消费的,而AMQP是基于Queue消费,所以AMQP的标准资源权限不适合用在这类协议中,而Topic权限也不适用于AMQP协议。所以,我们一般不会去使用它,只用在使用了MQTT这类的协议时才可能会用到。

2.2、vhost使用示例

1. 使用管理员用户登录Web管理界面。
2.页面添加一个名为 v1 的Virtual Hosts。(此时还需要为此vhost分配用户,添加一个新用户)

3.在 Admin -> Users 页面添加一个名为 order-user 的用户,并设置为 management 角色。

4. 从 Admin 进入 order-user 的用户设置界面,在 Permissions 中,为用户分配vhost为/v1,并为每种权限设置需要匹配的目标名称的正则表达式。
 

字段名
值 
说明 
Virtual Host 
/v1 
指定用户的vhost,以下权限都只限于 /v1 vhost中
Configure regexp
eq-.*
只能操作名称以eq-开头的exchange或queue;为空则不能操作任何exchange和queue 
Write regexp 
.*
能够发送消息到任意名称的exchange,并且能绑定到任意名称的队列和任意名称的目标交
换器(指交换器绑定到交换器),为空表示没有权限 
Read regexp 
^test$ 
只能消费名为test队列上的消息,并且只能绑定到名为test的交换器
5.代码演示
public class Producer {
public static void main(String[] args) {
// 1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2、设置连接属性
factory.setUsername("order-user");
factory.setPassword("order-user");
factory.setVirtualHost("v1"); Connection connection = null;
Channel channel = null; // 3、设置每个节点的链接地址和端口
Address[] addresses = new Address[]{
new Address("192.168.0.1", 5672),
new Address("192.168.0.2", 5672)
}; try {
// 开启/关闭连接自动恢复,默认是开启状态
factory.setAutomaticRecoveryEnabled(true); // 设置每100毫秒尝试恢复一次,默认是5秒:com.rabbitmq.client.ConnectionFactory.DEFAULT_NETWORK_RECOVERY_INTERVAL
factory.setNetworkRecoveryInterval(100); factory.setTopologyRecoveryEnabled(false); // 4、使用连接集合里面的地址获取连接
connection = factory.newConnection(addresses, "生产者"); // 添加重连监听器
((Recoverable) connection).addRecoveryListener(new RecoveryListener() {
/**
* 重连成功后的回调
* @param recoverable
*/
public void handleRecovery(Recoverable recoverable) {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS").format(new Date()) + " 已重新建立连接!");
} /**
* 开始重连时的回调
* @param recoverable
*/
public void handleRecoveryStarted(Recoverable recoverable) {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS").format(new Date()) + " 开始尝试重连!");
}
}); // 5、从链接中创建通道
channel = connection.createChannel(); /**
* 6、声明(创建)队列
* 如果队列不存在,才会创建
* RabbitMQ 不允许声明两个队列名相同,属性不同的队列,否则会报错
*
* queueDeclare参数说明:
* @param queue 队列名称
* @param durable 队列是否持久化
* @param exclusive 是否排他,即是否为私有的,如果为true,会对当前队列加锁,其它通道不能访问,并且在连接关闭时会自动删除,不受持久化和自动删除的属性控制
* @param autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除
* @param arguments 队列参数,设置队列的有效期、消息最大长度、队列中所有消息的生命周期等等
*/
channel.exchangeDeclare("test-exchange", "fanout");
channel.queueDeclare("queue1", false, false, false, null);
channel.queueBind("queue1", "test-exchange", "xxoo");
for (int i = 0; i < 100; i++) {
// 消息内容
String message = "Hello World " + i;
try {
// 7、发送消息
channel.basicPublish("test-exchange", "queue1", null, message.getBytes());
} catch (AlreadyClosedException e) {
// 可能连接已关闭,等待重连
System.out.println("消息 " + message + " 发送失败!");
i--;
TimeUnit.SECONDS.sleep(2);
continue;
}
System.out.println("消息 " + i + " 已发送!");
TimeUnit.SECONDS.sleep(2);
} } catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 8、关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
} // 9、关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class VirtualHosts {
public static void main(String[] args) {
// 1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2、设置连接属性
factory.setUsername("order-user");
factory.setPassword("order-user");
factory.setVirtualHost("v1"); Connection connection = null;
Channel prducerChannel = null;
Channel consumerChannel = null; // 3、设置每个节点的链接地址和端口
Address[] addresses = new Address[]{
new Address("192.168.0.1", 5672),
new Address("192.168.0.2", 5672)
}; try {
// 4、从连接工厂获取连接
connection = factory.newConnection(addresses, "消费者"); // 5、从链接中创建通道
prducerChannel = connection.createChannel(); prducerChannel.exchangeDeclare("test-exchange", "fanout");
prducerChannel.queueDeclare("queue1", false, false, true, null);
prducerChannel.queueBind("queue1", "test-exchange", "xxoo");
// 消息内容
String message = "Hello A";
prducerChannel.basicPublish("test-exchange", "c1", null, message.getBytes()); consumerChannel = connection.createChannel();
// 创建一个消费者对象
Consumer consumer = new DefaultConsumer(consumerChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("收到消息:" + new String(body, "UTF-8"));
}
};
consumerChannel.basicConsume("queue1", true, consumer); System.out.println("等待接收消息");
System.in.read();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
// 9、关闭通道
if (prducerChannel != null && prducerChannel.isOpen()) {
try {
prducerChannel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
} // 10、关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

2.3、集群连接恢复

官方资料:https://www.rabbitmq.com/api-guide.html#connection-recovery;根据官方文档说明可知:
  • 通过 factory.setAutomaticRecoveryEnabled(true); 可以设置连接自动恢复的开关,默认已开启
  • 通过 factory.setNetworkRecoveryInterval(10000); 可以设置间隔多长时间尝试恢复一次,默认是5秒: com.rabbitmq.client.ConnectionFactory.DEFAULT_NETWORK_RECOVERY_INTERVAL
  • 如果启用了自动连接恢复,将由以下事件触发:
    • 连接的I/O循环中抛出IOExceiption
    • 读取Socket套接字超时
    • 检测不到服务器心跳
    • 在连接的I/O循环中引发任何其他异常
  • 如果客户端第一次连接失败,不会自动恢复连接。需要我们自己负责重试连接、记录失败的尝试、实现重试次数的限制等等。
ConnectionFactory factory = new ConnectionFactory();
// configure various connection settings try {
Connection conn = factory.newConnection();
} catch (java.net.ConnectException e) {
Thread.sleep(5000);
// apply retry logic
}
    • 如果程序中调用了 Connection.Close ,也不会自动恢复连接。
    • 如果是 Channel-level 的异常,也不会自动恢复连接,因为这些异常通常是应用程序中存在语义问题(例如试图从不存在的队列消费)。
  • 在Connection和Channel上,可以设置重新连接的监听器,开始重连和重连成功时,会触发监听器。添加和移除监听,需要将Connection或Channel强制转换成Recoverable接口。
((Recoverable) connection).addRecoveryListener() 
((Recoverable) connection).removeRecoveryListener()

git源码:https://gitee.com/TongHuaShuShuoWoDeJieJu/rabbit.git

RabbitMQ的web页面介绍(三)的更多相关文章

  1. RabbitMQ 在 web 页面 创建 exchange, queue, routing key

    这里只是为了展示, 在实际开发中一般在消费端通过 注解来自动创建 消费端: https://www.cnblogs.com/huanggy/p/9695934.html 1, 创建 Exchange ...

  2. web页面的加载顺序

    1.页面顺序 一个典型的web页面由于三个部分组成:html.css和JS.执行的顺序是: 在构造完HTML的dom结构时.触发DOMContentLoaded事件. 整个执行过程安装html的顺序来 ...

  3. Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群

    Redis总结(五)缓存雪崩和缓存穿透等问题   前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...

  4. web页面相关的一些常见可用字符介绍

    首先是一张图片,是一张一些字符以及想对应的HTML实体表示的对照图片.如下: 一.引号模样或内心的些字符 请选择该表格要呈现的字体: 字符以及HTML实体 描述以及说明 " " 这 ...

  5. web页面相关的一些常见可用字符介绍——张鑫旭

    by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/wordpress/?p=1623 正文开始之前先 ...

  6. IdentityServer4 + SignalR Core +RabbitMQ 构建web即时通讯(三)

    IdentityServer4 + SignalR Core +RabbitMQ 构建web即时通讯(三) 后台服务用户与认证 新建一个空的.net core web项目Demo.Chat,端口配置为 ...

  7. 打印web页面指定区域的三种方法

    本文和大家分享一下web页面实现指定区域打印功能的三种方法,一起来看下吧. 第一种方法:使用CSS 定义一 个.noprint的class,将不打印的内容放入这个class内. 代码如下: <s ...

  8. (转)Django学习之 第三章:动态Web页面基础

    上一章我们解释了怎样开始一个Django项目和运行Django服务器 当然了,这个站点实际上什么也没有做------除了显示了"It worked"这条信息以外. 这一章我们介绍怎 ...

  9. [HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设

    [HeadFrist-HTMLCSS学习笔记]第三章构建模块:Web页面建设 敲黑板!! <q>元素添加短引用,<blockquote>添加长引用 在段落里添加引用就使用< ...

随机推荐

  1. Flink 保证ExactlyOnce

    Flink 保证 ExactlyOnce 1.使用执行ExactlyOnce 的数据源,比如 kafka 2.使用FlinkConsumer,开启CheckPointing,偏移量会保存通过Check ...

  2. springboot中redis取缓存类型转换异常

    异常如下: [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested ...

  3. jquery mobile cdn

    <head> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/jque ...

  4. Git入门配置

    1.账户注册: 无论是GitHub还是码云(下称Gitee),要使用他们,我们都需要先注册账户,已有账户的可以跳过此步骤. Gitee GitHub 2.创建仓库: a.创建远程仓库 登入Gitee后 ...

  5. kratos

    技术文章 日志库的使用姿势 通过 layout 探索 kratos 运行原理 发版日志 发布日志 - kratos v2.0.5 版本发布 发布日志 - kratos v2.0.4 版本发布

  6. 免费 CDN 玩法 —— 将整个网站打包成一个图片文件

    资源合并 前端开发者都知道,过多的请求对性能影响很大.而且有些 CDN 不仅按流量收费,请求数也收费,如果网页里有大量小文件,显然不划算. 为此不少开发者将零碎的小文件进行合并优化,例如 JS/CSS ...

  7. Java基础(四)——抽象类和接口

    一.抽象类 1.介绍 使用关键字 abstract 定义抽象类. abstract定义抽象方法,只有声明,不用实现. 包含抽象方法的类必须定义为抽象类. 抽象类中可以有普通方法,也可以有抽象方法. 抽 ...

  8. Python图像分割之区域增长法

    原文链接:https://blog.csdn.net/sgzqc/article/details/119682864 一.简介 区域增长法是一种已受到计算机视觉界十分关注的图像分割方法.它是以区域为处 ...

  9. 20210715 noip16

    考场 乍一看 T1 像是二分答案,手玩样例发现可以 \(O(k^2)\) 枚举点对,贪心地更新答案,完了?有点不信,先跳了 T2 的形式有点像逆序对,但没啥想法 T3 的式子完全不知道如何处理,一看就 ...

  10. Python常见问题 - 写入数据到 excel 报 ValueError: invalid literal for int() with base 10 错误

    背景 在上写入数据到excel中,报了以下错误 出现原因 对于写入excel场景下出现该错误的话,很大概率是写入数据的单元格原本的数据格式有问题 解决方法 清理掉单元格的旧数据,然后再写入就可以了