插件化开发Yate【4】 - 事件循环插件

事件循环插件主要通过封装libevent、evpp开源库实现事件循环处理,通过Yate实现事件处理插件化,通过Yate框架,可用将event的处理通过接口的方式,提供为不同的插件,方便使用事件方式进行代码开发。

事件循环处理

为了支撑事件处理接口定义,定义了几个基础类进行支撑,即Any(任意数据指针包装类),Duration(时间周期类),Timer(定时器类)

Any类

Any类的定义如下,

	// A variant type that can hold any other type.
	//
	// Usage 1 :
	//
	//    Buffer* buf(new Buffer());
	//    Any any(buf);
	//    Buffer* b = any_cast<Buffer*>(any);
	//    assert(buf == b);
	//    delete buf;
	//
	//
	// Usage 2 :
	//
	//    std::shared_ptr<Buffer> buf(new Buffer());
	//    Any any(buf);
	//    std::shared_ptr<Buffer> b = any_cast<std::shared_ptr<Buffer>>(any);
	//    assert(buf.get() == b.get());
	//
	//
	// Usage 3 :
	//
	//    std::shared_ptr<Buffer> buf(new Buffer());
	//    Any any(buf);
	//    std::shared_ptr<Buffer> b = any.Get<std::shared_ptr<Buffer>>();
	//    assert(buf.get() == b.get());
	//
	class Any {

	public:
		Any() : content_(nullptr) {}
		~Any() {
			delete content_;
		}

		template<typename ValueType>
		explicit Any(const ValueType& value)
			: content_(new Holder<ValueType>(value)) {}

		Any(const Any& other)
			: content_(other.content_ ? other.content_->clone() : nullptr) {}

	public:
		Any& swap(Any& rhs) {
			std::swap(content_, rhs.content_);
			return*this;
		}

		template<typename ValueType>
		Any& operator=(const ValueType& rhs) {
			Any(rhs).swap(*this);
			return*this;
		}

		Any& operator=(const Any& rhs) {
			Any(rhs).swap(*this);
			return*this;
		}

		bool IsEmpty() const {
			return !content_;
		}

		const std::type_info& GetType() const {
			return content_ ? content_->GetType() : typeid(void);
		}

		template<typename ValueType>
		ValueType operator()() const {
			return Get<ValueType>();
		}

		template<typename ValueType>
		ValueType Get() const {
			if (GetType() == typeid(ValueType)) {
				return static_cast<Any::Holder<ValueType>*>(content_)->held_;
			}
			else {
				return ValueType();
			}
		}

	protected:

		class PlaceHolder {
		public:
			virtual ~PlaceHolder() {}
		public:
			virtual const std::type_info& GetType() const = 0;
			virtual PlaceHolder* clone() const = 0;
		};

		template<typename ValueType>
		class Holder : public PlaceHolder {
		public:
			Holder(const ValueType& value)
				: held_(value) {}

			virtual const std::type_info& GetType() const {
				return typeid(ValueType);
			}

			virtual PlaceHolder* clone() const {
				return new Holder(held_);
			}

			ValueType held_;
		};

	protected:
		PlaceHolder* content_;
		template<typename ValueType>
		friend ValueType* any_cast(Any*);
	};

	template<typename ValueType>
	ValueType* any_cast(Any* any) {
		if (any && any->GetType() == typeid(ValueType)) {
			return &(static_cast<Any::Holder<ValueType>*>(any->content_)->held_);
		}

		return nullptr;
	}

	template<typename ValueType>
	const ValueType* any_cast(const Any* any) {
		return any_cast<ValueType>(const_cast<Any*>(any));
	}

	template<typename ValueType>
	ValueType any_cast(const Any& any) {
		const ValueType* result = any_cast<ValueType>(&any);
		//assert(result);

		if (!result) {
			return ValueType();
		}

		return *result;
	}

Any类即可以包装裸指针,也可以包装shared_ptr指针,

    裸指针用法:
        Buffer* buf(new Buffer());
        Any any(buf);
        Buffer* b = any_cast<Buffer*>(any);
        assert(buf == b);
        delete buf;

  shared_ptr指针用法

     std::shared_ptr<Buffer> buf(new Buffer());

     Any any(buf);

    std::shared_ptr<Buffer> b1 = any_cast<std::shared_ptr<Buffer>>(any);

   std::shared_ptr<Buffer> b2 = any.Get<std::shared_ptr<Buffer>>();

