Maxwell初始化准备阶段
当我们执行了 maxwell --host bigdata1时
maxwell会先进入 Maxwell.java 中的 main 方法中
在 mian 方法里,优先将配置信息 --host 赋予MaxwellConfig()并创建其对象,可见MaxwellConfig应该是初始化配置信息的类
public static void main(String[] args) {
try {
Logging.setupLogBridging();
MaxwellConfig config = new MaxwellConfig(args);
进入MaxwellConfig中,当存在配置信息参数时,MaxwellConfig将会调用重载方法MaxwellConfig,否则会调用没有形参的MaxwellConfig
//带形参
public MaxwellConfig(String argv[]) {
this();
this.parse(argv);
}
//不带形参,不带形参会将所有必须配置设置上默认值
public MaxwellConfig() { // argv is only null in tests
this.customProducerProperties = new Properties();
this.kafkaProperties = new Properties();
this.replayMode = false;
this.replicationMysql = new MaxwellMysqlConfig();
this.maxwellMysql = new MaxwellMysqlConfig();
this.schemaMysql = new MaxwellMysqlConfig();
this.masterRecovery = false;
this.gtidMode = false;
this.bufferedProducerSize = 200;
this.outputConfig = new MaxwellOutputConfig();
setup(null, null); // setup defaults
}
同时,将接收到的参数传递给 parse 方法
private void parse(String [] argv) {
MaxwellOptionParser parser = buildOptionParser();
OptionSet options = parser.parse(argv);
parse优先将拿到的配置信息交给 buildOptionParser()对象,当配置信息能够与 buildOptionParser 所必须的配置匹配上后,会返回给 parse
随后parse会进行判断:
final Properties properties;
if (options.has("config")) {
properties = parseFile((String) options.valueOf("config"), true);
} else {
properties = parseFile(DEFAULT_CONFIG_FILE, false);
}
如果配置信息上存在config,那么就使用指定的config,否则使用默认的config文件
同时进行env_config的判断,检查是否拥有 env_config文件,如果存在,则读取配置文件,并输出日志提醒env_config文件的配置已经更改
if (options.has("env_config")) {
Properties envConfigProperties = readPropertiesEnv((String) options.valueOf("env_config"));
for (Map.Entry<Object, Object> entry : envConfigProperties.entrySet()) {
Object key = entry.getKey();
if (properties.put(key, entry.getValue()) != null) {
LOGGER.debug("Replaced config key {} with value from env_config", key);
}
}
}
随后判断是否有env_config_prefix,由于这个配置参数是没有预先设置默认值的,所以需要采用 fetchStringOption 进行确定,具体是拿到 配置参数 和 env_config 中被修改的配置信息,然后在里面查找有没有 env_config_prefix,如果没有,则默认为null,随后进行判断,如果存在 env_config_prefix,那么其中的所有值将会:
首先被转换成流,筛选前缀为 prefix 的数据
对其中的每一条数据,获取到它的键,并将前缀去除,随后往config生成的properties中输入,目的是为了更改config中的配置
String envConfigPrefix = fetchStringOption("env_config_prefix", options, properties, null);
if (envConfigPrefix != null) {
String prefix = envConfigPrefix.toLowerCase();
System.getenv().entrySet().stream()
.filter(map -> map.getKey().toLowerCase().startsWith(prefix))
.forEach(config -> {
String rawKey = config.getKey();
String newKey = rawKey.toLowerCase().replaceFirst(prefix, "");
if (properties.put(newKey, config.getValue()) != null) {
LOGGER.debug("Got env variable {} and replacing config key {}", rawKey, newKey);
} else {
LOGGER.debug("Got env variable {} as config key {}", rawKey, newKey);
}
});
}
最后将 配置参数 和config配置传递给 setup;同时如果有help则输出教程,如果有未解析的选项则返回报错
if (options.has("help"))
usage("Help for Maxwell:", parser, (String) options.valueOf("help"));
setup(options, properties);
List<?> arguments = options.nonOptionArguments();
if(!arguments.isEmpty()) {
usage("Unknown argument(s): " + arguments);
}
至此,Maxwell 的初始化阶段基本结束
初始化初期
setup中保存一系列的参数,这些参数可以由前面初始化阶段进行更改,当一些选项没有出现时,一定程度上会有默认值或者直接置为null,接下来以kafka为例:
private void setup(OptionSet options, Properties properties) {
this.kafkaTopic = fetchStringOption("kafka_topic", options, properties, "maxwell");
this.kafkaKeyFormat = fetchStringOption("kafka_key_format", options, properties, "hash");
this.kafkaPartitionHash = fetchStringOption("kafka_partition_hash", options, properties, "default");
this.ddlKafkaTopic = fetchStringOption("ddl_kafka_topic", options, properties, this.kafkaTopic);
}
这里保存了一些需要使用的配置参数,其中由topic、分区格式等,topic如果为空时,默认为waxwell
同样的,mysql也一样
this.databaseName = fetchStringOption("schema_database", options, properties, "maxwell");
this.maxwellMysql.database = this.databaseName;
默认情况下,使用maxwell作为database
可以看到,setup最后也只是配置MaxwellConfig的属性,在validate中,也提供了一系列配置这些属性的逻辑,比如setup中缺失的kafka的bootstrap.servers:
public void validate() {
validatePartitionBy();
validateFilter();
if ( this.producerType.equals("kafka") ) {
if ( !this.kafkaProperties.containsKey("bootstrap.servers") ) {
usageForOptions("Please specify kafka.bootstrap.servers", "kafka");
}
if ( this.kafkaPartitionHash == null ) {
this.kafkaPartitionHash = "default";
} else if ( !this.kafkaPartitionHash.equals("default")
&& !this.kafkaPartitionHash.equals("murmur3") ) {
usageForOptions("please specify --kafka_partition_hash=default|murmur3", "kafka_partition_hash");
}
if ( !this.kafkaKeyFormat.equals("hash") && !this.kafkaKeyFormat.equals("array") )
usageForOptions("invalid kafka_key_format: " + this.kafkaKeyFormat, "kafka_key_format");
......
而mysql中缺失的配置,则是在AbstractConfig.java中,这是Maxwell最初的配置类
protected MaxwellMysqlConfig parseMysqlConfig(String prefix, OptionSet options, Properties properties) {
MaxwellMysqlConfig config = new MaxwellMysqlConfig();
config.host = fetchStringOption(prefix + "host", options, properties, null);
config.password = fetchStringOption(prefix + "password", options, properties, null);
config.user = fetchStringOption(prefix + "user", options, properties, null);
config.port = fetchIntegerOption(prefix + "port", options, properties, 3306);
config.sslMode = this.getSslModeFromString(fetchStringOption(prefix + "ssl", options, properties, null));
config.setJDBCOptions(
fetchStringOption(prefix + "jdbc_options", options, properties, null));
// binlog_heartbeat isn't prefixed, as it only affects replication
config.enableHeartbeat = fetchBooleanOption("binlog_heartbeat", options, properties, config.enableHeartbeat);
return config;
}
可以见到,这里直接配置了mysql和binlog的相关信息
至此,初始化初期阶段结束
Maxwell初始化
回到main方法
public static void main(String[] args) {
try {
Logging.setupLogBridging();
MaxwellConfig config = new MaxwellConfig(args);
if ( config.log_level != null ) {
Logging.setLevel(config.log_level);
}
final Maxwell maxwell = new Maxwell(config);
在完成初始化的参数配置后,main创建了一个Maxwell对象,同时将配置参数传递给这个对象
public Maxwell(MaxwellConfig config) throws SQLException, URISyntaxException {
this(new MaxwellContext(config));
}
这个Maxwell维护一个MaxwellContext对象,并将config配置参数传递给它
MaxwellContext中创建一个C3P0ConnectionPool对象,并使用这个对象创建mysql的连接池
this.replicationConnectionPool = new C3P0ConnectionPool(
config.replicationMysql.getConnectionURI(false),
config.replicationMysql.user,
config.replicationMysql.password
);
最后,开启一个线程,进行判断,如果hashMode存在,则使用maxwell HA模式,否则直接调用start()方法启动maxwell
public static void main(String[] args) {
try {
Logging.setupLogBridging();
MaxwellConfig config = new MaxwellConfig(args);
if ( config.log_level != null ) {
Logging.setLevel(config.log_level);
}
final Maxwell maxwell = new Maxwell(config);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
maxwell.terminate();
StaticShutdownCallbackRegistry.invoke();
}
});
LOGGER.info("Starting Maxwell. maxMemory: " + Runtime.getRuntime().maxMemory() + " bufferMemoryUsage: " + config.bufferMemoryUsage);
if ( config.haMode ) {
new MaxwellHA(maxwell, config.jgroupsConf, config.raftMemberID, config.clientID).startHA();
} else {
maxwell.start();
}
至此,Maxwell初始化完成
maxwell启动
进入start方法:
public void start() throws Exception {
try {
this.startInner();
} catch ( Exception e) {
this.context.terminate(e);
} finally {
onReplicatorEnd();
this.terminate();
}
Exception error = this.context.getError();
if (error != null) {
throw error;
}
}
() throws Exception {
try {
this.startInner();
} catch ( Exception e) {
this.context.terminate(e);
} finally {
onReplicatorEnd();
this.terminate();
}
Exception error = this.context.getError();
if (error != null) {
throw error;
}
}
待续