7 Fast DDS-持久化服务
- [7.1 配置方法](#7.1 配置方法)
- [7.2 PERSISTENCE:SQLITE3 内置插件](#7.2 PERSISTENCE:SQLITE3 内置插件)
- [7.3 示例](#7.3 示例)
使用默认 QoS 时,DataWriter 的历史记录仅在 DataWriter 的整个生命周期内对 DataReader 可用。这意味着历史记录不会在 DataWriter 的多次初始化之间持久化,因此在 DataWriter 创建时它处于空状态。类似地,DataReader 的历史记录不会在 DataReader 的生命周期之外持久化,因此在 DataReader 创建时也是空的。然而,eProsima Fast DDS 提供了将 DataWriter 的历史记录存储在持久化数据库中的可能性,以便 DataWriter 可以在创建时从该数据库加载其历史记录。此外,DataReader 可以配置为将最后一个通知的更改存储在数据库中,以便它们在创建时可以恢复其状态。
这种机制允许在启动数据分发服务时恢复先前的状态,从而在例如意外关闭的情况下增加应用程序的鲁棒性。通过配置持久化服务,DataWriter 和 DataReader 可以从关闭发生时它们所处的状态恢复操作。
Note
请注意,DataReader 不会将其整个历史记录存储到数据库中,而是存储来自 DataWriter 的最后一个被通知的变更。这意味着它们将从停止的地方恢复操作,但它们不会拥有之前的信息,因为这些信息已经通知给了应用程序。
Note
可能需要将 DataWriter 和 DataReader 的历史记录深度配置为一个足够大的值,以保证存储在数据库中的所有变更足以让应用程序正确恢复。
Note
需要
RELIABLE_RELIABILITY_QOS可靠性才能允许完全恢复,因为它保证所有变更都存储在数据库中,并且它们将在重启后对 DataWriter/DataReader 可用。
7.1 配置方法
持久化服务的配置通过设置适当的 DataWriter 和 DataReader 的 DurabilityQosPolicy,以及为每个实体(DomainParticipant、DataWriter 或 DataReader)的 PropertyPolicyQos 指定合适的属性来完成。
- 要使持久化服务生效,
DurabilityQosPolicyKind需要设置为TRANSIENT_DURABILITY_QOS或PERSISTENT_DURABILITY_QOS。 - 必须使用属性
dds.persistence.guid在端点(DataWriter 或 DataReader)上设置一个持久化标识符(Guid_t)。此标识符用于从数据库加载相应的数据,并在重启之间同步 DataWriter 和 DataReader。GUID 由 16 个字节组成,分为两组:- 前 12 个字节对应于
GuidPrefix_t。 - 最后 4 个字节对应于
EntityId_t。
持久化标识符使用一个由 12 个点分隔的字节组成的字符串来指定,以十六进制表示,后跟一个竖线分隔符(|)以及另外 4 个点分隔的字节,也以十六进制表示(参见示例)。为 DataReader 和 DataWriter 选择合适的 GUID,请参阅 RTPS 标准(第 9.3.1 节《全局唯一标识符(GUID)》)。
此属性必须在端点级别定义。如果不存在,无论任何插件配置如何,持久性行为都将回退到TRANSIENT_LOCAL_DURABILITY_QOS。
- 前 12 个字节对应于
- 必须使用属性
dds.persistence.plugin配置一个用于管理数据库的持久化插件(参见《PERSISTENCE:SQLITE3 内置插件》):
7.2 PERSISTENCE:SQLITE3 内置插件
此插件通过使用 SQLite3 API 的本地数据库文件提供持久化功能。要激活该插件,必须将 dds.persistence.plugin 属性添加到 DataWriter 或 DataReader 的 PropertyPolicyQos 中,其值为 builtin.SQLITE3。可选地,可以将 dds.persistence.sqlite3.filename 属性添加到实体的 PropertyPolicyQos 中以指定数据库文件名;如果省略,则使用默认值 persistence.db。这些属性总结如下表:
Persistence::SQLITE3 配置属性
| 属性名称 | 属性值 |
|---|---|
dds.persistence.plugin |
builtin.SQLITE3 |
dds.persistence.sqlite3.filename |
用于持久化存储的文件名。默认值:persistence.db |
Note
为避免因并发访问 SQLite3 数据库而导致意外延迟,强烈建议为每个 DataWriter 和 DataReader 指定不同的数据库文件。
Important
dds.persistence.plugin和dds.persistence.sqlite3.filename是作为一对来解析的:如果在端点(DataWriter/DataReader)级别未设置dds.persistence.plugin,则这两个属性都将从 DomainParticipant 获取;在端点级别定义了dds.persistence.sqlite3.filename但同一级别没有dds.persistence.plugin,则会被忽略。dds.persistence.guid必须在端点级别定义;如果缺失,无论任何插件配置如何,行为都将回退到TRANSIENT_LOCAL_DURABILITY_QOS。强烈建议直接在端点上设置所有三个属性,以获得可预测的行为。
7.3 示例
此示例展示了如何使用 PERSISTENCE:SQLITE3 内置插件,通过 C++ 和 eProsima Fast DDS XML 配置文件(参见 XML 配置文件)来配置持久化服务。
/*
* In order for this example to be self-contained, all the entities are created programatically, including the data
* type and type support. This has been done using Fast DDS Dynamic Types API, but it could be substituted with a
* Fast DDS-Gen generated type support if an IDL file is available. The Dynamic Type created here is the equivalent
* of the following IDL:
*
* struct persistence_topic_type
* {
* unsigned long index;
* string message;
* };
*/
// Configure persistence service plugin for DomainParticipant
DomainParticipantQos pqos;
pqos.properties().properties().emplace_back("dds.persistence.plugin", "builtin.SQLITE3");
pqos.properties().properties().emplace_back("dds.persistence.sqlite3.filename", "part_persistence_service.db");
DomainParticipant* participant = DomainParticipantFactory::get_instance()->create_participant(0, pqos);
/********************************************************************************************************
* CREATE TYPE AND TYPE SUPPORT
*********************************************************************************************************
* This part could be replaced if IDL file and Fast DDS-Gen are available.
* The type is created with name "persistence_topic_type"
* Additionally, create a data object and populate it, just to show how to do it
********************************************************************************************************/
// Create a struct builder for a type with name "persistence_topic_type"
const std::string topic_type_name = "persistence_topic_type";
TypeDescriptor::_ref_type struct_type_descriptor {traits<TypeDescriptor>::make_shared()};
struct_type_descriptor->kind(TK_STRUCTURE);
struct_type_descriptor->name(topic_type_name);
DynamicTypeBuilder::_ref_type struct_builder {DynamicTypeBuilderFactory::get_instance()->
create_type(struct_type_descriptor)};
// The type consists of two members, and index and a message. Add members to the struct.
MemberDescriptor::_ref_type index_member_descriptor {traits<MemberDescriptor>::make_shared()};
index_member_descriptor->name("index");
index_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->
get_primitive_type(TK_UINT32));
struct_builder->add_member(index_member_descriptor);
MemberDescriptor::_ref_type message_member_descriptor {traits<MemberDescriptor>::make_shared()};
message_member_descriptor->name("message");
message_member_descriptor->type(DynamicTypeBuilderFactory::get_instance()->
create_string_type(static_cast<uint32_t>(LENGTH_UNLIMITED))->build());
struct_builder->add_member(message_member_descriptor);
// Build the type
DynamicType::_ref_type struct_type {struct_builder->build()};
// Create type support and register the type
TypeSupport type_support(new DynamicPubSubType(struct_type));
type_support.register_type(participant);
// Create data sample a populate data. This is to be used when calling `writer->write()`
DynamicData::_ref_type dyn_helloworld {DynamicDataFactory::get_instance()->create_data(struct_type)};
dyn_helloworld->set_uint32_value(0, 0);
dyn_helloworld->set_string_value(1, "HelloWorld");
/********************************************************************************************************
* END CREATE TYPE AND TYPE SUPPORT
********************************************************************************************************/
// Create a topic
Topic* topic = participant->create_topic("persistence_topic_name", topic_type_name, TOPIC_QOS_DEFAULT);
// Create a publisher and a subscriber with default QoS
Publisher* publisher = participant->create_publisher(PUBLISHER_QOS_DEFAULT, nullptr);
Subscriber* subscriber = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT, nullptr);
// Configure DataWriter's durability and persistence GUID so it can use the persistence service
DataWriterQos dwqos = DATAWRITER_QOS_DEFAULT;
dwqos.history().kind = KEEP_LAST_HISTORY_QOS;
dwqos.history().depth = 20;
dwqos.reliability().kind = RELIABLE_RELIABILITY_QOS;
dwqos.durability().kind = TRANSIENT_DURABILITY_QOS;
dwqos.properties().properties().emplace_back("dds.persistence.plugin", "builtin.SQLITE3");
dwqos.properties().properties().emplace_back("dds.persistence.guid",
"77.72.69.74.65.72.5f.71.65.72.73.5f|67.75.69.64");
dwqos.properties().properties().emplace_back("dds.persistence.sqlite3.filename", "dw_persistence_service.db");
DataWriter* writer = publisher->create_datawriter(topic, dwqos);
// Configure DataReaders's durability and persistence GUID so it can use the persistence service
DataReaderQos drqos = DATAREADER_QOS_DEFAULT;
drqos.history().kind = KEEP_LAST_HISTORY_QOS;
drqos.history().depth = 20;
drqos.reliability().kind = RELIABLE_RELIABILITY_QOS;
drqos.durability().kind = TRANSIENT_DURABILITY_QOS;
drqos.properties().properties().emplace_back("dds.persistence.plugin", "builtin.SQLITE3");
drqos.properties().properties().emplace_back("dds.persistence.guid",
"77.72.69.74.65.72.5f.71.65.72.73.5f|67.75.69.65");
drqos.properties().properties().emplace_back("dds.persistence.sqlite3.filename", "dr_persistence_service.db");
DataReader* reader = subscriber->create_datareader(topic, drqos);
Note
有关如何创建 DomainParticipant、DataReader 和 DataWriter 的说明,请分别参阅《基于配置文件的 DomainParticipant 创建》、《基于配置文件的 DataWriter 创建》和《基于配置文件的 DataReader 创建》。