Duration类

Duration定义如下:

	// A Duration represents the elapsed time between two instants
	// as an int64 nanosecond count. The representation limits the
	// largest representable duration to approximately 290 years.
	class Duration {
	public:
		//static const int64_t kNanosecond; // = 1LL
		//static const int64_t kMicrosecond;// = 1000
		//static const int64_t kMillisecond;// = 1000 * kMicrosecond
		//static const int64_t kSecond; // = 1000 * kMillisecond
		//static const int64_t kMinute; // = 60 * kSecond
		//static const int64_t kHour;   // = 60 * kMinute
	public:
		Duration();
		explicit Duration(const struct timeval& t);
		explicit Duration(int64_t nanoseconds);
		explicit Duration(int nanoseconds);
		explicit Duration(double seconds);

		// Nanoseconds returns the duration as an integer nanosecond count.
		int64_t Nanoseconds() const;

		// These methods return double because the dominant
		// use case is for printing a floating point number like 1.5s, and
		// a truncation to integer would make them not useful in those cases.

		// Seconds returns the duration as a floating point number of seconds.
		double Seconds() const;

		double Milliseconds() const;
		double Microseconds() const;
		double Minutes() const;
		double Hours() const;

		struct timeval TimeVal() const;
		void To(struct timeval* t) const;

		bool IsZero() const;
		bool operator< (const Duration& rhs) const;
		bool operator<=(const Duration& rhs) const;
		bool operator> (const Duration& rhs) const;
		bool operator>=(const Duration& rhs) const;
		bool operator==(const Duration& rhs) const;

		Duration operator+=(const Duration& rhs);
		Duration operator-=(const Duration& rhs);
		Duration operator*=(int ns);
		Duration operator/=(int ns);

	private:
		int64_t ns_; // nanoseconds
	};

类的功能比较简单,看代码就能够确定其具体意义了。

Timer类

Timer类定义如下

    class Timer;

    typedef std::shared_ptr<Timer> TimerPtr;

	class Timer : public std::enable_shared_from_this<Timer> {
	public:
		typedef std::function<void()> Functor;

		virtual ~Timer() = default;

		// It is thread safe.
		// Start this timer.
		virtual void Start() = 0;

		// Cancel the timer and the cancel_callback_ will be invoked.
		virtual void Cancel() = 0;

		virtual void set_cancel_callback(const Functor& fn) = 0;

	};

Timer类为定时器的接口类,定义了定时器启动、定时器取消,以及设置定时器取消的回调函数功能的接口功能,该接口会被EventLoop实现和调用。

事件循环处理

事件循环接口通过EventLoop类定义,类的代码如下:

	class EventLoop {

	public:

		typedef std::function<void()> Functor;

	public:

		virtual ~EventLoop() = default;

		virtual TimerPtr RunAfter(Duration delay, const Functor& f) = 0;
		TimerPtr RunAfter(double delay_ms, const Functor& f) {
			return RunAfter(Duration(delay_ms / 1000), f);
		}

		// RunEvery executes Functor f every period interval time.
		virtual TimerPtr RunEvery(Duration interval, const Functor& f) = 0;
		TimerPtr RunEvery(double interval_ms, const Functor& f) {
			return RunEvery(Duration(interval_ms / 1000), f);
		}

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

		virtual TimerPtr RunAfter(Duration delay, Functor&& f) = 0;
		TimerPtr RunAfter(double delay_ms, Functor&& f) {
			return RunAfter(Duration(delay_ms / 1000), f);
		}

		// RunEvery executes Functor f every period interval time.
		virtual TimerPtr RunEvery(Duration interval, Functor&& f) = 0;
		TimerPtr RunEvery(double interval_ms, Functor&& f) {
			return RunEvery(Duration(interval_ms / 1000), f);
		}

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

		virtual void RunInLoop(const Functor& handler) = 0;
		virtual void QueueInLoop(const Functor& handler) = 0;

		virtual void RunInLoop(Functor&& handler) = 0;
		virtual void QueueInLoop(Functor&& handler) = 0;

		virtual size_t GetPendingQueueSize() = 0;
		virtual bool IsPendingQueueEmpty() = 0;

		virtual bool IsInLoopThread() const = 0;

		virtual const std::thread::id& tid() const = 0;

		void set_context(const Any& c) {
			context_[0] = c;
		}
		const Any& context() const {
			return context_[0];
		}
		void set_context(int index, const Any& c) {
			assert(index < kContextCount && index >= 0);
			context_[index] = c;
		}
		const Any& context(int index) const {
			assert(index < kContextCount && index >= 0);
			return context_[index];
		}


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

			// @brief Run the IO Event driving loop forever
			// @note It must be called in the IO Event thread
		virtual void Run() = 0;

		// @brief Stop the event loop
		virtual void Stop() = 0;

	protected:

		enum { kContextCount = 16, };
		Any context_[kContextCount];
	};
}

