技术标签: At least two sentinels should be de DNS redisson 3.8 redis
本文主要针对第5点进行分析和解决。
sentinel://redis:26379,redis:26380?masterNames=mymaster&poolSize=100&poolName=xxx
nameserver 127.0.0.1
search aaa.bbb ostechnix.lan
(在这里,aaa.bbb是解析不了的)
$ ping redis
PING redis.ostechnix.lan (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.026 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.057 ms
$ nslookup redis
Server: 127.0.0.1
Address: 127.0.0.1#53
Name: redis.ostechnix.lan
Address: 127.0.0.1
$ src/redis-cli -h redis -p 26379
redis:26379> sentinel sentinels mymaster
1) 1) "name"
2) "127.0.0.1:26380"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "26380"
...
$ src/redis-cli -h redis -p 26380
1) 1) "name"
2) "127.0.0.1:26379"
3) "ip"
4) "127.0.0.1"
5) "port"
6) "26379"
java程序使用redisson-3.8.2尝试连接redis,出现了错误
Exception in thread "main" org.redisson.client.RedisConnectionException: At least two sentinels should be defined in Redis configuration!
at org.redisson.connection.SentinelConnectionManager.<init>(SentinelConnectionManager.java:159)
at org.redisson.config.ConfigSupport.createConnectionManager(ConfigSupport.java:195)
at org.redisson.Redisson.<init>(Redisson.java:122)
at org.redisson.Redisson.create(Redisson.java:161)
...
打开redisson的debug日志:
2018-11-21 18:11:53 [main] WARN o.r.c.SentinelConnectionManager - Can't connect to sentinel server. Unable to connect to: redis://redis:26379
...
2018-11-21 18:12:03 [main] WARN o.r.c.SentinelConnectionManager - Can't connect to sentinel server. Unable to connect to: redis://redis:26380
Well, 为何 redis-cli 能连接得上sentinel,而java程序会出错?java程序在使用以前的版本redisson-2.5.1的时候是一切正常的。
重新看一次debug日志,发现了一个奇怪的地方:
2018-11-21 18:11:48 [main] DEBUG i.netty.resolver.dns.DnsQueryContext - [id: 0xda4ab0e7] WRITE: [49889: /127.0.0.1:53], DefaultDnsQuestion(redis.aaa.bbb ostechnix.lan. IN A)
2018-11-21 18:11:48 [main] DEBUG i.netty.resolver.dns.DnsQueryContext - [id: 0xda4ab0e7] WRITE: [34575: /127.0.0.1:53], DefaultDnsQuestion(redis.aaa.bbb ostechnix.lan. IN AAAA)
解释一下:
IN A:代表主机名到 IPv4 地址的映射
IN AAAA:代表主机名到 IPv6 地址的映射
“ostechnix.lan.” 最后的点,代表根,“lan.” 表示lan为根下的第一级域
再来看这个域名 “redis.aaa.bbb ostechnix.lan.”,有点奇怪,域名中间为何会出现空格?
回头看/etc/resolv.conf文件,发现
search aaa.bbb ostechnix.lan
解释一下:
search:各项间以空格或者tab分隔,当域名没有以点结尾时,需要从这里追加各项,作为完全限定域名再发送DNS请求。
很明显,在解析search项的时候,没有用空格分隔开各项,导致DNS请求的域名存在错误。
查看redisson的源码,发现RedisClient的resolvAddr方法会对地址进行解析,如果/etc/resolv.conf里面存在多个DNS server的配置,会给每个配置都建立一个DnsNameResolver(这部分是属于netty-4.1.30.Final的源码)。
查看DnsNameResolver的源码:
static {
String[] searchDomains;
try {
List<String> list = PlatformDependent.isWindows()
? getSearchDomainsHack()
: UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains();
searchDomains = list.toArray(new String[0]);
} catch (Exception ignore) {
// Failed to get the system name search domain list.
searchDomains = EmptyArrays.EMPTY_STRINGS;
}
DEFAULT_SEARCH_DOMAINS = searchDomains;
...
}
searchDomains 是通过UnixResolverDnsServerAddressStreamProvider.parseEtcResolverSearchDomains() 来解析的
查看该方法源码:
static List<String> parseEtcResolverSearchDomains(File etcResolvConf) throws IOException {
String localDomain = null;
List<String> searchDomains = new ArrayList<String>();
FileReader fr = new FileReader(etcResolvConf);
BufferedReader br = null;
try {
br = new BufferedReader(fr);
String line;
while ((line = br.readLine()) != null) {
if (localDomain == null && line.startsWith(DOMAIN_ROW_LABEL)) {
int i = indexOfNonWhiteSpace(line, DOMAIN_ROW_LABEL.length());
if (i >= 0) {
localDomain = line.substring(i);
}
} else if (line.startsWith(SEARCH_ROW_LABEL)) {
int i = indexOfNonWhiteSpace(line, SEARCH_ROW_LABEL.length());
if (i >= 0) {
searchDomains.add(line.substring(i));
}
}
}
} finally {
if (br == null) {
fr.close();
} else {
br.close();
}
}
Well,看来是netty对于search的解析有了新的想法,认为search是每项一行,所以木有再对每行进行空格或者tab的切割
searchDomains.add(line.substring(i));
把 UnixResolverDnsServerAddressStreamProvider 的源码 copy 到应用中,按照相同的package路径放置,然后修改 parseEtcResolverSearchDomains 方法,对每行进行split
修改/etc/resolv.conf文件,把search改成每行一项,譬如:
nameserver 127.0.0.1
search aaa.bbb
search ostechnix.lan
如果你自己搭建了DNS server来模拟上述的实验,有可能还是出错,说连接不了sentinel。
以实验为例,每个DnsNameResolver在上述改动后,都会拿到2个domain(aaa.bbb 和 ostechnix.lan)。
你可以尝试改变这2个domain的顺序,譬如:
search ostechnix.lan aaa.bbb
或者
search ostechnix.lan
search aaa.bbb
再一次实验,就会发现这次竟然通过了。
Well,是不是很神奇?
调试一下 DnsResolveContext的 resolve方法
searchDomainPromise.addListener(new FutureListener<List<T>>() {
private int searchDomainIdx = initialSearchDomainIdx;
@Override
public void operationComplete(Future<List<T>> future) {
Throwable cause = future.cause();
if (cause == null) {
promise.trySuccess(future.getNow());
} else {
if (DnsNameResolver.isTransportOrTimeoutError(cause)) {
promise.tryFailure(new SearchDomainUnknownHostException(cause, hostname));
} else if (searchDomainIdx < searchDomains.length) {
Promise<List<T>> newPromise = parent.executor().newPromise();
newPromise.addListener(this);
doSearchDomainQuery(hostname + '.' + searchDomains[searchDomainIdx++], newPromise);
} else if (!startWithoutSearchDomain) {
internalResolve(hostname, promise);
} else {
promise.tryFailure(new SearchDomainUnknownHostException(cause, hostname));
}
}
}
});
顺带说一下,这个方法会一个一个searchDomain的去尝试。
着重调试:
promise.tryFailure(new SearchDomainUnknownHostException(cause, hostname));
你会发现,当发送DNS请求(redis.aaa.bbb)到自己的DNS时,会出现5000ms的超时错误。
可以在命令行进行尝试:
$ nslookup redis.aaa.bbb 127.0.0.1
Server: 127.0.0.1
Address: 127.0.0.1#53
** server can't find redis.aaa.bbb: SERVFAIL
忽略这个错误,注意测量一下耗时,是不是早就过了5s?
结合上面的代码,当出现timeout错误时,下一个domain是不会继续去连接的,所以当顺序为"[aaa.bbb, ostechnix.lan]"时,程序一样报错。
那这个5000ms的限制是哪里加入的咧?
再次调试源码,发现DnsNameResolver是由DnsNameResolverBuilder来构造的
public final class DnsNameResolverBuilder {
...
private long queryTimeoutMillis = 5000;
...
copy源码到应用目录,改一下这个数字,完事
步骤一:编写Java代码java部分代码: case R.id.broadcast_reboot: //重启 Log.v("Reboot", "R...
单行文本换行css需设置属性:overflow: hidden;text-overflow: ellipsis;white-space: nowrap;多行文本换行css需设置属性:(-webkit内核才有效)overflow : hidden;display: -webkit-box;-webkit-line-clamp:3;-webkit-box-orient: vertical;-...
---------------------- Windows Phone 7手机开发、.Net培训、期待与您交流! ----------------------近几天学习了SQL的一些基础,现在总结一下通过T-SQL代码来实现数据库、表的创建。创建&删除数据库创建数据库create database stuDBon( name='stuDB_data',...
首先,祝大家双十一快乐。开始本文之前,希望大家参与一下下面的投票。做这个投票的主要原因是最近经常有找浪尖咨询大数据,自学,培训及找工作的事情,问题归类如下:大数据要不要培...
/** 结题思路:用dp[r][i][j]表示第r行r行状态为i,r-1行状态为j时的最大可部署炮兵的个数。通过求解合法状态,缩小需要遍历的状态范围。同时求出各个合法状态的二进制中1的个数,然后特殊处理首行,然后dp求解每行每种可行状态对应的可部兵的最大个数。状态转移方程dp[r][i][j]=max(dp[r][i][j],dp[r-1][j][k]+num_1[i])。*/
乾明 边策 发自 凹非寺量子位 报道 | 公众号 QbitAI华为方舟编译器终于正式开源,源代码放出,兑现了在8月开源的承诺。代码不在GitHub,而是在自家开源平台上...
hdu 5724 chess
1.1 CodeSmith一款人气很旺国外的基于模板的dotnet代码生成器官方网站:http://www.codesmithtools.com官方论坛:http://forum.codesmithtools.com/default.aspx版权形式:30天试用开源:否需要先注册确认后才能下载1.2 MyGeneratorMyGenerator是又一个国外...
环境; 阿里云ElasticSearch6.7.0需求: 利用updateByQuery同时修改一条记录的多个字段POST /ads_lading_trade_brief_es/_update_by_query?conflicts=proceed&wait_for_completion=false{ "script": { "source": "ctx._source.exporter_province='浙江省';ctx._source.exporter_city='绍兴市'",
算起来,我已经一个人生活了很久。一个人生活,难免连周边的空气里都有种颓靡感。一个人逛街,在姹紫嫣红里盯着广告牌发呆,扑面而来的人群几欲将你的寂寞打翻;一个人看电影,无人讨论便默默回味着剧情,影子被路灯拉的笔长;一个人吃饭,都不好意思进入那些嘈杂的饭馆去,坐在圆桌边,慢悠悠的品上几菜几汤。……一个人的寂寞,更多是来自于快乐时无人分享,难过时无人倾诉。一个人常常要去担心很多问题,比如钥匙有没有带
1、遗传算法介绍遗传算法是类借鉴生物界的进化规律(适者生存,优胜劣汰遗传机制)演化而来的随机化搜索方法,其主要特点是直接对结构对象进行操作,不存在求导和函数连续性的限定,具有内在的隐并行性和更好的全局寻优能力,采用概率化的寻优方法,能自动获取和指导优化的搜索空间,自适应地调整搜索方向,不需要确定的规则。以这篇博文代码篇为例(点击打开)本文采用代码与原理相结合的办法给大家讲解遗传算法,有助于大家更好的理解遗传算法。在遗传算法中包括几个重要的步骤,分别为 个体、种群、适应度、选择、交叉、变异,下边为大家
前言本文将介绍如何通过使用EasyExcel自定义拦截器实现在最终的Excel文件中新增一列自增的序号列,最终的效果如下:此外,本文所使用的完整代码示例已上传到GitHub。实现本文主要是通过自定义一个继承AbstractRowWriteHandler的拦截器来实现在最终导出的结果中新增序号列,通过修改源码中保存头部标题的Map内容来给自己添加的序号列留出位置,先展示最终的代码:/** * 自定义 excel 行处理器, 增加序号列 * * @author butterfly * @da