• 基于zookeeper的主备切换方法


    继承CZookeeperHelper即可快速实现主备切换:

    https://github.com/eyjian/libmooon/blob/master/include/mooon/net/zookeeper_helper.h

    zookeeper的ZOO_EPHEMERAL节点(如果ZOO_EPHEMERAL满足不了需求,可以考虑和ZOO_SEQUENCE结合使用),在会话关闭或过期时,会自动删除,利用这一特性可以实现两个或多节点间的主备切换。


    实现方法:
    1)在进程启动时调用zookeeper_init()初始化:
    bool X::init_zookeeper()
    {
        // 第一次调用时_clientid总是为NULL,
        // 状态为ZOO_EXPIRED_SESSION_STATE时,需要重新调用zookeeper_init,
        // 这个时候可传入的_clientid为前一次zookeeper_init()产生的_clientid


        // 请注意zookeeper_init()是一个异步调用,返回非NULL并不表示会话建立成功,
        // 只有当zk_watcher中的type为ZOO_SESSION_EVENT和state为ZOO_CONNECTED_STATE时,
        // 才真正表示会话建立成功。
        _zhandle = zookeeper_init(zk_hosts, zk_watcher, 5000, _clientid, this, 0);
        if (NULL == _zhandle)
        {
            MYLOG_ERROR("init zookeeper failed: %s ", zerror(errno));
            return false;
        }


        MYLOG_INFO("init zookeeper(%s) successfully ", zk_hosts);
        return true;
    }


    2)进入工作之前,先尝试切换成主,只有成功切换成主后才进入work
    bool X::run()
    {
        while (true)
        {
            int num_items = 0;
            // 备机最简单的方法是每隔一定时间,如1秒就尝试转成master,
            // 如果不使用轮询,则可以采用监视_zk_path的方式
            mooon::sys::CUtils::millisleep(1000);


            // 如果不是master,则尝试转成master,如果转成不成功则继续下一次尝试
            if (!is_master() && !change_to_master())
                continue;


            do_work();
        }
    }


    bool X::is_master() const
    {
        return _is_master;
    }


    bool X::change_to_master()
    {
        static uint64_t log_counter = 0; // 打log计数器,备状态时的日志输出


        // ZOO_EPHEMERAL|ZOO_SEQUENCE
        // _myip为本地IP地址,可以通过它来判断当前谁是master
        // _zk_path值示例:/master/test,注意需要先保证/master已存在
        int errcode = zoo_create(_zhandle, _zk_path.c_str(), _myip.c_str(), _myip.size()+1, &ZOO_OPEN_ACL_UNSAFE, ZOO_EPHEMERAL, NULL, 0);


        // (-4)connection loss,比如为zookeeper_init()指定了无效的hosts(一个有效的host也没有)
        if (errcode != ZOK)
        {
            _is_master = false;


            // 减少为备状态时的日志输出
            if (0 == log_counter++ % 600)
            {
                MYLOG_DEBUG("become master[%s] failed: (%d)%s ", _zk_path.c_str(), errcode, zerror(errcode));
            }


            return false;
        }
        else
        {
            _is_master = true;
            log_counter = 0;
            MYLOG_INFO("becase master[%s] ", _zk_path.c_str());


            // sleep一下,以便让原master正在进行的完成
            mooon::sys::CUtils::millisleep(2000);
            return true;
        }
    }


    3)当zookeeper会话成功建立或过期时均会触发zk_watcher,可通过type和state来区分
    void zk_watcher(zhandle_t *zh, int type, int state, const char *path, void *context)
    {
        X* x = static_cast<X*>(context);
        MYLOG_DEBUG("zh=%p, type=%d, state=%d, context=%p, path=%s ", zh, type, state, context, path);


        // zookeeper_init成功时type为ZOO_SESSION_EVENT,state为ZOO_CONNECTED_STATE
        if ((ZOO_SESSION_EVENT == type) && (ZOO_CONNECTED_STATE == state))
        {
            x->on_zookeeper_connected(path);
        }
        else if ((ZOO_SESSION_EVENT == type) && (ZOO_EXPIRED_SESSION_STATE == state))
        {
            // 需要重新调用zookeeper_init(),简单点可以退出当前进程重启
            x->on_zookeeper_expired();
        }
    }


    附: zookeeper日志
    默认情况下zookeeper日志是输出到stderr,但可以通过zoo_set_log_stream()来定向到自己的日志输出中,还可以使用zoo_set_debug_level()来控制zookeeper的日志级别。

  • 相关阅读:
    课时8:环绕通知
    课时7:后置通知、异常通知
    课时6::AOP、execution表达式、前置通知
    课时:5 使用注解实现声明式事务
    课时22::PageHelper分页插件
    课时21 :使用MyBatis实现批量操作
    课时4:特殊值的注入问题和各种类型的自动装配
    课时3:三种方式的依赖注入、给各种集合类型的属性注入
    课时2:解耦合发展史、控制反转、依赖注入
    课时1:Spring环境搭建、STS工具、第一个Spring程序
  • 原文地址:https://www.cnblogs.com/aquester/p/9891538.html
Copyright © 2020-2023  润新知