解决Doris单机部署中Spark写入报错:Connection refused问题全记录
问题背景
最近按照Apache Doris官方文档(https://doris.apache.org/docs/3.0/gettingStarted/quick-start)部署了Doris单机版本。部署完成后,通过MySQL客户端进行新建表、插入数据等基本操作都正常。但当尝试添加Spark任务将HDFS上的Iceberg表数据聚合后写入Doris时,却遇到了一个棘手的问题:
Connect to 127.0.0.1:8041 [/127.0.0.1] failed: Connection refused
at org.apache.doris.shaded.org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:156)
at org.apache.doris.shaded.org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:376)
at org.apache.doris.shaded.org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
本文将详细记录问题的分析过程和最终解决方案。
问题分析
1. 初步排查
首先,确认了Spark任务中已经正确配置了Doris的连接信息:
.config("doris.fenodes", "192.168.104.8:8038")
.config("doris.backend.nodes", "192.168.104.8:8041")
在服务器上测试端口连通性也正常:
telnet 192.168.104.8 8038 # FE端口 - 成功
telnet 192.168.104.8 8041 # BE端口 - 成功
2. 关键发现:BE地址解析问题
通过分析Spark错误日志以及咨询相关社区,我发现了问题的核心:
Doris的FE在数据导入时,会返回BE的地址给客户端。在我的环境中,FE返回的是127.0.0.1:8041,而不是我期望的192.168.104.8:8041。
这导致Spark任务尝试连接本地的8041端口(127.0.0.1:8041)而不是实际的服务器地址。
3. 错误尝试:配置priority_networks
根据社区建议,我尝试修改BE和FE的priority_networks配置:
# be.conf
priority_networks=192.168.104.0/24
# fe.conf
priority_networks=192.168.104.0/24
重启服务后,BE日志出现了奇怪的现象:
W20250528 14:18:12.481160 1766514 heartbeat_server.cpp:182] update localhost done, the new localhost is 192.168.104.8
W20250528 14:18:12.481467 1766053 heartbeat_server.cpp:182] update localhost done, the new localhost is 127.0.0.1
BE似乎注册了两个地址:127.0.0.1和192.168.104.8。这导致FE控制台操作也出现问题:
Execution failed: Error Failed to execute sql: java.sql.SQLException: (conn=15) errCode = 2, detailMessage = Failed to create partition[user].Timeout:30 seconds. Unfinished: (backendId = 1748402251461, tabletId = 1748402251560), ...
手动移除127.0.0.1的BE节点后,FE控制台操作恢复正常,但Spark任务依然报错连接不上127.0.0.1:8041。
根本原因
经过深入分析,发现问题核心在于Doris单机部署时的网络配置:
- Doris在启动时默认会绑定到127.0.0.1和实际IP
- 当配置priority_networks=192.168.104.0/24时,Doris会识别网段内的所有IP
- 导致BE节点注册了多个地址(127.0.0.1和192.168.104.8)
- FE在返回BE地址时,有时会返回127.0.0.1而不是实际IP
最终解决方案
1. 精确指定IP地址
修改BE和FE的配置文件,精确指定服务器的实际IP:
# be.conf
priority_networks = 192.168.104.8/32 # 注意是/32而不是/24
# fe.conf
priority_networks = 192.168.104.8/32
2. 清理元数据并重启 (生产慎用)
# 停止服务
$DORIS_HOME/fe/bin/stop_fe.sh
$DORIS_HOME/be/bin/stop_be.sh
# 清理元数据(重要!)
rm -rf $DORIS_HOME/fe/doris-meta/*
rm -rf $DORIS_HOME/be/storage/*
# 启动服务(指定精确IP)
$DORIS_HOME/fe/bin/start_fe.sh --daemon --host_type=IP --priority_networks=192.168.104.8/32
$DORIS_HOME/be/bin/start_be.sh --daemon --host_type=IP --priority_networks=192.168.104.8/32
3. 验证节点状态
在MySQL客户端执行:
SHOW BACKENDS;
SHOW FRONTENDS;
正确输出应该只有192.168.104.8的节点:
+-------------------+-------+--------+---------+-----+
| Host | Port | Alive | Role | ... |
+-------------------+-------+--------+---------+-----+
| 192.168.104.8 | 9061 | true | BACKEND | ... |
+-------------------+-------+--------+---------+-----+
4. 调整Spark配置
// 确保使用精确IP
.config("doris.fenodes", "192.168.104.8:8038")
.config("doris.backend.nodes", "192.168.104.8:8041")
// 添加超时和重试配置
.config("spark.doris.request.timeout", "60000")
.config("spark.doris.request.retries", "5")
为什么这个方案有效?
- /32子网掩码:指定只绑定到精确的192.168.104.8 IP,而不是整个网段
- 清理元数据:移除之前错误注册的127.0.0.1节点信息
- 启动参数指定host_type:强制Doris使用IP模式而非混合模式
效果验证
完成上述步骤后:
- Doris控制台操作(建表、插入、查询)全部正常
- Spark任务成功执行,数据正确写入Doris表
- BE日志中不再出现127.0.0.1相关的记录
经验总结
- 精确IP配置:在单机部署中,priority_networks应使用/32指定精确IP
- 清理元数据:修改网络配置后务必清理元数据,避免旧配置残留
- 日志分析:BE的heartbeat_server日志是诊断网络问题的关键
- 版本注意:Doris 3.0版本对网络配置更加敏感,需要精确设置
附录:最终配置文件
be.conf
# 网络配置
priority_networks = 192.168.104.8/32
be_port = 9061
webserver_port = 8041
# 性能优化
brpc_num_threads = 8
storage_page_cache_limit=40%
max_compaction_threads=2
fe.conf
# 网络配置
priority_networks = 192.168.104.8/32
http_port = 8038
query_port = 9030
# 超时设置
tablet_create_timeout_second=300
max_create_table_timeout_second=600
good day!!!