8 个版本
0.3.2 | 2022年12月16日 |
---|---|
0.3.1 | 2022年11月12日 |
0.3.0 | 2022年8月28日 |
0.2.4 | 2022年7月28日 |
#593 in 异步
每月 22 下载
390KB
10K SLoC
RSM: Rust 实时系统中间件
简介
实时系统定义为能够在特定确定时间内响应用户请求的系统。为了在通用计算机系统中实现这一目标,我们必须在软件系统上采用实时调度策略,并避免一些耗时操作,如同步 I/O 操作、内存垃圾回收和锁。
RSM 是用 Rust 编写的轻量级实时中间件实现,支持事件驱动、消息导向的无锁编程原则。在 RSM 中,每个软件模块都是一个 组件,通常是一个有限状态机,主要处理事件循环。每个组件可以实例化为多个任务,每个任务映射到一个专用的 OS 线程,并拥有自己的消息队列。
开发者可以分别设置任务的调度优先级和它们的消息队列长度,通常基于服务模型和性能 & 延迟要求。
RSM 适用于以下应用
- 网络设备控制平面,例如路由协议、服务控制
- 嵌入式系统应用
- 遥控系统
- 实时遥测和仪表
编程
概念
每个 RSM 组件都必须实现 rsm::Runnable trait 并提供一个任务创建回调函数。
main.rs 中的代码是一个 RSM 应用程序实现的示例。
pub trait Runnable {
fn on_init(&mut self,cid:&rsm_component_t);
fn on_timer(&mut self,cid:&rsm_component_t,timer_id:rsm_timer_id_t,timer_data:usize);
fn on_socket_event(&mut self,cid:&rsm_component_t,event:rsm_socket_event_t);
fn on_message(&mut self,cid:&rsm_component_t,msg_id:rsm_message_id_t,msg:&rsm_message_t);
fn on_close(&mut self,cid:&rsm_component_t);
}
type rsm_new_task = fn(cid: &rsm_component_t) -> &'static mut dyn Runnable
初始化 RSM
使用 rsm_init 函数初始化 rsm 系统,然后应用程序可以将它们的组件注册到 RSM。
rsm_init_cfg_t 是 RSM 的配置文件,它以 json 格式存在。rsm_init(conf: &config::rsm_init_cfg_t) -> errcode::RESULT
pub fn registry_component(cid: u32, attrs: &component_attrs_t, callback: rsm_new_task) -> errcode::RESULT
组件注册完成后,应调用 start_rsm() 函数来启动系统。
运行时
每个运行任务都可以通过 rsm_component_t 唯一标识
任务可以相互发送消息,包括普通消息或高优先级消息 pub fn send_asyn_msg(dst:&rsm_component_t,msg:rsm_message_t)->errcode::RESUL
pub fn send_asyn_priority_msg(dst:&rsm_component_t,msg:rsm_message_t)->errcode::RESULT
对于接收方,应用程序必须使用 msg.decode::(v) 来将消息恢复为应用程序定义的类型
RSM 还提供定时器服务,应用程序可以通过调用 set_timer 函数来设置定时器,一旦定时器设置并到期,rsm 任务将收到一个 on_timer 事件,该事件在 Runnable 特性中定义。
pub fn set_timer(dur_msec:u64,loop_count:u64,timer_data:usize)->Option<rsm_timer_id_t> pub fn kill_timer_by_id(timer_id:rsm_timer_id_t)->errcode::RESULT
调度优先级
RSM 支持几个预定义的任务优先级,它们映射到底层操作系统线程调度策略或优先级。
对于 Linux OS,实时优先级映射到 schedule_policy=SCHED_RR,非实时优先级映射到 policy=SCHED_OTHER。
对于 Windows,RSM 使用 THREAD_PRIORITY_TIME_CRITICAL 常量表示实时优先级。
pub enum E_RSM_TASK_PRIORITY { THREAD_PRI_LOW = 0, THREAD_PRI_NORMAL = 1, THREAD_PRI_HIGH = 2,
THREAD_PRI_REALTIME = 3, THREAD_PRI_REALTIME_HIGH = 4, THREAD_PRI_REALTIME_HIGHEST = 5, }
异步套接字 API
从 0.3.0 版本开始,RSM 组件 Runnable 特性中添加了套接字事件方法
fn on_socket_event(&mut self,cid:&rsm_component_t,event:rsm_socket_event_t);
并且 RSM 提供了几个 API 对象来实现异步套接字编程。应用程序可以初始化一个套接字,然后处理 RSM 发送的套接字事件(新的 TCP 套接字或可读套接字)
TCPListener
应用程序可以启动 TCPListener,设置负载均衡策略。
如果 tcplistener 的发起者组件有 4 个任务,RSM 将通过哈希算法将客户端连接分配给 4 个任务,并确保每个客户端连接仅由一个任务处理。
这是默认行为,应用程序可以通过设置不同的 LB_POLICY 来更改此行为
pub enum SOCKET_LB_POLICY { ///通过哈希结果将 TCP 客户端连接分配到所有组件实例 SOCK_LB_ALL_INSTANCE=0, ///TCP 连接仅由调用实例处理 SOCK_LB_CALLER_INSTANCE=1, ///将 TCP 连接分配到除了调用者之外的所有组件实例 SOCK_LB_EXCLUDE_CALLER_INSTANCE=2, }
TCPSocket
一个 Tcp 客户端套接字 API 对象,不持有任何套接字运行时状态
对于 RSM 消息接口,默认使用 socket_id,应用程序可以将此 ID 转换为 Socket API 对象,例如。
let mut sock=socket::TcpSocket::get_socket_by_id(event.socket_id);
UDPSocket
一个 UDP 套接字,无连接的套接字 API 包装器,其余部分与 TCPSocket 相似。
您可以使用以下方法从 socket_id 获取套接字实例。
let mut sock=socket::UdpSocket::get_socket_by_id(event.socket_id);
诊断
开发人员和用户可以使用 REST API 获取 RSM 的运行状态和统计信息。
内置 API
help,curl http://127.0.0.1:12000/rsm/help 获取任务运行状态, curl http://127.0.0.1:12000/rsm/task?1:2 获取组件配置,curl http://127.0.0.1:12000/rsm/component?1
应用程序定义的 OAM API
应用程序模块必须实现 OamReqCallBack 函数,并调用 RegisterOamModule 来注册自己 OamReqCallBack=fn(op:E_RSM_OAM_OP,url:&String,param:&String)->oam_cmd_resp_t
///注册模块回调,urls 是 REST API URL 的列表,不包括前缀 /rsm 和随后的 "?" 后的 id RegisterOamModule(urls:&[String], callback:OamReqCallBack)
其他服务 & 库函数
xlog 服务
xlog 服务基于客户端/服务器架构,客户端简单地将日志消息发送到服务器,服务器负责日志文件操作,在应用程序的上下文中避免写入磁盘,这对于实时应用非常重要。
let log = rsm::new_xlog(module_name:&str)->xlog::xlogger_t;
log.Errorf(postion, err, logDesc);
其他线程安全算法和数据结构
- spin_lock_t,基于自旋锁的原子操作锁。
- AtomicQueue,基于spin_lock
- TsIdAllocator,线程安全ID分配器
- 位图
- 以太网数据包解析器
- IP路由表
- 其他几个网络功能和方法包装器
如果您有任何问题,请发送电子邮件至:[email protected]
依赖关系
~4-18MB
~232K SLoC