【SkyWalking】Python服务从零接入全链路追踪解决方案

1,693 阅读3分钟

本文正在参加「金石计划」

前言

本来以为作为一名单纯的练习两年半的Javaboy,把Java服务接入好Skywalking就完事了,谁曾想Python服务也要接入进来,只能说君让臣死,臣不得不死,由于不怎么属于python,虽然有点磕磕绊绊总体还是挺顺利,特此记录下接入的方案

1.安装

这个参考下**Skywalking官网的示例**

pip安装只需要pip install一下即可,不过要注意python版本是必须要求python3

pip install "apache-skywalking

2.使用及原理

2.1.使用

在python服务的启动文件加入以下

from skywalking import agent, config
config.init() 
agent.start()

所有的配置均从环境变量读取,环境变量名可以在init方法里查看,源码里定义了各种常量。

2.2.原理

2.2.1.插件源码

  • agent.start() 执行时会加载内置的插件,然后插件会拦截相关的函数,然后上报数据
  • 以request为例
def install():
    from requests import Session
 
    _request = Session.request
 
    def _sw_request(this: Session, method, url,
        params=None, data=None, headers=None, cookies=None, files=None,
        auth=None, timeout=None, allow_redirects=True, proxies=None,
        hooks=None, stream=None, verify=None, cert=None, json=None):
 
        from skywalking.utils.filter import sw_urlparse
        url_param = sw_urlparse(url)
 
        # ignore trace skywalking self request
        if config.protocol == 'http' and config.collector_address.rstrip('/').endswith(url_param.netloc):
            return _request(this, method, url, params, data, headers, cookies, files, auth, timeout,
                allow_redirects, proxies, hooks, stream, verify, cert, json)
  
        span = NoopSpan(NoopContext()) if config.ignore_http_method_check(method) \
            else get_context().new_exit_span(op=url_param.path or "/", peer=url_param.netloc,
            component=Component.Requests)
 
        # skywakling.trace.Span上下文
        # 退出是会执行__exit__ -> self.stop() -> 会上报数据
        with span:
            carrier = span.inject()
            span.layer = Layer.Http
 
            if headers is None:
                headers = {}
            # 将相关参数放置在请求头headers里 Sw8
            for item in carrier:
                headers[item.key] = item.val
 
            span.tag(TagHttpMethod(method.upper()))
            span.tag(TagHttpURL(url_param.geturl()))
 
            # 执行requests函数本身的request函数并返回
            res = _request(this, method, url, params, data, headers, cookies, files, auth, timeout,
                allow_redirects, proxies, hooks, stream, verify, cert, json)
 
            span.tag(TagHttpStatusCode(res.status_code))
            if res.status_code >= 400:
                span.error_occurred = True
 
            return res
 
    # 这里是替换了内部的request函数
    Session.request = _sw_request

2.2.2.插件原理总结

1. 做一些前置操作, 如: requests包设置headers信息, web框架包解析headers信息
2. 执行本身包的操作
3. 做一些后置操作(上报数据)
4. 返回本身包执行的返回结果

插件非常类似Java的动态代理

2.2.3.headers的数据格式

接入后链路追踪通过Header获取相应数据,需要满足固定的格式

return '-'.join([
    '1',
    b64encode(self.trace_id),
    b64encode(self.segment_id),
    self.span_id,
    b64encode(self.service),
    b64encode(self.service_instance),
    b64encode(self.endpoint),
    b64encode(self.client_address),
])

注意项

我们的服务web框架是使用的tornado,当使用多进程时,使用grpc时会有bug,grpc不适配多进程,当tornado使用server.start(20)时只能使用http上报信息

解决方案: server.start(1),然后run使用run_on_executor线程池,通过supervisord管理,使用多个端口启动服务,再通过nginx做负载均衡(或使用nacos进行服务转发)**

3.相关配置及插件

3.1.配置

重点配置,通过环境变量配置, python初始话配置见 config.init() 文件

`SW_AGENT_NAME:                        服务名称`

`SW_AGENT_NAMESPACE:                   服务命名空间`

`SW_AGENT_COLLECTOR_BACKEND_SERVICES:  上报的地址`

`SW_AGENT_PROTOCOL:                    上报的方式`

`SW_AGENT_LOGGING_LEVEL:               skywalking日志等级`

`SW_AGENT_PROFILE_ACTIVE:              是否上报日志`

`SW_AGENT_LOG_REPORTER_LEVEL:          上报日志的等级`

官方文档的所有配置

skywalking.apache.org/docs/skywal…

3.2.插件

python支持的插件

skywalking.apache.org/docs/skywal…

`sw_aiohttp`

`sw_celery`

`sw_django`

`sw_elasticsearch`

`sw_falcon`

`sw_fastapi`

`sw_flask`

`sw_http_server`

`sw_http_server`

`sw_kafka`

`sw_mysqlclient`

`sw_psycopg`

`sw_psycopg2`

`sw_pymongo`

`sw_pymysql`

`sw_pyramid`

`sw_rabbitmq`

`sw_redis`

`sw_requests`

`sw_sanic`

`sw_tornado`

`sw_urllib3`

`sw_urllib_request`

基本支持了所有web框架,服务中只需要在启动类里config.init() agent.start()一下,插件会自动帮我们根据环境变量的Skywalking配置将服务注册到oap中。