此项目是根据sylar框架实现,是从零开始重写sylar,也是对sylar丰富与完善
项目地址:https://gitee.com/lzhiqiang1999/server-framework
项目介绍:实现了一个基于协程的服务器框架,支持多线程、多协程协同调度;支持以异步处理的方式提高服务器性能;封装了网络相关的模块,包括socket、http、servlet等,支持快速搭建HTTP服务器或WebSokcet服务器。
详细内容:日志模块,使用宏实现流式输出,支持同步日志与异步日志、自定义日志格式、日志级别、多日志分离等功能。线程模块,封装pthread相关方法,封装常用的锁包括(信号量,读写锁,自旋锁等)。IO协程调度模块,基于ucontext_t实现非对称协程模型,以线程池的方式实现多线程,多协程协同调度,同时依赖epoll实现了事件监听机制。定时器模块,使用最小堆管理定时器,配合IO协程调度模块可以完成基于协程的定时任务调度。hook模块,将同步的系统调用封装成异步操作(accept, recv, send等),配合IO协程调度能够极大的提升服务器性能。Http模块,封装了sokcet常用方法,支持http协议解析,客户端实现连接池发送请求,服务器端实现servlet模式处理客户端请求,支持单Reator多线程,多Reator多线程模式的服务器。
void test_timer()
{
johnsonli::IOManager iom(1);
iom.schedule(&test_fiber1);
// 1s触发一次
s_timer = iom.addTimer(1000, [](){
static int i = 0;
LOG_INFO(g_logger) << "timer test " << i;
if(++i == 3)
{
s_timer->reset(2000, true); // 3s后设为2s触发一次定时器
s_timer->cancel();
}
}, true);
}
class TimerManager;
//定时器类
class Timer : public std::enable_shared_from_this<Timer>
{
friend TimerManager;
public:
typedef std::shared_ptr<Timer> ptr;
bool cancel(); // 取消定时器
bool refresh(); // 刷新设置定时器的执行时间
/**
* @brief 重置定时器时间
* @param[in] ms 定时器执行间隔时间(毫秒)
* @param[in] from_now 是否从当前时间开始计算
*/
bool reset(uint64_t ms, bool from_now);
private:
Timer(uint64_t ms, std::function<void()> cb, bool recurring, TimerManager* manager);
Timer(uint64_t next);
private:
//比较器,给set用,按时间从小到大排序
struct Comperator
{
bool operator()(const Timer::ptr& lhs, const Timer::ptr& rhs) const;
};
private:
bool m_recurring = false; //是否循环定时器
uint64_t m_ms = 0; //执行周期,等待的时间,ms后执行
uint64_t m_next = 0; //下一次要执行的时间,当前时间 + m_ms周期
std::function<void()> m_cb; //回调函数
TimerManager* m_manager = nullptr; //当前timer在哪个timer管理对象内
};
TimerManager
类进行管理,TimerManager
包含一个std::set类型的Timer集合,这个集合就是定时器的最小堆结构,因为set
里的元素总是排序过的,所以总是可以很方便地获取到当前的最小定时器。TimerManager
提供创建定时器,获取最近一个定时器的超时时间,以及获取全部已经超时的定时器回调函数的方法,并且提供了一个onTimerInsertedAtFront()
方法,这是一个虚函数,由IOManager继承时实现,当新的定时器插入到Timer集合的首部时,TimerManager
通过该方法来通知IOManager立刻更新当前的epoll_wait超时。//定时器管理类
class TimerManager
{
friend Timer;
public:
typedef RWMutex RWMutexType;
TimerManager();
virtual ~TimerManager();
Timer::ptr addTimer(uint64_t ms, std::function<void()> cb
,bool recurring = false);
//weak_cond:满足条件再执行,智能指针=0时,就不触发
Timer::ptr addConditionTimer(uint64_t ms, std::function<void()> cb
,std::weak_ptr<void> weak_cond
,bool recurring = false);
//获取最近一次定时器执行,还需要等待的时间
uint64_t getNextTimer();
//将那些应该执行,但没有执行的定时器删除,并将其回调方法添加到cbs
void listExpiredCb(std::vector<std::function<void()>>& cbs);
protected:
/**
* @brief 当有新的定时器插入到定时器的首部,执行该函数
*/
virtual void onTimerInsertedAtFront() = 0;
/**
* @brief 将定时器添加到管理器中,如果加在第一个,就会触发onTimerInsertedAtFront
*/
void addTimer(Timer::ptr val, RWMutexType::WriteLock& lock);
private:
/**
* @brief 检测服务器时间是否被调前了
*/
bool detectClockRollover(uint64_t now_ms);
private:
RWMutexType m_mutex;
std::set<Timer::ptr, Timer::Comperator> m_timers; // 最小堆
bool m_tickled = false; // 是否触发了onTimerInsertedAtFront
uint64_t m_previouseTime = 0;
};
addConditionTimer
。在创建定时器时绑定一个变量,在定时器触发时判断一下该变量是否仍然有效,如果变量无效,那就取消触发。 static void OnTimer(std::weak_ptr<void> weak_cond, std::function<void()> cb)
{
//当wealk ptr还有计数时,lock才会返回其智能指针,否则tmp为空
std::shared_ptr<void> tmp = weak_cond.lock();
if(tmp)
{
cb();
}
}
//weak_cond:满足条件再执行,智能指针=0时,就不触发
Timer::ptr TimerManager::addConditionTimer(uint64_t ms, std::function<void()> cb
,std::weak_ptr<void> weak_cond
,bool recurring)
{
return addTimer(ms, std::bind(&OnTimer, weak_cond, cb), recurring);
}
IOManager
通过继承的方式获得TimerManager
类的所有方法,这种方式相当于给IOManager
外挂了一个定时器管理模块。class IOManager : public Scheduler, public TimerManager {
...
}
void IOManager::idle() {
// 一次epoll_wait最多检测256个就绪事件,如果就绪事件超过了这个数,那么会在下轮epoll_wati继续处理
const uint64_t MAX_EVNETS = 256;
epoll_event *events = new epoll_event[MAX_EVNETS]();
std::shared_ptr<epoll_event> shared_events(events, [](epoll_event *ptr) {
delete[] ptr;
});
while (true) {
// 获取下一个定时器的超时时间,顺便判断调度器是否停止
uint64_t next_timeout = 0;
if(stopping(next_timeout)) {
next_timeout = getNextTimer();
LOG_DEBUG(g_logger) << "name=" << getName() << "idle stopping exit";
break;
}
// 阻塞在epoll_wait上,等待事件发生或定时器超时
int rt = 0;
do{
// 默认超时时间5秒,如果下一个定时器的超时时间大于5秒,仍以5秒来计算超时,避免定时器超时时间太大时,epoll_wait一直阻塞
static const int MAX_TIMEOUT = 5000;
if(next_timeout != ~0ull) {
next_timeout = std::min((int)next_timeout, MAX_TIMEOUT);
} else {
next_timeout = MAX_TIMEOUT;
}
rt = epoll_wait(m_epfd, events, MAX_EVNETS, (int)next_timeout);
if(rt < 0 && errno == EINTR) {
continue;
} else {
break;
}
} while(true);
// 收集所有已超时的定时器,执行回调函数
std::vector<std::function<void()>> cbs;
listExpiredCb(cbs);
if(!cbs.empty()) {
for(const auto &cb : cbs) {
schedule(cb);
}
cbs.clear();
}
...
}