RPC 调用概览

一次完整的 URPC 函数调用涉及 Client 端、Server 端、Worker 端的协同。下图展示了从 urpc_func_call() 到 urpc_func_return() 的完整代码路径。

sequenceDiagram participant C as Client App participant CP as dp.c (Client) participant Q as Queue (Jetty) participant U as URMA (UB Bus) participant QS as Queue (Jetty) participant SP as dp.c (Server) participant S as Server App C->>CP: urpc_func_call() Note over CP: 1. channel_get()
2. RR select queues
3. req_entry_get()
4. fill REQ header CP->>Q: queue->ops->send() Q->>U: urma_post_jetty_send_wr() U->>QS: hardware transport QS->>SP: urma_poll_jfc() Note over SP: 5. process_rx_cr()
6. urpc_func_exec()
7. sync_handler() SP->>S: user function executes S-->>SP: return result SP->>QS: urpc_func_return() QS->>U: urma_post_jetty_send_wr() U->>Q: hardware transport Q->>CP: urma_poll_jfc() Note over CP: 8. process_tx_msg()
9. user callback CP-->>C: callback(rsps, err)

Client 端:urpc_func_call()

flowchart TB N0[uint32_t chid, urpc_call_wr_t *wr, urpc_call_option_t *option] N1[chid] N2[channel, option] N3[l_queue, QUEUE_CTX_TYPE_TX] N4[channel, ctx] N5[size-1] N6[req_entry->valid] N7[ req_id++; linear_probe ] N8[urpc_req_head_t *] N9[0] N10[req_head, ack, channel_id] N11[req_head, func_id, len, req_id, func_defined] N12[func_defined, channel, r_queue, req_entry, wr] N13[ctx, chid, l_qh, req_id, wr, option] N14[option, req_entry, func_defined, wr] N15[l_queue, &queue_wr]

Server 端:接收 REQ 与分发

flowchart TB N0[jfr_jfc, cr_cnt, cr] N1[local_queue, msg, cr] N2[func_id, args, args_sge_num, &rsps, &rsps_sge_num] N3[func_id] N4[args, args_sge_num, ctx, &rsps, &rsps_sge_num]

Server 端:返回 RSP

// dp.c: urpc_func_return()
int urpc_func_return(uint64_t urpc_qh, void *req_ctx, urpc_return_wr_t *wr, ...) {
    // 1. 获取 queue 和 req_ctx
    l_queue = (queue_t *)urpc_qh;
    ctx = (req_ctx_t *)req_ctx;

    // 2. 分配 tx_ctx
    tx_ctx = queue_ctx_get(l_queue, QUEUE_CTX_TYPE_TX);

    // 3. 填充 RSP 协议头
    rsp_head = (urpc_rsp_head_t *)rsps[0].addr;
    urpc_rsp_fill_basic_info(rsp_head, status, client_channel, ack);
    urpc_rsp_fill_one_req_info(rsp_head, req_id, response_total_size, option);

    // 4. 发送 RSP
    l_queue->ops->send(l_queue, &queue_wr);
}

Client 端:接收 ACK/RSP

flowchart TB N0[jfs_jfc, ...] N1[jfr_jfc, ...] N2[req_entry, err_code] N3[rsps, rsps_sge_num, err, arg, ctx]

tx_ctx 生命周期(双次释放机制)

URPC 使用 tx_ctx_try_put() 实现双次释放:tx_ctx 必须经历两个"完成事件"才能被释放回 slab:

flowchart TB N0[tx_ctx_t *ctx] N1[ctx->free_events_completed == FALSE] N2[QUEUE_CTX_TYPE_TX, ctx]

这解决了 TX CQE 和 ACK/RSP 并发到达时的竞态条件——无论哪个先到,都必须等两个都完成。


调用模式对比

模式ACKRSPtx_ctx 释放时机适用场景
EARLY_RSP无无TX CQE 后立即释放流式调用,无需等待结果
WAIT_RSP无有收到 RSP 后释放标准同步调用
ACK有有收到 ACK+RSP 后释放需要确认参数接收
ACK | EARLY_RSP有无收到 ACK 后释放流式调用,需确认接收