美文网首页
实战:微服务之分布式跟踪系统(Zipkin+Python)

实战:微服务之分布式跟踪系统(Zipkin+Python)

作者: c4a1d989518e | 来源:发表于2018-05-11 16:40 被阅读1715次

    启动Zipkin

    有这样两种推荐的启动方式,一个是用docker,拉取对应的images,然后用命令

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

    接着访问http://localhost:9411就会跳转至ZipKin的UI界面了。

    ZipKin首页

    除了单独启动一个zipkin的docker,还可以启动一个集群。比如这个docker-zipkin
    上面的项目的用法是下面这样:

    git clone https://github.com/openzipkin/docker-zipkin
    cd docker-zipkin
    docker-compose up
    

    这样的做法是启动了一个集群。比如我用docker-compose启动之后,docker中的启动了如下容器。


    zipkin集群

    如果是刚开始接触zipkin,可以不用管这些,只启动一个openzipkin/zipkin一个容器就好了。关于这个集群中其他容器的用法,上面的那个文档上都有说明是用来干什么的。
    比如默认启动的容器中,mysql那个容器可以用来保存日志,还可以指定日志保存在哪里。也可以保存在Cassandra和Elasticsearch中(zipkin是有内建的存储的,但是不能持久化,要想持久化,还是需要用数据库),指定的方式是

    $ docker-compose -f docker-compose.yml -f docker-compose-cassandra.yml up
    

    第二种方式是用java方式启动。
    下载jar包,并启动,命令为:

    curl -sSL https://zipkin.io/quickstart.sh | bash -s
    java -jar zipkin.jar
    

    这种做法是和单独启动一个zipkin的docker的容器作用一样。

    Python的测试代码

    有关的代码在这篇文章的基础上而来, Introducing Distributed Tracing in Your Python Application via Zipkin。这篇文章中的代码有个坑,让我调试了好久。

    其中的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
    })
    
    @zipkin_span(service_name='webapp', span_name='do_stuff')
    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
        ):
            do_stuff()
            time.sleep(1)
        return 'OK', 200
    
    if __name__=='__main__':
        app.run(host="0.0.0.0",port=5000,debug=True)
    
    

    文章中提到的service1.py的代码为:

    from flask import request
    import requests
    from flask import Flask
    from py_zipkin.zipkin import zipkin_span,ZipkinAttrs
    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
    })
    
    @zipkin_span(service_name='service1', span_name='service1_do_stuff')
    def do_stuff():
        time.sleep(2)
        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
        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
        ):
            do_stuff()
        return 'OK', 200
    
    if __name__=='__main__':
        app.run(host="0.0.0.0",port=6000,debug=True)
    
    

    我当时面对的问题是,zipkin是提供一些api接口的,对于这些接口的测试,有这样一个swagger网站。zipkin接口

    但无论是通过swagger请求还是在浏览器调试,都没有信息返回


    以前

    这是我后来调试成功的结果


    success

    对,应该是有返回的。

    上面的代码中,http_transport是负责把标准化的信息,或者说log,发送给zipkin的Collector。zipkin的Collector就是http://127.0.0.1:9411/api/v1/spans这个接口。通过post方式提交相应格式的log。

    zipkin的架构

    但我这里面对的问题是,无论我是用swagger上的例子去post这个请求,还是用postman去post这个接口,它都会返回404,并报Cannot decode spans这个错误。这个错误的意思是没有吧spans转换成byte类型。

    可我代码中明明是已经转换成byte类型了啊。比如我注释掉的

    body = b"\x0c\x00\x00\x00\x01" + encoded_span
    

    这里面的body就已经是byte类型了啊,后来我终于找到了错误,错误在b"\x0c\x00\x00\x00\x01"这里。有人说这句话的意思是
    "As I understand: b'\x0c\x00\x00\x00\x01' - stands for list with length 1 and 0.9.0 already encodes everything as a list so it doesn't required anymore."
    来自于这个

    Cannot decode spans错误
    然后我就把b"\x0c\x00\x00\x00\x01"给删除了,程序就正常了。
    在去zipkin的ui页面,首页点击"Find Traces",点点就出来了。
    success zipkin success

    代码实际调试

    这是我新添加的一个函数does_stuff

    产生的效果是

    下面的这张图可以清楚的看出函数占用的时间,如果看过上面的源码,webapp是调用service1的,所以service1的完成才代表webapp的完成。

    即时是service中的名字都为webapp,它们的spanId也不相同。

    还有就是在调用的服务中加zipkin_attrs与不加zipkin_attrs的区别。


    不加zipkin_attrs 加zipkin_attrs

    打印demo.py传过来的请求,也就是打印request.header,打印结果为:


    request.header

    点击其中的一个service是这样,从这里可以看到zipkin中一些常见的名词,cs、sr、cr、ss、traceId、spanId、parentId。多点开两个,就可以看出spanId和parentId之间的关系。

    service的信息

    这是它的依赖分析,这就可以直观清楚的看出函数之间的调用关系。


    原理

    原理1

    原理2


    原理2

    研究的热点时期是2016年,Google的Dapper,Twitter的zipkin,淘宝的鹰眼,新浪的Watchman,京东的Hydra,唯品会的Microscope,窝窝网的Tracing
    eBay叫Centralized Activity Logging (CAL),大众点评网叫CAT

    其他参考文章

    zipkin的官方文档
    下面这篇可以看做是官方文档的部分翻译:
    微服务之分布式跟踪系统(springboot+zipkin)

    相关文章

      网友评论

          本文标题:实战:微服务之分布式跟踪系统(Zipkin+Python)

          本文链接:https://www.haomeiwen.com/subject/axcvrftx.html