等待事件:reliable message

本文解析了Oracle RAC环境中出现的可靠消息(reliablemessage)等待事件,该事件表明跨实例消息发送过程中,发送方未能及时从接收方获得确认,从而导致性能问题。通常这反映了节点间通信异常或网络负载过高的情况。

author:skate
time:2011/05/03


 

等待事件:reliable message

 

在metalink中的解释如下:

 

 

    When you send a message using the 'KSR' intra-instance broadcast
    service, the message publisher waits on this wait-event until
    all subscribers have consumed the 'reliable message' just sent.
    The publisher waits on this wait-event for three seconds and
    then re-tests if all subscribers have consumed the message, or
    until posted.

 

也就是说当跨实例发送消息时,发送者期望收到订阅者的回复信息,如果得不到可信回复,
就会一直处于等待。等待以3秒为周期进行反复尝试,直到收到所有订阅者的回复或者被唤醒。

 

在RAC环境中如果此等待事件出现在top5中,那就说明在节点间的通讯已经出现问题,一个节点得不到
另外一个节点的可信回复,这个等待事件已经严重影响了性能,这个时候就要检查节点间的通信是否
正常,是否网络负载过大等。

 

 

 

 

 

 

---------end------------

 

 

 

 

 

 

 

 

 

BLE Mesh 消息可靠传输实现指南 根据您提供的文档信息,我来详细解释如何实现消息不丢包和如何发送无应答消息。 1. 实现消息传输不丢包(有应答消息) 要实现可靠传输不丢包,需要使用有应答的消息机制。以下是具体实现方法: 1.1 配置超时时间 首先在 menuconfig 中配置默认超时时间: ``` Component config → Bluetooth Mesh → Client model message timeout (seconds) ``` 或者直接修改 sdkconfig: ```c CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT=10 # 设置为10秒超时 ``` 1.2 发送有应答消息 ```c /** * @brief 发送有应答的厂商消息(确保可靠传输) * * @param server_addr 目标地址 * @param data 发送数据 * @param length 数据长度 * @param opcode 操作码 * @param timeout_ms 自定义超时时间(毫秒),0表示使用默认值 * @return esp_err_t 发送结果 */ esp_err_t example_ble_mesh_send_reliable_message(uint16_t server_addr, uint8_t *data, uint16_t length, uint32_t opcode, uint32_t timeout_ms) { esp_ble_mesh_msg_ctx_t ctx = {0}; esp_err_t err; // 设置消息上下文 ctx.net_idx = gStore.net_idx; ctx.app_idx = gStore.app_idx; ctx.addr = server_addr; ctx.send_ttl = MSG_SEND_TTL; ctx.send_rel = true; // 要求可靠传输 if (opcode == 0) { opcode = ESP_BLE_MESH_VND_MODEL_OP_SEND; } // 转换超时时间(毫秒到毫秒,但API可能需要不同的单位) int msg_timeout = (timeout_ms > 0) ? (timeout_ms / 1000) : 0; ESP_LOGI(BLE_MESH, "Sending reliable message to 0x%04x, timeout: %d ms", server_addr, (timeout_ms > 0) ? timeout_ms : CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT * 1000); // 发送有应答的消息(need_rsp = true) err = esp_ble_mesh_client_model_send_msg(vendor_client.model, &ctx, opcode, length, data, msg_timeout, // 超时时间 true, // need_rsp = true 要求应答 MSG_ROLE); if (err != ESP_OK) { ESP_LOGE(BLE_MESH, "Failed to send reliable message: %s", esp_err_to_name(err)); } return err; } ``` 1.3 处理应答和超时事件 需要在Mesh事件回调中处理应答和超时: ```c static void mesh_event_cb(esp_ble_mesh_cb_event_t event, esp_ble_mesh_cb_param_t *param) { switch (event) { case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT: // 消息发送超时处理 ESP_LOGE(BLE_MESH, "Message timeout, opcode: 0x%06x, addr: 0x%04x", param->client_recv_timeout.opcode, param->client_recv_timeout.ctx->addr); // 这里可以触发重试机制 break; case ESP_BLE_MESH_CLIENT_MODEL_RECV_RSP_EVT: // 收到应答处理 ESP_LOGI(BLE_MESH, "Received response, opcode: 0x%06x, addr: 0x%04x", param->client_recv_rsp.opcode, param->client_recv_rsp.ctx->addr); // 处理应答数据 ESP_LOG_BUFFER_HEX_LEVEL(BLE_MESH, param->client_recv_rsp.params, param->client_recv_rsp.length, ESP_LOG_DEBUG); break; // 其他事件处理... } } ``` 1.4 带自动重试的可靠发送函数 ```c /** * @brief 带自动重试的可靠消息发送 */ esp_err_t example_ble_mesh_send_with_retry(uint16_t server_addr, uint8_t *data, uint16_t length, uint32_t opcode, uint8_t max_retries, uint32_t retry_delay_ms) { esp_err_t err; uint8_t retry_count = 0; do { err = example_ble_mesh_send_reliable_message(server_addr, data, length, opcode, 5000); if (err == ESP_OK) { // 发送成功,等待应答或超时事件 // 这里需要实现一个等待机制,比如使用信号量或消息队列 // 等待 ESP_BLE_MESH_CLIENT_MODEL_RECV_RSP_EVT 事件 ESP_LOGI(BLE_MESH, "Message sent successfully, waiting for response..."); return ESP_OK; } retry_count++; if (retry_count <= max_retries) { ESP_LOGW(BLE_MESH, "Send failed, retry %d/%d after %d ms", retry_count, max_retries, retry_delay_ms); vTaskDelay(pdMS_TO_TICKS(retry_delay_ms)); } } while (retry_count <= max_retries); ESP_LOGE(BLE_MESH, "All retries failed for message to 0x%04x", server_addr); return err; } ``` 1. 发送无应答的消息 2.1 客户端模型发送无应答消息 ```c /** * @brief 发送无应答的厂商消息(快速,不保证可靠) * * @param server_addr 目标地址 * @param data 发送数据 * @param length 数据长度 * @param opcode 操作码 * @return esp_err_t 发送结果 */ esp_err_t example_ble_mesh_send_unreliable_message(uint16_t server_addr, uint8_t *data, uint16_t length, uint32_t opcode) { esp_ble_mesh_msg_ctx_t ctx = {0}; esp_err_t err; // 设置消息上下文 ctx.net_idx = gStore.net_idx; ctx.app_idx = gStore.app_idx; ctx.addr = server_addr; ctx.send_ttl = MSG_SEND_TTL; ctx.send_rel = false; // 不要求可靠传输 if (opcode == 0) { opcode = ESP_BLE_MESH_VND_MODEL_OP_SEND; } ESP_LOGI(BLE_MESH, "Sending unreliable message to 0x%04x", server_addr); // 发送无应答的消息(need_rsp = false) err = esp_ble_mesh_client_model_send_msg(vendor_client.model, &ctx, opcode, length, data, 0, // 超时时间设为0 false, // need_rsp = false 不要求应答 MSG_ROLE); if (err != ESP_OK) { ESP_LOGE(BLE_MESH, "Failed to send unreliable message: %s", esp_err_to_name(err)); } else { ESP_LOGD(BLE_MESH, "Unreliable message sent successfully"); } return err; } ``` 2.2 服务器模型发送消息(总是无应答) ```c /** * @brief 服务器模型发送消息(总是无应答) * * 服务器模型发送的消息总是无应答的,适用于状态通知等场景 * * @param model 服务器模型指针 * @param ctx 消息上下文 * @param opcode 操作码 * @param data 发送数据 * @param length 数据长度 * @return esp_err_t 发送结果 */ esp_err_t example_ble_mesh_server_send_message(esp_ble_mesh_model_t *model, esp_ble_mesh_msg_ctx_t *ctx, uint32_t opcode, uint8_t *data, uint16_t length) { esp_err_t err; if (model == NULL || ctx == NULL || data == NULL || length == 0) { return ESP_ERR_INVALID_ARG; } ESP_LOGI(BLE_MESH, "Server sending message to 0x%04x", ctx->addr); // 服务器模型发送消息(总是无应答) err = esp_ble_mesh_server_model_send_msg(model, ctx, opcode, length, data); if (err != ESP_OK) { ESP_LOGE(BLE_MESH, "Server failed to send message: %s", esp_err_to_name(err)); } else { ESP_LOGD(BLE_MESH, "Server message sent successfully"); } return err; } ``` 1. 完整的使用示例 ```c // 初始化Mesh回调 esp_ble_mesh_register_prov_callback(mesh_event_cb); // 发送有应答的可靠消息(不丢包) uint8_t important_data[] = {0x01, 0x02, 0x03}; esp_err_t err = example_ble_mesh_send_reliable_message(0x1001, important_data, sizeof(important_data), 0, 5000); if (err == ESP_OK) { ESP_LOGI(BLE_MESH, "Reliable message sent, waiting for response..."); } // 发送无应答的快速消息 uint8_t status_data[] = {0xAA, 0xBB}; err = example_ble_mesh_send_unreliable_message(0x1002, status_data, sizeof(status_data), 0); if (err == ESP_OK) { ESP_LOGI(BLE_MESH, "Unreliable message sent successfully"); } // 服务器发送状态通知 esp_ble_mesh_msg_ctx_t ctx = { .net_idx = gStore.net_idx, .app_idx = gStore.app_idx, .addr = 0xFFFF, // 广播地址 .send_ttl = 3, .send_rel = false }; uint8_t notification_data[] = {0x55, 0x66}; err = example_ble_mesh_server_send_message(vendor_server_model, &ctx, 0x2001, notification_data, sizeof(notification_data)); ``` 1. 选择策略建议 2. 使用有应答消息的场景: · 关键控制指令(开关、调节等) · 需要确认执行结果的操作 · 数据传输完整性要求高的场景 3. 使用无应答消息的场景: · 状态通知和心跳包 · 实时传感器数据(可以容忍偶尔丢失) · 广播消息和多播消息 · 对延迟敏感的应用 4. 超时时间设置建议: · 局域网内:2-5秒 · 多跳网络:5-10秒 · 高延迟网络:10-30秒 这样的实现可以确保在需要可靠性的场景下不丢包,同时在追求效率的场景下快速发送消息。 能够解决节点“ignoring old seqauth" 指南中api貌似不支持序列号持久化管理
最新发布
09-02
### BLE Mesh消息可靠传输实现方法 #### 消息不丢包(有应答消息) 要实现消息不丢包,可采用有应答消息机制。发送方发送消息后,等待接收方的应答消息。若在指定时间内未收到应答,发送方会重新发送消息,直至收到应答或达到最大重试次数。 示例代码(伪代码): ```python max_retries = 3 retry_count = 0 while retry_count < max_retries: send_message(message) if wait_for_ack(timeout): break retry_count += 1 if retry_count == max_retries: print("消息发送失败,达到最大重试次数") ``` #### 发送无应答消息 无应答消息发送时,发送方不等待接收方的应答,直接发送消息。这种方式适用于对实时性要求较高、允许少量消息丢失的场景。 示例代码(伪代码): ```python send_message(message) ``` ### 完整使用示例 以下是一个简单的 BLE Mesh 消息发送和接收示例,结合有应答和无应答消息: ```python # 初始化 BLE Mesh init_ble_mesh() # 发送有应答消息 message_with_ack = "This is a message with ack" max_retries = 3 retry_count = 0 while retry_count < max_retries: send_message(message_with_ack) if wait_for_ack(500): # 超时时间 500ms print("有应答消息发送成功") break retry_count += 1 if retry_count == max_retries: print("有应答消息发送失败,达到最大重试次数") # 发送无应答消息 message_without_ack = "This is a message without ack" send_message(message_without_ack) print("无应答消息发送成功") ``` ### 选择策略建议 - **有应答消息**:适用于对消息可靠性要求较高的场景,如控制指令、重要数据传输等。但会增加通信延迟,降低通信效率。 - **无应答消息**:适用于对实时性要求较高、允许少量消息丢失的场景,如传感器数据采集、状态更新等。 ### 超时时间设置建议 超时时间的设置需要根据具体的应用场景和网络环境进行调整。一般来说,超时时间不宜过短,否则可能会导致频繁重发消息;也不宜过长,以免影响通信效率。建议在弱网环境下适当增加超时时间,在强网环境下适当缩短超时时间。 ### 解决节点‘ignoring old seqauth’ API序列号持久化管理问题 在 BLE Mesh 中,序列号用于确保消息的顺序和唯一性。当节点出现‘ignoring old seqauth’错误时,可能是因为序列号管理出现问题。可通过实现序列号持久化管理来解决该问题。 可使用 NVS(非易失性存储)来实现序列号的持久化管理。以下是示例代码(ESP32 平台): ```c #include "nvs_flash.h" #include "nvs.h" #define SERIAL_NUMBER_KEY "serial_number" // 保存序列号 void save_serial_number(uint32_t serial_number) { nvs_handle_t nvs_handle; esp_err_t err = nvs_open("storage", NVS_READWRITE, &nvs_handle); if (err != ESP_OK) { printf("Failed to open NVS storage: %d\n", err); return; } err = nvs_set_u32(nvs_handle, SERIAL_NUMBER_KEY, serial_number); if (err != ESP_OK) { printf("Failed to save serial number: %d\n", err); } nvs_commit(nvs_handle); nvs_close(nvs_handle); } // 读取序列号 uint32_t read_serial_number() { nvs_handle_t nvs_handle; esp_err_t err = nvs_open("storage", NVS_READWRITE, &nvs_handle); if (err != ESP_OK) { printf("Failed to open NVS storage: %d\n", err); return 0; } uint32_t serial_number = 0; err = nvs_get_u32(nvs_handle, SERIAL_NUMBER_KEY, &serial_number); if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) { printf("Failed to read serial number: %d\n", err); } nvs_close(nvs_handle); return serial_number; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值