系列文章
Flink源码分析:基于事件时间场景下WaterMark源码分析
Flink源码分析: 重启策略机制RestartStrategy
Flink源码分析: Flink JDBC Upsert模式实现原理
目录
关注收藏不迷路~~
一. SqlClient.class
我们打开Flink Sql启动客户端脚本sql-client.sh,从中看出脚本主要是调用了
org.apache.flink.table.client.SqlClient这个类。
# start client without jar
exec $JAVA_RUN $JVM_ARGS "${log_setting[@]}" -classpath "`manglePathList "$CC_CLASSPATH:$INTERNAL_HADOOP_CLASSPATHS"`" org.apache.flink.table.client.SqlClient "$@"
我们找到SqlClient.class这个类,首先SQL Client作用是为了用户提交SQL语句,并且支持两种模式:embedded mode 和 gateway mode (Flink1.12.0版本不支持getway模式,下面的源码里写了)。
创建SqlClient对象时会调用start()类型,这里主要是封装了SqlClient的主要SQL提交操作。
private void start() {
if (isEmbedded) {
// create local executor with default environment
final List<URL> jars;
if (options.getJars() != null) {
jars = options.getJars();
} else {
jars = Collections.emptyList();
}
final List<URL> libDirs;
if (options.getLibraryDirs() != null) {
libDirs = options.getLibraryDirs();
} else {
libDirs = Collections.emptyList();
}
final Executor executor = new LocalExecutor(options.getDefaults(), jars, libDirs);
executor.start();
// create CLI client with session environment
final Environment sessionEnv = readSessionEnvironment(options.getEnvironment());
appendPythonConfig(sessionEnv, options.getPythonConfiguration());
final SessionContext context;
if (options.getSessionId() == null) {
context = new SessionContext(DEFAULT_SESSION_ID, sessionEnv);
} else {
context = new SessionContext(options.getSessionId(), sessionEnv);
}
// Open an new session
String sessionId = executor.openSession(context);
try {
// add shutdown hook
Runtime.getRuntime()
.addShutdownHook(new EmbeddedShutdownThread(sessionId, executor));
// do the actual work
openCli(sessionId, executor);
} finally {
executor.closeSession(sessionId);
}
} else {
throw new SqlClientException("Gateway mode is not supported yet.");
}
}
start()私有方法主要干了以下几件事:
- 加载外部jar包
- 加载lib包下的jar包
- 创建一个本地执行器
- 用执行器加载默认环境创建客户端
- 客户端中打开一个新的会话
- 执行SQL
加载外部jar包
是一个外部连接的集合,这里的Jar包可以是程序所需要的的依赖,也可以是自定义函数
加载lib包下的jar包
是一个外部连接的集合,这里的Jar包可以是程序所需要的的依赖,也可以是自定义函数
创建一个本地执行器
LocalExecutor对象的作用主要是用来提交程序以及返回执行结果,本地执行器会加载上文的外部Jar包以及默认的配置文件。
用执行器加载默认环境创建客户端
从CliOptions里加载环境信息,初始化一个默认环境给执行器创建客户端,这个默认环境由系统默认的SessionID决定。
客户端中打开一个新的会话
这里没什么好讲的,源码里很简单
执行SQL
我们来看下执行SQL的过程,主要封装在openCli方法中,该方法需要传入两个参数:
String sessionId, Executor executor
当所有环境配置都准备好,会执行以下代码提交SQL语句:
try (CliClient cli = new CliClient(sessionId, executor, historyFilePath)) {
// interactive CLI mode
if (options.getUpdateStatement() == null) {
cli.open();
}
// execute single update statement
else {
final boolean success = cli.submitUpdate(options.getUpdateStatement());
if (!success) {
throw new SqlClientException(
"Could not submit given SQL update statement to cluster.");
}
}
}
可以看出关键点在
cli.submitUpdate(options.getUpdateStatement())
点进去看下submitUpdate源码
public boolean submitUpdate(String statement) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_WILL_EXECUTE).toAnsi());
terminal.writer().println(new AttributedString(statement).toString());
terminal.flush();
final Optional<SqlCommandCall> parsedStatement = parseCommand(statement);
// only support INSERT INTO/OVERWRITE
return parsedStatement
.map(
cmdCall -> {
switch (cmdCall.command) {
case INSERT_INTO:
case INSERT_OVERWRITE:
return callInsert(cmdCall);
default:
printError(CliStrings.MESSAGE_UNSUPPORTED_SQL);
return false;
}
})
.orElse(false);
}
这里一个很关键的地方:
final Optional<SqlCommandCall> parsedStatement = parseCommand(statement);
这条代码的作用很神奇,它可以把SQL语言转成Table API来执行,这就是我们常说的数据库的解析过程,从上层的SQL解析成底层代码API执行,接着跟进这段代码:
private Optional<SqlCommandCall> parseCommand(String line) {
final SqlCommandCall parsedLine;
try {
parsedLine = SqlCommandParser.parse(executor.getSqlParser(sessionId), line);
} catch (SqlExecutionException e) {
printExecutionException(e);
return Optional.empty();
}
return Optional.of(parsedLine);
}
接着跟进SqlCommandParse这个类,来看看这个类的结构:
看到了吧,SQL的常规语法全部在里面,String类型的SQL字符串会在这里做解析成语法树,这已经是Table API层面的代码了。由于篇幅太长,会在下期具体讲解SQL是如何解析的。
到这里一个SQL语句通过客户端提交流程已经完全讲完了,再次帮大家总结下SQL提交过程:
加载Jar包(jar/lib包下) -> 创建本地执行器 -> 初始化环境 -> 创建一个新的会话 -> 提交SQL -> 提交后交给Table API层面的解析器 -> 生成语法树
总结
下面一章节会给大家讲解Flink SQL在解析层面的源码是如果走的,记得关注收藏哦!!