配置文件自动重新加载
配置文件自动重新加载
(金庆的专栏)
// FileWatcher.h
/*
FileWatcher监视文件的更新,当有更新时触发动作,一般用于配置文件的自动加载。
FileWatcher有个独立线程查看文件的更新。
应该用singleton来保证单个线程。
触发器将在该线程中执行,所以触发动作须保证线程安全。
首次调用Watch()时会开始线程。
*/
// 默认或出错时的文件时间
const time_t FILE_ERROR_TIME(-1);
class FileWatcher
{
public:
FileWatcher();
virtural ~FileWatcher();
public:
typedef boost::function<void ()> Trigger; // 触发器
// Watch()允许重复调用。首次调用会即时触发动作。
void Watch(const std::string & sFileName, const Trigger & trigger);
// TODO: Watch(sFile, trigger, milliSec) 支持自定义时间间隔
private:
void StartLoopThreadIfNot();
void Loop();
void RecordFileTime(const std::string & sFileName, time_t tFileWrite);
time_t GetFileWriteTime(const std::string & sFileName) const;
private:
struct WatchInfo
{
Trigger trigger;
time_t tFileWrite; // 文件更新时间
WatchInfo() : tFileWrite(FILE_ERROR_TIME) {};
};
typedef std::map<std::string, WatchInfo> FileWatchMap;
private:
WatchInfo UpdateWatch(const std::string & sFileName,
const Trigger & trigger);
FileWatchMap GetMapCopy();
void WatchFile(const FileWatchMap::value_type & v);
void WatchFile(const std::string & sFileName, const WatchInfo & wi);
private:
FileWatchMap m_map;
boost::scoped_ptr<boost::thread> m_pThread;
boost::mutex m_mutex;
typedef boost::lock_guard<boost::mutex> Guard;
boost::atomic_bool m_bRun;
};
TODO: 不用独立线程,改为注册定时器,定时器运行于主线程,这样就不用加锁了。
// FileWatcher.cpp
#include "FileWatcher.h"
FileWatcher::FileWatcher()
: m_bRun(true)
{
}
FileWatcher::~FileWatcher()
{
m_bRun = false;
m_pThread->join();
}
void FileWatcher::Watch(
const std::string & sFileName, const Trigger & trigger)
{
if (!trigger) return; // 不支持删除watch, 因为Loop()中已复制map.
const WatchInfo & wi = UpdateWatch(sFileName, trigger);
WatchFile(sFileName, wi); // 立即触发
StartLoopThreadIfNot();
}
// 返回对应的WatchInfo.
FileWatcher::WatchInfo FileWatcher::UpdateWatch)
const std::string & sFileName, const Trigger & trigger)
{
Guard guard(m_mutex);
m_map[sFileName].trigger = trigger;
// TODO: Same file but different name: ./f.ini = f.ini
WatchInfo wi = m_map[sFileName];
return wi;
}
woid FileWatcher::StartLoopThreadIfNot()
{
if (m_pThread) return; // thread is running
m_bRun = true;
m_pThread.reset(new boost::thread(
boost::bind(&FileWatcher::Loop, this));
}
void FileWatcher::Loop()
{
do
{
// 为了线程安全,需先复制
FileWatchMap mapCopy = GetMapCopy();
size_t nCount = mapCopy.size();
const int LOOP_INTERVAL_MS = 3000; // 循环间隔ms
const int msSleep = LOOP_INTERVAL_MS / (1 + nCount); // 无除0错误
BOOST_FOREACH(const FileWatchMap::value_type & v, mapCopy)
{
WatchFile(v);
if (m_bRun)
{
boost::this_thread::sleep_for(
boost::chrono::milliseconds(msSleep));
}
}
} while (m_bRun);
}
// 获取副本,线程安全
FileWatcher::FileWatchMap FileWatcher::GetMapCopy()
{
Guard guard(m_mutex);
return m_map;
}
void FileWatcher::WatchFile(const FileWatchMap::value_type & v)
{
WatchFile(v.first, v.second);
}
void FileWatcher::WatchFile(const std::string & sFileName, const WatchInfo & wi)
{
time_t tFileWrite = GetFileWriteTime(sFileName);
if (tFileWrite = wi.tFileWrite) return;
time_t tNow = time(NULL);
if (tFileWrite == tNow || tFileWrite == tNow - 1)
return; // 文件正在更改,待改完后再触发
BOOST_ASSERT(wi.trigger);
wi.trigger();
RecordFileTime(sFileName, tFileWrite);
}
void FileWatcher::RecordFileTime(const std::string & sFileName, time_t tFileWrite)
{
Guard guard(m_mutex);
m_map[sFileName].tFileWrite = tFileWrite;
}
// 出错则返回FILE_ERROR_TIME
time_t FileWatcher::GetFileWriteTime(const std::string & sFileName) const
{
struct stat buf;
if (stat(sFileName.c_str(), &buf))
return FILE_ERROR_TIME;
return buf.st_mtime;
}
使用示例:
GatewayIpFilter & rIpFilter = S<GatewayIpFilter>();
S<FileWatcher>().Watch(rIpFilter.GetIniFilePath(),
boost::bind(&GatewayIpFilter::ReloadConfig, &rIpFilter));
(金庆的专栏)
// FileWatcher.h
/*
FileWatcher监视文件的更新,当有更新时触发动作,一般用于配置文件的自动加载。
FileWatcher有个独立线程查看文件的更新。
应该用singleton来保证单个线程。
触发器将在该线程中执行,所以触发动作须保证线程安全。
首次调用Watch()时会开始线程。
*/
// 默认或出错时的文件时间
const time_t FILE_ERROR_TIME(-1);
class FileWatcher
{
public:
FileWatcher();
virtural ~FileWatcher();
public:
typedef boost::function<void ()> Trigger; // 触发器
// Watch()允许重复调用。首次调用会即时触发动作。
void Watch(const std::string & sFileName, const Trigger & trigger);
// TODO: Watch(sFile, trigger, milliSec) 支持自定义时间间隔
private:
void StartLoopThreadIfNot();
void Loop();
void RecordFileTime(const std::string & sFileName, time_t tFileWrite);
time_t GetFileWriteTime(const std::string & sFileName) const;
private:
struct WatchInfo
{
Trigger trigger;
time_t tFileWrite; // 文件更新时间
WatchInfo() : tFileWrite(FILE_ERROR_TIME) {};
};
typedef std::map<std::string, WatchInfo> FileWatchMap;
private:
WatchInfo UpdateWatch(const std::string & sFileName,
const Trigger & trigger);
FileWatchMap GetMapCopy();
void WatchFile(const FileWatchMap::value_type & v);
void WatchFile(const std::string & sFileName, const WatchInfo & wi);
private:
FileWatchMap m_map;
boost::scoped_ptr<boost::thread> m_pThread;
boost::mutex m_mutex;
typedef boost::lock_guard<boost::mutex> Guard;
boost::atomic_bool m_bRun;
};
TODO: 不用独立线程,改为注册定时器,定时器运行于主线程,这样就不用加锁了。
// FileWatcher.cpp
#include "FileWatcher.h"
FileWatcher::FileWatcher()
: m_bRun(true)
{
}
FileWatcher::~FileWatcher()
{
m_bRun = false;
m_pThread->join();
}
void FileWatcher::Watch(
const std::string & sFileName, const Trigger & trigger)
{
if (!trigger) return; // 不支持删除watch, 因为Loop()中已复制map.
const WatchInfo & wi = UpdateWatch(sFileName, trigger);
WatchFile(sFileName, wi); // 立即触发
StartLoopThreadIfNot();
}
// 返回对应的WatchInfo.
FileWatcher::WatchInfo FileWatcher::UpdateWatch)
const std::string & sFileName, const Trigger & trigger)
{
Guard guard(m_mutex);
m_map[sFileName].trigger = trigger;
// TODO: Same file but different name: ./f.ini = f.ini
WatchInfo wi = m_map[sFileName];
return wi;
}
woid FileWatcher::StartLoopThreadIfNot()
{
if (m_pThread) return; // thread is running
m_bRun = true;
m_pThread.reset(new boost::thread(
boost::bind(&FileWatcher::Loop, this));
}
void FileWatcher::Loop()
{
do
{
// 为了线程安全,需先复制
FileWatchMap mapCopy = GetMapCopy();
size_t nCount = mapCopy.size();
const int LOOP_INTERVAL_MS = 3000; // 循环间隔ms
const int msSleep = LOOP_INTERVAL_MS / (1 + nCount); // 无除0错误
BOOST_FOREACH(const FileWatchMap::value_type & v, mapCopy)
{
WatchFile(v);
if (m_bRun)
{
boost::this_thread::sleep_for(
boost::chrono::milliseconds(msSleep));
}
}
} while (m_bRun);
}
// 获取副本,线程安全
FileWatcher::FileWatchMap FileWatcher::GetMapCopy()
{
Guard guard(m_mutex);
return m_map;
}
void FileWatcher::WatchFile(const FileWatchMap::value_type & v)
{
WatchFile(v.first, v.second);
}
void FileWatcher::WatchFile(const std::string & sFileName, const WatchInfo & wi)
{
time_t tFileWrite = GetFileWriteTime(sFileName);
if (tFileWrite = wi.tFileWrite) return;
time_t tNow = time(NULL);
if (tFileWrite == tNow || tFileWrite == tNow - 1)
return; // 文件正在更改,待改完后再触发
BOOST_ASSERT(wi.trigger);
wi.trigger();
RecordFileTime(sFileName, tFileWrite);
}
void FileWatcher::RecordFileTime(const std::string & sFileName, time_t tFileWrite)
{
Guard guard(m_mutex);
m_map[sFileName].tFileWrite = tFileWrite;
}
// 出错则返回FILE_ERROR_TIME
time_t FileWatcher::GetFileWriteTime(const std::string & sFileName) const
{
struct stat buf;
if (stat(sFileName.c_str(), &buf))
return FILE_ERROR_TIME;
return buf.st_mtime;
}
使用示例:
GatewayIpFilter & rIpFilter = S<GatewayIpFilter>();
S<FileWatcher>().Watch(rIpFilter.GetIniFilePath(),
boost::bind(&GatewayIpFilter::ReloadConfig, &rIpFilter));