使用了SPD_LOG第三方日志库
clickhousePool.h
#include <iostream>
#include <unordered_map>
#include <vector>
#include <clickhouse/client.h>
#include <mutex>
struct ClickhousePoolConfig {
std::string name; //名称
std::vector<std::string> hostVec; //服务的地址
int poolSize; // 连接池大小
std::string user; //用户名
std::string password; //密码
};
//每次获取到一个client,使用完以后都需要释放资源
struct ClientWrapper {
clickhouse::Client *pClient;
std::mutex mutex_; //临界资源
};
class ClickhousePool {
public:
//创建单例,第一次获取的时候创建
static ClickhousePool &GetInstance() {
static ClickhousePool instance_;
return instance_;
}
void test() {
printf("内存地址: %p\n", this);
}
bool SetPoolByName(const ClickhousePoolConfig &chConfig);
//通过名称随机获取一个连接
ClientWrapper *DB(const std::string &name);
protected:
ClickhousePool() = default;
virtual ~ClickhousePool() = default;
private:
private:
ClickhousePool(const ClickhousePool &) = delete;
ClickhousePool &operator=(const ClickhousePool &) = delete;
//每个clickhouse服务建立一个vector连接池
std::unordered_map<std::string, std::vector<ClientWrapper *>> clientPoolMap;
};
clickhousePool.cpp
#include "ClickhousePool.h"
#include <chrono>
#include <thread>
#include <chrono>
#include "../utils/utils.h"
#include "spdlog/spdlog.h"
bool ClickhousePool::SetPoolByName(const ClickhousePoolConfig &chConfig) {
//创建连接池
if (clientPoolMap.find(chConfig.name) != clientPoolMap.end()) {
SPDLOG_ERROR("{},连接池中已经有此名称的连接", chConfig.name);
return false;
}
auto &poolVec = clientPoolMap[chConfig.name];
poolVec.reserve(chConfig.poolSize);
//集群中多个服务建立连接,每台clickhouse机器建立poolSize个连接,总计连接=(poolSize * hostCount)
for (auto host : chConfig.hostVec) {
for (int i = 0; i < chConfig.poolSize; i++) {
try {
clickhouse::Client *pClient =
new clickhouse::Client(clickhouse::ClientOptions()
.SetHost(host)
.SetUser(chConfig.user)
.SetPassword(chConfig.password)
.SetPingBeforeQuery(true)
.SetCompressionMethod(clickhouse::CompressionMethod::LZ4));
ClientWrapper *pClientWrapper = new ClientWrapper;
pClientWrapper->pClient = pClient;
poolVec.push_back(pClientWrapper);
} catch (std::exception &e) {
std::cerr << "创建clickhouse连接异常," << e.what() << std::endl;
SPDLOG_ERROR("{},创建clickhouse连接异常", chConfig.name);
return false;
}
SPDLOG_INFO("{},创建clickhouse连接,{},{}", chConfig.name, host, i);
}
}
return true;
}
//每次获取一个资源,都需要释放,记得在应用层unlock
ClientWrapper *ClickhousePool::DB(const std::string &name) {
if (clientPoolMap.find(name) == clientPoolMap.end()) {
SPDLOG_ERROR("{}没有找到对应的连接池,请检查是否创建", name);
return nullptr;
}
auto &clientVec = clientPoolMap[name];
//从连接池中选择一个空闲的连接,尝试获取3次
int reTryCount = 3;
while (reTryCount) {
//根据时间,随机选择一台机器服务
int64_t nanoSecond = risk::util::getNanoSecond();
auto pos = nanoSecond % clientVec.size();
auto pClient = clientVec[pos];
//尝试加锁,加锁成功返回
if (pClient->mutex_.try_lock()) {
SPDLOG_INFO("获取了第{}个连接", pos);
return pClient;
}
reTryCount--;
//获取失败等待5毫秒再获取
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
SPDLOG_ERROR("{}服务资源忙,请开发人员增加此资源的数量", name);
return nullptr;
}
demo使用
//初始化 (连接池为单例模式)
ClickhousePoolConfig clickhousePoolConfig;
clickhousePoolConfig.name = "clikchouse-cluster-server-01";
clickhousePoolConfig.poolSize = 5;
clickhousePoolConfig.hostVec = {"host1","host2","host3"}; //支持多个服务地址,每个地址创建poolSize个连接
clickhousePoolConfig.user = "username";
clickhousePoolConfig.password = "password";
if (false == ClickhousePool::GetInstance().SetPoolByName(clickhousePoolConfig)) {
std::cerr << "初始化clickhouse失败\n";
exit(-1);
}
//获取
ClientWrapper *client = ClickhousePool::GetInstance().DB("clikchouse-cluster-server-01");
if (!client) {
SPDLOG_ERROR("获取连接{}失败", riskMonitorClickhouseServer01);
return -1;
}
//使用
client->pClient->Select(sqlStr, func);
//记得释放锁,归还资源
client->mutex_.unlock();