Clixon 线程模型
- clixon 线程模型解析
- 更新
1 | 2025.05.00 初始 初始 |
Clixon 线程模型
官方文档:
“The Clixon programs run as non-blocking single-threaded applications.”
“The callback code must be written with this programming model in mind. The behavior of the callback directly impacts the behavior of the caller and the whole system.”
Clixon 采用单线程 非阻塞 I/O 和事件驱动模型. 这意味着所有操作,包括插件回调,都在同一个主线程中顺序执行.
核心事件循环机制
Clixon 的事件处理核心是 lib/src/clixon_event.c
关键数据结构 struct event_data
1 | /* 事件数据结构 */ |
事件注册接口:
clixon_event_reg_fd()
: 注册文件描述符事件.clixon_event_reg_fd_prio()
: 注册带优先级的文件描述符事件.clixon_event_reg_timeout()
: 注册定时器事件.
clixon_event_loop()
是事件循环核心
select()
监听多个文件描述符- 当有描述符就绪时,依次
*e->e_fn
调用对应的回调函数 - 所有回调在同一个线程中顺序执行
模块中的事件注册与处理
Clixon 的各个主要模块,如 Backend 和 RESTCONF,都依赖此事件模型.
Backend 模块
Backend 服务在 backend_main.c
中启动.其 main
函数会调用 backend_server_socket()
来创建监听套接字.
1 | static int |
clixon_event_loop(h)
启动事件循环.
新的客户端连接请求, select
检测到套接字可读,调用 backend_accept_client
回调函数处理新的连接.
RESTCONF 模块
RESTCONF 服务 在 restconf_main_native.c
启动.通过 openssl_init_socket()
创建监听套接字,并为该套接字注册 restconf_accept_client
回调函数.
1 | // openssl_init_socket 函数片段 |
新连接到达时触发 restconf_accept_client
RESTCONF GET 请求处理流程示例
以一次简化的 RESTCONF GET 请求为例,说明其在单线程模型下的处理路径:
连接建立:
- RESTCONF 客户端发起连接.
- 事件循环检测到 RESTCONF 监听套接字可读,调用
restconf_accept_client
回调,建立连接,并为新的客户端连接套接字注册新的回调 (例如处理 HTTP 请求的函数).
请求接收与转换:
- 当客户端发送 GET 请求数据,对应客户端套接字变为可读.
- 事件循环调用注册的 HTTP 请求处理回调.
- 此回调解析 RESTCONF GET 请求,最终可能调用如
api_data_get()
->api_data_get2()
. api_data_get2()
将 RESTCONF 请求转换为 NETCONF GET 请求的 XML 消息.- 调用
clicon_rpc_get()
->clicon_rpc_msg()
向 Backend 发送此 NETCONF GET 请求.
Backend 处理连接与请求:
clicon_rpc_msg()
内部尝试连接 Backend.如果这是第一个到 Backend 的请求或连接已断开,会建立新的 Unix socket 连接.- Backend 的监听套接字 (之前由
backend_server_socket
创建并注册) 变为可读. - 事件循环调用
backend_accept_client
回调. backend_accept_client
接受来自 RESTCONF (作为客户端) 的连接,创建一个新的套接字s
代表这个连接,并为s
注册from_client
回调.
1
2
3
4
5
6// backend_accept_client 函数片段
if ((s = accept(fd, &from, &len)) < 0){ /* … */ } // fd 是监听套接字
// …
// 为新的客户端连接 s 注册 from_client 回调
if (clixon_event_reg_fd_prio(s, from_client, (void*)ce, "local netconf client socket", …) < 0)
goto done;Backend 消息处理与插件回调:
- 当 RESTCONF 通过
s
发送 NETCONF GET 请求消息后,套接字s
变为可读. - 事件循环调用
from_client
回调. from_client
读取消息,然后调用from_client_msg()
.from_client_msg()
进一步调用rpc_callback_call()
,它会根据 RPC 名称查找并执行注册的回调.对于<get>
操作,这通常是from_client_get
(在backend_rpc_init
中注册).
1
2
3
4// backend_main.c -> main()
// 注册 <get> 操作的回调
if (rpc_callback_register(h, from_client_get, NULL, NETCONF_BASE_NAMESPACE, "get") < 0)
// …from_client_get
调用get_common()
.- 如果请求的是
state data
(operational data),路径通常会导向get_state_data()
. get_state_data()
调用clixon_plugin_statedata_all()
,它会遍历所有已加载的 Backend 插件.clixon_plugin_statedata_all()
对每个插件调用clixon_plugin_statedata_one()
.clixon_plugin_statedata_one()
获取插件注册的ca_statedata
回调函数,并执行它.
1
2
3
4
5
6
7
8
9
10// clixon_plugin_statedata_one 函数片段
// 获取插件的 statedata 回调函数
if ((fn = clixon_plugin_api_get(cp)->ca_statedata) != NULL){
// …
// 执行插件的回调函数
if (fn(h, nsc, xpath, x) < 0){
// …
}
// …
}关键点: 所有这些步骤,从 RESTCONF 接收请求到 Backend 插件回调的执行,都在同一个主线程中按顺序发生.
- 当 RESTCONF 通过
响应返回:
- 插件回调填充数据后,结果会逐层返回,最终由 RESTCONF 模块发送 HTTP 响应给客户端.
sequenceDiagram
participant Client
participant RESTCONF as RESTCONF Module (Event Thread)
participant EventLoop as Clixon Event Loop
participant Backend as Backend Module (Event Thread)
participant Plugin as Backend Plugin (Event Thread)
Client ->> RESTCONF: HTTP GET Request
EventLoop ->> RESTCONF: Calls HTTP request handler
RESTCONF ->> RESTCONF: api_data_get() / clicon_rpc_get()
Note over RESTCONF, Backend: (Potentially) Establishes Unix socket to Backend
EventLoop ->> Backend: backend_accept_client() for new connection from RESTCONF
RESTCONF ->> Backend: Sends NETCONF <get> message
EventLoop ->> Backend: Calls from_client()
Backend ->> Backend: from_client_msg() -> rpc_callback_call()
Backend ->> Backend: from_client_get() -> get_common() -> get_state_data()
Backend ->> Backend: clixon_plugin_statedata_all()
Backend ->> Plugin: clixon_plugin_statedata_one() -> plugin_statedata_callback()
Plugin -->> Backend: Returns data
Backend -->> RESTCONF: Returns NETCONF <rpc-reply>
RESTCONF -->> Client: HTTP Response
总结
Clixon 的线程模型:
- 单线程执行: 所有 Clixon 程序 (包括 clixon_backend, clixon_cli, clixon_restconf) 均作为非阻塞单线程应用程序运行.
- 事件驱动: 通过
select()
监听文件描述符事件,触发对应回调 - 同步调用: 插件回调在主线程中同步执行,一个回调未完成前,其他请求无法处理
- 顺序处理: 事件回调按照注册优先级顺序执行,无并发机制
这种设计简化了并发控制和状态管理,但要求插件开发者编写非阻塞的回调代码,以避免长时间操作阻塞整个系统.