Redis、Es内网网络映射问题排查及解决

这篇具有很好参考价值的文章主要介绍了Redis、Es内网网络映射问题排查及解决。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Redis、Es内网网络映射问题排查及解决

背景

我们在客户环境上安装了SpringCloud应用,需要使用中间件Redis集群、ES集群,集群不可以自建、只能使用客户提供的集群(下面IP地址均为假IP)

Redis外网集群信息210.0.0.1:6379、210.0.0.1:6380、210.0.0.1:6381、210.0.0.1:6382、210.0.0.1:6383、210.0.0.1:6384

Redis内网集群信息:190.0.0.1:6379、190.0.0.1:6380、190.0.0.1:6381、190.0.0.1:6382、190.0.0.1:6383、190.0.0.1:6384

ES集群信息:210.0.1.1:9201

Nginx信息:210.0.1.2:9200

网络环境示意

Redis、Es内网网络映射问题排查及解决,源码学习,现场问题排查经历,中间件安装和运行,redis,elasticsearch,网络,nginx

ES网络问题:

错误信息:

[DataAsset] - 16:01:22.281 [es_rest_client_sniffer[T#1]] ERROR o.e.c.sniff.Sniffer - [run,141] - error while sniffing nodes 
java.net.ConnectException: Connection refused 
	at org.elasticsearch.client.RestClient.extractAndWrapCause(RestClient.java:918) 
	at org.elasticsearch.client.RestClient.performRequest(RestClient.java:299) 
	at org.elasticsearch.client.RestClient.performRequest(RestClient.java:287) 
	at org.elasticsearch.client.sniff.ElasticsearchNodesSniffer.sniff(ElasticsearchNodesSniffer.java:105) 
	at org.elasticsearch.client.sniff.Sniffer.sniff(Sniffer.java:209) 
	at org.elasticsearch.client.sniff.Sniffer$Task.run(Sniffer.java:139) 
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 
	at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) 
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) 
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 
	at java.lang.Thread.run(Thread.java:748) 
Caused by: java.net.ConnectException: Connection refused 
	***************

排查过程:

现在服务器上调用了ES接口

# 获取索引的名称、文档数、存储大小等详细信息
http://210.0.1.1:9200/_cat/indices?v 
# 集群的名称、状态、节点数量、分片数量、未分配分片数量等信息
http://210.0.1.1:9200/_cat/health?v 
# 这个接口用来获取 Elasticsearch 中所有节点的详细信息
http://210.0.1.1:9200/_cat/nodes?v

这些接口都可以正常返回集群及相关信息。但是返回的内容和预期稍微有点不同。返回的集群节点相关信息都是127.0.0.1,和预期相差有点区别

浏览器可以和http://210.0.1.1:9200这个地址产生正确的交互,但是后台确一直报错,这让我们十分的不解。我们对源码进行了详细的排查。

发现异常栈下面这段代码

at org.elasticsearch.client.RestClient.performRequest(RestClient.java:299) 

发现它对异常进行了try catch并且对异常进行了打印。

RequestLogger.logFailedRequest(logger, request.httpRequest, context.node, e);

随后我们开启了logback的debug级调试。

logging.level.root=debug

随后我们获取到了全新日志。

[DataAsset] - 17:26:42.666 [es_rest_client_sniffer[T#1]] DEBUG o.a.h.i.n.c.PoolingNHttpClientConnectionManager - [requestConnection,279] - Connection request: [route: {}->http://127.0.0.1:9201][total kept alive: 1; route allocated: 0 of 10; total allocated: 1 of 30] 
[DataAsset] - 17:26:42.669 [pool-4-thread-1] DEBUG o.a.h.i.n.c.PoolingNHttpClientConnectionManager - [failed,316] - Connection request failed 
java.net.ConnectException: Connection refused 
	at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) 
	at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:716) 
	at org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processEvent(DefaultConnectingIOReactor.java:174) 
	at org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processEvents(DefaultConnectingIOReactor.java:148) 
	at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor.execute(AbstractMultiworkerIOReactor.java:351) 
	at org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.execute(PoolingNHttpClientConnectionManager.java:221) 
	at org.apache.http.impl.nio.client.CloseableHttpAsyncClientBase$1.run(CloseableHttpAsyncClientBase.java:64) 
	at java.lang.Thread.run(Thread.java:748)

在本地调试过程种发现上层是由于使用了ESSniffer,导致我们的ES失效了。

Sniffer sniffer = Sniffer.builder(restClient).setSniffIntervalMillis(30000).build();

为什么日志中报错连接地址为 http://127.0.0.1:9201 ,这个地址哪里来的,为什么会连接9201端口?我们只是显示的配置了210.0.1.2:9200,完全不应该出现这样的情况才对,然后根据堆栈信息我们向上排查,在ElasticsearchNodesSniffer这个类中发现了关键信息。

