presto-cli模块
概要
- 该模块主要负责查询SQL的客户端功能,利用RESTful请求发送给Coordinator实现SQL语句的查询。
- PrestoDB可以通过presto-cli客户端和JDBC连接这两种方法实现SQL的查询。
- presto-cli模块查询流程:通过用户输入SQL将语句组装成一个RESTful请求,发送给Coordinator执行该SQL,并启动查询方法,分批查询结果和用于下一个RESTful请求的nextUrl地址,如果地址为空则停止查询,否则再次发送HTTP请求进行结果查询,不断返回给客户端并在前端显示。
源码
- 进入客户端主方法:Presto类(该方法主要是客户端的启动方法)
//这是进入客户端的主方法
public final class Presto
{
private Presto() {}
public static void main(String[] args)
{
//根据传递的参数初始化一个Console对象,该对象保存了启动cli时的所有参数
Console console = singleCommand(Console.class).parse(args);
//如果启动的时候有--help等,返回帮助信息然后退出
if (console.helpOption.showHelpIfRequested() ||
console.versionOption.showVersionIfRequested()) {
return;
}
System.exit(console.run() ? 0 : 1);
}
}
- 进入实际运行的线程run:Console类(该方法主要包装,获取参数并提交查询)
public boolean run()
{
//获取所有客户端传入的参数,参数已经被组装到ClientSession
ClientSession session = clientOptions.toClientSession();
//如果Cli启动的时候指定了--execute,则hasQuery为true,说明需要直接执行shell传入的参数
boolean hasQuery = !isNullOrEmpty(clientOptions.execute);
//如果Cli启动的时候指定了-f或者--file,则isFromFile为true,说明需要直接执行文件中的SQL
boolean isFromFile = !isNullOrEmpty(clientOptions.file);
if (!hasQuery && !isFromFile) {
AnsiConsole.systemInstall();
}
//初始化日志
initializeLogging(clientOptions.logLevelsFile);
//获取--execute对应的sql,这里指:如果通过--execute在运行客户端jar包时就指定了sql的话直接从这里拿sql
String query = clientOptions.execute;
if (hasQuery) {
query += ";";
}
if (isFromFile) {
if (hasQuery) {
//在启动时不能同时指定execute和file
throw new RuntimeException("both --execute and --file specified");
}
try {
//读取文件中的sql
query = Files.asCharSource(new File(clientOptions.file), UTF_8).read();
hasQuery = true;
}
catch (IOException e) {
throw new RuntimeException(format("Error reading from file %s: %s", clientOptions.file, e.getMessage()));
}
}
// abort any running query if the CLI is terminated
AtomicBoolean exiting = new AtomicBoolean();
ThreadInterruptor interruptor = new ThreadInterruptor();
CountDownLatch exited = new CountDownLatch(1);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
exiting.set(true);
interruptor.interrupt();
awaitUninterruptibly(exited, EXIT_DELAY.toMillis(), MILLISECONDS);
}));
//生成一个查询的包装类,后面的查询会通过该类启动和执行
try (QueryRunner queryRunner = new QueryRunner(
session,
clientOptions.debug,
Optional.ofNullable(clientOptions.socksProxy),
Optional.ofNullable(clientOptions.httpProxy),
Optional.ofNullable(clientOptions.keystorePath),
Optional.ofNullable(clientOptions.keystorePassword),
Optional.ofNullable(clientOptions.truststorePath),
Optional.ofNullable(clientOptions.truststorePassword),
Optional.ofNullable(clientOptions.accessToken),
Optional.ofNullable(clientOptions.user),
clientOptions.password ? Optional.of(getPassword()) : Optional.empty(),
Optional.ofNullable(clientOptions.krb5Principal),
Optional.ofNullable(clientOptions.krb5RemoteServiceName),
Optional.ofNullable(clientOptions.krb5ConfigPath),
Optional.ofNullable(clientOptions.krb5KeytabPath),
Optional.ofNullable(clientOptions.krb5CredentialCachePath),
!clientOptions.krb5DisableRemoteServiceHostnameCanonicalization)) {
if (hasQuery) {
//启动Cli的时候指定--execute或--file参数,则执行该方法,否则就直接启动Cli,接受终端用户在Cli的输入,并提交查询
return executeCommand(queryRunner, query, clientOptions.outputFormat, clientOptions.ignoreErrors);
}