在开发完kylin三大框架的扩展代码后,将jar上传放置在kylin中时遇到了一些类加载相关的问题。
1、kylin报错NoSuchMethodException信息,即找不到自定义扩展的数据源。
2021-08-29 18:08:20,811 ERROR [http-bio-7070-exec-10] source.SourceManager:118 : Failed to create source: SourceType=0
java.lang.NoSuchMethodException: com.wyt.source.KylinSource.<init>(org.apache.kylin.common.KylinConfig)
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at org.apache.kylin.source.SourceManager.createSource(SourceManager.java:115)
at org.apache.kylin.source.SourceManager.getCachedSource(SourceManager.java:84)
at org.apache.kylin.source.SourceManager.getSource(SourceManager.java:126)
at org.apache.kylin.rest.service.JobService.submitJobInternal(JobService.java:253)
at org.apache.kylin.rest.service.JobService.submitJob(JobService.java:226)
at org.apache.kylin.rest.controller.CubeController.buildInternal(CubeController.java:402)
at org.apache.kylin.rest.controller.CubeController.rebuild(CubeController.java:362)
追踪源码,定位到如下代码:
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
int which) throws NoSuchMethodException
{
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}
我把开发好的jar放置在kylin根目录下的ext文件夹里面。debug情况如下图:
能进入该方法中,说明类肯定已经存在,此时在比较存在类中的构造器参数类型和已有的参数类型是否匹配,如果匹配就正常返回,如果不匹配就抛出上面看到的异常。
参数类型都是KylinConfig,包名类型都一样,但是结果却是两个参数类型不匹配。这说明他们的类加载肯定不同,即运行时包不同(不清楚的可以上网查查运行时包、命令空间一类的定义)。parameterTypes类加载情况如下(它主要加载的是/WEB/lib目录下的包):
constructor.getParameterTypes的类加载如下:
对比可以发现源码中KylinConfig与开发包中KylinConfig对应的类加载器不同,而源码默认的类加载器加载路径是 {kylin-home}/tomcat/webapps/kylin/WEB-INF/lib/。
所以将包重放置到lib目录后解决了上述问题(因为类加载是服务启动时做的事情,所以在移动包之后要重启下kylin才能生效)。
2、报错信息
Error: java.lang.IllegalArgumentException: Implementations missing, ID 2, interface org.apache.kylin.engine.IBatchCubingEngine at org.apache.kylin.common.util.ImplementationSwitch.get(ImplementationSwitch.java:77) at org.apache.kylin.engine.EngineFactory.batchEngine(EngineFactory.java:42) at org.apache.kylin.engine.EngineFactory.getJoinedFlatTableDesc(EngineFactory.java:52) at org.apache.kylin.engine.mr.MRUtil.getBatchCubingInputSide(MRUtil.java:42) at org.apache.kylin.engine.mr.steps.FactDistinctColumnsMapperBase.doSetup(FactDistinctColumnsMapperBase.java:77) at org.apache.kylin.engine.mr.steps.FactDistinctColumnsMapper.doSetup(FactDistinctColumnsMapper.java:83) at org.apache.kylin.engine.mr.KylinMapper.setup(KylinMapper.java:48) at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:143) 首先定位到源码位置如下: 这个方法会抛出两种异常,异常内容比较相似,所以具体分析时一定要定位好是哪出问题。本报错中出问题的地方在末尾,说明获取到了要加载的类名称,即kylin加载了我们的扩展包,但是实例化失败了。没什么头绪,于是我在构建的时候debug源码追踪了一下,截图如下: 可以看到kylin源码中显示这一块没问题,是可以正常实例化的。这一下给我整懵了,于是我再次回头仔细看了下日志,发现这个报错来自于mapreduce,详情如下: 所以推测可能是mapreduce找不到我们的扩展包,查找资料发现kylin运行mapreduce任务默认会从本地加载需要的依赖包,而且在kylin.log中也有提示: 2021-08-29 20:35:37,542 INFO [Scheduler 729395435 Job a1147e90-2f08-b987-8a50-60151a8b72ef-92] common.AbstractHadoopJob:227 : Didn't find mapreduce.application.classpath in job configuration, will run 'mapred classpath' to get the default value。 即不主动指定mapreduce加载路径的情况下,会去取本地的默认值(这行日志后一般会打印出从本地加载的jar信息),于是我把扩展包又放了一份在mapreduce的目录下:{hadoop-home}/share/hadoop/mapreduce/lib (直接放mapreduce会报错)。
再次启动,项目运行正常(每次mapreduce任务都会加载,所以不需要重启kylin或者hadoop)
3、小结
上面两个问题是我在3.1.0版本遇到的问题,而在3.1.0版本如果想自定义扩展kylin功能,在开发完打包上传时至少要传到两个目录下才行。
如果想了解具体如何扩展kylin的数据源、计算引擎、存储源三大框架,可以参考我的这篇文章:kylin扩展数据源、计算引擎、存储框架源码_java实现 kylin 不同的数据源_Interest1_wyt的博客-CSDN博客