最近学习ClickHouse相关的原理知识,基本了解了它的存储结构和设计,但是对在ClickHouse中执行一个SQL语句的过程不是很了解,所以就再次学习了一下。在这里做个总结。
准备出一个系列文章,通过源码阅读的方式,来逐步拆解Clickhouse的运行过程。今天,我们来看看Clickhouse是如何启动的。
入口
大家都知道,在启动Clickhouse的时候,我们会执行clickhouse-server …或clickhouse server …或clickhouse --server …命令。那么,执行后Clickhouse做了哪些事情呢?
首先我们需要找到入口。在最新的v18.14版本以后,Clickhouse的入口文件调整到programs/main.cpp,该文件为clickhouse入口文件:
我们直接来看看main函数的代码:
int main(int argc_, char ** argv_)
{
inside_main = true;
SCOPE_EXIT({ inside_main = false; });

/// Reset new handler to default (that throws std::bad_alloc)
/// It is needed because LLVM library clobbers it.
std::set_new_handler(nullptr);/// PHDR cache is required for query profiler to work reliably
/// It also speed up exception handling, but exceptions from dynamically loaded libraries (dlopen)
///  will work only after additional call of this function.
updatePHDRCache(); // 共享库缓存std::vector<char *> argv(argv_, argv_ + argc_);/// Print a basic help if nothing was matched
MainFunc main_func = printHelp;  // 默认执行help函数for (auto & application : clickhouse_applications)
{if (isClickhouseApp(application.first, argv)) // 这里判断是不是clickhouse-xx // 或 clickhous e --xx // 或 clickhouse xx  命令// 支持的命令看后面的说明{main_func = application.second; // 当匹配到指定的命令后退出循环break;}
}return main_func(static_cast<int>(argv.size()), argv.data()); // 执行响应的函数

}
所以,main函数实际上是从clickhouse_applications中匹配指令,然后执行了响应的指令函数。
顺便看一下clickhouse支持的命令:

指令的声明源码如下:
/// Add an item here to register new application
std::pair<const char *, MainFunc> clickhouse_applications[] =
{
#if ENABLE_CLICKHOUSE_LOCAL
{“local”, mainEntryClickHouseLocal}, // 启动本地服务
#endif
#if ENABLE_CLICKHOUSE_CLIENT
{“client”, mainEntryClickHouseClient}, // 启动clickhouse的内置客户端
#endif
#if ENABLE_CLICKHOUSE_BENCHMARK
{“benchmark”, mainEntryClickHouseBenchmark},
#endif
#if ENABLE_CLICKHOUSE_SERVER
{“server”, mainEntryClickHouseServer}, // 启动clickhouse服务端
#endif
#if ENABLE_CLICKHOUSE_EXTRACT_FROM_CONFIG
{“extract-from-config”, mainEntryClickHouseExtractFromConfig},
#endif
#if ENABLE_CLICKHOUSE_COMPRESSOR
{“compressor”, mainEntryClickHouseCompressor},
#endif
#if ENABLE_CLICKHOUSE_FORMAT
{“format”, mainEntryClickHouseFormat},
#endif
#if ENABLE_CLICKHOUSE_COPIER
{“copier”, mainEntryClickHouseClusterCopier},
#endif
#if ENABLE_CLICKHOUSE_OBFUSCATOR
{“obfuscator”, mainEntryClickHouseObfuscator},
#endif
#if ENABLE_CLICKHOUSE_GIT_IMPORT
{“git-import”, mainEntryClickHouseGitImport},
#endif
#if ENABLE_CLICKHOUSE_INSTALL // clickhouse部署命令
{“install”, mainEntryClickHouseInstall},
{“start”, mainEntryClickHouseStart},
{“stop”, mainEntryClickHouseStop},
{“status”, mainEntryClickHouseStatus},
{“restart”, mainEntryClickHouseRestart},
#endif
{“hash-binary”, mainEntryClickHouseHashBinary},
};
复制代码
所以,我们在执行clickhouse-server …命令时,实际上运行了mainEntryClickHouseServer函数。接下来我们看看这个函数做了什么。
启动服务 Server
我们从main.cpp中找到mainEntryClickHouseServer,该函数来自于programs/server/Server.cpp文件:
int mainEntryClickHouseServer(int argc, char ** argv)
{
DB::Server app;

// ...try
{return app.run(argc, argv); # 调用Pococ的run函数启动Server服务
}
catch (...)
{std::cerr << DB::getCurrentExceptionMessage(true) << "\n";auto code = DB::getCurrentExceptionCode();return code ? code : 1;
}

}

