SparkSQL Java UDF开发报错求助:No handler for Hive UDF
先看你遇到的错误:Error in query: No handler for Hive UDF 'com.sqyc.datedistance.DateDistance'; line 1 pos 7,和你的代码对比就能发现——你代码里的UDF类包名是com.test.datedistance.DateDistance,但注册临时函数的时候写的是com.sqyc.datedistance.DateDistance,包名完全写错了!这是最直接导致报错的原因,先把这个改过来试试。
除了这个明显的错误,我还整理了几个潜在问题和优化点,帮你彻底解决问题:
1. 修正注册函数的包名
执行create temporary function时,确保类的全限定名和代码里的完全一致:
create temporary function tmp_date_distance as 'com.test.datedistance.DateDistance'
2. 确认JAR包加载状态
- 执行
add jar命令后,用list jars命令检查JAR是否已经被正确加载到Spark的classpath中 - 若使用本地路径,要确保集群所有节点都能访问该JAR;若用HDFS路径,记得加上
hdfs://前缀,也可以在提交Spark作业时通过--jars参数直接指定JAR路径
3. 修复日期格式的隐藏bug
你代码里的SimpleDateFormat格式写的是yyyy-mm-ss HH:mm:ss.SSS,这里的mm代表分钟,不是月份!月份必须用大写的MM,否则解析2017-12-26时会把12当成分钟,导致日期解析错误。正确格式应该是:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
4. 解决SimpleDateFormat的线程安全问题
SimpleDateFormat不是线程安全的,而Spark UDF会被多线程并发调用,直接在call方法里创建实例虽然能运行,但可能出现奇怪的日期解析错误。建议用ThreadLocal封装:
private static final ThreadLocal<SimpleDateFormat> SDF = ThreadLocal.withInitial( () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") ); public Double call(String s, String s2) throws Exception { Double result=0D; if(StringUtils.isNotBlank(s)&&StringUtils.isNotBlank(s2)){ Date parse = SDF.get().parse(s); Date parse2= SDF.get().parse(s2); Long milisecond1= parse.getTime(); Long milisecond2= parse2.getTime(); Long abs = Math.abs(milisecond1 - milisecond2); result = (abs.doubleValue()) / 1000D; } return result; }
如果你的环境是Java 8及以上,更推荐用线程安全的DateTimeFormatter替代:
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); public Double call(String s, String s2) throws Exception { Double result=0D; if(StringUtils.isNotBlank(s)&&StringUtils.isNotBlank(s2)){ LocalDateTime dt1 = LocalDateTime.parse(s, FORMATTER); LocalDateTime dt2 = LocalDateTime.parse(s2, FORMATTER); long millisDiff = Math.abs(java.time.Duration.between(dt1, dt2).toMillis()); result = millisDiff / 1000.0; } return result; }
5. 优化Maven依赖配置
你的Spark依赖不需要打包进JAR(集群环境已经自带),可以将其设置为provided scope,避免版本冲突同时减小JAR体积:
<dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.11</artifactId> <version>2.1.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.11</artifactId> <version>2.1.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-hive_2.11</artifactId> <version>2.1.2</version> <scope>provided</scope> </dependency> </dependencies>
先修正包名的问题,应该就能解决当前的报错了,其他优化点可以逐步调整,避免后续出现隐藏问题。
内容的提问来源于stack exchange,提问作者lixy

