##sqoop.config.dir从何而来?
看代码的时候,读到sqoopconfiguration类初始化的时候,总是觉得很疑惑这个system properties怎么凭空就冒出来了。
今天终于明白了,这个property是可以在shell里被指定的 。具体可以看sqoop.sh
JAVA_OPTS="$JAVA_OPTS -Dsqoop.config.dir=$SQOOP_CONF_DIR"
##job是如何被执行的
在JobManager.java里, 执行
public MSubmission start(String jobName, HttpEventContext ctx)
来启动任务
而这个函数里面,真正有用的是
submissionEngine.submit(jobRequest);
那么submissionEngine是哪里来的呢?
String submissionEngineClassName =
context.getString(DriverConstants.SYSCFG_SUBMISSION_ENGINE);//org.apache.sqoop.submission.mapreduce.MapreduceSubmissionEngine
submissionEngine = (SubmissionEngine) ClassUtils.instantiate(submissionEngineClassName);
所以需要阅读一下MapreduceSubmissionEngine.java的代码。
/*MapreduceSubmissionEngine.java*/
private void submitToCluster(MRJobRequest request, Job job) throws IOException, InterruptedException, ClassNotFoundException {
job.submit();
request.getJobSubmission().setExternalJobId(job.getJobID().toString());
request.getJobSubmission().setExternalLink(job.getTrackingURL());
}
在MapreduceSubmissionEngine.java里,最关键的还是submitToCluster方法。这里直接把一个job给提交了。这个job是个hadoop mapreduce类型的job,超出了sqoop的范围。需要去看看hadoop文档,对这一块是如何描述的。
看完之后发现,submit就是submit,并没有多余的花招。关键还是在于job是如何初始化的。所以还要往前翻,看看MapreduceSubmissionEngine.java里的public boolean submit(JobRequest mrJobRequest)方法。而在这个方法里,对于job的确做了一些配置,其实最关键的还是mapper class.
job.setMapperClass(request.getMapperClass());
job.setMapOutputKeyClass(request.getMapOutputKeyClass());
job.setMapOutputValueClass(request.getMapOutputValueClass());
这个job提交之后,干些什么,其实都在mapperclass里定义了。所以接下来看看mapper class即可。
但是mapper class从何而来,其实来自于jobRequest.而这个变量,其实来自于jobmanager.java.
JobRequest jobRequest = createJobRequest(mSubmission, job);
// Bootstrap job to execute in the configured execution engine
prepareJob(jobRequest);
这里的这个jobRequest,是通过createJobRequest这个函数构造的。如果我们进去看看,会发现,这个变量其实是由executionEngine构造的,虽然它在submission.start方法里。
JobRequest jobRequest = executionEngine.createJobRequest();
不过这里还是按照jobRequest类的内容去配置,尚未涉及mapper class。接下来要看,prepareJob方法。
void prepareJob(JobRequest request) {
JobConfiguration jobConfiguration = (JobConfiguration) request.getDriverConfig();
request.setExtractors(jobConfiguration.throttlingConfig.numExtractors);
request.setLoaders(jobConfiguration.throttlingConfig.numLoaders);
// Delegate rest of the job to execution engine
executionEngine.prepareJob(request);
}
然后我们又很神奇地发现,还是executionEngine在继续准备jobrequest.
而且,的确是在这里,我们找到了具体的配置命令:
mrJobRequest.setMapperClass(SqoopMapper.class);
mrJobRequest.setMapOutputKeyClass(SqoopWritable.class);
mrJobRequest.setMapOutputValueClass(NullWritable.class);
所以说,在提交任务到hadoop engine前 ,用executionEngine设置好对应的mapper class.而真正的mapper class就是sqoopmapper.java.
这个文件是对mapper的扩展,里面最关键的就是run方法,这里重写了run方法。由于这是一个数据抽取任务,所以我们可以直接看和抽取相关的命令,也就是下文:
String extractorName = conf.get(MRJobConstants.JOB_ETL_EXTRACTOR);
Extractor extractor = (Extractor) ClassUtils.instantiate(extractorName);
这里定义了一个抽取器。这个抽取器叫什么名字?我们还要回executionEngine.preparejob里去看,因为是在那里设置的。
context.setString(MRJobConstants.JOB_ETL_EXTRACTOR, from.getExtractor().getName());
这里返回的是extractor class的名字。每个connector都有完整的一套。对于oracle connector来说,就是OracleJdbcExtractor.class。
每一个extrator class都要实现extract方法,而OracleJdbcExtractor在实现时,执行到以下命令,出错。
String mapperJdbcUrl = context.getString(mapperJdbcUrlPropertyName, null);
try {
connection = OracleConnectionFactory.createOracleJdbcConnection(
OracleJdbcConnectorConstants.ORACLE_JDBC_DRIVER_CLASS,
mapperJdbcUrl,
linkConfiguration.connectionConfig.username,
linkConfiguration.connectionConfig.password);
} catch (SQLException ex) {
throw new RuntimeException(String.format(
"Unable to connect to the Oracle database at %s\nError:%s",
linkConfiguration.connectionConfig.connectionString, ex
.getMessage()), ex);
}
所以,关键任务是找到以上参数的真实值。这些参数里,最容易有问题的其实是mapperjdbcurl,其他都很简单。
在这里,有一个getString的过程,要提取某个配置信息的值。这个配置信息的key="oraoop.mapper.jdbc.url.%d"。这个肯定是事先写进去的。
通过搜索程序里包含以上字样的代码,我发现问题主要出在OracleJdbcCommonInitializer.这个文件用于对连接进行初始化,而且还会给每一个mapper准备好jdbcurl。所以要看日志,看看在执行OracleJdbcCommonInitializer时,是否正确的连接了oracle数据库。然后再看看,它是如何重新翻译connectionstring的。
很关键,这里是真正去数据库里读取配置参数。是否读对了,需要检查对应的sql。:
private static final String STMT_SELECT_CONFIG_FOR_CONFIGURATION =
"SELECT "
+ CommonRepoUtils.escapeColumnName(COLUMN_SQ_CFG_ID) + ", "
+ CommonRepoUtils.escapeColumnName(COLUMN_SQ_CFG_CONFIGURABLE) + ", "
+ CommonRepoUtils.escapeColumnName(COLUMN_SQ_CFG_NAME) + ", "
+ CommonRepoUtils.escapeColumnName(COLUMN_SQ_CFG_TYPE) + ", "
+ CommonRepoUtils.escapeColumnName(COLUMN_SQ_CFG_INDEX)
+ " FROM " + CommonRepoUtils.getTableName(SCHEMA_SQOOP, TABLE_SQ_CONFIG_NAME)
+ " WHERE " + CommonRepoUtils.escapeColumnName(COLUMN_SQ_CFG_CONFIGURABLE) + " = ? "
+ " AND " + CommonRepoUtils.escapeColumnName(COLUMN_SQ_CFG_NAME) + " = ? "
+ " ORDER BY " + CommonRepoUtils.escapeColumnName(COLUMN_SQ_CFG_INDEX);
-- select "sq_cfg_id","sq_cfg_configurable","sq_cfg_name","sq_cfg_type","sq_cfg_index" from "sqoop"."SQ_CONFIG" where sq_cfg_configurable=? and sq_cfg_name=? order by sq_cfg_index
接下来就是要研究下面这段代码:
// the only driver config for the job
Object driverConfig = ClassUtils
.instantiate(Driver.getInstance().getDriverJobConfigurationClass());
ConfigUtils.fromConfigs(job.getDriverConfig().getConfigs(), driverConfig);
##Context 从何而来?
在执行job的时候,有一个很重要的变量贯穿始终,它的名字叫context,里面存放了和执行有关的配置信息。接下来,就要研究一下,这个context从何而来。
第一次出现是在JobManager.java里。
public synchronized void initialize() {
MapContext context = SqoopConfiguration.getInstance().getContext();
Sqoop2居然支持,动态修改sqoop.properties参数文件,大概6000毫秒会reload一次。
仔细看了一下,context=sqoop.properties.
MapReduceSubmissionEngine.java里,将hadoop相关的配置文件读入内存,并放入jobClient的配置信息中。
jobClient = new JobClient(new JobConf(globalConfiguration));
看了半天,发现问题的关键还是在jobmanager.java里createJobRequest函数里。
在createJobRequest函数里,以下命令涉及配置信息
fromConnector.getLinkConfigurationClass()
而这个函数直接返回一个LinkConfiguration类。这个类里面存放了一个连接信息类,包括连接串,用户名,口令等信息,而且还能验证是否可以连接到数据库。