<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-db2-client</artifactId>
<version>5.2.1</version>
</dependency>
响应式 DB2 客户端(Reactive DB2 Client)
Reactive DB2 Client 是一款直观易用的 DB2 客户端,专注于高伸缩性与低开销。
该客户端采用响应式与非阻塞设计,允许仅凭单个线程即可处理大量数据库连接。
特性列表
-
支持运行于 Linux、Unix 以及 Windows 平台的 DB2
-
对 z/OS 上的 DB2 提供有限支持
-
事件驱动
-
轻量高效
-
内置连接池
-
预编译查询(Prepared Queries)缓存
-
批量操作与游标
-
行流式处理(Row streaming)
-
提供 RxJava API
-
零不必要拷贝,直接从内存映射到对象
-
支持 Java 8 日期和时间类型
-
支持 SSL/TLS
-
支持 HTTP/1.x CONNECT、SOCKS4a 或 SOCKS5 代理
当前已知限制
-
暂不支持存储过程
-
暂不支持部分列类型(如 BLOB 和 CLOB)
引入依赖(Usage)
要使用 Reactive DB2 Client,请将以下依赖项添加到构建描述文件的 dependencies 部分:
-
Maven(在
pom.xml中):
-
Gradle(在
build.gradle中):
dependencies {
compile 'io.vertx:vertx-db2-client:5.2.1'
}
快速入门(Getting started)
以下是连接、查询以及断开连接的最简单示例:
DB2ConnectOptions connectOptions = new DB2ConnectOptions()
.setPort(50000)
.setHost("the-host")
.setDatabase("the-db")
.setUser("user")
.setPassword("secret");
// 连接池选项
PoolOptions poolOptions = new PoolOptions()
.setMaxSize(5);
// 创建客户端连接池
Pool client = DB2Builder.pool()
.with(poolOptions)
.connectingTo(connectOptions)
.build();
// 执行简单查询
client
.query("SELECT * FROM users WHERE id='julien'")
.execute()
.onComplete(ar -> {
if (ar.succeeded()) {
RowSet<Row> result = ar.result();
System.out.println("Got " + result.size() + " rows ");
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
// 随后关闭连接池
client.close();
});
连接至 DB2 数据库
大多数情况下,你会使用连接池来连接 DB2:
DB2ConnectOptions connectOptions = new DB2ConnectOptions()
.setPort(50000)
.setHost("the-host")
.setDatabase("the-db")
.setUser("user")
.setPassword("secret");
// 连接池选项
PoolOptions poolOptions = new PoolOptions()
.setMaxSize(5);
// 创建池化客户端
SqlClient client = DB2Builder.client()
.with(poolOptions)
.connectingTo(connectOptions)
.using(vertx)
.build();
池化客户端底层使用连接池,执行任何操作时都会从连接池中借用一个连接,执行完毕后将其归还给连接池。
如果你在 Vert.x 环境中运行,可以将你的 Vertx 实例传递给它:
DB2ConnectOptions connectOptions = new DB2ConnectOptions()
.setPort(50000)
.setHost("the-host")
.setDatabase("the-db")
.setUser("user")
.setPassword("secret");
// 连接池选项
PoolOptions poolOptions = new PoolOptions()
.setMaxSize(5);
// 创建池化客户端
SqlClient client = DB2Builder.client()
.with(poolOptions)
.connectingTo(connectOptions)
.using(vertx)
.build();
当你不再需要该客户端时,应当将其释放关闭:
client.close();
当你需要在同一连接上执行多次操作时,需要从连接池中获取一个
连接(connection)。
你可以很方便地从池中获取连接:
DB2ConnectOptions connectOptions = new DB2ConnectOptions()
.setPort(50000)
.setHost("the-host")
.setDatabase("the-db")
.setUser("user")
.setPassword("secret");
// 连接池选项
PoolOptions poolOptions = new PoolOptions()
.setMaxSize(5);
// 创建连接池客户端
Pool client = DB2Builder.pool()
.with(poolOptions)
.connectingTo(connectOptions)
.using(vertx)
.build();
// 从连接池获取连接
client.getConnection().compose(conn -> {
System.out.println("Got a connection from the pool");
// 所有操作均在同一连接上执行
return conn
.query("SELECT * FROM users WHERE id='julien'")
.execute()
.compose(res -> conn
.query("SELECT * FROM users WHERE id='emad'")
.execute())
.onComplete(ar -> {
// 将连接释放回连接池
conn.close();
});
}).onComplete(ar -> {
if (ar.succeeded()) {
System.out.println("Done");
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
连接使用完毕后,你必须将其关闭以归还给连接池,以便连接能够被复用。
连接池 vs 池化客户端(Pool versus pooled client)
DB2Builder 允许你创建连接池(pool)或池化客户端(pooled client):
SqlClient client = DB2Builder.client()
.with(poolOptions)
.connectingTo(connectOptions)
.using(vertx)
.build();
// 流水线式(Pipelined)执行
Future<RowSet<Row>> res1 = client.query(sql).execute();
// 连接池
Pool pool = DB2Builder.pool()
.with(poolOptions)
.connectingTo(connectOptions)
.using(vertx)
.build();
// 非流水线式执行
Future<RowSet<Row>> res2 = pool.query(sql).execute();
-
连接池(pool)操作默认不采用流水线式(pipelined),只有从连接池借出的连接才会进行流水线式处理
-
池化客户端(pooled client)操作采用流水线式,但你无法直接从池化客户端中借取连接
连接池共享(Pool sharing)
你可以在多个 Verticle 或同一 Verticle 的多个实例之间共享同一个连接池。此类连接池应在 Verticle 外部创建,否则当创建它的 Verticle 被卸载时,连接池也会被随之关闭:
Pool pool = Pool.pool(database, new PoolOptions().setMaxSize(maxSize));
vertx.deployVerticle(() -> new VerticleBase() {
@Override
public Future<?> start() throws Exception {
// 使用连接池
return super.start();
}
}, new DeploymentOptions().setInstances(4));
你也可以直接在各个 Verticle 内部声明获取共享连接池:
vertx.deployVerticle(() -> new VerticleBase() {
Pool pool;
@Override
public Future<?> start() throws Exception {
// 获取或创建共享连接池
// 本质上是获取对该池的一份租约(Lease)
// 当该 Verticle 卸载时,租约会自动释放
pool = DB2Builder.pool()
.with(new PoolOptions()
.setMaxSize(maxSize)
.setShared(true)
.setName("my-pool"))
.connectingTo(database)
.using(vertx)
.build();
return super.start();
}
}, new DeploymentOptions().setInstances(4));
首次创建共享连接池时,系统会为该连接池分配底层资源。随后的调用将直接复用该池并创建新的租约。当所有租约均被关闭后,底层系统资源才会被完全释放。
默认情况下,当连接池需要创建 TCP 物理连接时,会复用当前的 EventLoop。因此共享连接池会随机使用正在使用它的各 Verticle 的 EventLoop。
你也可以独立于调用上下文,为连接池显式分配指定数量的 EventLoop:
Pool pool = DB2Builder.pool()
.with(new PoolOptions()
.setMaxSize(maxSize)
.setShared(true)
.setName("my-pool")
.setEventLoopSize(4))
.connectingTo(database)
.using(vertx)
.build();
配置(Configuration)
客户端为你提供了多种配置方式。
数据对象(Data Object)
配置客户端的一种简单方式是指定 DB2ConnectOptions 数据对象。
DB2ConnectOptions connectOptions = new DB2ConnectOptions()
.setPort(50000)
.setHost("the-host")
.setDatabase("the-db")
.setUser("user")
.setPassword("secret");
// 连接池选项
PoolOptions poolOptions = new PoolOptions().setMaxSize(5);
// 基于数据对象创建连接池
Pool pool = DB2Builder.pool()
.with(poolOptions)
.connectingTo(connectOptions)
.using(vertx)
.build();
pool.getConnection()
.onComplete(ar -> {
// 处理你的数据库连接
});
你也可以通过 setProperties 或 addProperty 方法配置通用属性。请注意,setProperties 会覆盖客户端的默认属性。
连接 URI(Connection URI)
除了使用 DB2ConnectOptions 数据对象进行配置外,如果你想通过连接 URI 进行配置,我们也为你提供了另一种连接方式:
String connectionUri = "db2://dbuser:secret@the-host:50000/mydb";
// 基于连接 URI 创建连接池
Pool pool = DB2Builder.pool()
.connectingTo(connectionUri)
.using(vertx)
.build();
// 基于连接 URI 创建连接
DB2Connection.connect(vertx, connectionUri)
.onComplete(res -> {
// 处理你的数据库连接
});
连接字符串的 URI 格式为:
db2://[user[:[password]]@]host[:port][/database][?<key1>=<value1>[&<key2>=<value2>]]
同时支持 IBM JDBC URL 语法,使用 : 分隔数据库与参数,并在参数键值对之间使用 ;:
db2://[user[:[password]]@]host[:port]/database:<key1>=<value1>;[<key2>=<value2>;]
| 在连接 URI 中配置的参数将会覆盖默认属性。 |
目前,客户端支持以下参数键:
-
host -
port -
user -
password -
database
其他额外参数将被添加至 properties 中。
| 连接 URI 解析器会将所有属性键转换为小写。 |
连接重试(Connect retries)
你可以配置客户端在建立连接失败时自动进行重试。
options
.setReconnectAttempts(2)
.setReconnectInterval(1000);
执行查询
当你不需要事务或仅运行单次查询时,可以直接在连接池上执行查询;连接池会自动从池中借调一个连接来执行该查询并向你返回结果。
以下演示如何执行简单查询:
client
.query("SELECT * FROM users WHERE id='andy'")
.execute()
.onComplete(ar -> {
if (ar.succeeded()) {
RowSet<Row> result = ar.result();
System.out.println("Got " + result.size() + " rows ");
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
预编译查询(Prepared Query)
你同样可以对预编译查询执行类似操作。
SQL 字符串中可以通过位置引用参数,使用数据库特定的占位符语法 `?`:
client
.preparedQuery("SELECT * FROM users WHERE id=?")
.execute(Tuple.of("andy"))
.onComplete(ar -> {
if (ar.succeeded()) {
RowSet<Row> rows = ar.result();
System.out.println("Got " + rows.size() + " rows ");
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
查询方法返回异步的 RowSet 实例,适用于各类 SELECT 查询:
client
.preparedQuery("SELECT first_name, last_name FROM users")
.execute()
.onComplete(ar -> {
if (ar.succeeded()) {
RowSet<Row> rows = ar.result();
for (Row row : rows) {
System.out.println("User " + row.getString(0) + " " + row.getString(1));
}
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
对于数据变更操作,同样支持获取受影响的行数:
client
.preparedQuery("INSERT INTO users (first_name, last_name) VALUES (?, ?)")
.execute(Tuple.of("Andy", "Guibert"))
.onComplete(ar -> {
if (ar.succeeded()) {
RowSet<Row> rows = ar.result();
System.out.println(rows.rowCount());
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
Row 允许通过索引序号或列名访问数据字段:
System.out.println("User " + row.getString(0) + " " + row.getString(1));
System.out.println("User " + row.getString("first_name") + " " + row.getString("last_name"));
客户端不会对参数类型进行任何魔法猜测,所有的参数都应当严格遵守 SQL 类型系统。
你可以执行批处理预编译查询:
List<Tuple> batch = new ArrayList<>();
batch.add(Tuple.of("julien", "Julien Viet"));
batch.add(Tuple.of("emad", "Emad Alblueshi"));
batch.add(Tuple.of("andy", "Andy Guibert"));
// 执行批处理
client
.preparedQuery("INSERT INTO USERS (id, name) VALUES (?, ?)")
.executeBatch(batch)
.onComplete(res -> {
if (res.succeeded()) {
// 获取所有行结果
RowSet<Row> rows = res.result();
} else {
System.out.println("Batch failed " + res.cause());
}
});
你可以通过将查询包装在 SELECT <COLUMNS> FROM FINAL TABLE ( <SQL> ) 中来获取自动生成的主键,例如:
client
.preparedQuery("SELECT color_id FROM FINAL TABLE ( INSERT INTO color (color_name) VALUES (?), (?), (?) )")
.execute(Tuple.of("white", "red", "blue"))
.onComplete(ar -> {
if (ar.succeeded()) {
RowSet<Row> rows = ar.result();
System.out.println("Inserted " + rows.rowCount() + " new rows.");
for (Row row : rows) {
System.out.println("generated key: " + row.getInteger("color_id"));
}
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
使用独立连接
获取数据库连接
当你需要执行一系列顺序查询(不包含事务)时,可以创建新连接或直接从连接池借取连接。请牢记,在从池中获取连接到归还连接期间,你需要妥善维护该连接,因为它可能因服务器端空闲超时等原因被意外关闭:
pool
.getConnection()
.compose(connection ->
connection
.preparedQuery("INSERT INTO Users (first_name,last_name) VALUES (?, ?)")
.executeBatch(Arrays.asList(
Tuple.of("Julien", "Viet"),
Tuple.of("Emad", "Alblueshi")
))
.compose(res -> connection
// 处理返回的行数据
.query("SELECT COUNT(*) FROM Users")
.execute()
.map(rows -> rows.iterator().next().getInteger(0)))
// 将连接安全归还给连接池
.eventually(() -> connection.close())
).onSuccess(count -> {
System.out.println("Insert users, now the number of users is " + count);
});
同样可以创建并使用预编译查询(Prepared Query):
connection
.prepare("SELECT * FROM users WHERE first_name LIKE ?")
.compose(pq ->
pq.query()
.execute(Tuple.of("Julien"))
.eventually(() -> pq.close())
).onSuccess(rows -> {
// 获取所有行数据
});
简化的连接管理 API
在使用连接池时,可以调用 withConnection 方法,传入一个在连接作用域内执行的函数。
该方法会自动从连接池借出连接,并将该连接作为参数调用你的函数。
传入的函数必须返回一个包含任意结果的 Future。
在该 Future 完成后,连接会被自动安全归还给连接池,并对外返回最终的处理结果:
pool.withConnection(connection ->
connection
.preparedQuery("INSERT INTO Users (first_name,last_name) VALUES (?, ?)")
.executeBatch(Arrays.asList(
Tuple.of("Julien", "Viet"),
Tuple.of("Emad", "Alblueshi")
))
.compose(res -> connection
// 处理查询返回的行数据
.query("SELECT COUNT(*) FROM Users")
.execute()
.map(rows -> rows.iterator().next().getInteger(0)))
).onSuccess(count -> {
System.out.println("Insert users, now the number of users is " + count);
});
使用事务
基于连接的事务管理
你可以使用 SQL BEGIN/COMMIT/ROLLBACK 语句手动执行事务;若这么做,你必须获取并自行管理 SqlConnection 连接。
或者,推荐使用 SqlConnection 提供的事务 API:
pool.getConnection()
// 事务必须在独占的物理连接中运行
.onSuccess(conn -> {
// 开启事务
conn.begin()
.compose(tx -> conn
// 执行一系列操作
.query("INSERT INTO Users (first_name,last_name) VALUES ('Julien','Viet')")
.execute()
.compose(res2 -> conn
.query("INSERT INTO Users (first_name,last_name) VALUES ('Emad','Alblueshi')")
.execute())
// 提交事务
.compose(res3 -> tx.commit()))
// 无论成功还是失败,均将连接归还至连接池
.eventually(() -> conn.close())
.onSuccess(v -> System.out.println("Transaction succeeded"))
.onFailure(err -> System.out.println("Transaction failed: " + err.getMessage()));
});
当数据库服务端判定当前事务失败时(例如出现著名的 current transaction is aborted, commands ignored until end of transaction block),事务会自动回滚,并且 completion Future 会以 TransactionRollbackException 异常宣告失败:
tx.completion()
.onFailure(err -> {
System.out.println("Transaction failed => rolled back");
});
简化的事务管理 API
在使用连接池时,你可以调用 withTransaction,传入一个在事务内执行的函数。
该方法会自动从连接池借取连接、开启事务,并调用你的业务函数,由客户端在该事务的上下文中执行所有数据库操作。
该业务函数必须返回包含任意操作结果的 Future:
-
当 Future 成功时,客户端会自动提交该事务;
-
当 Future 失败时,客户端会自动回滚该事务。
事务执行完毕(无论提交或回滚)后,连接都会自动归还给连接池,并向调用方返回整体操作的最终结果:
pool.withTransaction(client -> client
.query("INSERT INTO Users (first_name,last_name) VALUES ('Julien','Viet')")
.execute()
.flatMap(res -> client
.query("INSERT INTO Users (first_name,last_name) VALUES ('Emad','Alblueshi')")
.execute()
// 转换为业务提示消息
.map("Users inserted")))
.onSuccess(v -> System.out.println("Transaction succeeded"))
.onFailure(err -> System.out.println("Transaction failed: " + err.getMessage()));
游标(Cursor)与流式查询
默认情况下,执行预编译查询会一次性抓取所有数据行。你可以使用 Cursor 来精准控制每次读取的数据行数:
connection
.prepare("SELECT * FROM users WHERE first_name LIKE ?")
.onComplete(ar0 -> {
if (ar0.succeeded()) {
PreparedStatement pq = ar0.result();
// 游标要求在事务内部运行
connection
.begin()
.onComplete(ar1 -> {
if (ar1.succeeded()) {
Transaction tx = ar1.result();
// 创建游标
Cursor cursor = pq.cursor(Tuple.of("julien"));
// 读取 50 行
cursor.read(50).onComplete(ar2 -> {
if (ar2.succeeded()) {
RowSet<Row> rows = ar2.result();
// 检查是否还有剩余数据行?
if (cursor.hasMore()) {
// 重复读取流程...
} else {
// 全部数据行读取完毕 - 提交事务
tx.commit();
}
}
});
}
});
}
});
当游标需要提前释放时,必须显式调用关闭方法:
cursor
.read(50)
.onComplete(ar2 -> {
if (ar2.succeeded()) {
// 关闭游标
cursor.close();
}
});
游标还提供了更方便的流式 API,尤其是在搭配 RxJava 版本时极为顺畅:
connection
.prepare("SELECT * FROM users WHERE first_name LIKE ?")
.onComplete(ar0 -> {
if (ar0.succeeded()) {
PreparedStatement ps = ar0.result();
// 数据流要求在事务内部运行
connection
.begin()
.onComplete(ar1 -> {
if (ar1.succeeded()) {
Transaction tx = ar1.result();
// 每次抓取 50 行
RowStream<Row> stream = ps.createStream(50, Tuple.of("julien"));
// 使用数据流
stream.exceptionHandler(err -> {
System.out.println("Error: " + err.getMessage());
ps.close();
});
stream.endHandler(v -> {
// 关闭流以释放数据库中的游标资源
stream
.close()
.onComplete(closed -> {
tx.commit()
.onComplete(committed -> {
System.out.println("End of stream");
ps.close();
});
});
});
stream.handler(row -> {
System.out.println("User: " + row.getString("last_name"));
});
}
});
}
});
数据流会以每次 50 行的批次读取并推送;当该批数据行全部传递给处理器后,再读取新的 50 行批次,以此类推。
该流支持随时暂停(Pause)或恢复(Resume);已加载的行将缓存在内存中直到被消费,在此期间游标会暂停迭代抓取。
查询链路追踪(Tracing queries)
当 Vert.x 启用了链路追踪(Tracing)时,SQL 客户端可以对查询执行过程进行全链路追踪。
客户端会上报以下 客户端(Client) Span:
-
操作名称为
QueryMessage -
包含以下 Tags:
-
db.system:数据库管理系统产品名称 -
db.user:数据库用户名 -
db.instance:数据库实例 -
db.statement:SQL 查询语句 -
db.type:固定为 sql
默认的链路追踪策略为 PROPAGATE,客户端仅在参与现有活跃链路时才会创建 Span。
你可以通过 setTracingPolicy 更改客户端追踪策略,例如设置为 ALWAYS 以始终上报 Span:
options.setTracingPolicy(TracingPolicy.ALWAYS);
DB2 数据类型映射(DB2 type mapping)
目前,客户端支持以下 DB2 类型:
| DB2 类型 | Java 类型 | 说明 |
|---|---|---|
|
|
仅支持 DB2 LUW |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
仅支持 DB2 z/OS |
暂不支持的类型(Unsupported types)
以下类型当前暂不支持:
| 类型 | 说明 |
|---|---|
|
尚未实现 |
|
尚未实现 |
|
尚未实现 |
|
尚未实现 |
|
尚未实现 |
有关 DB2 数据类型的更多文档,请参阅:
Tuple 在解码并存储数值时会采用上述类型。在可能的情况下,它还会对实际值执行动态类型转换:
pool
.query("SELECT an_int_column FROM exampleTable")
.execute().onSuccess(rowSet -> {
Row row = rowSet.iterator().next();
// 存储为 INTEGER 列类型,并表示为 java.lang.Integer
Object value = row.getValue(0);
// 转换为 java.lang.Long
Long longValue = row.getLong(0);
});
使用 Java 枚举类型(Using Java enum types)
你可以将 Java 枚举类型 映射到以下数据库列类型:
-
字符串(VARCHAR、TEXT)
-
数值(SMALLINT、INTEGER、BIGINT)
client.preparedQuery("SELECT day_name FROM FINAL TABLE ( INSERT INTO days (day_name) VALUES (?), (?), (?) )")
.execute(Tuple.of(Days.FRIDAY, Days.SATURDAY, Days.SUNDAY))
.onComplete(ar -> {
if (ar.succeeded()) {
RowSet<Row> rows = ar.result();
System.out.println("Inserted " + rows.rowCount() + " new rows");
for (Row row : rows) {
System.out.println("Day: " + row.get(Days.class, "day_name"));
}
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
client.preparedQuery("SELECT day_num FROM FINAL TABLE ( INSERT INTO days (day_num) VALUES (?), (?), (?) )")
.execute(Tuple.of(Days.FRIDAY.ordinal(), Days.SATURDAY.ordinal(), Days.SUNDAY.ordinal()))
.onComplete(ar -> {
if (ar.succeeded()) {
RowSet<Row> rows = ar.result();
System.out.println("Inserted " + rows.rowCount() + " new rows");
for (Row row : rows) {
System.out.println("Day: " + row.get(Days.class, "day_num"));
}
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
字符串类型会与 Java 枚举通过 name() 方法返回的名称进行匹配。
数值类型会与 Java 枚举通过 ordinal() 方法返回的序号进行匹配,且 row.get() 方法会根据所检索到的整数序号位置返回对应枚举的 name() 值。
使用收集器查询(Collector queries)
你可以在查询 API 中使用 Java Collector 收集器:
Collector<Row, ?, Map<Long, String>> collector = Collectors.toMap(
row -> row.getLong("id"),
row -> row.getString("last_name"));
// 结合收集器执行查询
client.query("SELECT * FROM users")
.collecting(collector)
.execute()
.onComplete(ar -> {
if (ar.succeeded()) {
SqlResult<Map<Long, String>> result = ar.result();
// 获取收集器生成的 Map
Map<Long, String> map = result.value();
System.out.println("Got " + map);
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
收集器在处理过程中绝不能保留对 Row 对象的引用,因为在处理整个结果集时内部复用的是单一行实例。
Java Collectors 提供了许多开箱即用的预定义收集器,例如你可以轻松地直接从行结果集中构建字符串:
Collector<Row, ?, String> collector = Collectors.mapping(
row -> row.getString("last_name"),
Collectors.joining(",", "(", ")")
);
// 结合收集器执行查询
client
.query("SELECT * FROM users")
.collecting(collector)
.execute()
.onComplete(ar -> {
if (ar.succeeded()) {
SqlResult<String> result = ar.result();
// 获取收集器生成的字符串
String list = result.value();
System.out.println("Got " + list);
} else {
System.out.println("Failure: " + ar.cause().getMessage());
}
});
使用 SSL/TLS
若要配置客户端使用 SSL 连接,你可以像配置 Vert.x NetClient 一样配置 DB2ConnectOptions。
DB2ConnectOptions options = new DB2ConnectOptions()
.setPort(50001)
.setHost("the-host")
.setDatabase("the-db")
.setUser("user")
.setPassword("secret")
.setSsl(true)
.setSslOptions(new ClientSSLOptions().setTrustOptions(new JksOptions()
.setPath("/path/to/keystore.p12")
.setPassword("keystoreSecret")));
DB2Connection.connect(vertx, options)
.onComplete(res -> {
if (res.succeeded()) {
// 已通过 SSL 成功连接
} else {
System.out.println("Could not connect " + res.cause());
}
});
更多信息请参阅 Vert.x 官方文档。
使用代理(Using a proxy)
你还可以配置客户端使用 HTTP/1.x CONNECT、SOCKS4a 或 SOCKS5 代理。
更多信息请参阅 Vert.x 官方文档。
连接池高级配置(Advanced pool configuration)
服务器负载均衡
你可以为连接池配置多个服务器地址列表以实现负载均衡,而不是仅指定单一服务器:
Pool pool = DB2Builder.pool()
.with(options)
.connectingTo(Arrays.asList(server1, server2, server3))
.using(vertx)
.build();
连接池在创建底层物理连接时采用轮询(Round-Robin)算法在不同服务器之间进行分配。
| 该负载均衡策略发生在连接创建阶段,而非从池中借取(Borrow)连接的阶段。 |
连接池初始化处理器
你可以使用 withConnectHandler 在物理连接创建完成后、放入连接池之前与其进行交互并执行初始化设置:
builder.withConnectHandler(conn -> {
conn.query(sql).execute().onSuccess(res -> {
// 将连接归还给池,准备供应用程序正常借用
conn.close();
});
});
一旦你完成对该连接的初始化操作,只需直接调用 close() 将其标记为就绪,以便连接池开始调度使用。
动态连接配置
你可以使用 Java Supplier 函数式接口动态提供连接配置详情,而非传入静态的 SqlConnectOptions 实例。
由于该 Supplier 是异步的,因此可用于实现连接配置的动态变更(例如数据库密码自动轮换):
Pool pool = DB2Builder.pool()
.with(poolOptions)
.connectingTo(() -> {
Future<SqlConnectOptions> connectOptions = retrieveOptions();
return connectOptions;
})
.using(vertx)
.build();