我尝试使用 ListenableFuture 对Cassandra群集进行异步写入,如下所示:
private static Cluster cluster = null;
private ListeningExecutorService executorService;
private PreparedStatement preparedStatement;
private Session session = null;
...
executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(POOL_SIZE));
...
public void writeValue(Tuple tuple) {
ListenableFuture<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
if(session == null) {
session = getCluster().connect("dbname");
preparedStatement = session.prepare(queryString);
}
try {
BoundStatement boundStatement = preparedStatement.bind(tuple values);
session.execute(boundStatement);
} catch(Exception exception) {
// handle exception
}
return null;
}
});
如果我将 POOL_SIZE 设置为1,一切正常 如果我将 POOL_SIZE 设置为&gt; 1我得到如下错误:
引起:com.datastax.driver.core.exceptions.InvalidQueryException:尝试执行未知的准备查询:0x75c5b41b9f07afa5384a69790503f963。您可能已经使用了使用另一个Cluster实例创建的PreparedStatement。
所以我session
和preparedStatement
进入当地的变种。然后我收到有关Re-preparing already prepared query ...
的警告以及每次创建新会话的警告。
我想尽可能多地重用。我做错了什么,我有什么选择?
使这个类静止会有帮助吗?
答案 0 :(得分:5)
这里有各种各样的竞争条件,并且执行不是线程安全的。
Cluster
,Session
和PreparedStatement
中的每一个都是应用程序范围的单例,即。您只需要一个(PreparedStatement
的每个查询一个)。
但是,您正在重新创建Session
并可能多次准备PreparedStatement
。
唐&#39;吨。在构造函数或仅运行一次的某个位置初始化Session
一次,并同时准备语句。然后在适当的地方使用Session
和PreparedStatement
。
使用单线程执行程序,一切都像同步一样运行。当您添加更多线程时,其中许多可能会调用
session.prepare(queryString);
同时。或者你在这里使用的PreparedStatement
BoundStatement boundStatement = preparedStatement.bind(tuple values);
session.execute(boundStatement);
可能与您初始化的
不同preparedStatement = session.prepare(queryString);
甚至在同一执行线程内。或者您可能尝试使用与用于初始化它的PreparedStatement
不同的Session
执行static final String OFFSETS_BC_FUSE ="CREATE TABLE IF NOT EXISTS "+OFFSETS_B_FUSE+"("+ID+" integer primary key autoincrement, "+OFFSET_VALUE+" INT);";
。
Here are some things you should be doing when using CQL drivers.
- 醇>
准备好的语句是绑定在一个会话上还是可以在另一个会话中使用?
预准备语句派生自特定会话实例。 因此,当您准备一个语句并将其发送到服务器时,它 被发送到与此会话实例关联的集群 用。
Session
州的javadoc
会话实例是线程安全的,通常是单个实例 足够的每个应用程序。
答案 1 :(得分:2)
您可能还想使用驱动程序的异步API。而不是调用execute
(它将在查询期间阻塞您的线程),而是调用executeAsync
并在生成的future上注册回调以处理结果。
如果该回调很昂贵并且您不想阻止驱动程序的内部I / O线程,那么您可以提供自己的执行程序:
ListenableFuture<ResultSet> future = session.executeAsync(statement);
Futures.addCallback(future, new FutureCallback<ResultSet>() {
public void onSuccess(ResultSet rs) { ... }
public void onFailure(Throwable t) { ... }
},
executorService);
文档中的This page提供了一些关于异步编程的技巧。