从 C++ 访问 Coherence
1. 代码差异概述
整体而言,代码与原版差别不大。下面我们逐一分析这些差异。
2. 理解规范(Specifications)
最奇特的部分当属继承语句:
public cloneable_spec<Account>
这是对象模型的一部分,被称为基于“spec”的类定义,其中“spec”是“specification”的缩写。Spec 会注入大量样板代码,使新管理类的编写变得更加容易。例如,会注入以下内容:
- 隐式虚拟继承自
Object
,使其成为管理类。
- 定义
Account::Handle/View/Holder
嵌套智能指针类型定义。
- 定义
Account::super
类型定义指向父类。
- 添加静态
create
方法以匹配
Account
的构造函数,并返回
Account::Handle
。
- 添加
clone()
方法实现,委托给
Account
的拷贝构造函数。
Spec 有多种类型:
| Spec 类型 | 描述 |
| ---- | ---- |
|
class_spec
| 最基本的 spec,用于定义管理类 |
|
cloneable_spec
| 继承自
class_spec
,支持克隆 |
|
abstract_spec
| 定义一个不可实例化的类,具有部分实现 |
|
interface_spec
| 定义一个不可实例化的类,包含所有纯虚方法 |
|
throwable_spec
| 定义一个基于 spec 的异常类 |
所有的 spec 都会自动添加对
Object
的继承,并且各自会添加一些特定的特性。Spec 接受以下模板参数:
spec<class, extends<parent>, implements<interface, ...> >
参数说明如下:
-
class
:必需,指定要定义的类的名称。
-
extends<parent>
:可选,指定要派生的类,默认为
extends <Object>
,
interface_spec
不包含此参数。
-
implements<interface1, interface2, ...>
:可选,该类实现的接口列表,默认为
implements<>
。
3. 工厂方法
相关的部分是:
friend class factory<Account>;
这个友元声明允许自动生成的
create
方法访问受保护的构造函数。这是 spec 自身无法注入的唯一样板代码。
4. 成员变量
数据成员声明如下:
private:
const int64_t m_lId;
MemberView<String> m_sDescription;
MemberHandle<Money> m_balance;
int64_t m_lLastTransactionId;
const int64_t m_lCustomerId;
-
我们将
long long数据成员改为使用int64_t。在大多数现代 C++ 编译器中,long long是 64 位整数类型,但不保证特定大小。int64_t是固定大小的类型,保证为 64 位宽。按照惯例,管理类型使用固定大小的原语,但这不是强制要求。 -
我们将
std::string替换为 Coherence 管理的String,通过View引用。Money类也进行了类似的修改,允许通过Handle引用。这样做是为了提高序列化效率,避免在序列化过程中进行类型转换。 -
对于
String和Money,我们使用MemberView<String>和MemberHandle<Money>而不是嵌套的String::View和Money::Handle。这是因为嵌套的Handle/View/Holder智能指针类型不是线程安全的。由于存储在缓存中的对象可能会被多个线程访问,因此内部必须是线程安全的。MemberHandle/View/Holder是线程安全的变体,应作为数据成员引用使用。
Coherence C++ 还包含另外两种线程安全的智能指针变体:
-
FinalHandle/View/Holder
:不可变的智能指针,具有对象模型性能优势。
-
WeakHandle/View/Holder
:弱引用风格的智能指针,用于处理对象图中的循环引用。
5. 实现构造函数
构造函数如下:
protected:
Account(int64_t lId, String::View sDesc, Money::View balance,
int64_t lLastTransId, int64_t lCustomerId)
: m_lId(lId), m_sDescription(self(), sDesc),
m_balance(self(), balance),
m_lLastTransactionId(lLastTransId),
m_lCustomerId(lCustomerId)
{
}
Account()
: m_lId(0), m_sDescription(self(), sDesc),
m_balance(self(), balance), m_lLastTransactionId(0),
m_lCustomerId(0)
{
}
Account(const Account& that)
: m_lId(lId), m_sDescription(self(), sDesc),
m_balance(self(), cast<Money::View>(balance->clone()),
m_lLastTransactionId(lLastTransId),
m_lCustomerId(lCustomerId)
{
}
-
构造函数现在声明为受保护的,这会阻止基于栈和
operator new的分配,使静态create方法成为唯一的分配机制。 -
MemberView和MemberHandle使用self()进行初始化。线程安全的智能指针接受一个可选的第二个参数,即它们要引用的对象,如果省略则默认为NULL。self()方法返回对正在创建的管理对象的基类的引用,概念上类似于this指针,但它引用的是完全初始化的基类,而this引用的是部分初始化的派生类型。
6. 实现方法
方法现在都声明为
virtual
。虽然这不是必需的,但通常管理类的设计类似于 Java,所有方法都是虚拟的。最后,我们重写了
Object
声明的一些标准方法,用于哈希、相等性测试和对象打印。
7. 实现 PortableObject 接口
要使类实现
PortableObject
接口,我们只需修改继承语句以表明该类实现
PortableObject
,然后实现其方法并注册类型:
class Account
: public cloneable_spec<Account,
extends<Object>,
implements<PortableObject> >
{
...
// ----- PortableObject methods --------------------------------
public:
virtual void writeExternal(PofWriter::Handle hWriter) const
{
hWriter->writeInt64(0, getId());
hWriter->writeString(1, getDescription());
hWriter->writeObject(2, getBalance());
hWriter->writeInt64(3, getLastTransactionId());
hWriter->writeInt64(4, getCustomerId());
}
virtual void readExternal(PofReader::Handle hReader)
{
m_lId = hReader->readInt64(0, getId());
setDescription(hReader->readString(1));
setBalance(cast<Money::Handle>(hReader->readObject(2)));
setLastTransactionId(hReader->readInt64(3));
m_lCustomerId = hReader->readInt64(4);
}
};
COH_REGISTER_PORTABLE_CLASS(
POF_TYPE_ACCOUNT, Account); // must be in .cpp
需要注意的是,在
readExternal
中,我们需要设置两个
const
数据成员,这是不允许的。这是因为
PortableObject
反序列化发生在对象已经实例化之后。为了实现
const
正确性,我们需要要么从这些数据成员的声明中移除
const
修饰符,要么在
readExternal
中强制转换掉它。
8. 实现外部序列化器
最后一种序列化选项是为我们的管理数据对象编写外部序列化器。这里我们为非
PortableObject
版本的管理
Account
类创建一个序列化器:
class AccountSerializer
: public class_spec<AccountSerializer,
extends<Object>,
implements<PofSerializer> >
{
friend class factory<AccountSerializer>;
public:
virtual void serialize(PofWriter::Handle hWriter, Object::View v)
const
{
Account::View vAccount = cast<Account::View>(v);
hWriter->writeInt64(0, vAccount->getId());
hWriter->writeString(1, vAccount->getDescription());
hWriter->writeObject(2, vAccount->getBalance());
hWriter->writeInt64(3, vAccount->getLastTransactionId());
hWriter->writeInt64(4, vAccount->getCustomerId());
hWriter->writeRemainder(NULL); // mark end of object
}
virtual Object::Holder deserialize(PofReader::Handle hReader)
const
{
int64_t lId = hReader->readInt64(0);
String::View sDesc = hReader->readString(1);
Money::Handle hBalance = cast<Money::Handle>(
hReader->readObject(2));
int64_t lTransId = hReader->readInt64(3);
int64_t lCustomerId = hReader->readInt64(4);
hReader->readRemainder(); // read to end of object
return Account::create(lId, sDesc, hBalance, lTransId,
lCustomerId);
}
};
COH_REGISTER_POF_SERIALIZER(POF_TYPE_ACCOUNT,
TypedClass<Account>::create(),
AccountSerializer::create()); // must be in .cpp
使用新的管理
Account
类比使用
Managed<Account>
更直接:
// construct managed key and value
Account::Handle hAccount = Account::create(32105, "checking",
Money::create(7374, 10, "USD"), 55, 62409);
Integer64::Handle hlKey = Integer64::create(32105);
// cache hAccount
hCache->put(hlKey, hAccount);
// retrieve the cached value
Account::View vResult = cast<Account::View>(hCache->get(hlKey));
std::cout << "retrieved " << vResult << " from cache for key "
<< vResult->getId() << std::endl;
9. 执行查询
Coherence C++ 提供了一个与 Java 版本非常相似的
QueryMap
接口:
class QueryMap
: public interface_spec<QueryMap,
implements<Map> >
{
public:
virtual Set::View keySet(Filter::View vFilter) const;
virtual Set::View entrySet(Filter::View vFilter) const;
virtual Set::View entrySet(Filter::View vFilter,
Comparator::View vComparator) const;
virtual void addIndex(ValueExtractor::View vExtractor,
bool fOrdered, Comparator::View vComparator);
virtual void removeIndex(ValueExtractor::View vExtractor);
};
执行查询的步骤如下:
1. 构造一个过滤器来确定匹配记录的条件。
2. 将过滤器提供给
keySet
或
entrySet
方法。
3. 如果需要,可以使用
Comparator
变体对结果集进行排序。
除非在本地缓存上操作,否则提供的过滤器、提取器和比较器仅用作可序列化的存根,所有处理都将在远程 Java 缓存服务器上完成。
10. 值提取器(Value Extractors)
QueryMap
的核心概念是
ValueExtractor
,用于从缓存条目中提取嵌入式值。例如,可以使用值提取器从
Account
数据对象中提取余额。这些提取器用于表达过滤条件和对缓存应用索引以优化查询性能。
Coherence C++ 包含一个
ReflectionExtractor
实现,适用于对远程缓存进行查询,前提是缓存服务器包含数据对象的 Java 版本:
hCache->addIndex(ReflectionExtractor::create("getBalance"), false,
NULL);
对于本地缓存,由于 C++ 语言没有反射支持,
ReflectionExtractor
在用于 C++ 本地缓存时会抛出
UnsupportedOperationExpection
。不过,Coherence C++ 提供了
TypedExtractor
,它结合了宏、模板和函数指针,能够很好地模拟 Java 的
ReflectionExtractor
:
ValueExtractor::View vExtractor =
COH_TYPED_EXTRACTOR(Money::View, Account, getBalance);
宏参数分别是提取值的类型、要执行提取的类类型以及用于获取值的常量访问器方法。对于返回非管理类型的访问器方法,有一个
BoxExtractor
变体,它会将原始类型包装回相应的管理类型。
graph TD;
A[开始] --> B[构造过滤器];
B --> C[选择 keySet 或 entrySet 方法];
C --> D[提供过滤器];
D --> E[根据需要使用 Comparator 排序];
E --> F[获取结果集];
F --> G[结束];
另外,还有
PofExtractor
类型,它允许在服务器端进行高效提取,而无需完全反序列化或对应的 Java 类:
ValueExtractor::View vExtractor = PofExtractor::create(typeid(Money),
Account::BALANCE);
在使用
PofExtractor
时,最好为属性索引创建静态标识符。在 Coherence 3.5 中,C++
PofExtractor
只能应用于远程缓存。
我们还可以实现
PropertyExtractor
:
#define COH_PROPERTY_EXTRACTOR(TYPE, CLASS, PROPERTY) \
coherence::util::extractor::TypedExtractor< \
TYPE, CLASS, &CLASS::get##PROPERTY> \
::create(COH_TO_STRING("get" << #PROPERTY));
ValueExtractor::View vExtractor =
COH_PROPERTY_EXTRACTOR(Money::View, Account, Balance);
对于
Managed<>
数据对象,需要使用特殊版本:
#define COH_MANAGED_PROPERTY_EXTRACTOR(TYPE, CLASS, PROPERTY) \
coherence::util::extractor::BoxExtractor< \
TYPE, CLASS, &CLASS::get##PROPERTY, \
coherence::lang::Managed<CLASS>::Holder> \
::create(COH_TO_STRING("get" << #PROPERTY));
ValueExtractor::View vExtractor =
COH_MANAGED_PROPERTY_EXTRACTOR(Money::View, Account, Balance);
11. 过滤器(Filters)
Coherence C++ 提供了与 Java 相同的内置过滤器集,这些过滤器能够在本地和远程缓存上执行。当内置过滤器的组合不足以表达查询时,你可以编写自定义过滤器。除非过滤器仅针对本地缓存,否则你还需要提供一个 Java 版本,因为在远程缓存上运行时,实际的过滤操作将由 Java 版本完成。
Java 和 C++ 实现的逻辑不必完全相同,但实现必须兼容,即对于给定的一组输入,两个实现应产生相同的结果,并且两个实现的序列化形式应相同。
12. 在 C++ 中执行查询
现在我们可以将上述内容结合起来,在 C++ 中执行查询:
// add an index for the description property on the Account class
ValueExtractor::View vExtractor =
COH_PROPERTY_EXTRACTOR(String::View, Account, Description);
hCache->addIndex(vExtractor, false, NULL);
// query for all "checking" Accounts
Set::View vSetResult =
hCache->entrySet(LikeFilter::create(vExtractor, "checking%"));
// iterate the result set printing each matching entry
for (Iterator::Handle hIter = vSetResult->iterator();
hIter->hasNext(); )
{
Map::Entry::View vEntry = cast<Map::Entry::View>(hIter->next());
Account::View vAccount = cast<Account::View>(vEntry->getValue());
std::cout << vAccount << std::endl;
}
执行查询和迭代结果非常简单,真正的工作在于定义提取器和过滤器。了解如何实现自定义提取器和过滤器很重要,但要记住,使用内置的提取器和过滤器也能满足很多需求。
13. 执行聚合器和条目处理器
Coherence C++ 通过本地
InvocableMap
接口提供对聚合器和条目处理器的全面支持,该接口与 Java 版本非常相似:
class InvocableMap
: public interface_spec<InvocableMap,
implements<Map> >
{
public:
virtual Object::Holder invoke(Object::View vKey,
EntryProcessor::Handle hAgent);
virtual Map::View invokeAll(Collection::View vCollKeys,
EntryProcessor::Handle hAgent);
virtual Map::View invokeAll(Filter::View vFilter,
EntryProcessor::Handle hAgent);
virtual Object::Holder aggregate(Collection::View vCollKeys,
EntryAggregator::Handle hAgent) const;
virtual Object::Holder aggregate(Filter::View vFilter,
EntryAggregator::Handle hAgent) const;
};
该接口允许通过显式的键和过滤器选择要处理或聚合的条目。要执行的操作可以表示为
EntryProcessor
或
Aggregator
,有许多内置实现,你也可以提供自己的自定义实现。
从 C++ 访问 Coherence
14. 综合运用示例
为了更好地理解如何在实际场景中运用上述的各种功能,我们来看一个更完整的示例。假设我们有一个账户管理系统,需要对账户信息进行存储、查询和统计。
首先,我们回顾之前定义的
Account
类,它已经实现了
PortableObject
接口,方便进行序列化和反序列化操作。
class Account
: public cloneable_spec<Account,
extends<Object>,
implements<PortableObject> >
{
// 之前定义的成员变量、构造函数、方法等
...
// ----- PortableObject methods --------------------------------
public:
virtual void writeExternal(PofWriter::Handle hWriter) const
{
hWriter->writeInt64(0, getId());
hWriter->writeString(1, getDescription());
hWriter->writeObject(2, getBalance());
hWriter->writeInt64(3, getLastTransactionId());
hWriter->writeInt64(4, getCustomerId());
}
virtual void readExternal(PofReader::Handle hReader)
{
m_lId = hReader->readInt64(0, getId());
setDescription(hReader->readString(1));
setBalance(cast<Money::Handle>(hReader->readObject(2)));
setLastTransactionId(hReader->readInt64(3));
m_lCustomerId = hReader->readInt64(4);
}
};
COH_REGISTER_PORTABLE_CLASS(
POF_TYPE_ACCOUNT, Account); // must be in .cpp
接下来,我们创建一些账户并将它们存储到缓存中:
// 创建缓存
NamedCache::Handle hCache = CacheFactory::getCache("accounts-cache");
// 创建多个账户
Account::Handle hAccount1 = Account::create(1, "checking", Money::create(1000, 0, "USD"), 1, 1001);
Account::Handle hAccount2 = Account::create(2, "savings", Money::create(5000, 0, "USD"), 2, 1002);
Account::Handle hAccount3 = Account::create(3, "checking", Money::create(2000, 0, "USD"), 3, 1001);
// 将账户存入缓存
hCache->put(Integer64::create(1), hAccount1);
hCache->put(Integer64::create(2), hAccount2);
hCache->put(Integer64::create(3), hAccount3);
现在,我们可以执行一些查询操作。例如,查询所有类型为 “checking” 的账户:
// 创建值提取器
ValueExtractor::View vExtractor = COH_PROPERTY_EXTRACTOR(String::View, Account, Description);
// 创建过滤器
Filter::View vFilter = LikeFilter::create(vExtractor, "checking");
// 执行查询
Set::View vSetResult = hCache->entrySet(vFilter);
// 遍历结果
for (Iterator::Handle hIter = vSetResult->iterator(); hIter->hasNext(); )
{
Map::Entry::View vEntry = cast<Map::Entry::View>(hIter->next());
Account::View vAccount = cast<Account::View>(vEntry->getValue());
std::cout << "Found account: " << vAccount->getId() << " - " << vAccount->getDescription() << std::endl;
}
我们还可以使用聚合器来统计所有账户的总余额:
// 创建值提取器用于提取余额
ValueExtractor::View vBalanceExtractor = COH_PROPERTY_EXTRACTOR(Money::View, Account, Balance);
// 创建求和聚合器
EntryAggregator::Handle hSumAggregator = SummingAggregator::create(vBalanceExtractor);
// 执行聚合操作
Object::Holder hResult = hCache->aggregate(hCache->keySet(), hSumAggregator);
Money::View vTotalBalance = cast<Money::View>(hResult);
std::cout << "Total balance of all accounts: " << vTotalBalance->getAmount() << " " << vTotalBalance->getCurrency() << std::endl;
15. 性能优化建议
在使用 Coherence C++ 进行开发时,为了提高性能,我们可以采取以下一些建议:
-
合理使用索引
:根据查询的频率和条件,为经常查询的属性添加索引。例如,如果经常根据账户类型进行查询,就可以为账户类型属性添加索引。
ValueExtractor::View vTypeExtractor = COH_PROPERTY_EXTRACTOR(String::View, Account, Description);
hCache->addIndex(vTypeExtractor, false, NULL);
-
选择合适的过滤器和提取器
:根据具体的查询需求,选择最适合的过滤器和提取器。对于本地缓存,优先使用
TypedExtractor等高效的提取器;对于远程缓存,确保使用的过滤器和提取器在 Java 端有对应的实现。 - 避免不必要的序列化和反序列化 :尽量减少对象的序列化和反序列化操作,因为这些操作会消耗一定的性能。可以通过合理设计数据结构和使用高效的序列化方式来实现。
16. 总结
从 C++ 访问 Coherence 为我们提供了强大的功能,包括对象管理、序列化、查询、聚合等。通过使用基于 spec 的类定义,我们可以简化管理类的编写;利用各种值提取器和过滤器,我们可以灵活地进行数据查询和处理;同时,通过聚合器和条目处理器,我们可以对数据进行统计和修改。
在实际开发中,我们需要根据具体的业务需求,合理选择和使用这些功能,并注意性能优化。通过不断地实践和探索,我们可以更好地发挥 Coherence C++ 的优势,开发出高效、稳定的应用程序。
以下是一个总结的流程图,展示了从创建账户到查询和聚合的主要步骤:
graph LR;
A[创建账户] --> B[存入缓存];
B --> C[添加索引];
C --> D[创建过滤器和提取器];
D --> E[执行查询];
E --> F[遍历结果];
D --> G[创建聚合器];
G --> H[执行聚合操作];
H --> I[输出聚合结果];
希望本文能够帮助你更好地理解和使用 Coherence C++,在实际项目中取得更好的效果。
超级会员免费看
1万+

被折叠的 条评论
为什么被折叠?



