一、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. redis BLPOP命令阻塞,非阻塞(读了好久才懂)

    来源于:http://redisdoc.com/list/blpop.html#id1BLPOP key [key -] timeout 可用版本: >= 2.0.0 时间复杂度: O(1) B ...

  2. @ModelAttribute 与@InitBinder

    3.4.6 @ModelAttribute 注解 Mod lAttribut 通常作用在 Controller 的某个方法上,此方法会首先被调用, 井将方法 结果作为 Model 的属性 然后再调用对 ...

  3. linux centos7 移动文件到指定目录

    2021-08-26 在 centos7 环境下怎么移动一个文件到其他的目录下呢? 使用命令  mv 文件名 指定目录  即可完成该操作. 那么怎么将一个文件夹下的内容移动到另一个文件夹下呢?比如有时 ...

  4. C# ThreadPool 分批处理数据,所有数据执行完再返回

    这是一个调用翻译数据的功能,所有数据一次性提交会造成后台服务压力大,接口反应时间也长. 所以做了一个分批处理,等待所有批次的数据调用接口都返回后再执行下一步. 1 /// <summary> ...

  5. Linux服务器JDK的安装

    JDK安装 开发java程序必须要的环境 下载JDK rpm. 安装环境 #检测当前系统是否安装Java环境 java -version #如果有就需要卸载 #rpm -qa|grep jdk #检测 ...

  6. 01_Keil与Proteus联合仿真的注意事项

    01. 关于keil5和Proteus8的联合仿真的操作步骤,这里就不细说,给个链接,步骤差不多是最齐全的 CSDN博客链接:https://blog.csdn.net/wzk456/article/ ...

  7. Powershell免杀从入门到实践

    转载https://www.jianshu.com/p/fb078a99e0d8 前言 文章首发于Freebuf 在之前发布的一篇 渗透技巧之Powershell实战思路 中,学习了powershel ...

  8. 洛谷P1083 借教室 题解

    题目 [NOIP2012 提高组] 借教室 题解 这道题是几周之前做到的一道题,本来不想讲的,因为这道题也是用到了二分答案的方法,这类题目之前已经发布过两篇题解了.但这道题还运用了差分数组这个思想,所 ...

  9. NOIP模拟测试17&18

    NOIP模拟测试17&18 17-T1 给定一个序列,选取其中一个闭区间,使得其中每个元素可以在重新排列后成为一个等比数列的子序列,问区间最长是? 特判比值为1的情况,预处理比值2~1000的 ...

  10. Servlet体系结构

    一.使用HttpServlet 其中,HttpServlet在重写的service()方法中对http请求的共7中提交方式进行了判断,所以只要我们只要重写对应的请求方式处理逻辑方法 doGet()和d ...