一次定时器事件通过接口RunAfter实现,接口参数包括定时间隔和响应函数,返回定时器指针,可以通过该指针取消还未触发的定时器事件。

周期定时器通过接口RunEvery实现,接口参数包括定时间隔和响应函数,返回定时器指针,可以通过该指针取消该的定时器事件。

在事件循环中处理事件通过接口RunInLoop实现,如果在事件循环中调用,则会直接处理,如果在事件循环外调用,则会将该事件插入到RunInLoop事件循环队列中,等待循环线程依次处理。

接口QueueInLoop将事件处理插入到EventLoop事件循环队列中,等待循环线程依次处理。

GetPendingQueueSize接口获取事件队列中还未处理事件的数量。

IsPendingQueueEmpty接口判断事件队列是否为空。

IsInLoopThread接口用于判断当前代码是否在EventLoop线程中进行处理。       

QueueInLoop还提供了多个任意对象的Any包装,可以方便管理处理循环的上下文信息。

关于事件循环的详细信息,请参看Evpp的代码。

循环事件插件隔离

通过Yate插件系统,可以进行接口隔离,即在一个特定的插件中实现事件循环插件,而在其他的插件中通过接口调用事件循环接口,由于插件的隔离作用,可以有效避免接口实现与接口调用之间可能导致的库冲突等问题,接口实现也可以采用多种技术实现,而事件接口调用方通过插件系统的消息机制,选择合适的接口实现。

事件接口API

事件接口API通过类RxEventApi定义

	class RxEventApi : public std::enable_shared_from_this<RxEventApi> {

	public:
		virtual ~RxEventApi() = default;
		virtual const std::string& get_name() = 0;
		virtual const std::string& get_version() = 0;
		virtual EventLoop* get_main_loop_np() = 0;
		virtual std::shared_ptr< EventLoop > get_main_loop() = 0;
		virtual std::vector< EventLoopPtr > get_loop_pool() = 0;
		virtual std::shared_ptr< EventLoop > get_task_loop(const std::string& task) = 0;
		virtual std::vector< EventLoopPtr > get_task_thread_pool(const std::string& task) = 0;

	};

定义了主事件处理循环获取接口,事件循环池获取接口,根据名称获取事件循环、事件循环池的功能。     

事件循环接口实现                      

事件循环接口实现插件通过开源工程demoevpploop: evpp事件处理提供,该工程的实现依赖libevent、evpp、librxevent开源库。

libevent库地址:https://gitee.com/iseelgy/libevent

evpp库地址:https://gitee.com/iseelgy/evpp 

librxevent:https://gitee.com/iseelgy/librxevent

librxevent提供了RxEventApi接口的实现,demoevpploop提供了RxEventApi的实例化和Yate消息处理,提供RxEventApi的查询、关闭和生成等功能。

在demoevpploop插件工程中,事件循环接口主要通过EvppShop进行管理,

EvppShop类定义如下

class EvppShop {

	using RxEventApi = TE::RxEventApi;
	using RxEventApiImpl = TE::RxEventApiImpl;

	using Duration = TE::Duration;
	using EventLoop = TE::EventLoop;

	typedef std::function<void()> DoneCallback;


public:

	EvppShop();

	~EvppShop();

	EventLoop* loop() {
		if (_rx_event_api) {
			return _rx_event_api->get_main_loop_np();
		}
		return 0;
	}

	bool start(uint32_t thread_num=0);

