#ifndef _WATCHER_H_
#define _WATCHER_H_
#include <sys/time.h>
#include <sys/stat.h>
#include <string>
// We often want to know if a file was just now created or updated (then we
// may reload the file to get latest data). FileWatcher does boilerplate part
// for you as below.
// Example:
// Watcher fw;
// fw.create ("path_of_file_to_be_watched");
// ....
// if (fw.is_timestamp_updated() > 0) { // >0 means that the file is created or updated
// ......
// fw.update_timestamp(); // update recorded timestamp
// }
//
// You may call is_timestamp_updated() multiple times to know latest status
// of the file before calling update_timestamp(), or you may combine calls to
// the two functions by:
// if (fw.check_and_update_timestamp() > 0) {
// ....
// }
class Watcher {
public:
static const time_t NON_EXIST_TS = (time_t)-1;
// must be negative
static const int SKEW = -2;//文件来自未来的异常:文件的时间戳异常或者从时间较快的服务器上拷贝来的文件
static const int DELETED = -1;
// must be zero
static const int UNCHANGED = 0;
// must be positive
static const int UPDATED = 1;
static const int CREATED = 2;
// Default constructor
explicit Watcher ()
: _chk_ts(NON_EXIST_TS) , _last_ts(NON_EXIST_TS)//这里的explicit没有实际意义,可以删除
{}
// Create this watcher on a file
// Returns:
// -1 file_path is NULL
// 0 success
int create (const char* file_path)
{
if (NULL == file_path) {
FATAL("file_path is NULL");
return -1;
}
file_path_ = file_path;//后置下划线。。。好吧。。。
struct stat tmp_st;
int ret = stat (file_path_.c_str(), &tmp_st);//为了存储之前的状态
if (ret < 0) {
WARN("%s does not exist", file_path_.c_str());
_chk_ts = NON_EXIST_TS;
_last_ts = NON_EXIST_TS;
} else {
_chk_ts = tmp_st.st_mtime;
_last_ts = tmp_st.st_mtime;
}
return 0;
}
// Check if watched file was created/modified/deleted. Recorded timestamp
// is not changed so it's safe to call this function multiple times, you
// won't miss an update.
// Returns:
// UPDATED the file is modified or created since last call to this
// function
// UNCHANGED the file is not modified or still not existing since last
// call to this function
// DELETED the file was deleted since last call to this function
// SKEW the file exists but the timestamp steps back
// Note: this method only checks, to tell file_reader_t to forget the change
// (because you already processed the file), you must call
// update_timestamp(). You may call check_and_update_timestamp() to
// combine two calls.
// Note: If the file is updated too frequently(< 500ms), this method may
// return UNCHANGED due to precision of stat(2); If the file is
// being updated too frequently for a period of time, this method
// should eventually return several UPDATEs; If the file is being
// updated and deleted too frequently, the update events are
// probably undetectable. Fortunately, this is unseen in our app.
int is_timestamp_updated ()
{
struct stat tmp_st;
const int ret = stat (file_path_.c_str(), &tmp_st);
if (ret < 0) {
_chk_ts = NON_EXIST_TS;
if (NON_EXIST_TS != _last_ts) {
return DELETED;
} else {
return UNCHANGED;
}
} else {
_chk_ts = tmp_st.st_mtime;
if (NON_EXIST_TS != _last_ts) {
if (_chk_ts > _last_ts) {
return UPDATED;
} else if (_chk_ts == _last_ts) {
return UNCHANGED;
} else {
return SKEW;
}
} else {
return CREATED;
}
}
}
// Change recorded time
void update_timestamp ()
{
_last_ts = _chk_ts;
}
// Combine is_timestamp_updated() and update_timestamp()
int check_and_update_timestamp ()
{
const int ret = is_timestamp_updated ();
update_timestamp ();
return ret;
}
// Get path of the watched file
const char* filepath () const {
return file_path_.c_str();
}
private:
std::string file_path_;
time_t _chk_ts;
time_t _last_ts;
};
typedef Watcher watcher_t;
#endif
·代码整体比较简单,其实就是根据时间戳的各种情况做判断。
那么有了监控的程序,如果当文件更新的时候我需要动态加载该文件,怎么处理?因为加载了文件需要更新已经存储的数据,并且这些数据在动态加载的时候还可能被使用。
解决方案自然是开线程监控,使用两个buffer,再任一时刻,客户只能访问其中的一个buffer,而访问哪一个,由加载程序决定。当文件被更新时,将数据更新到没有被使用的buffer中,这样能够保证旧数据的正常访问;当更新完成,并且旧数据不再被调用的时候,通知给客户传递数据的接口使用新数据,清空之前的buffer,并将该buffer设置为未使用,等待下次更新时使用。
#ifndef _RELOADER_H
#define _RELOADER_H
#include <stdio.h>
#include <time.h>
#include <stdint.h>
#include <sys/stat.h>
#include <string.h>
#include <new>
template <bool> class static_assert;
template <> class static_assert<true> {};
// @brief 双 buffer 管理类
// @note T 一定要实现 int load(const char* cp_dir, const char* cp_fname) 函数, 否则编译失败
// @note 如果有 init 需求, 可以在构造时, 提供一个 int T::init() 函数
// @note 以上两个函数在返回 < 0 时,表示失败
// @note 推荐设计:文件不存在或者内容为空,也认为是成功
template <class T>
class reloader_t
{
public:
typedef T content_type;
// @param init_func,如果传入的话,reload会对new出来的对象调用 init_func
// @note init_func必须是静态函数
reloader_t(const char* cp_path,
const char* cp_fname,
int (content_type::*init_func)() = NULL);
~reloader_t();
// @return -1, 文件不存在; 0, 不需要reload; 1, 可以reload
int need_reload() const;
// @brief 双 buffer 切换
// @return -1, reload失败; 1, reload成功
int reload();
// 取得当前可以使用的 buffer 的指针
content_type* get_content();
// 取得当前可以使用的 buffer 的指针
const content_type* get_content() const;
time_t get_file_modtime() const
{
return _tm;
}
time_t get_file_loadtime() const
{
return _last_update_tm;
}
protected:
time_t _tm;
time_t _last_update_tm;
uint8_t _using_no;
// 两个buffer的指针
content_type* _p_content[2];
// 实际的buffer,避免reload时频繁的申请内存
char _mem[2 * sizeof(content_type)];
char _path[512];
char _fname[512];
// 个性 init 函数
int (content_type::*_init_func)();
//以下函数保证 T::load 的返回值必须为int
typedef char small_t;
struct big_t {
char c[2];
};
static small_t _is_int(int);
template <class V>
static big_t _is_int(V);
template <class RT, class CLA>
static RT _get_load_ret_type(RT (CLA::*)(const char*, const char*));
};
template <class T>
reloader_t<T>::reloader_t(const char* cp_path,
const char* cp_fname,
int (T::*init_func)()):
_tm(0),
_last_update_tm(0),
_using_no(1),
_init_func(init_func)
{
// 静态检查 load 的返回值必须是 int
static_assert<sizeof(small_t) == sizeof(_is_int(_get_load_ret_type(&T::load)))>();
if (cp_path == NULL) {
cp_path = "./";
}
if (cp_fname == NULL) {
cp_fname = "reload_file";
}
snprintf(_path, sizeof(_path), "%s", cp_path);
snprintf(_fname, sizeof(_fname), "%s", cp_fname);
_p_content[0] = NULL;
_p_content[1] = NULL;
memset(&_mem, 0, sizeof(_mem));
}
template <class T>
reloader_t<T>::~reloader_t()
{
if (_p_content[0] != NULL)
{
_p_content[0]->~T();
_p_content[0] = NULL;
}
if (_p_content[1] != NULL)
{
_p_content[1]->~T();
_p_content[1] = NULL;
}
}
template <class T>
int reloader_t<T>::need_reload() const
{
char fpath[sizeof(_path) + sizeof(_fname) + 1] = "";
snprintf(fpath, sizeof(fpath), "%s/%s", _path, _fname);
struct stat curr_stat;
// 记录当前状态文件状态
if ((stat(fpath, &curr_stat) != 0)
|| ((curr_stat.st_mode & S_IFREG) == 0))
{
return -1;
}
if (curr_stat.st_mtime <= _tm)
{
// 当前数据为新
return 0;
}
return 1;
}
template <class T>
int reloader_t<T>::reload()
{
if (_using_no != 0 && _using_no != 1) {
return -1;
}
// sleep 1 秒,保证所有线程不再使用该资源
// 外围保证
sleep(1);
struct stat curr_stat;
char fpath[sizeof(_path) + sizeof(_fname) + 1] = "";
snprintf(fpath, sizeof(fpath), "%s/%s", _path, _fname);
// 记录当前状态文件状态
if ((stat(fpath, &curr_stat) != 0)
|| ((curr_stat.st_mode & S_IFREG) == 0)) {
WARN("get file:%s status failed", fpath);
return -1;
}
//保证使用的buffer编号为1或者0
int reload_no = 1 - _using_no;
if (_p_content[reload_no] != NULL) {
_p_content[reload_no]->~T();
_p_content[reload_no] = NULL;
}
_p_content[reload_no] = new (&_mem[reload_no * sizeof(T)]) T();
// init
if (_init_func != NULL) {
int init_ret = (_p_content[reload_no]->*_init_func)();
if (init_ret < 0) {
FATAL("init failed.init ret:%d", init_ret);
goto RELOAD_FAILED;
}
}
{
int load_ret = _p_content[reload_no]->load(_path, _fname);
if (load_ret < 0) {
FATAL("load file:%s/%s failed.load ret:%d",
_path, _fname, load_ret);
goto RELOAD_FAILED;
}
}
_tm = curr_stat.st_mtime;
_last_update_tm = time(NULL);
_using_no = reload_no;
INFO("reload [%s] success and shift to data [%d]", fpath, _using_no);
return 1;
RELOAD_FAILED:
if (_p_content[reload_no] != NULL) {
_p_content[reload_no]->~T();
_p_content[reload_no] = NULL;
}
return -1;
}
template <class T>
T* reloader_t<T>::get_content()
{
if (_tm == 0 || (_using_no != 1 && _using_no != 0)) {
return NULL;
}
return _p_content[_using_no];
}
template <class T>
const T* reloader_t<T>::get_content() const
{
return get_content();
}
#endif
该类中也实现了文件状态的判断,但是个人觉得,这种功能还是应该独立,采用组合的方式进行处理,让reloader拥有一个watcher对象是一个不错的选择。
最后,再加上线程,搞定:
#ifndef _HIT_RELOAD_H
#define _HIT_RELOAD_H
#include <pthread.h>
#include "watcher.h"
static const char WORD_RELOAD_FILE[] = "./conf/reloadWord";
class Thread
{/*{{{*/
Thread(const Thread &);
void operator=(const Thread &);
pthread_t _thread;
public:
Thread() : _thread(0), _status(true), _start(false) {}
virtual ~ Thread() {}
//启动线程函数
int start()
{
int ret = 0;
if (_start == false) {
ret = pthread_create(&_thread, NULL, Thread::func, this);
if (ret != 0) {
FATAL("create thread failed.");
_status = false;
} else {
_start = true;
}
}
return ret;
}
//取得线程的状态
bool valid() const{return _status;}
//取得线程句柄
pthread_t & get(){return _thread;}
const pthread_t & get() const{return _thread;}
//等待线程结束函数
int join(){
if (_status == true && _start == true) {
return pthread_join(_thread, NULL);
}
return -1;
}
virtual void *run() = 0;
protected:
bool _status;
bool _start;
static void *func(void *args){
Thread *p_thread = static_cast <Thread *>(args);
return p_thread->run();
}
};/*}}}*/
class ReloadThread:public Thread
{/*{{{*/
public:
template<class T>
void reloadCheck(Watcher *watcher, reloader_t<T> *mgr, const char* file)
{/*{{{*/
bool all_reloaded = false;
int ret = watcher->check_and_update_timestamp();
if (ret > 0) {
all_reloaded = true;
if (mgr != NULL){
if (mgr->reload() < 0) {
printf("reload %s config failed", file);
all_reloaded = false;
}
}
} else if (ret < 0) {
printf("watcher status not valid");
// 检测状态失败, reload未成功
all_reloaded = false;
} else {
// 未检测到更新
all_reloaded = false;
}
if (all_reloaded) {
printf("%s is reloaded!", file);
}
}/*}}}*/
void *run(){
Watcher watcher_word;
watcher_word.create("word_filter_file");
while (true)
{
sleep(g_conf.iSleepTime);
//reload word.conf
reloadCheck<WordFilter>(&watcher_word,
g_data.word_filter_mgr,
g_conf.word_filter_file);
}
return NULL;
}
};/*}}}*/
#endif
/* vim: set ts=4 sw=4 noet: */