• 使用Kazoo操作ZooKeeper服务治理


    单机服务的可靠性及可扩展性有限,某台服务宕机可能会影响整个系统的正常使用;分布式服务能够有效地解决这一问题,但同时分布式服务也会带来一些新的问题,如:服务发现(新增或者删除了服务如何确保能让客户端知道),容灾(某些服务出现故障如何让客户端只访问正常的服务);ZooKeeper的提出主要是为了解决分布式服务的治理问题,它在分布式环境中协调和管理服务。

    Zookeeper协调管理服务的过程如下图:

    服务端:每台服务器都要向注册中心Zookeeper进行注册登记,并且保持与Zookeeper的连接,如果服务器与Zookeeper断开了连接,Zookeeper将删除该服务器的地址。
    客户端:需要服务的时候先向Zookeeper订阅服务器的地址信息,Zookeeper返回给客户端已注册的服务器信息列表,客户端从服务器信息列表中选择服务器进行服务调用,如果Zookeeper记录的服务器信息发生了变更,服务器会通知客户端变更事件,客户端可以获取最新的服务器信息。
    ZooKeeper文件系统的数据结构是个树状结构,它的每个节点(znode)由一个名称标识,并用路径/分割:

     ZooKeeper的节点类型有:

      1. 持久节点(ZooKeeper默认的节点类型,创建该节点的客户端断开连接后,持久节点仍然存在)

      2. 顺序节点(将10位的序列号附加到原始名称来设置节点的路径,如:/server0000000001)

      3. 临时节点(当客户端与ZooKeeper断开连接时,临时节点会自动删除)

    RPC服务注册到ZooKeeper

    服务端:

     1 import threading
     2 import json
     3 import socket
     4 import sys
     5 from kazoo.client import KazooClient
     6 from divide_rpc import ServerStub
     7 from divide_rpc import InvalidOperation
     8 
     9 
    10 class ThreadServer(object):
    11     def __init__(self, host, port, handlers):
    12         self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    13         self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    14         self.host = host
    15         self.port = port
    16         self.sock.bind((host, port))
    17         self.handlers = handlers
    18 
    19     def serve(self):
    20         """
    21         开始服务
    22         """
    23         self.sock.listen(128)
    24         self.register_zk()
    25         print("开始监听")
    26         while True:
    27             conn, addr = self.sock.accept()
    28             print("建立链接%s" % str(addr))
    29             t = threading.Thread(target=self.handle, args=(conn,))
    30             t.start()
    31 
    32     def handle(self, client):
    33         stub = ServerStub(client, self.handlers)
    34         try:
    35             while True:
    36                 stub.process()
    37         except EOFError:
    38             print("客户端关闭连接")
    39 
    40         client.close()
    41 
    42     def register_zk(self):
    43         """
    44         注册到zookeeper
    45         """
    46         self.zk = KazooClient(hosts='127.0.0.1:2181')
    47         self.zk.start()
    48         self.zk.ensure_path('/rpc')  # 创建根节点
    49         value = json.dumps({'host': self.host, 'port': self.port})
    50         # 创建服务子节点
    51         self.zk.create('/rpc/server', value.encode(), ephemeral=True, sequence=True)
    52 
    53 
    54 class Handlers:
    55     @staticmethod
    56     def divide(num1, num2=1):
    57         """
    58         除法
    59         :param num1:
    60         :param num2:
    61         :return:
    62         """
    63         if num2 == 0:
    64             raise InvalidOperation()
    65         val = num1 / num2
    66         return val
    67 
    68 
    69 if __name__ == '__main__':
    70     if len(sys.argv) < 3:
    71         print("usage:python server.py [host] [port]")
    72         exit(1)
    73     host = sys.argv[1]
    74     port = sys.argv[2]
    75     server = ThreadServer(host, int(port), Handlers)
    76     server.serve()

    服务端通过kazoo连接zookeeper,依次创建根节点和服务的子节点,当启动多线程服务器的时候,会根据ip和端口创建不同的节点,依次启动两个server(8001、8002),查看zookeeper的节点信息:

    1 >>> from kazoo.client import KazooClient
    2 >>> zk = KazooClient(hosts='127.0.0.1:2181')
    3 >>> zk.start() 
    4 >>> children = zk.get_children("/rpc")
    5 >>> print(children)
    6 ['server0000000001', 'server0000000000']

    客户端:

     1 import random
     2 import time
     3 import json
     4 import socket
     5 from divide_rpc import (
     6     ClientStub, InvalidOperation
     7 )
     8 from kazoo.client import KazooClient
     9 
    10 
    11 class DistributedChannel(object):
    12     def __init__(self):
    13         self._zk = KazooClient(hosts='127.0.0.1:2181')
    14         self._zk.start()
    15         self._get_servers()
    16 
    17     def _get_servers(self, event=None):
    18         """
    19         从zookeeper获取服务器地址信息列表
    20         """
    21         servers = self._zk.get_children('/rpc', watch=self._get_servers)
    22         print(servers)
    23         self._servers = []
    24         for server in servers:
    25             data = self._zk.get('/rpc/' + server)[0]
    26             if data:
    27                 addr = json.loads(data.decode())
    28                 self._servers.append(addr)
    29 
    30     def _get_server(self):
    31         """
    32         随机选出一个可用的服务器
    33         """
    34         return random.choice(self._servers)
    35 
    36     def get_connection(self):
    37         """
    38         提供一个可用的tcp连接
    39         """
    40         while True:
    41             server = self._get_server()
    42             print(server)
    43             try:
    44                 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    45                 sock.connect((server['host'], server['port']))
    46             except ConnectionRefusedError:
    47                 time.sleep(1)
    48                 continue
    49             else:
    50                 break
    51         return sock
    52 
    53 
    54 channel = DistributedChannel()
    55 
    56 for i in range(50):
    57     try:
    58         stub = ClientStub(channel)
    59         val = stub.divide(i)
    60     except InvalidOperation as e:
    61         print(e.message)
    62     else:
    63         print(val)
    64     time.sleep(1)

    客户端连接zookeeper,通过get_children来获取服务器信息,并watch监听服务器的变化情况,启动客户端会发现它会调用8001端口的server和8002端口的server:

    此时服务端新增加一个结点,8003,客户端变化情况:

    可以看出zookeeper总共有三个节点了,前面调用的server都是8001和8002,当8003加入后,zookeeper会发现并调用它

    此时服务端断开一个server,8001,客户端变化情况:

    断开server前客户端会调用8001、8002、8003这三个服务,当断开server 8001以后,zookeeper只会调用8002和8003这两个server了

  • 相关阅读:
    查找谁调用了BTE事件
    ABAP标准屏幕调用选择屏幕
    CG3Y&nbsp;CG3Z&nbsp;一个上传一个下载
    捕获BDC报的错误
    MM主要的表和主要字段
    获取随机数&nbsp;&nbsp;QF05_RANDOM_INTEGER
    Query-Convert&nbsp;QuickView是灰…
    SAP_整体修改一个内表的某一个字段…
    程序员永远的痛之字符编码的奥秘
    关于绑定变量、关于占位符
  • 原文地址:https://www.cnblogs.com/FG123/p/10261682.html
Copyright © 2020-2023  润新知