	void stop(DoneCallback cb = DoneCallback());

	void stopInLoop(DoneCallback on_stopped_cb);

	void onStart();

	// 接口
	std::shared_ptr<RxEventApiImpl> _rx_event_api;

	// 服务端
	std::shared_ptr<EvppServer> _evpp_server;

	bool onMessage(TE::Message& msg);

public:

	void demo();


public:

};

EvppShop主要提供start、stop接口,用于启动和关闭事件循环。

start函数

代码如下

bool EvppShop::start(uint32_t thread_num )
{
    if (_rx_event_api) {
        Y_ERROR("already running");
        return false;
    }

    auto api = std::make_shared<RxEventApiImpl>("Demo");
    RxEventApiImpl* impl = (RxEventApiImpl * )api.get();
	if (!impl->start(thread_num, std::bind(&EvppShop::onStart, this))) {
		Y_ERROR("RxEventApiImpl  start error");
		return false;
	}
	_rx_event_api = api;
	return true;
}

start的功能比较简单,

1. 首先判断接口是否已经实现,如果已经实现,返回失败

2. 生成接口的实现对象RxEventApiImpl,并进行启动(将启动主事件循环、thread_num个事件循环池),如果失败,返回错误; 启动成功后,将调用EvppShop::onStart进行后续处理。

3. 保存对象指针,返回结果,并在事件循环中进行后续处理。

后续处理的代码如下,

void EvppShop::onStart()
{
    EventLoop* l = loop();

    Y_INFO("EvppShop::onStart(), ******");

	//loop()->IsRunning();

	loop()->RunInLoop([]() {
		Y_INFO("loop()->RunInLoop(), ++++++");
		});

	auto* cfg = get_configuration();

	// 配置启动任务相关事件循环
	for (unsigned int i = 0; i < cfg->sections(); i++) {
		TE::NamedList* acct = cfg->getSection(i);
		if (!acct) {
			continue;
		}
		if (!acct->startsWith("loop_")) {
			continue;
		}
		bool start = cfg->getBoolValue(*acct, "start", true);
		if (!start) {
			return;
		}
		std::string task = acct->substr(5).safe();
		_rx_event_api->create_task_loop(task);
	}

	evpp::EventLoop* evloop = 0;
	evpp::EventLoopThreadPool* evpool = 0;

	if (typeid(l) == typeid(evloop)) {

		evpp::RxEventApiImpl* impl = (evpp::RxEventApiImpl*)_rx_event_api.get();

		evpool = impl->_tpool.get();
		evloop = (evpp::EventLoop * )l;
	}
	else {

		TE::RxEventApiImpl* impl = (TE::RxEventApiImpl*)_rx_event_api.get();
		
		evloop = impl->get_evpp_main_loop().get();
		evpool = impl->get_evpp_thread_pool().get();
	}

	_evpp_server = std::make_shared<EvppServer>(_rx_event_api);


	// 通知消息, 已经启动
	TE::Message* msg = new TE::Message(MSG_YATE, 0, true);

	msg->setParam("Op", "EventLoop.start");
	msg->setParam("app.name", get_app_name());

	AnyRefObject<RxEventApi>* user_data = new AnyRefObject<RxEventApi>();

	user_data->set(_rx_event_api);

	msg->userData(user_data);

	// 释放本地引用,由msg消息持有引用
	user_data->deref();

	TE::Engine::enqueue(msg);

}

在onStart函数中,会通过Yate消息,发送广播消息,系统中的其他插件,可以通过订阅消息,处理事件循环上线的情况。

stop函数

代码如下:

void EvppShop::stop(DoneCallback cb)
{
    if (!_rx_event_api) {
        return;
    }

	S_INFO("EvppShop::stop, ****");

	TE::Message msg(MSG_YATE, nullptr, true);

	msg.setParam("Op", "EventLoop.stop");
	msg.setParam("app.name", get_app_name());

	// 处理结束, 通过消息进行
	TE::Engine::dispatch( msg );

    stopInLoop( cb );

    RxEventApiImpl* impl = (RxEventApiImpl*)_rx_event_api.get();

    impl->stop();

    _rx_event_api.reset();

}

首先通过TE::Engine::dispatch( msg )消息,通知其他插件,关闭对于接口RxEventApi的使用,并调用,

