周末凌晨三点,睡梦中被手机震醒,我迷迷糊糊睁开眼,看着告警群疯狂飚异常告警,公司组里几乎所有基于Spark的离线任务全部失败,重试三次后依旧报错。揉了揉太阳穴,我知道今晚是没的睡了。
公司图有水印,找了张网图,差不多就是这个效果
噩梦开始
组里我们团队负责搭建一套基于 Spark 和 Cassandra 的数据处理系统,用于分析用户行为数据。系统稳定运行了几个月,就在大家以为一切都步入正轨时,问题毫无征兆地出现了。
夜里,原本应该在凌晨两点就完成的 Spark 任务,不仅没有按时结束,反而接二连三地抛出异常。打开日志一看,满屏都是让人摸不着头脑的报错信息:
Caused by: com.datastax.driver.core.exceptions.NoHostAvailableException:
All host(s) tried for query failed (tried: 10.0.0.5 (com.datastax.driver.core.exceptions.TransportException: [/10.0.0.5:9042] Cannot connect))
从报错信息来看,是 Spark 无法连接到 Cassandra 集群。但奇怪的是,集群状态监控显示一切正常,其他依赖 Cassandra 的服务也运行得好好的,为什么偏偏 Spark 任务集体 “罢工”?
迷雾重重
起初,我们怀疑是网络问题。检查了防火墙配置、服务器网络连接,甚至联系了运维团队确认网络设备状态,结果一无所获。又怀疑是 Cassandra 集群负载过高,但通过nodetool命令查看集群状态,各项指标都在正常范围内。
接着,我们开始检查 Spark 任务的配置和代码。代码已经稳定运行了很长时间,最近也没有改动,似乎可以排除代码问题。难道是配置参数出了问题?我们仔细核对了每一个与 Cassandra 连接相关的配置,spark.cassandra.connection.host、spark.cassandra.auth.username 等参数都正确无误。
直到翻到spark.cassandra.connection.local_dc
这个参数时,我心里 “咯噔” 一下。这个参数用于指定本地数据中心(Data Center,简称 DC)名称,以便 Spark 和 Cassandra 进行更高效的通信,而我们的代码里,它被 写死成了 prod。
scala
spark.conf.set("spark.cassandra.connection.local_dc", "prod")
真相大白:写死参数引发的 “血案”
经过进一步排查,我们终于弄清楚了问题的根源。原来,公司近期对 Cassandra 集群进行了一次架构调整,将部分数据迁移到了新的数据中心,而新数据中心的名称并非prod。
spark.cassandra.connection.local_dc
这个参数如果设置不当,Spark 就无法正确地与 Cassandra 集群进行通信,导致任务失败。在我们的场景中,写死的 prod 与实际的数据中心名称不匹配,使得 Spark 无法找到合适的 Cassandra 节点,从而抛出 NoHostAvailableException 异常。
而且,后来再翻看dag日志,更明确了这点:
ERROR Client: Application diagnostics message: User class threw exception: com.datastax.spark.connector.util.ConfigCheck$ConnectorConfigurationException: Could not determine suitable nodes in local DC for known nodes
更要命的是,这个参数在任务正常运行时,即使设置错误也不会立刻暴露问题。只有当集群架构发生变化,或者某些特定的网络环境下,错误才会显现出来,这也是为什么问题潜伏了这么久才爆发。
绝地反击:解决问题与反思
找到了问题所在,解决起来就相对简单了。我们将spark.cassandra.connection.local_dc
的值修改为新数据中心的实际名称,并将配置改为从配置文件中读取,而不是写死在代码里,避免后续再次出现类似问题。
// 从配置文件读取数据中心名称
val localDc = config.getString("cassandra.local_dc")
spark.conf.set("spark.cassandra.connection.local_dc", localDc)
修改完配置后,我们重新提交任务,忐忑不安地盯着日志。终于,报错信息消失了,任务开始正常运行,数据也顺利写入 Cassandra。长舒一口气的同时,我看了眼窗外,天已经蒙蒙亮了。
这次经历给我们团队敲响了警钟:任何配置参数都不能随意写死。即使是一个看似无关紧要的小参数,也可能在特定情况下引发严重的问题。后续我们建立了更严格的配置管理机制,所有与环境相关的参数都统一从配置中心读取,并增加了配置校验环节,确保参数的正确性和一致性。
希望我的这次 “踩坑” 经历,能给正在使用 Spark 和 Cassandra 的开发者们提个醒。在编程的世界里,细节真的能决定成败,一个小小的配置失误,就可能让你在深夜里焦头烂额。也欢迎大家在评论区分享你的奇葩 Bug 经历,咱们一起交流避坑经验!