IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    配置文件自动重新加载

    金庆发表于 2014-04-04 12:55:00
    love 0
    配置文件自动重新加载

    (金庆的专栏)

    // FileWatcher.h
    /*
    FileWatcher监视文件的更新,当有更新时触发动作,一般用于配置文件的自动加载。
    FileWatcher有个独立线程查看文件的更新。
    应该用singleton来保证单个线程。
    触发器将在该线程中执行,所以触发动作须保证线程安全。
    首次调用Watch()时会开始线程。
    */

    // 默认或出错时的文件时间
    const time_t FILE_ERROR_TIME(-1);

    class FileWatcher
    {
    public:
    FileWatcher();
    virtural ~FileWatcher();

    public:
    typedef boost::function 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 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 m_pThread;
    boost::mutex m_mutex;
    typedef boost::lock_guard 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();
    S().Watch(rIpFilter.GetIniFilePath(),
    boost::bind(&GatewayIpFilter;::ReloadConfig, &rIpFilter;));


    金庆 2014-04-04 20:55 发表评论


沪ICP备19023445号-2号
友情链接