然后进行清理动作,释放相关资源;

最后关闭事件循环框架,置空_rx_event_api指针。

插件提供了几个Yate消息处理函数,用于管理_rx_event_api,代码如下:

	bool MyPlugin::onYateMessage(Message& msg)
	{
		String Op = msg.getParam("Op");

		if (Op == "RxEventApi") {
			Lock lck(this);	 // 消息多线程保护
			if (!_evpp_shop) {
				return false;
			}
			if (!_evpp_shop->_rx_event_api) {
				return false;
			}
			AnyRefObject<TE::RxEventApi>* user_data = new AnyRefObject<TE::RxEventApi>();
			user_data->set(_evpp_shop->_rx_event_api);
			msg.userData(user_data);
			// 释放本地引用,由msg消息持有引用
			user_data->deref();
			msg.retValue() = "ok";
			return true;
		}

		if (Op == "evpp::RxEventApi") {
			Lock lck(this); // 消息多线程保护
			if (!_evpp_shop) {
				return false;
			}
			if (!_evpp_shop->_rx_event_api) {
				return false;
			}

			if (!_evpp_shop->_rx_event_api->_attch_evpp_api) {
				return false;
			}
			AnyRefObject<evpp::RxEventApi>* user_data = new AnyRefObject<evpp::RxEventApi>();
			user_data->set(_evpp_shop->_rx_event_api->_attch_evpp_api);
			msg.userData(user_data);
			// 释放本地引用,由msg消息持有引用
			user_data->deref();
			msg.retValue() = "ok";
			return true;
		}


		if (Op == "close.RxEventApi") {
			Lock lck(this); // 消息多线程保护
			if (!_evpp_shop) {
				return false;
			}
			_evpp_shop->stop();
			_evpp_shop.reset();
			return true;
		}

		if (Op == "Evpp.loop") {
			Lock lck(this); // 消息多线程保护
			if (!_evpp_shop) {
				return false;
			}
			return _evpp_shop->onMessage(msg);
		}

		return false;
	}

事件循环接口调用

事件循环接口RxEventApi的使用Demo,在工程demo02中,源码地址为 https://gitee.com/iseelgy/demo02

通过类YateLoopShop进行管理,类定义如下

class YateLoopShop {

public:

	using Duration = TE::Duration;
	using EventLoop = TE::EventLoop;
	using RxEventApi = TE::RxEventApi;


public:

	YateLoopShop(std::shared_ptr<RxEventApi> api);

	~YateLoopShop();

	EventLoop* loop() {
		return _yateloop;
	}

public:


	void stop();

	void start();

protected:

	// 接口
	std::shared_ptr<RxEventApi> _rx_event_api;

	std::atomic_bool _stopped;

	EventLoop* _yateloop = nullptr;

	void stopInLoop();

	// 判断资源是否已经清理完成
	bool is_clean();

	// 等待资源清理
	void waiting_clean();

};

typedef std::shared_ptr<YateLoopShop> YateLoopShopPtr;

在插件中,定义了消息处理函数,用于处理与API相关的消息事件

		if (Op == "EventLoop.start"){
			String app = s_cfg.getValue("general", "app.name", __plugin.name());
			if (app != msg.getParam("app.name")) {
				return false;
			}
			std::shared_ptr<RxEventApi> api;
			api = AnyRefObject_sp<TE::RxEventApi>(msg);
			if (!api) {
				return false;
			}
			_evpp_shop = std::make_shared<YateLoopShop>(api);
			api->get_main_loop_np()->QueueInLoop(
				std::bind(&YateLoopShop::start, _evpp_shop.get())
			);
			return false;
		}

		if (Op == "EventLoop.stop"){
			String app = s_cfg.getValue("general", "app.name", __plugin.name());
			if (app != msg.getParam("app.name")) {
				return false;
			}
			if (_evpp_shop) {
				_evpp_shop->stop();
				_evpp_shop.reset();
			}
			return false;
		}

用于处理消息处理循环上线通知、下线通知;在上线通知中,通过Yate消息体携带的接口指针,初始化YateLoopShop对象,后续即可通过RxEventApi,使用事件处理机制。

在下线通知中,释放资源,关闭事件循环处理流程。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值