Skip to content

C++客户端

Ignite 3 的客户端通过标准套接字连接接入集群。与 Ignite 2 不同,Ignite 3 中没有单独的胖和瘦客户端,只有客户端。

客户端不会成为集群拓扑的一部分,不保存任何数据,也不会用来承载计算任务。

1.入门

1.1.环境要求

运行 C++ 瘦客户端需要一个C++构建环境以运行cmake命令。

  • 支持C++ 17的编译器;
  • CMake 3.10 + 或更高版本;
  • 一个构建系统:makeninjaMS Visual Studio或其他。

1.2.安装

Ignite 3 的二进制包已经自带了C++客户端的源代码,可以使用下面的命令来构建:

bash
mkdir cmake-build-release
cd cmake-build-release
cmake ..
cmake --build . -j8
bash
mkdir cmake-build-release
cd cmake-build-release
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j8
bash
mkdir cmake-build-release
cd cmake-build-release
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j8

1.3.在CentOS 7 和 RHEL 7 环境中构建C++客户端

如果运行在比较旧的系统上,需要按照如下步骤配置环境:

  1. 安装epel-releasecentos-release-scl
bash
yum install epel-release centos-release-scl
  1. 更新yum并接受epel-release密钥:
bash
 yum update
  1. 从主仓库中安装构建工具以及devtoolset-11
bash
yum install devtoolset-11-gcc devtoolset-11-gcc-c++ cmake3 git java-11-openjdk-devel gtest-devel gmock-devel
  1. 创建并更新cmake的备选列表,以强制使用cmake3

    a. 创建一个优先级为10的cmake2备选:

    bash
    sudo 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备选:

    bash
    sudo 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

    bash
    sudo alternatives --config cmake
  2. 启用devtoolset-11编译器并在更新的PATH中启动bash

    bash
    scl enable devtoolset-11 bash
  3. 在命令行中启动构建。

2.客户端连接配置

客户端连接参数由客户端连接器配置控制。Ignite 默认接受端口 10800 上的客户端连接,可以随时使用命令行工具更改节点的配置。

提示

在 Ignite 3 中,配置文件可以使用 HOCONJSON 格式。

下面是客户端连接器配置的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"
      },
    },
  },
}

下表是客户端连接器支持的配置参数:

属性默认值描述
connectTimeout5000连接超时(毫秒)。
idleTimeout0在断开连接之前,客户端可以处于空闲状态的时间(毫秒),默认没有限制(0)。
metricsEnabledfalse是否收集客户端指标。
port10800客户端连接器监听的端口。
sendServerExceptionStackTraceToClientfalse是否将集群端的异常堆栈信息回传给客户端。
ssl.ciphers要启用的密码列表。
ssl.clientAuthnone客户端使用的客户端认证类型,具体请参见SSL/TLS
ssl.enabledfalse是否启用SSL。
ssl.keyStore.password""SSL密钥库密码。
ssl.keyStore.pathSSL密钥库路径。
ssl.keyStore.typePKCS12SSL密钥库类型。
ssl.trustStore.passwordSSL信任库密码。
ssl.trustStore.pathSSL信任库路径。
ssl.trustStore.typePKCS12SSL信任库类型。

下面是修改参数的方法:

node config update clientConnector.port=10469

3.接入集群

客户端的初始化方式如下:

cpp
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.使用示例

cpp
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 请求的方法:

cpp
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()方法,这些语句将按顺序执行:

cpp
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 调用的第一个参数传入。如果未提供显式事务,则将为每个调用创建一个隐式事务。

以下是显式事务的使用方法:

cpp
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()方法列出所有现有表。

cpp
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.二进制记录视图

该视图可用于直接操作表元组。

cpp
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.记录视图

该视图映射到一个业务类型上,可以使用映射到表元组的用户对象来操作表。

cpp
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.二进制键-值视图

该视图可以分别使用键和值元组来操作表。

cpp
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.键-值视图

该视图映射到一个业务类型上,可以使用映射到表元组的键对象和值对象来操作表。

cpp
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