可以看到,Server的启动最终是交给了Poco框架来完成的。那么我们是否到这里就结束了呢?让我们来看一下Poco::Util::ServerApplication:run(int argc, char * * argv)函数做了什么。这里我们找到Poco的Poco/Util/src/ServerApplication.h源码,可以很清楚的看到一段说明:
{
public:
ServerApplication();
/// Creates the ServerApplication.

~ServerApplication();/// Destroys the ServerApplication.// 就是这里:run方法执行的时候会调用当前Application类的main函数
int run(int argc, char** argv);/// Runs the application by performing additional initializations/// and calling the main() method.// ...

protected:
int run();
void waitForTerminationRequest();
#if !defined(_WIN32_WCE)
void defineOptions(OptionSet& options);
#endif

我们再打开Poco/Util/src/ServerApplication.cpp看一下run函数的代码:
int ServerApplication::run(int argc, char** argv)
{
if (!hasConsole() && isService())
{
return 0;
}
else
{
int rc = EXIT_OK;
try
{
init(argc, argv);
switch (_action)
{
case SRV_REGISTER:
registerService();
rc = EXIT_OK;
break;
case SRV_UNREGISTER:
unregisterService();
rc = EXIT_OK;
break;
default:
rc = run(); // 这里会调用当前类的run()函数
}
}
catch (Exception& exc)
{
logger().log(exc);
rc = EXIT_SOFTWARE;
}
return rc;
}
}

//…

// run函数会直接调用Application的run方法
int ServerApplication::run()
{
return Application::run();
}
那么我们来打开Poco/Util/src/Application.cpp看一下Application::run()函数:
int Application::run()
{
int rc = EXIT_CONFIG;
initialize(*this); // 先调用initialize钩子

try
{rc = EXIT_SOFTWARE;rc = main(_unprocessedArgs); // 然后这里会调用main函数
}
catch (Poco::Exception& exc)
{logger().log(exc);
}
//...uninitialize(); // 最后调用uninitialize钩子
return rc;

}
复制代码
兜兜转转我们又要回到Clickhouse的programs/server/Server.cpp,我们直接看Server::main()函数吧,这个函数有点长。。。大致做了以下这些事吧。
int Server::main(const std::vectorstd::string & /args/)
{
Poco::Logger * log = &logger();
// 1. RegisterXXX 注册一些基础的函数等

// 2. 创建GlobalThreadPool// 3. 初始化全局上下文以及内容(配置等)// 4. 创建系统数据库(system、default等)、初始化内置表// 5. 注册不同协议的服务(http/https/tcp等)// 6. 启动所有服务(Poco::Net::TCPServer.start())
for (auto & server : *servers)server.start();// 最后等待结束
waitForTerminationRequest();

}
这里面创建http服务的关键代码为:
servers->emplace_back(
port_name,
std::make_unique(
context(), createHandlerFactory(*this, async_metrics, “HTTPHandler-factory”), server_pool, socket, http_params));
复制代码
可以看到servers中添加了HTTPServer对象,这个HTTPServer对象是继承了Poco::Net::TCPServer的,所以实际上就是在servers向量表中加了一个TCPServer对象,等待后续的启动。
这里再进入到createHandlerFactory中看一下,发现会调用Server/HTTPHandlerFactory.cpp中的addDefaultHandlersFactory添加默认的一些处理器:
void addDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IServer & server, AsynchronousMetrics & async_metrics)
{
// 1. 添加默认的处理器:
// a. / -> 静态资源。如.js.css等
// b. /ping -> 处理ping消息
// c. /replicas_status -> 处理集群状态查询指令
// d. /play -> 内置的查询ui页面
addCommonDefaultHandlersFactory(factory, server);

auto query_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(server, "query");
query_handler->allowPostAndGetParamsRequest();
factory.addHandler(query_handler);/// ...

}