this.request = new Request("GET", "/_nodes/http");
request.addParameter("timeout", sniffRequestTimeoutMillis + "ms");

发现在源码中使用了下面这个接口

#  Elasticsearch 集群中所有节点的 HTTP(S) 端口信息。它提供了每个节点的IP地址、HTTP(S)监听的端口号、协议、主机名等信息
http://210.0.1.1:9200/_nodes/http

在响应体中确实发现了返回127.0.0.1:9201这个内网IP。思路从找到127.0.0.1:9201转向了如何修改这个地址。

在搜索es sniffer过程中,查到了相关资料在

https://www.elastic.co/cn/blog/elasticsearch-sniffing-best-practices-what-when-why-how

在官网的以下节点可以得出答案!

什么场景适合使用监听功能?

  • 如果您的 Elasticsearch 集群位于负载均衡器后面会怎样?

此处为对http节点的配置的相关解释

https://www.elastic.co/guide/en/elasticsearch/reference/7.8/modules-http.html#_http_settings

下方为ES网络配置的相关说明

[]: https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-network.html “ES官网”

解决方案:

1.解决方案http.publish_porthttp.publish_host,将前方两个配置修改为210.0.1.2:9200(实际的端口地址)后,问题解决!

2.在官网:https://discuss.elastic.co/t/error-while-sniffing-nodes/291186/2

中有类似的人提问,这位同学禁用了ElasticsearchRestClientAutoConfiguration这个类达到了相似的效果,由于我们没有使用这个自动装配,所以暂时无法测试。

Redis网络问题:

错误信息:

日志不便展示。

我们的日志信息是这样的,我们的Redis集群有6个节点、标准的3主3从结构。210.0.0.1:6379、210.0.0.1:6380、210.0.0.1:6381、210.0.0.1:6382、210.0.0.1:6383、210.0.0.1:6384

我们在配置文件中配置为:

spring:
  redis:
    cluster:
      max-redirects: 5
      nodes: 210.0.0.1:6379、210.0.0.1:6380、210.0.0.1:6381、210.0.0.1:6382、210.0.0.1:6383、210.0.0.1:6384
    password: 123456

很奇怪的是我们在业务中连接redis的过程中,前台报错190.0.0.1:6384,有的时候连的上,有的时候连不上,这个节点redis连不上,奇怪的是,这个IP是我们第六个redis,而且我们没有任何地方配置了它们的内网地址,迷惑????????????

在redis-cli上使用cluster info的时候反馈了很多地址,都是190.0.0.1:6379、190.0.0.1:6380、190.0.0.1:6381、190.0.0.1:6382、190.0.0.1:6383、190.0.0.1:6384

排查过程:

