插件化开发Yate【3】 - 热插拔插件Demo

Yate插件支持热插拔功能,即可以在系统运行时,动态卸载插件动态库,并在需要的时候,加载动态库;也可以修改动态库,并重新加载插件。从而实现插件的热插拔。

本文将以一个简单的插件为例,说明Yate插件热插拔功能的实现

插件的源码地址为:https://gitee.com/iseelgy/demo01

#include "Aux_cmd_define.h"
#include "Aux_yate_hal.h"
#include <functional>


//=====================================================================================================================
//=====================================================================================================================
//=====================================================================================================================
//=====================================================================================================================

using namespace TelEngine;

#define MY_PLUGIN_NAME "demo01"

namespace {	 // 使用匿名空间


	//===============================
	// 类定义


	class MyPlugin : public Plugin
	{
	public:


		MyPlugin() : Plugin(MY_PLUGIN_NAME)
			, m_initialized(false) 
		{
			
		}


		~MyPlugin() {

		}

		virtual void initialize(void);

		void onEngineStart();
		void onEngineStop();

		bool onYateMessage(Message& msg);
		bool onDemoMessage(Message& msg);

		bool unload();

	private:

		bool m_initialized;

		YateMsgHandler* _debug_hdl=0;

		// debug消息
		bool onYateDebug(Message& msg);

	protected:
		std::vector<MessageHandler*> _msg_hanlers;

	};

	INIT_PLUGIN(MyPlugin);


	//===============================
	// 变量定义
	static Configuration s_cfg;


	//===============================
	// 功能实现

	void MyPlugin::initialize()
	{
		Output( "Initializing module " MY_PLUGIN_NAME);

		s_cfg = Engine::configFile(MY_PLUGIN_NAME);

		s_cfg.load();

		int level = s_cfg.getIntValue("debug", "level", TelEngine::debugLevel());
		withDebugEnabler()->debugLevel(level);

		if (!m_initialized) {

			// 设置初始化标记,避免多次调用情况出现问题
			m_initialized = true;


			// 订阅消息处理
			YateMsgHandler* handler;

			// 引擎启动消息
			handler = new YateMsgHandler(MSG_ENGINE_START);
			handler->_handler = [this](Message& msg) {
				this->onEngineStart();
				return false;
			};
			Engine::install(handler);
			_msg_hanlers.push_back(handler);

			// 引擎退出消息
			handler = new YateMsgHandler(MSG_ENGINE_STOP);
			handler->_handler = [this](Message& msg) {
				this->onEngineStop();
				return false;
			};
			Engine::install(handler);
			_msg_hanlers.push_back(handler);

			int priority = s_cfg.getIntValue("general", "priority", 100);
			handler = new YateMsgHandler(MSG_YATE, priority);
			handler->_handler = [this](Message& msg) {
				return this->onYateMessage(msg);
			};
			Engine::install(handler);
			_msg_hanlers.push_back(handler);

			handler = new YateMsgHandler("demo01.cmd");
			handler->_handler = [this](Message& msg) {
				return this->onDemoMessage(msg);
			};
			Engine::install(handler);
			_msg_hanlers.push_back(handler);


		}

		// 调试消息处理
		int on = s_cfg.getBoolValue("debug", "on", false);
		if (on) {
			if (!_debug_hdl) {
				int priority = s_cfg.getIntValue("debug", "priority", 100);
				_debug_hdl = new YateMsgHandler("Yateshop.debug", priority);
				_debug_hdl->_handler = [this](Message& msg) {
					return this->onYateDebug(msg);
				};
				Engine::install(_debug_hdl);
			}
		}
		else {
			if (_debug_hdl) {
				Engine::uninstall(_debug_hdl);
				delete _debug_hdl;
				_debug_hdl = nullptr;
			}
		}


	}


	void MyPlugin::onEngineStart()
	{

		//S_INFO("onEngineStart, ***************************");
		//Y_WARN("onEngineStart, ***************************");

		const String& shared = Engine::sharedPath();

		const String& user = Engine::configPath(true);

		const String& config = Engine::configPath(false);


	}


	void MyPlugin::onEngineStop()
	{
		S_INFO( "onEngineStop ")  ;

	}


	bool MyPlugin::onYateMessage(Message& msg)
	{

		String Op = msg.getParam("Op");

		if (Op == "demo.01"){

			int what = msg.getIntValue("what");
			int arg1 = msg.getIntValue("arg1");
			int arg2 = msg.getIntValue("arg2");

			Y_INFO( fmt::format("demo1 what {}, arg1 {}, arg2 {}", what, arg1, arg2) );


			return true;
		}

		if (Op == "demo") {

			int what = msg.getIntValue("what");
			int arg1 = msg.getIntValue("arg1");
			int arg2 = msg.getIntValue("arg2");

			S_INFO("demo, ******");

			return true;
		}

		if (Op == "demo.all") {
			S_INFO("demo.all, ******");
			return false;
		}

		return false;
	}

	bool MyPlugin::onDemoMessage(Message& msg)
	{
		S_INFO("onDemoMessage");
		msg.setParam("hello", "yate");
		return true;
	}
	
	bool MyPlugin::onYateDebug(Message& msg)
	{

		return false;
	}


	bool MyPlugin::unload()
	{
		// 注销消息处理器,以支持动态卸载模块

		if (_debug_hdl) {
			Engine::uninstall(_debug_hdl);
			delete _debug_hdl;
			_debug_hdl = nullptr;
		}

		auto i = _msg_hanlers.begin();
		for (; i != _msg_hanlers.end(); i++) {
			Engine::uninstall(*i);
			delete *i;
		}
		_msg_hanlers.clear();

		return true;
	}


}

/*
* 支持模块内日志宏
*/


TelEngine::DebugEnabler * withDebugEnabler()
{
	return &__plugin;
}

TE::Configuration * get_configuration()
{
	return &s_cfg;
}

const char* get_app_name()
{
	return s_cfg.getValue("general", "app.name", __plugin.name());
}


/*
 *
 *	支持模块动态卸载接口
 *
*/
UNLOAD_PLUGIN(unloadNow)
{
	if (unloadNow) {
		// 动态卸载模块
		if (!__plugin.unload()) {
			S_INFO("unload module false, " << __plugin.name().safe());
			return false;
		}
		S_INFO("unload module, " << __plugin.name().safe());
		return true;
	}
	else {
		// 检测是否能够动态卸载
		return true;
	}
}

热插拔插件需要导出函数 extern "C" bool _unload(bool unloadNow)

可以通过宏UNLOAD_PLUGIN(unloadNow)实现。

在卸载函数中,需要管理该插件使用的对象,并释放对象资源。如果订阅的消息处理,也需要取消订阅的消息,并释放分配的内存,返回true,以确认卸载完成。如果无法释放资源,可用通过返回false值,阻止卸载。

插件卸载是通过消息engine.command实现的,该功能需要定义line参数。

TE::String name = "demo01";

TE::String line = "module unload ";
line += name;

Message msg("engine.command");
msg.setParam("line", line);
        
if (Engine::dispatch(msg)) {

}
else {
            
}

插件加载载是通过消息engine.command实现的,该功能需要定义line参数

TE::String strSoPathname = "./modules/demo01.yate";

TE::String line = "module load ";
line += strSoPathname;

Message msg("engine.command");
msg.setParam("line", line);
        
if (Engine::dispatch(msg)) {

}
else {
            
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值