RabbitMQ镜像队列初始化连接时的“优化”
之前发过一篇帖子应用.Net+Consul维护RabbitMq的高可用性,然后最近老大问我当初我这么搞是抽的什么想法- -然后顺便贴了两行C#代码:
var factory = new ConnectionFactory()
{
UserName = "username",
Password = "password",
AutomaticRecoveryEnabled = true,
TopologyRecoveryEnabled = true
};
Connection = factory.CreateConnection(new string[] { "ip1", "ip2", "ip3" });
AutomaticRecoveryEnabled的作用就是断线重连,假如当前的连接断开了,连接不会释放
TopologyRecoveryEnabled 重连后恢复当前的工作进程,比如channel、queue、发布的消息进度等。
看上去其实却是比较方便的,为了狡辩我当场列出了我设计的方案的主要优点:
1.rabbitmq网关设计的时候一方面是为了让客户端能够保证建立与master节点的连接,直连master性能较高。
2.通过配置队列名+VirthHost可以获取队列信息,Consul可以起到配置中心的作用,可配置性高一些(其实此处是狡辩。。)
3.RabbitMQ与Master队列建立的连接在发生故障时会第一时间查询网关获取新的Mater队列信息进行重连(当然代码内部也有重试机制,不会随便就更换连接的),相比于AutomaticRecoveryEnabled效率更高一些。
4.客户端SDK内部实现定时更新队列连接,发现Master节点更换时重新建立连接
看上去我设计的方案还是有点意义的(其实是真懒得改代码)
不过此处有个问题就是既然创建连接时的参数可以提供多个IP的集合,假如RabbitMQ提供的客户端SDK内部实现的更好,那我狡辩什么不也完戏了吗。。。于是假装下载了下客户端sdk代码扫了扫,此处以C#代码为例,代码还是比较简单的。github地址
直接定位ConnectionFactory类找CreateConnection()方法
/// <summary>
/// Create a connection using a list of hostnames using the configured port.
/// By default each hostname is tried in a random order until a successful connection is
/// found or the list is exhausted using the DefaultEndpointResolver.
/// The selection behaviour can be overriden by configuring the EndpointResolverFactory.
/// </summary>
/// <param name="hostnames">
/// List of hostnames to use for the initial
/// connection and recovery.
/// </param>
/// <returns>Open connection</returns>
/// <exception cref="BrokerUnreachableException">
/// When no hostname was reachable.
/// </exception>
public IConnection CreateConnection(IList<string> hostnames)
{
return CreateConnection(hostnames, null);
} /// <summary>
/// Create a connection using a list of hostnames using the configured port.
/// By default each endpoint is tried in a random order until a successful connection is
/// found or the list is exhausted.
/// The selection behaviour can be overriden by configuring the EndpointResolverFactory.
/// </summary>
/// <param name="hostnames">
/// List of hostnames to use for the initial
/// connection and recovery.
/// </param>
/// <param name="clientProvidedName">
/// Application-specific connection name, will be displayed in the management UI
/// if RabbitMQ server supports it. This value doesn't have to be unique and cannot
/// be used as a connection identifier, e.g. in HTTP API requests.
/// This value is supposed to be human-readable.
/// </param>
/// <returns>Open connection</returns>
/// <exception cref="BrokerUnreachableException">
/// When no hostname was reachable.
/// </exception>
public IConnection CreateConnection(IList<string> hostnames, String clientProvidedName)
{
var endpoints = hostnames.Select(h => new AmqpTcpEndpoint(h, this.Port, this.Ssl));
return CreateConnection(new DefaultEndpointResolver(endpoints), clientProvidedName);
} /// <summary>
/// Create a connection using a list of endpoints. By default each endpoint will be tried
/// in a random order until a successful connection is found or the list is exhausted.
/// The selection behaviour can be overriden by configuring the EndpointResolverFactory.
/// </summary>
/// <param name="endpoints">
/// List of endpoints to use for the initial
/// connection and recovery.
/// </param>
/// <returns>Open connection</returns>
/// <exception cref="BrokerUnreachableException">
/// When no hostname was reachable.
/// </exception>
public IConnection CreateConnection(IList<AmqpTcpEndpoint> endpoints)
{
return CreateConnection(new DefaultEndpointResolver(endpoints), null);
} /// <summary>
/// Create a connection using an IEndpointResolver.
/// </summary>
/// <param name="endpointResolver">
/// The endpointResolver that returns the endpoints to use for the connection attempt.
/// </param>
/// <param name="clientProvidedName">
/// Application-specific connection name, will be displayed in the management UI
/// if RabbitMQ server supports it. This value doesn't have to be unique and cannot
/// be used as a connection identifier, e.g. in HTTP API requests.
/// This value is supposed to be human-readable.
/// </param>
/// <returns>Open connection</returns>
/// <exception cref="BrokerUnreachableException">
/// When no hostname was reachable.
/// </exception>
public IConnection CreateConnection(IEndpointResolver endpointResolver, String clientProvidedName)
{
IConnection conn;
try
{
if (AutomaticRecoveryEnabled)
{
var autorecoveringConnection = new AutorecoveringConnection(this, clientProvidedName);
autorecoveringConnection.Init(endpointResolver);
conn = autorecoveringConnection;
}
else
{
IProtocol protocol = Protocols.DefaultProtocol;
conn = protocol.CreateConnection(this, false, endpointResolver.SelectOne(this.CreateFrameHandler), clientProvidedName);
}
}
catch (Exception e)
{
throw new BrokerUnreachableException(e);
} return conn;
}
代码比较直观,第二个方法的时候把传入的字符串集合转换成了AmqpTcpEndpoint集合,AmqpTcpEndpoint里包含队列绑定信息,包含默认ip,端口,SSL配置信息,RabbitMQ的Amqp协议信息,初始化Socket连接的协议类型(一个AddressFamily枚举),当然都是可配置的(除了ip貌似都喜欢使用默认的)。
然后就倒主要部分了。
if (AutomaticRecoveryEnabled)
{
var autorecoveringConnection = new AutorecoveringConnection(this, clientProvidedName);
autorecoveringConnection.Init(endpointResolver);
conn = autorecoveringConnection;
}
else
{
IProtocol protocol = Protocols.DefaultProtocol;
conn = protocol.CreateConnection(this, false, endpointResolver.SelectOne(this.CreateFrameHandler), clientProvidedName);
}
由于主要想看看传入多个连接是不是可以智能的选择master连接,所以此处直接看else里的代码就行了。。。(其实一样。。)
protocol.CreateConnection方法调用时传入的第三个参数endpointResolver.SelectOne(this.CreateFrameHandler)作用其实就是选择连接然后扔到CreateFrameHandler委托里作为参数进行Socket初始化的。看一下SelectOne这个扩展方法即可。。看了两行终于可以继续狡辩了。。
public static T SelectOne<T>(this IEndpointResolver resolver, Func<AmqpTcpEndpoint, T> selector)
{
var t = default(T);
Exception exception = null;
foreach(var ep in resolver.All())
{
try
{
t = selector(ep);
if(t.Equals(default(T)) == false)
{
return t;
}
}
catch (Exception e)
{
exception = e;
}
} if(Object.Equals(t, default(T)) && exception != null)
{
throw exception;
} return t;
}
这个foreach还是“比较好的”,能够建立一个Socket连接就愉快的返回了!,不过这个resolver.All()里是不是还有玄机呢!看了一眼。。终于放心了。。
public class DefaultEndpointResolver : IEndpointResolver
{
private List<AmqpTcpEndpoint> endpoints;
private Random rnd = new Random(); public DefaultEndpointResolver (IEnumerable<AmqpTcpEndpoint> tcpEndpoints)
{
this.endpoints = tcpEndpoints.ToList();
} public IEnumerable<AmqpTcpEndpoint> All()
{
return endpoints.OrderBy(item => rnd.Next());
}
就是随机排序一下。。不过这么看自己实现下IEndpointResolver接口改个选择master队列的策略也是不错的。
RabbitMQ镜像队列初始化连接时的“优化”的更多相关文章
- rabbitmq——镜像队列
转自:http://my.oschina.net/hncscwc/blog/186350?p=1 1. 镜像队列的设置 镜像队列的配置通过添加policy完成,policy添加的命令为: rabbit ...
- RabbitMQ镜像队列集群搭建、与SpringBoot整合
镜像模式 集群模式非常经典的就是Mirror镜像模式,保证100%数据不丢失,在实际工作中也是用的最多的,并且实现集群比较的简单. Mirror镜像队列,目的是为了保证 RabbitMQ 数据的高可靠 ...
- springboot~rabbitmq的队列初始化和绑定
配置文件,在rabbit中自动建立exchange,queue和绑定它们的关系 代码里初始化exchange 代码里初始化queue 代码里绑定exchange,queue和routekey 配置文件 ...
- RabbitMQ如何保证发送端消息的可靠投递-发生镜像队列发生故障转移时
上一篇最后提到了mandatory这个参数,对于设置mandatory参数个人感觉还是很重要的,尤其在RabbitMQ镜像队列发生故障转移时. 模拟个测试环境如下: 首先在集群队列中增加两个镜像队列的 ...
- Oracle 表三种连接方式(sql优化)
在查看sql执行计划时,我们会发现表的连接方式有多种,本文对表的连接方式进行介绍以便更好看懂执行计划和理解sql执行原理. 一.连接方式: 嵌套循环(Nested Loops (NL)) (散列)哈希 ...
- 18-基于CentOS7搭建RabbitMQ3.10.7集群镜像队列+HaProxy+Keepalived高可用架构
集群架构 虚拟机规划 IP hostname 节点说明 端口 控制台地址 192.168.247.150 rabbitmq.master rabbitmq master 5672 http://192 ...
- RabbitMQ镜像模式双节点部署时故障转移过程中队列中消息的状态
场景 现有节点Node1和Node2,建立Exchange:yu.exchange,创建队列yu1.queue镜像队列master位于Node1,yu2.queue镜像队列位于Node2,使用topi ...
- RabbitMQ 高可用之镜像队列
如果RabbitMQ集群只有一个broker节点,那么该节点的失效将导致整个服务临时性的不可用,并且可能会导致message的丢失(尤其是在非持久化message存储于非持久化queue中的时候).可 ...
- RabbitMQ高可用镜像队列
## RabbitMQ高可用镜像队列 在分布式系统中,通常使用多个术语来标识主要副本和辅助副本.本指南通常使用"主"来引用队列的主要副本,而对于辅助副本则使用"镜像&qu ...
随机推荐
- Generator函数自执行
Generator函数实现 function* gen(x){ var y = yield x + 2; return y } //1.调用g函数会返回一个内部的指针 var g = gen(1); ...
- [SD2015]序列统计——solution
http://www.lydsy.com/JudgeOnline/problem.php?id=3992 很容易得出DP方程: f[i][c]=f[i-1][a]*f[1][b]① 其中a*b%M=c ...
- svg矢量图标在html中的使用, (知识点:1.通过h5中的css实现点击变色,2.一个svg文件包含多个图标)
svg矢量文件体积小,不变形,比传统的png先进,比现在流行的icon-font灵活.然而在使用过程中还是遇到了很多坑.今天花了一天时间把经验整理出来,以供后来者借鉴.如果您从本文收益,请留言mark ...
- Postman-Tests模块测试方法记录
用Postman的时候大多数测试结果是可以用Tests模块的测试方法来代替人工检查的,测试方法本质上是JavaScript代码,我们可以通过运行测试用例(测试脚本是在发送请求之后并且从服务器接收到响应 ...
- sublime text 3中browsersync的使用
1.在项目所在位置右键选择Git Bash Here 2.输入 // --files 路径是相对于运行该命令的项目(目录) browser-sync start --server --files &q ...
- Java中的数据类型转换
先来看一个题: Java类Demo中存在方法func0.func1.func2.func3和func4,请问该方法中,哪些是不合法的定义?( ) public class Demo{ float fu ...
- CentOS6.5(4)----宿主机无法访问虚拟机中的web服务解决方案
宿主机无法访问虚拟机中的web服务 在Windows7宿主机中的VMware虚拟机中安装了CentOS6.5操作系统,并且基于Nginx搭建了Web服务器,网页刚刚搭建好的时候,通过宿主机的浏览器可以 ...
- mybatis 关联表心得
1,例如订单表与用户表的关联,一个订单对应一个用户,这是一对一关联: 用代码实现是这样: A(用resultType去实现的话,如下) 1,使用到继承, OrderUser extend Order{ ...
- Centos7 下安装Apache2 + MySQL + PHP7
Apache 1.安装Apache yum install httpd 2.设置服务器开机自动启动Apache systemctl enable httpd.service 若要验证是否自动启动可在重 ...
- The difference between creating a string object constructor and assigning it directly
字符串对象构造方法创建和直接赋值的区别? package com.itheima_02; /* * 通过构造方法创建的字符串对象和直接赋值方式创建的字符串对象有什么区别呢? * 通过构造方法创建字符串 ...