启动Zipkin
有这样两种推荐的启动方式,一个是用docker,拉取对应的images,然后用命令
docker run -d -p 9411:9411 openzipkin/zipkin
接着访问http://localhost:9411
就会跳转至ZipKin的UI界面了。
除了单独启动一个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。
但我这里面对的问题是,无论我是用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."
来自于这个
然后我就把
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)
网友评论