我们底层使用了LettuceConnectionFactorySpring默认的Redis集群工具初始化的Redis连接,它的底层是由 Lettuce 通过 接口ClusterTopologyRefresh,具体的实现类是:DefaultClusterTopologyRefresh定期检查集群的拓扑,确保在集群节点发生变化时更新拓扑信息。它会周期性地向任意一个节点发送 CLUSTER NODES 命令并解析响应,将新的拓扑信息存储在 io.lettuce.core.cluster.models.partitions.Partitions 对象中。
下方代码个人增加了部分中文注释(如果有错误,欢迎指出)

        if (!isEventLoopActive()) {
            return CompletableFuture.completedFuture(Collections.emptyMap());
        }

        long commandTimeoutNs = getCommandTimeoutNs(seed);
        ConnectionTracker tracker = new ConnectionTracker();
        long connectionTimeout = commandTimeoutNs + connectTimeout.toNanos();
        openConnections(tracker, seed, connectionTimeout, TimeUnit.NANOSECONDS);
		
        CompletableFuture<NodeTopologyViews> composition = tracker.whenComplete(map -> {
            return new Connections(clientResources, map);
        }).thenCompose(connections -> {
            // 创建异步请求,向Redis Server发出请求,或者Cluster info以及Cluster nodes
            Requests requestedTopology = connections.requestTopology(commandTimeoutNs, TimeUnit.NANOSECONDS);
            Requests requestedInfo = connections.requestInfo(commandTimeoutNs, TimeUnit.NANOSECONDS);
            // Requests对象封装了Map<RedisURI, TimedAsyncCommand<String, String, String>>的一个成员变量,
            // 下面这个位置是说等待这两个request对象请求完成后,再进行调用
            return CompletableFuture.allOf(requestedTopology.allCompleted(), requestedInfo.allCompleted())
           	// 在getNodeSpecificViews 方法中从异步请求中获取了【集群信息】和【集群节点的信息】
                    .thenApplyAsync(ignore -> getNodeSpecificViews(requestedTopology, requestedInfo),
                            clientResources.eventExecutorGroup())
                    .thenCompose(views -> {
                        if (discovery && isEventLoopActive()) {

                            Set<RedisURI> allKnownUris = views.getClusterNodes();
                            Set<RedisURI> discoveredNodes = difference(allKnownUris, toSet(seed));

                            if (discoveredNodes.isEmpty()) {
                                return CompletableFuture.completedFuture(views);
                            }

                            openConnections(tracker, discoveredNodes, connectionTimeout, TimeUnit.NANOSECONDS);

                            return tracker.whenComplete(map -> {
                                return new Connections(clientResources, map).retainAll(discoveredNodes);
                            }).thenCompose(newConnections -> {

                                Requests additionalTopology = newConnections
                                        .requestTopology(commandTimeoutNs, TimeUnit.NANOSECONDS).mergeWith(requestedTopology);
                                Requests additionalInfo = newConnections.requestInfo(commandTimeoutNs, TimeUnit.NANOSECONDS)
                                        .mergeWith(requestedInfo);
                                return CompletableFuture
                                        .allOf(additionalTopology.allCompleted(), additionalInfo.allCompleted())
                                        .thenApplyAsync(ignore2 -> getNodeSpecificViews(additionalTopology, additionalInfo),
                                                clientResources.eventExecutorGroup());
                            });
                        }

                        return CompletableFuture.completedFuture(views);
                    }).whenComplete((ignore, throwable) -> {

                        if (throwable != null) {
                            try {
                                tracker.close();
                            } catch (Exception e) {
                                logger.debug("Cannot close ClusterTopologyRefresh connections", e);
                            }
                        }
                    }).thenCompose((it) -> tracker.close().thenApply(ignore -> it)).thenCompose(it -> {

                        if (it.isEmpty()) {
                            Exception exception = tryFail(requestedTopology, tracker, seed);
                            return Futures.failed(exception);
                        }

                        return CompletableFuture.completedFuture(it);
                    });
        });

        return composition.thenApply(NodeTopologyViews::toMap);

RedisURI对象中的Value属性中发现了TimedAsyncCommand对象中的result属性中包含了集群信息,集群信息中可以发现都是内网的IP

解决方案:

找了很多大佬,提供了大概4种解决方案。

1.在使用Redis地址的时候,修改源码,将Redis使用的地址映射成新的外网地址

2.使用MappingSocketAddressResolver 这个类,用于网络映射的解析

https://github.com/lettuce-io/lettuce-core/discussions/1872

3.也有大佬说使用下面这个方法可以改善这个问题

 public JedisConnectionFactory jedisConnectionFactory() {
        RedisClusterConfiguration config = new RedisClusterConfiguration(Arrays.asList(redisNodes));
        config.setMaxRedirects(maxRedirects);
        return new JedisConnectionFactory(config);
    }

4.解决方案Redis-server的配置,在其配置文件种加入下面这个配置,可以让它反馈的节点地址变成下面这个

cluster-announce-ip 210.0.1.2:9200
cluster-announce-port 9200

5.使用iptables组件
在最后补一个最新的解决方案
参考自:
https://cloud.tencent.com/developer/article/2348224文章来源地址https://www.toymoban.com/news/detail-791541.html

sudo iptables -t nat -A OUTPUT -d 172.17.0.2 -p tcp --dport 8001 -j DNAT --to-destination 10.8.46.40:8001
sudo iptables -t nat -A OUTPUT -d 172.17.0.3 -p tcp --dport 8002 -j DNAT --to-destination 10.8.46.40:8002
sudo iptables -t nat -A OUTPUT -d 172.17.0.4 -p tcp --dport 8003 -j DNAT --to-destination 10.8.46.40:8003
$ sudo iptables -t nat -nvL --line-number
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1       95  6219 DOCKER     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1        0     0 DOCKER     all  --  *      *       0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL
2        0     0 DNAT       tcp  --  *      *       0.0.0.0/0            172.17.0.2           tcp dpt:8001 to:10.8.46.40:8001
3        0     0 DNAT       tcp  --  *      *       0.0.0.0/0            172.17.0.3           tcp dpt:8002 to:10.8.46.40:8002
4        0     0 DNAT       tcp  --  *      *       0.0.0.0/0            172.17.0.4           tcp dpt:8003 to:10.8.46.40:8003

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination         
1        6   360 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0           

Chain DOCKER (2 references)
num   pkts bytes target     prot opt in     out     source               destination         
1        0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0 

