C++客户端
Ignite 3 的客户端通过标准套接字连接接入集群。与 Ignite 2 不同,Ignite 3 中没有单独的胖和瘦客户端,只有瘦
客户端。
客户端不会成为集群拓扑的一部分,不保存任何数据,也不会用来承载计算任务。
1.入门
1.1.环境要求
运行 C++ 瘦客户端需要一个C++构建环境以运行cmake
命令。
- 支持C++ 17的编译器;
- CMake 3.10 + 或更高版本;
- 一个构建系统:
make
、ninja
、MS Visual Studio
或其他。
1.2.安装
Ignite 3 的二进制包已经自带了C++客户端的源代码,可以使用下面的命令来构建:
mkdir cmake-build-release
cd cmake-build-release
cmake ..
cmake --build . -j8
mkdir cmake-build-release
cd cmake-build-release
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j8
mkdir cmake-build-release
cd cmake-build-release
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j8
1.3.在CentOS 7 和 RHEL 7 环境中构建C++客户端
如果运行在比较旧的系统上,需要按照如下步骤配置环境:
- 安装
epel-release
和centos-release-scl
:
yum install epel-release centos-release-scl
- 更新yum并接受
epel-release
密钥:
yum update
- 从主仓库中安装构建工具以及
devtoolset-11
:
yum install devtoolset-11-gcc devtoolset-11-gcc-c++ cmake3 git java-11-openjdk-devel gtest-devel gmock-devel
创建并更新
cmake
的备选列表,以强制使用cmake3
:a. 创建一个优先级为10的
cmake2
备选:bashsudo alternatives --install /usr/local/bin/cmake cmake /usr/bin/cmake 10 \ --slave /usr/local/bin/ctest ctest /usr/bin/ctest \ --slave /usr/local/bin/cpack cpack /usr/bin/cpack \ --slave /usr/local/bin/ccmake ccmake /usr/bin/ccmake \ --family cmake
b. 创建一个优先级为20的
cmake3
备选:bashsudo alternatives --install /usr/local/bin/cmake cmake /usr/bin/cmake3 20 \ --slave /usr/local/bin/ctest ctest /usr/bin/ctest3 \ --slave /usr/local/bin/cpack cpack /usr/bin/cpack3 \ --slave /usr/local/bin/ccmake ccmake /usr/bin/ccmake3 \ --family cmake
c. 检查默认的备选指向了
cmake3
:bashsudo alternatives --config cmake
启用
devtoolset-11
编译器并在更新的PATH中启动bash
:bashscl enable devtoolset-11 bash
在命令行中启动构建。
2.客户端连接配置
客户端连接参数由客户端连接器配置控制。Ignite 默认接受端口 10800 上的客户端连接,可以随时使用命令行工具更改节点的配置。
提示
在 Ignite 3 中,配置文件可以使用 HOCON
或 JSON
格式。
下面是客户端连接器配置的JSON格式示例:
"ignite" : {
"clientConnector" : {
"port" : 10800,
"idleTimeout" :3000,
"sendServerExceptionStackTraceToClient" : true,
"ssl" : {
"enabled" : true,
"clientAuth" : "require",
"keyStore" : {
"path" : "KEYSTORE_PATH",
"password" : "SSL_STORE_PASS"
},
"trustStore" : {
"path" : "TRUSTSTORE_PATH",
"password" : "SSL_STORE_PASS"
},
},
},
}
下表是客户端连接器支持的配置参数:
属性 | 默认值 | 描述 |
---|---|---|
connectTimeout | 5000 | 连接超时(毫秒)。 |
idleTimeout | 0 | 在断开连接之前,客户端可以处于空闲状态的时间(毫秒),默认没有限制(0)。 |
metricsEnabled | false | 是否收集客户端指标。 |
port | 10800 | 客户端连接器监听的端口。 |
sendServerExceptionStackTraceToClient | false | 是否将集群端的异常堆栈信息回传给客户端。 |
ssl.ciphers | 要启用的密码列表。 | |
ssl.clientAuth | none | 客户端使用的客户端认证类型,具体请参见SSL/TLS。 |
ssl.enabled | false | 是否启用SSL。 |
ssl.keyStore.password | "" | SSL密钥库密码。 |
ssl.keyStore.path | SSL密钥库路径。 | |
ssl.keyStore.type | PKCS12 | SSL密钥库类型。 |
ssl.trustStore.password | SSL信任库密码。 | |
ssl.trustStore.path | SSL信任库路径。 | |
ssl.trustStore.type | PKCS12 | SSL信任库类型。 |
下面是修改参数的方法:
node config update clientConnector.port=10469
3.接入集群
客户端的初始化方式如下:
using namespace ignite;
ignite_client_configuration cfg{"127.0.0.1"};
auto client = ignite_client::start(cfg, std::chrono::seconds(5));
4.认证
5.用户对象序列化
Ignite 支持将用户对象映射到表元组,这样以任何编程语言创建的对象都可以直接用于键-值操作。
5.1.限制
此类映射存在一些限制,有些限制属于常规限制,其他的限制则和所使用的编程语言有关。
- 仅支持一层字段结构,不支持嵌套。这是因为 Ignite 表及其衍生的元组仅支持一层字段结构;
- 字段应映射到 Ignite 类型;
- 用户类型中的所有字段都应映射到表的列或显式排除;
- 表中的所有列都应该映射到用户类型中的某个字段;
- 仅限 C++:开发者必须显式提供编组函数,因为没有反射来根据用户类型结构动态生成。
5.2.使用示例
struct account {
account() = default;
account(std::int64_t id) : id(id) {}
account(std::int64_t id, std::int64_t balance) : id(id), balance(balance) {}
std::int64_t id{0};
std::int64_t balance{0};
};
namespace ignite {
template<>
ignite_tuple convert_to_tuple(account &&value) {
ignite_tuple tuple;
tuple.set("id", value.id);
tuple.set("balance", value.balance);
return tuple;
}
template<>
account convert_from_tuple(ignite_tuple&& value) {
account res;
res.id = value.get<std::int64_t>("id");
// Sometimes only key columns are returned, i.e. "id",
// so we have to check whether there are any other columns.
if (value.column_count() > 1)
res.balance = value.get<std::int64_t>("balance");
return res;
}
} // namespace ignite
6.SQL API
Ignite 3 聚焦于 SQL,SQL API 是处理数据的主要方式。Ignite 支持的SQL语法,具体请参见SQL 参考章节的内容,以下是发送 SQL 请求的方法:
result_set result = client.get_sql().execute(nullptr, {"select name from tbl where id = ?"}, {std::int64_t{42});
std::vector<ignite_tuple> page = result_set.current_page();
ignite_tuple& row = page.front();
6.1.SQL脚本
SQL API 默认一次执行一个 SQL 语句,如果要执行一组 SQL 语句,需要使用execute_script()
方法,这些语句将按顺序执行:
std::string script = ""
+ "CREATE TABLE IF NOT EXISTS Person (id int primary key, city_id int, name varchar, age int, company varchar);"
+ "INSERT INTO Person (1,3, 'John', 43, 'Sample')";
client.get_sql().execute_script(script);
提示
当第一页准备好返回时,每个语句的执行都被视为完成。因此在处理大型数据集时,SELECT 语句可能会受到同一脚本中后续语句的影响。
7.事务
Ignite 3 中的所有表操作都是事务性的,可以将显式事务作为任何 Table 和 SQL API 调用的第一个参数传入。如果未提供显式事务,则将为每个调用创建一个隐式事务。
以下是显式事务的使用方法:
auto accounts = table.get_key_value_view<account, account>();
account init_value(42, 16'000);
accounts.put(nullptr, {42}, init_value);
auto tx = client.get_transactions().begin();
std::optional<account> res_account = accounts.get(&tx, {42});
res_account->balance += 500;
accounts.put(&tx, {42}, res_account);
assert(accounts.get(&tx, {42})->balance == 16'500);
tx.rollback();
assert(accounts.get(&tx, {42})->balance == 16'000);
8.Table API
要操作某个表,需要获取该表的特定视图并调用其方法,只能使用 SQL API 创建表。
使用表时,可以使用内置的 Tuple 类型,这是一组底层的键值对,或者将数据映射到自己定义的业务类型以实现强类型访问。
8.1.获取Table实例
使用IgniteTables.get_table(String)
方法可以获取表的实例,还可以使用IgniteTables.get_tables()
方法列出所有现有表。
using namespace ignite;
auto table_api = client.get_tables();
std::vector<table> existing_tables = table_api.get_tables();
table first_table = existing_tables.front();
std::optional<table> my_table = table_api.get_table("MY_TABLE);
8.2.Table的基本操作
拿到Table
实例后,就需要选择一个视图来确定如何操作数据。
8.2.1.二进制记录视图
该视图可用于直接操作表元组。
record_view<ignite_tuple> view = table.get_record_binary_view();
ignite_tuple record{
{"id", 42},
{"name", "John Doe"}
};
view.upsert(nullptr, record);
std::optional<ignite_tuple> res_record = view.get(nullptr, {"id", 42});
assert(res_record.has_value());
assert(res_record->column_count() == 2);
assert(res_record->get<std::int64_t>("id") == 42);
assert(res_record->get<std::string>("name") == "John Doe");
8.2.2.记录视图
该视图映射到一个业务类型上,可以使用映射到表元组的用户对象来操作表。
record_view<person> view = table.get_record_view<person>();
person record(42, "John Doe");
view.upsert(nullptr, record);
std::optional<person> res_record = view.get(nullptr, person{42});
assert(res.has_value());
assert(res->id == 42);
assert(res->name == "John Doe");
8.2.3.二进制键-值视图
该视图可以分别使用键和值元组来操作表。
key_value_view<ignite_tuple, ignite_tuple> kv_view = table.get_key_value_binary_view();
ignite_tuple key_tuple{{"id", 42}};
ignite_tuple val_tuple{{"name", "John Doe"}};
kv_view.put(nullptr, key_tuple, val_tuple);
std::optional<ignite_tuple> res_tuple = kv_view.get(nullptr, key_tuple);
assert(res_tuple.has_value());
assert(res_tuple->column_count() == 2);
assert(res_tuple->get<std::int64_t>("id") == 42);
assert(res_tuple->get<std::string>("name") == "John Doe");
8.2.4.键-值视图
该视图映射到一个业务类型上,可以使用映射到表元组的键对象和值对象来操作表。
key_value_view<person, person> kv_view = table.get_key_value_view<person, person>();
kv_view.put(nullptr, {42}, {"John Doe"});
std::optional<person> res = kv_view.get(nullptr, {42});
assert(res.has_value());
assert(res->id == 42);
assert(res->name == "John Doe");
18624049226