1 rpc调用
1.1 调用自动生成代码client.lua提供的rpc调用方法
自动生成代码示例
function network_adapter_client:PSmsSmsGetChannelType(ctx, path_params, IsPowerOffPer)
return pcall(function()
local req = Sms.GetChannelTypeReq.new(IsPowerOffPer):validate()
local obj = self:GetSmsSmsObject(path_params)
return Sms.GetChannelTypeRsp.new(obj:GetChannelType(ctx, req:unpack(true)))
end)
end使用示例
local client = require 'network_adapter.client'
client:PSmsSmsGetChannelType() -- 方法调用1.2 使用自动生成代码client.lua获取的代理对象进行rpc调用
自动生成代码示例
function network_adapter_client:GetBlockIOObjects()
return get_non_virtual_interface_objects(self:get_bus(), 'bmc.kepler.Chip.BlockIO', true)
end使用示例
local client = require 'network_adapter.client'
local obj = client:GetBlockIOObjects()
obj:method() -- 方法调用1.3 使用sd_bus库提供的call、timeout_call、pcall、timeout_pcall、sync_call等方法
框架公共库sd_bus接口
- call(service_name, path, interface, method, signature, ...)
- timeout_call(timeout_ms, service_name, path, interface, method, signature, ...)
- pcall(service_name, path, interface, method, signature, ...)
- timeout_pcall(timeout_ms, service_name, path, interface, method, signature, ...)
- sync_call(service_name, path, interface, method, signature, ...)
local ok, rsp = pcall(self.bus.call, self.bus, handler.ServiceName, handler.path, 'bmc.kepler.CmdInfo', 'Process', 'a{ss}ayay', ipmi_context, req.Payload, json.encode(ctx_table))1.4 使用远程引用对象属性进行rpc调用
例:obj1对象包含远程引用对象属性,可以调用远程引用对象obj2资源树方法
obj1.remote_ref_obj_prop = obj22 信号订阅
2.1 自动生成代码client.lua提供的接口新增、接口删除、属性变更、自定义信号订阅接口
-- 接口新增
function network_adapter_client:OnBlockIOInterfacesAdded(cb)
self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_interfaces_added(self:get_bus(), '/bmc', cb, 'bmc.kepler.Chip.BlockIO')
end
-- 接口删除
function network_adapter_client:OnBlockIOInterfacesRemoved(cb)
self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_interfaces_removed(self:get_bus(), '/bmc', cb, 'bmc.kepler.Chip.BlockIO')
end
-- 属性变更
function network_adapter_client:OnFruCtrlPropertiesChanged(cb)
self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_properties_changed(self:get_bus(), '/bmc', cb, 'bmc.kepler.Systems.FruCtrl', {'PowerState', 'SysResetDetected'})
end
-- 自定义信号订阅接口
function network_adapter_client:SubscribeLldpConfigLLDPOverNCSIStateChanged(cb)
local sig = match_rule.signal('LLDPOverNCSIStateChanged', 'bmc.kepler.Managers.LldpConfig')
self.signal_slots[#self.signal_slots + 1] = self:get_bus():match(sig, function(msg)
cb(msg:read())
end)
end2.2 使用sd_bus库提供的match方法进行信号订阅
self.lldp_receive_listener = bus:match(sig, function(msg)
self:next_tick(function()
local ok, lldp_rsp = pcall(libmgmt_protocol.vdpci_lldp_parser, msg:read())
...
port:update_lldp_receive(lldp_rsp)
end)
end)3 属性设置
3.1 使用自动生成代码client.lua获取的代理对象设置属性
local client = require 'network_adapter.client'
local obj = client:GetBlockIOObjects()
obj.prop_name = prop_value3.2 使用远程引用对象设置被引用对象的属性
例:obj1.remote_ref_obj_prop引用远程对象obj2, obj1对象可设置obj2的属性
4 设置本组件属性
4.1 设置远程引用属性
-- prop_name为远程引用属性
obj:set_prop(prop_name, prop_value)或
-- prop_name为远程引用属性
obj.prop_name = prop_value4.2 设置其被引用属性在被设置时存在协程切换操作的本地引用属性
例:本地引用属性local_ref_prop_name,被引用属性prop在被设置时存在协程切换操作
obj.local_ref_prop_name = prop4.3 设置在property_before_change、property_changed回调处理中有切换协程操作的属性
function network_adapter_client:OnFruCtrlPropertiesChanged(cb)
self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_properties_changed(self:get_bus(), '/bmc', cb, 'bmc.kepler.Systems.FruCtrl', {'PowerState', 'SysResetDetected'})
end4.4 使用本地引用对象设置被设置时会切换协程的被引用对象的属性
例:obj1.prop_a引用obj2,obj2.prop_b注册了属性变更信号会切换协程
obj1.prop_a.prop_b = prop_value5 读取属性
5.1 读取property_read回调处理中有切换协程操作的属性
obj.property_read:on(function()
-- 包含切换协程操作的属性
end)5.2 读取引用属性
-- prop_name为引用属性
obj:get_prop(prop_name)或
obj.ref_prop_name5.3 使用自动生成代码client.lua获取的代理对象读取属性
function fructl.get_system_reset_flag()
local obj = fructl.get_fructl_obj()
if not obj then
log:error('[network_adapter]get_system_reset_flag: get power object failed')
return nil
end
return obj.SysResetDetected
end5.4 使用远程引用对象读取被引用对象的属性
例:obj1.remote_ref_obj_prop引用远程对象obj2,obj1对象可获取obj2的属性
5.5 使用本地引用对象读取,被读取时会切换协程的被引用对象的属性
例:本地引用对象obj,被引用对象的属性prop_name在被读取时会切换协程,obj获取属性prop_name,使用方法为obj.prop_name
6 其它libmc4lua库提供的API
6.1 任务管理机制task_mgmt
- task_mgmt.update_task(id, data)
task_mgmt.update_task(task_id, {State = task_state.Running, Progress = 20})6.2 sd_bus提供的ping方法
org_freedesktop_dbus.ping(bus, service_name, app_path)7 其它自动生成代码中会切换协程的API
7.1 自动生成代码client.lua中获取代理对象的方法
以下API获取失败重试过程中会睡眠等待
- get_non_virtual_interface_objects(bus, interface, retry)
- foreach_non_virtual_interface_objects(bus, interface, cb, retry)
例如network_adapter组件自动生成代码
function network_adapter_client:GetBlockIOObjects()
return get_non_virtual_interface_objects(self:get_bus(), 'bmc.kepler.Chip.BlockIO', true)
end
function network_adapter_client:ForeachBlockIOObjects(cb)
return foreach_non_virtual_interface_objects(self:get_bus(), 'bmc.kepler.Chip.BlockIO', cb, true)
end8 skynet中会切换协程的API
同一个skynet服务中的一条消息处理中,如果调用了一个阻塞API,那么它会被挂起。挂起过程中,这个服务可以响应其它消息,产生协程切换行为,这很可能造成时序问题,开发过程中需要重点注意这些操作
| API名称 | 功能描述 | 协程切换场景 |
|---|---|---|
| skynet.sleep(ti) | 阻塞API,将当前coroutine挂起ti个单位时间,一个单位时间是0.01秒 | 当前协程会被挂起指定的时间 |
| skynet.yield | 相当于skynet.sleep(0) | 让出当前任务执行流程,使本服务内其它任务有机会执行 |
| skynet.call | 阻塞住当前的coroutine,而没有阻塞整个服务。注意,在skynet.call之前获得的服务内的状态,到返回后,很有可能改变 | 如果接收方没有立即回应,则当前协程会被挂起并等待,直到收到回应 |
| skynet.wait | 挂起当前协程,之后由skynet.wakeup唤醒 | 让出当前任务执行流程,直到使用wakeup唤醒它 |
9 案例介绍
如下伪代码,增加调用skynet.sleep(10)等待100毫秒后,当前协程被挂起,服务中其它协程在执行,产生了协程切换,执行时序发生了变化。新增前串行执行结果是collections集合中先保存后删除的path,新增后变成先删除后新增,最后collections集合依旧保留着path,结果与预期不一致
local collections = {}
function classX:listen_sig_interfaces_added(sig_interfaces_added, source)
return self.bus:match(sig_interfaces_added, function(msg)
...
skynet(10)
collections[path] = true
...
end)
end
function classX:listen_sig_interfaces_removed(sig_interfaces_removed, source)
return self.bus:match(sig_interfaces_removed, function(msg)
...
collections[path] = nil
...
end)
end要避免协程切换导致的非预期结果,可以将串行执行的逻辑放到队列中执行,保证执行顺序符合预期,正常流程如下
local collections = {}
function classX:listen_sig_interfaces_added(sig_interfaces_added, source)
return self.bus:match(sig_interfaces_added, function(msg)
local _, interfaces_and_properties = msg:read('oa{sa{sv}}')
...
self.queue(function()
skynet(10)
collections[path] = true
end)
...
end)
end
function classX:listen_sig_interfaces_removed(sig_interfaces_removed, source)
return self.bus:match(sig_interfaces_removed, function(msg)
local _, interfaces_and_properties = msg:read('oa{sa{sv}}')
...
self.queue(function()
skynet(10)
collections[path] = nil
end)
...
end)
endskynet queue函数说明:queue函数调用得到一个新的临界区,可以保护一段代码不被同时运行。服务收到多条消息,一定是处理完一条后,才处理下一条,即使业务中包含有skynet.call这类的阻塞调用。一旦它们被挂起,新的消息到来后,新的处理流程会被排到队列尾部,等待前面的流程执行完毕才会开始