到了这里,关于Redis、Es内网网络映射问题排查及解决的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【问题排查篇】一次业务问题对 ES 的 cardinality 原理探究 | 京东云技术团队

    作者:京东科技 王长春 小编工作中负责业务的一个服务端系统,使用了 Elasticsearch 服务做数据存储,业务运营人员反馈,用户在使用该产品时发现, 用户后台统计的订单笔数和导出的订单笔数不一致 ! 交易订单笔数不对,出现差错订单了?这一听极为震撼!出现这样的问

    2024年02月05日
    浏览(39)
  • Redis Cluster 集群故障排查与解决方案

    Redis是一个开源的内存数据结构存储系统,可以用作数据库、缓存和消息中间件。它支持多种数据结构(如字符串、哈希表、列表、集合等)和功能(如事务、分布式锁、Lua脚本等),能够满足不同场景的需求。 Redis Cluster是Redis提供的分布式解决方案,主要由以下几个组成部

    2024年02月14日
    浏览(33)
  • 【Elasticsearch学习笔记二】es的Mapping字段映射、Mapping字段常用类型、Mapping映射的创建、查看和更新、es数据迁移、ik分词器

    目录 1、Mapping字段映射概述 2、Mapping字段常用类型 3、映射中对时间类型详解 1)采取自动映射器来映射 2)手工映射提前指定日期类型 4、ES的keyword的属性ignore_above 5、Mapping映射的查看和创建 1)查看mapping信息:GET 索引名/_mapping 2)创建映射:PUT /索引名 3) 查看所有索引映

    2024年01月20日
    浏览(60)
  • OpenHarmony 4.0 源码编译hb 问题排查记录

    OS:Ubuntu 22.04 x86_64 下载好Openharmony 4.0Beta2 的源码 从错信息看是找到某个目录,hb 是python写的,所以打算看看源码是找个目录出错了,根据出错信息直接看源码文件。 查看python 代码可知报错原因是没找到 build/lite/hb_internal ,在OpenHamony 源码下确实没有发现有 build/lite/hb_internal

    2024年02月09日
    浏览(42)
  • 执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折)

    这个问题坑惨我了,估计耗费了我两个小时😫,中间走了不少弯路,好在我灵光一闪+GPT给我的灵感否则就栽在这上面了 问题背景 在使用 Redis 实现接口调用次数扣减操作时,发现响应结果一直返回的是 -1,也就是查询不到数据 问题排查过程1 : 经过一段排查,加上GPT的提示

    2024年02月13日
    浏览(48)
  • 【远程调用返回400问题排查(已解决)】

    我最近给公司一个两年前开发的项目售后,帮助客户验视功能点,顺便帮助解决项目中的问题,由于原负责该项目的项目组已经全都离职了,导致验收之路漫长且艰苦… 在解决问题的过程中碰到了许多疑难杂症都一一解决了,唯独其中有一个问题让我和同事绞尽脑汁花了三天

    2024年02月02日
    浏览(47)
  • CPU 飙高问题排查和解决方法

    摘要 本文档记录了排查 CPU 飙高问题的处理过程和解决方法,从多个方面进行分析和排查。 问题简述 在一个生产环境中发现 CPU 飙高问题,但是无法确定问题的具体原因。 排查方法 使用 jstack 导出 JAVA 进程的线程栈信息,并分析线程栈信息,看能否定位到耗费 CPU 的线程。

    2024年02月07日
    浏览(49)
  • 频繁GC引起卡顿问题排查与解决

    今天测试组更新测试环境后发现系统卡顿,无法办理任何业务,重启系统后问题仍然存在。已经到项目后期,迭代测试时间十分紧张。此问题直接影响到项目进度 1.查看进程资源占用情况 Linux 下常用top命令显示系统中各个进程的资源占用状况,查看资源发现PID26799进程信息中

    2023年04月23日
    浏览(31)
  • 【工作中问题解决实践 九】Spring中事务传播的问题排查

    最近在工作中遇到了三个关于事务操作的问题,顺便就着这三个问题又回顾了一遍Spring的事务相关的操作,想着一次性把这个问题研究明白了,后续使用事务的时候也能踏实点,让事务发挥真实的作用 什么是事务? 事务就是把一系列的动作当成一个独立的工作单元,这些动

    2024年02月14日
    浏览(38)
  • K8s出现问题时,如何排查解决!

    K8s问题的排查 1、POD启动异常、部分节点无法启动pod 容器里管理应用 pod出现异常的原因: 2. 审视集群状态 审视集群状态 K8S的集群状态是排查故障的关键起点。使用kubectl get nodes命令来检查节点状态。如果有节点未能就绪或出现异常状态,可能会对应用程序造成故障。确保基

    2024年02月02日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包