zipkin微服务调用链分析(python)

发布时间:2020-03-19 08:52:38编辑:admin阅读(2843)

    一,概述

    zipkin的作用

    在微服务架构下,一个http请求从发出到响应,中间可能经过了N多服务的调用,或者N多逻辑操作,
    如何监控某个服务,或者某个逻辑操作的执行情况,对分析耗时操作,性能瓶颈具有很大价值,
    zipkin帮助我们实现了这一监控功能。

     

    二、安装zipkin

    环境说明

    操作系统:centos 7.6

    ip:192.168.31.232

    配置:2核4g

    python版本:3.5.2

     

    启动zipkin

    启动方式有2种,一个是docker,一个jar包。任选其一即可。

    本文采用jar包方式启动

    docker

    docker run --name zipkin -d -p 9411:9411 openzipkin/zipkin

     

    jar包

    wget https://dl.bintray.com/openzipkin/maven/io/zipkin/java/zipkin-server/2.12.9/zipkin-server-2.12.9-exec.jar
    java -jar zipkin-server-2.12.9-exec.jar

     

    访问zipkin

    http://192.168.31.232:9411

     

    效果如下:

    1.png

     

     

    三、python实现zipkin

    使用py_zipkin模块来实现,这里以flask项目来测试。

    安装模块

    pip3 install  py_zipkin pymysql flask

     

    创建项目

    新建demo.py

    mkdir -p /data/flask_demo/cd /data/flask_demo/vim demo.py

    内容如下:

    import requests
    from flask import Flask
    from py_zipkin.zipkin import zipkin_span,create_http_headers_for_new_span
    import time
    
    app = Flask(__name__)
    
    app.config.update({
        "ZIPKIN_HOST":"127.0.0.1",
        "ZIPKIN_PORT":"9411",
        "APP_PORT":5000,
        # any other app config-y things
    })
    
    
    def do_stuff():
        time.sleep(2)
        headers = create_http_headers_for_new_span()
        requests.get('http://localhost:6000/service1/', headers=headers)
        return 'OK'
    
    
    def http_transport(encoded_span):
        # encoding prefix explained in https://github.com/Yelp/py_zipkin#transport
        #body = b"\x0c\x00\x00\x00\x01"+encoded_span
        body=encoded_span
        zipkin_url="http://127.0.0.1:9411/api/v1/spans"
        #zipkin_url = "http://{host}:{port}/api/v1/spans".format(
         #   host=app.config["ZIPKIN_HOST"], port=app.config["ZIPKIN_PORT"])
        headers = {"Content-Type": "application/x-thrift"}
    
        # You'd probably want to wrap this in a try/except in case POSTing fails
        r=requests.post(zipkin_url, data=body, headers=headers)
        print(type(encoded_span))
        print(encoded_span)
        print(body)
        print(r)
        print(r.content)
    
    
    @app.route('/')
    def index():
        with zipkin_span(
            service_name='webapp',
            span_name='index',
            transport_handler=http_transport,
            port=5000,
            sample_rate=100, #0.05, # Value between 0.0 and 100.0
        ):
            with zipkin_span(service_name='webapp', span_name='do_stuff'):
                do_stuff()
            time.sleep(1)
        return 'OK', 200
    
    if __name__=='__main__':
        app.run(host="0.0.0.0",port=5000,debug=True)

     

    新建server1.py

    from flask import request
    import requests
    from flask import Flask
    from py_zipkin.zipkin import zipkin_span,ZipkinAttrs
    import time
    import pymysql
    
    app = Flask(__name__)
    app.config.update({
        "ZIPKIN_HOST":"127.0.0.1",
        "ZIPKIN_PORT":"9411",
        "APP_PORT":5000,
        # any other app config-y things
    })
    
    
    def do_stuff():
        time.sleep(2)
        with zipkin_span(service_name='service1', span_name='service1_db_search'):
            db_search()
        return 'OK'
    
    
    def db_search():
        # 打开数据库连接
        db = pymysql.connect("127.0.0.1", "root", "123456", "mysql", charset='utf8')
        # 使用cursor()方法获取操作游标
        cursor = db.cursor()
        # 使用execute方法执行SQL语句
        cursor.execute("SELECT VERSION()")
        # 使用 fetchone() 方法获取一条数据
        data = cursor.fetchone()
        print("Database version : %s " % data)
        # 关闭数据库连接
        db.close()
    
    def http_transport(encoded_span):
        # encoding prefix explained in https://github.com/Yelp/py_zipkin#transport
        #body = b"\x0c\x00\x00\x00\x01" + encoded_span
        body=encoded_span
        zipkin_url="http://127.0.0.1:9411/api/v1/spans"
        #zipkin_url = "http://{host}:{port}/api/v1/spans".format(
        #    host=app.config["ZIPKIN_HOST"], port=app.config["ZIPKIN_PORT"])
        headers = {"Content-Type": "application/x-thrift"}
    
        # You'd probably want to wrap this in a try/except in case POSTing fails
        requests.post(zipkin_url, data=body, headers=headers)
    
    
    @app.route('/service1/')
    def index():
        with zipkin_span(
            service_name='service1',
            zipkin_attrs=ZipkinAttrs(
                trace_id=request.headers['X-B3-TraceID'],
                span_id=request.headers['X-B3-SpanID'],
                parent_span_id=request.headers['X-B3-ParentSpanID'],
                flags=request.headers['X-B3-Flags'],
                is_sampled=request.headers['X-B3-Sampled'],
            ),
            span_name='index_service1',
            transport_handler=http_transport,
            port=6000,
            sample_rate=100, #0.05, # Value between 0.0 and 100.0
        ):
            with zipkin_span(service_name='service1', span_name='service1_do_stuff'):
                do_stuff()
        return 'OK', 200
    
    if __name__=='__main__':
        app.run(host="0.0.0.0",port=6000,debug=True)

    运行demo.py

    python3 demo.py

     

    运行server1.py

    python3 server1.py

     

    访问5000端口

    1.png

    四、查看调用链

    点击查证,点击下面的结果

    1.png

     

     效果如下:

    1.png

     

    可以看到,有webapp和services两个service,5个span标签,可以清楚看到service和service,service和span,span和span之间的关系,和各span耗时情况。

     

    点击依赖,效果如下:

    1.png

     

    点击webapp,效果如下:

     1.png

     

    五、api调用

    官网api文档:https://zipkin.io/zipkin-api/#/default/get_traces

     

    这里演示一下,调用2个api

    services

    返回与span终结点关联的所有服务名称的列表。

     

    http://192.168.31.232:9411/api/v2/services

     效果如下:

    1.png

     

     

     

    traces

     调用此请求将检索与以下筛选器匹配的跟踪。

     

     http://192.168.31.232:9411/api/v2/traces

    效果如下:

    1.png

     

     这里的tags,可以显示错误信息。

     

    有错误时,就是红色的,点击红色区块

    1.png

     

     

    就可以看到具体信息

    1.png

    这个错误信息表示,无法连接到mysql。因为这台机器,还没有mysql服务。

     

    为了消除这个错误,可以再启动一个mysql数据库。

    mkdir -p /data/mysql
    docker pull mysql:5.7
    
    docker run -itd -p 3306:3306 --name wiki-mysql -e MYSQL_ROOT_PASSWORD=123456 --restart=always --restart=on-failure:1 --oom-score-adj -1000 --privileged=true --log-opt max-size=10m --log-opt max-file=1 -v /data/mysql:/var/lib/mysql  mysql:5.7

     

    重新启动server1.py

    再次访问5000端口

    1.png

    再次查询一次,就没有红色了

    1.png

     

    如果需要做报警,可以通过调用api,获取到error信息,进行统一的邮件通知。

     

    六、mysql的方式存储

    注意:zipkin的数据,默认是存在内存中的,如果重启服务,会造成数据丢失。 

    在现有数据库基础上,新建实例,实例名为zipkin。

    CREATE DATABASE zipkin DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

     

    然后执行官网建库脚本

    https://github.com/openzipkin/zipkin/blob/master/zipkin-storage/mysql-v1/src/main/resources/mysql.sql

     

    执行sql之后,会建立3张表

     

    这样我们的数据库就建好了。

     

    执行

    STORAGE_TYPE=mysql MYSQL_USER=root MYSQL_PASS=123456 MYSQL_HOST=127.0.0.1 MYSQL_TCP_PORT=3306 java -jar zipkin-server-2.12.9-exec.jar

    这样启动zipkin,就自动连上mysql,并存储数据了。

    如图,大功告成

    1.png

     

     

    注意,一般我们都在后台运行zipkin,所以用nohup的方式启动,命令如下

    STORAGE_TYPE=mysql MYSQL_USER=root MYSQL_PASS=123456 MYSQL_HOST=127.0.0.1 MYSQL_TCP_PORT=3306 nohup java -jar zipkin-server-2.12.9-exec.jar &

     

     

    本文参考链接:

    https://www.cnblogs.com/shijingjing07/p/9340131.html

    https://www.cnblogs.com/tseng-iOS/p/8005889.html


关键字