原因及解决方法:
这是一个比较常见的问题,flink、spark,都有可能遇到类似问题。
由于两者都是分布式计算引擎,都不能在算子中传入未经序列化的数据。
所以此类问题:
原因:基本上都是因为在算子中传入了未经序列化的数据。
解决方法:就是找到那个未经序列化的数据,然后在算子前提前定义或者序列化。
项目场景:
我需要将一个util.HashMap[Integer, DataSet[util.Map[String, Object]]]
类型的数据,改造为mutable.Iterable[DataSet[(Integer, util.Map[String, Object])]]
,即将外层map中的key,放入value中的dataSet中去。
问题描述:
报错内容
org.apache.flink.api.common.InvalidProgramException: Task not serializable
Caused by: java.io.NotSerializableException: org.apache.flink.api.scala.DataSet
报错代码
value.map(d => (v._1, d))
这行代码会报错
private def sinkToMysql(connection: Connection, statement: Statement, sqlMap: util.HashMap[Integer, DataSet[util.Map[String, Object]]]): Unit = {
sqlMap.map(v => {
val value: DataSet[util.Map[String, Object]] = v._2
//下面这行代码会报错
value.map(d => (v._1, d))
})
.reduce((l, r) => l.union(r))
.collect()
.foreach(m => {
nodeId = m._1
taskResult = JsonUtil.toJSON(m).toString
insertSql = String.format("INSERT INTO `sigma`.`sigma_test_task_result`(`id`, `task_id`, `flow_id`, `node_id`, `task_result`, `time`) VALUES (null, '%s', '%s', '%s', '%s', null);", taskId, flowId, nodeId, taskResult)
addBatch(insertSql, statement)
})
//批量插入mysql
executeBatch(statement)
//关闭mysql连接
close(connection, statement, null)
}
原因分析:
从报错内容中:
Caused by: java.io.NotSerializableException: org.apache.flink.api.scala.DataSet
我们可以得出是因为在map算子中使用了org.apache.flink.api.scala.DataSet类,查看源码后我发现Flink的DataSet类是没有被序列化的,所以会抛出此异常。
我一开始以为是v._2没有被序列化,所以一直在想办法序列化v._2。后来经过朋友的提示,才明白原来是我在v._2的map算子中传入了dataSet。
map算子中只有两个参数,d和v._1,d显然不是dataSet,而v._1是由sqlMap得来的,sqlMap中包含了dataSet,传入v._1相当于传入了v,也就是dataSet,所以我应该对v._1进行修改
解决方案:
将v._1提出来,在v._2的map算子之前进行定义
代码修改为如下:
private def sinkToMysql(connection: Connection, statement: Statement, sqlMap: util.HashMap[Integer, DataSet[util.Map[String, Object]]]): Unit = {
sqlMap.map(v => {
//在这里将v._1提出来,在v._2的map算子之前进行定义
val value1: Integer = v._1
val value: DataSet[util.Map[String, Object]] = v._2
value.map(d => (value1, d))
})
.reduce((l, r) => l.union(r))
.collect()
.foreach(m => {
nodeId = m._1
taskResult = JsonUtil.toJSON(m).toString
insertSql = String.format("INSERT INTO `sigma`.`sigma_test_task_result`(`id`, `task_id`, `flow_id`, `node_id`, `task_result`, `time`) VALUES (null, '%s', '%s', '%s', '%s', null);", taskId, flowId, nodeId, taskResult)
addBatch(insertSql, statement)
})
//批量插入mysql
executeBatch(statement)
//关闭mysql连接
close(connection, statement, null)
//没有这句代码的话,不会调用env.execute(),会报错
val tailDataSet: DataSet[util.Map[String, Object]] = sqlMap.last._2
tailDataSet.output(new CustomCloseOutputFormat[util.Map[String, Object]])
}
成功解决。