功能:用python的kazoo工具包,检测 dubbo在zookeeper上是否活跃。

情况:dubbo部署在机器上,端口是存在的,但在zookeeper上缺没有dubbo提供者了。

所以写个脚本,利用kazoo链接到zookeeper来检测指定的服务是否存在。

检测项目:

  1. 需要自己指定相应的检测service
  2. 可以检测提供服务的机器,是否是线上机器。
  3. 可以检测版本号是否正确。

调用方式:可以在linux上的crontab来定时执行。例如2分钟检测一次。

#checkzookeeper.py

import sys

import time
from kazoo.client import KazooClient
from kazoo.client import ChildrenWatch
from kazoo.client import DataWatch

from checkNode import watcherNode

def zkWatcher(host, port):
    """
    查看zookeeper的链接
    :param host:
    :param port:
    :return:
    """

    # 最终的报错信息
    error_info = ""

    addr = host + ":" + port
    zk = KazooClient(hosts=addr)

    try:
        zk.start()
        node_list = zk.get_children('/dubbo')  # 查看dubbo根节点有多少个子节点
        if node_list and len(node_list) > 0:
            # 调用检测函数,如果有问题就会返回错误字符串
            error_info = watcherNode(zk, node_list)
        else:
            # 没有提供任何的服务列表,发送报警短信
            error_info = "zk中没有提供任何服务!!!"
    except BaseException as e:
        # 如果kazoo是打开的状态,就需要关闭
        print(e)
        # 没有提供任何的服务列表,发送报警短信
        error_info = "连接zookeeper错误!!!" + str(e);

    zk.stop()

    # 发送报警短信
    if len(error_info) > 0:
        print(error_info)


if __name__ == "__main__":
    # 调用检测函数
    zkWatcher(host="192.168.80.221", port="2181")

    sys.exit()











#checkNode.py
import sys

import time
from urllib import parse

from kazoo.client import KazooClient


def needCheckNode():
    """
    需要检测的服务节点
    :return: ['provide名称': ['provider服务1', 'provider服务2']]
    """

    need_check_node_dict = {}
    
    need_check_node_dict['pay'] = ['com.yuedooli.pay.service.BillServiceEx',
                                   'com.yuedooli.pay.service.PayArrearServiceEx',
                                   'com.yuedooli.pay.service.bill.Bill2ServiceEx',
                                  'com.yuedooli.pay.service.bill.Bill2Service333Ex']

    return need_check_node_dict


def watcherNode(kazoo, node_list):
    """
    查看dubbo下的node数据:
        关键的服务进行抽样检查
    :param kazoo: kazoo的回话对象
    :param node_list: 等同于"ls /dubbo"命令
    :return: result_error_info.如果有服务没有提供者,返回错误信息。
    """

    # print(node_list)

    # 存储每一个节点的键值对: com.yuedooli.found.service.CityServiceEx : com.yuedooli.found.service.CityServiceEx
    node_dict = {}

    for node in node_list:
        print(node)
        node_dict[node] = node

    # 打印所有的字典
    # print(node_dict)

    # 获取要检测的数据
    need_check_node_dict = needCheckNode()

    # 错误的信息记录:zookeeper中有该节点,但没有提供者
    result_error_info = ""
    # 错误的信息记录:zookeeper上未找到的节点
    result_error_info2 = ""
    # 错误的信息记录:zookeeper中有该节点,有提供者,但版本号不对
    result_error_info3 = ""
    # 错误的信息记录:zookeeper中有该节点,有提供者,但IP地址不对
    result_error_info4 = ""

    # 线上的版本
    version_prefix = "version=2.0"
    # 线上提供机器IP的前缀
    ip_prefix = "172.16"

    for check_node in need_check_node_dict.keys():
        for check_node_son in need_check_node_dict[check_node]:
            try:
                print("检测provider:" + check_node + ", 检测的服务为:" + check_node_son)
                key_service = check_node_son.split(".")
                key_str = check_node + ":" + key_service[key_service.__len__() - 1]

                # 获取提供服务的列表
                provider_list = kazoo.get_children("/dubbo/" + check_node_son + "/providers")

                # 如果是提供服务的列表不为空,就是正常,否则记录错误
                if provider_list and len(provider_list) > 0:
                    for dubbo_item in provider_list:
                        dubbo_item_uncode = parse.unquote(dubbo_item)  # 解码url
                        # print(dubbo_item_uncode)

                        # 是否有必要继续检测下去,如果有报错,就没有必要继续检测了,因为每个服务有4个节点,都会轮询则错误就会重复
                        need_next_check_state = True
                        # 检测IP地址
                        if dubbo_item_uncode.find(ip_prefix, 0, len(dubbo_item_uncode)) == -1:
                            result_error_info4 += key_str + ","
                            need_next_check_state = False

                        # 检测版本号
                        if dubbo_item_uncode.find(version_prefix, 0, len(version_prefix)) == -1:
                            result_error_info3 += key_str + ","
                            need_next_check_state = False

                        # 已经有报错了,没有必要再继续检测下去了
                        if not need_next_check_state:
                            break;

                else:
                    # 报错,没有提供服务
                    result_error_info += key_str + ", "
            except BaseException as e:
                print(str(e))
                # 获取节点报错,没有提供服务
                result_error_info2 += key_str + ", "
                pass

    # 最终返回的字符串
    result_info = ""

    if len(result_error_info) > 0:
        result_error_info = ", Dubbo中有服务但没有提供者:(" + result_error_info + ")"
        result_info += result_error_info

    if len(result_error_info2) > 0:
        result_error_info2 = ", Dubbo中无此服务:(" + result_error_info2 + ")"
        result_info += result_error_info2

    if len(result_error_info3) > 0:
        result_error_info3 = ", 版本号错误:(" + result_error_info3 + ")"
        result_info += result_error_info3

    if len(result_error_info4) > 0:
        result_error_info4 = ", 线下机器提供了线上服务:(" + result_error_info4 + ")"
        result_info += result_error_info4

    return result_info