• Zookeeper实现服务动态ip注册和发现,并能在网络波动后重连(重新注册)


    #encoding=utf8
    #架构:后端服务 --> zookeeper -> 注册模型服务
    import socket
    import time
    from kazoo import security
    from kazoo.client import KazooClient
    from kazoo.client import EventType, WatchedEvent
    from kazoo.exceptions import NoNodeError, SessionExpiredError, ConnectionLossException, NoAuthError
    import random
    from functools import wraps
     
     
    class ExceptionHandler(object):
        def __init__(self):
            #KazooTimeoutError
            pass
     
        def __call__(self, func):
            @wraps(func)
            def wrapped_function(*args, **kwargs):
                is_success = False
                while not is_success:
                    try:
                        is_success = func(*args, **kwargs)
                    except (ConnectionLossException, SessionExpiredError) as e:
                        is_success = False
                    except NoAuthError as e:
                        raise e
                    except Exception as e:
                        is_success = False
                        time.sleep(1)
            return wrapped_function
     
    class ZKModelWatcher(object):
     
        def __init__(self, hosts, acl_user, acl_pass, model_name, model_port):
            hosts_arr = hosts.split(',')
            self.acl = security.make_digest_acl(username=acl_user, password=acl_pass, read=True, create=True)
            self.auth_data = auth_data = [("digest", f"{acl_user}:{acl_pass}")]
            self._a_host = hosts_arr[0].split(':')[0]
            self._a_port = int(hosts_arr[0].split(':')[1])
            self._hosts = hosts #zookeeper集群连接地址
            self.model_name = model_name #注册模型服务名称,用于zookeeper根节点名称
            self.model_port = model_port #注册模型服务端口号
            self._zkc = KazooClient(hosts=self._hosts)
     
        def get_host_ip(self):
            '''
                用于获取注册模型服务的ip地址(能够动态改变)
                后端服务与zookeeper在同一网段中,注册模型服务的请求地址与获取服务的网段一致
            '''
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.connect((self._a_host, self._a_port))
                ip = s.getsockname()[0]
            finally:
                s.close()
            return ip
     
        def create_model_node(self):
            #创建临时、序列化节点。
            ip = self.get_host_ip()
            res = self._zkc.create(f'/{self.model_name}/model', bytes('%s:%s' % (ip, self.model_port), encoding='utf8')
                            ,makepath=True, sequence=True, ephemeral=True)
            return res
     
        def restart_zk(self):
            try:
                self._zkc.stop()
            except:
                pass
            self._zkc = KazooClient(hosts=self._hosts, default_acl=[self.acl], auth_data=self.auth_data)
            self._zkc.start()
     
        @ExceptionHandler()
        def register(self, data, stat, event):
            print(event)
            if event and event.type == EventType.NONE:
                self.restart_zk()
                res = self.create_model_node()
                self._zkc.DataWatch(path=f'{res}', func=self.register)
                if not self._zkc.exists(path=f'{res}'):
                    return False
                print(res)
            elif event and event.type == EventType.DELETED:
                res = self.create_model_node()
                self._zkc.DataWatch(path=f'{res}', func=self.register)
                if not self._zkc.exists(path=f'{res}'):
                    return False
                print(res)
            return True
     
        def get_model_host(self):
            #后端服务通过zookeeper获取注册模型服务的地址,如果注册模型服务存在多个,则需要随机选择
            name = self.model_name
            is_success = False
            while not is_success:
                children = self._zkc.get_children(f'/{name}')
                if len(children) <= 0:
                    raise Exception('没有可以运行的服务')
                try:
                    index = random.randint(0, len(children) - 1)
                    host = self._zkc.get(f'/{name}/{children[index]}')
                    is_success = True
                    return host[0].decode()
                except NoNodeError as e:
                    is_success = False
                    # 选中的节点已经失效的情况
     
        @ExceptionHandler()
        def run(self):
            self.restart_zk()
            res = self.create_model_node()
            self._zkc.DataWatch(path=f'{res}', func=self.register)
            if not self._zkc.exists(path=f'{res}'):
                return False
            self.get_model_host()
            return True
     
        def close(self):
            try:
                self._zkc.stop()
                self._zkc.close()
            except Exception as e:
                print(str(e))
     
    if __name__ == '__main__':
        acl_username = 'user'
        acl_password = 'pass'
        zw = ZKModelWatcher('127.0.0.1:2181,127.0.0.1:12181,127.0.0.1:22181', acl_username,  acl_password, 'model', 5000)
        zw.run()
        time.sleep(4000) #此处可以开启注册模型服务
        zw.close()
    

    参考资料:

    1、Zookeeper简介 - https://blog.51cto.com/hmtk520/2105110

    2、使用kazoo连接zookeeper并监听节点数量以及值变化 - https://blog.csdn.net/pysense/article/details/100709138

    3、python kazoo 监视zookeeper节点数据发生变化 - https://blog.csdn.net/KWSY2008/article/details/52042303?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

    4、关于Zookeeper中Session Expired和Watch - https://www.coder4.com/archives/3181

    5、ZK session客户端过期(Expired)过程 - https://blog.csdn.net/lovingprince/article/details/6885746

    6、zookeeper Session Expired - https://blog.csdn.net/specialsun/article/details/84812575

    7、zookeeper curator处理会话过期session expired - https://www.cnblogs.com/kangoroo/p/7538314.html

    8、python获取自身ip - https://www.cnblogs.com/hei-hei-hei/p/10489924.html

  • 相关阅读:
    go 字符串转换
    GRU模型结构
    ElasticSearch实战系列八: Filebeat快速入门和使用---图文详解
    H5可视化编辑器(H5 Dooring)
    (转)如何防止Axios对我的请求参数进行编码?
    vue使用element-ui,如何给Label加标签
    vue中$router.push打开新窗口
    (转)webstorm配置svn
    打开gitee.com网站报错
    监控$route无效
  • 原文地址:https://www.cnblogs.com/usaddew/p/14862289.html
Copyright © 2020-2023  润新知