OLAP组件-Clickhouse源码相关推荐

  1. 2022-10-24 ClickHouse 源码解析-查询引擎经典理论

    ClickHouse 源码解析: 综述 ClickHouse 源码解析: MergeTree Write-Path ClickHouse 源码解析: MergeTree Read-Path Click ...

  2. 如何在Eclipse中查看Android源码或者第三方组件包源码

    文章出处:http://blog.csdn.net/cjjky/article/details/6535426 在学习过程中如果经常阅读源码,理解程度会比较深,学习效率也会比较高,那么如何方便快捷的阅 ...

  3. 【clickhouse】clickhouse源码 Distributed之表select流程

    文章目录 1.概述 2.Distributed之表查询流程 1.概述 转载:[ClickHouse源码]Distributed之表select流程 仅仅转载一下,防止以后用到 2.Distribute ...

  4. 《微信小程序-进阶篇》Lin-ui组件库源码分析-列表组件List(一)

    大家好,这是小程序系列的第二十篇文章,在这一个阶段,我们的目标是 由简单入手,逐渐的可以较为深入的了解组件化开发,从本文开始,将记录分享lin-ui的源码分析,期望通过对lin-ui源码的学习能加深组 ...

  5. 通过深挖Clickhouse源码,数据去重精通~

    大家好,我是老兵. 数据去重是大数据面试当中经常会遇到的一个问题,它在面试以不同的方式提出.这里我例举几个常见问题. 限制内存大小要求去重 不同组件的去重实现 生产环境中实时大规模去重实践 上述的每一 ...

  6. 深度 | 一条查询SQL的前世今生 —— ClickHouse 源码阅读

    作者:逍凯,阿里云数据库实习开发工程师 注:以下分析基于开源 v19.15.2.2-stable 版本进行,社区最新版本代码改动较大,但是总体思路是不变的. 01 用户提交一条查询SQL背后发生了什么 ...

  7. ClickHouse 源码阅读 —— SQL的前世今生

    注:以下分析基于开源 v19.15.2.2-stable 版本进行,社区最新版本代码改动较大,但是总体思路是不变的. 用户提交一条查询SQL背后发生了什么? 在传统关系型数据库中,SQL处理器的组件主 ...

  8. 基于微软企业库的AOP组件(含源码)

    软件开发,离不开对日志的操作.日志可以帮助我们查找和检测问题,比较传统的日志是在方法执行前或后,手动调用日志代码保存.但自从AOP出现后,我们就可以避免这种繁琐但又必须要实现的方式.本文是在微软企业库 ...

  9. android 生命周期_Android生命周期组件 Lifecycle 源码详解(一)

    在上篇文章: warmcheng:Android生命周期组件 Lifecycle 使用详解​zhuanlan.zhihu.com 中,我们讲了 Lifecycle 的简单使用,本篇我们来研究下它的源码 ...

最新文章

  1. np.max()和np.argmax()
  2. python的学习笔记/002-1(2018-5-18 )
  3. 一句话告诉你们什么是大数据
  4. [网络] SOCKET, TCP/UDP, HTTP, FTP
  5. 【theano-windows】学习笔记二十——LSTM理论及实现
  6. Codeforces Round #674 (Div. 3)
  7. Koa项目搭建过程详细记录
  8. openfire:openfire单独编译指定插件的方法
  9. 连接动态链接库时找不到链接库的解决办法
  10. Transmission下载安装
  11. chmod 755 与chmod +x的区别
  12. 电脑计算机未输出任何信号 键盘没亮,戴尔计算机不显示信号,为什么计算机屏幕不显示...
  13. [Python36] 01 start
  14. 有一座山就像一个笔架子
  15. 【OpenCV】 全景拼接——多张图像拼接
  16. 计算机中pdf怎么预览,如何在浏览器中开启PDF时默认显示Adobe Reader XI工具栏
  17. air flow空调上是什么意思_air flow空调滤芯上是什么意思
  18. Springboot中new出来的实例中含有@Autowired注入时的Spring Bean为NULL
  19. Linux(CentOS)下安装NVIDIA GPU驱动
  20. Linux 命令随笔

热门文章

  1. 带UpdatePanel页面返回js问题
  2. LG电子计划到2010年实现利润翻番
  3. Django使用python-docx-template,并根据模板来生成有数据的word文档
  4. SpringBoot集成MyBatis-Plus自定义SQL
  5. 从无到有整合SpringMVC-MyBatis项目(3):整合SpringMVC+Mybatis
  6. mysql 主从 通俗易懂_MySQL 主从同步架构中你不知道的“坑”(完结篇)
  7. 关于原生AJAX和jQueryAJAX的编程
  8. 如何更改Inactive object的ownership
  9. Java的接口与继承
  10. 什么是A记录,子域名,CNAME别名,MX记录,TXT记录,SRV 记录,泛域名(泛解析),域名转向,域名绑定...