• 使用火焰图来分析展示top响应时长的请求

    对线上应用进行性能分析的时候,我们首先得知道请求时间大部分都消耗在了哪些地方,之前在SAE上,我们提供了一个top响应时长分析的功能,它的输出类似于下面这样:

    PATH               响应累积时长(降序排列)
    ================== =======================
    /job/p=4.html      1509
    /job/p=s00013.html 1450
    ...
    

    但是从线上的使用来看,这个功能在很多时候作用有限,原因主要是:

    • 部分PHP的框架的PATH只有一个 /index.php ,然后通过query_string来做route。
    • 有些框架的PATH里也会传递参数,比如 /user/100 ,这一类PATH的请求需要聚合。

    比如上面的示例输出中,/job/* 的请求需要聚合,单独一个一个的累积得到的结果就是上面这样,没有多大的意义。我们需要有一个方法能够聚合同类请求的响应时长,类似于下面这样:

    /job               2959
        /p=4.html      1509
        /p=s00013.html 1450
    

    有一个展示方式可以非常好的展示上面的分析结果,就是性能分析里常用的 火焰图

    下面我们尝试使用来火焰图来可视化时长分析的结果。

    假设一条日志是这样的:

    GET /path/to/request?args1=value1&args2=value2 0.096
    

    我们将这个请求按照以下规则转换成类似于火焰图中的调用栈的形式:

    1. 栈底为请求的Method。
    2. 将请求的path用 / 分隔后依次压入栈中。
    3. 如果有querystring的话,将querystring用 & 分隔后依次压入栈中。
    4. 将请求时长转换成毫秒作为调用栈出现的次数。

    最终生成的调用栈的形式如下:

        96
    
    args2=value2
    args1=value1
    ?
    /request
    /to
    /path
    GET
    

    从下往上join起来就是完整的url。

    接下来,我们将日志一条一条转换后按照火焰图工具的输入要求输出给火焰图工具。下面是使用新浪云的日志接口对应用的最后10000次请求时长做分析的代码:

    import urlparse
    import urllib2
    import re
    import collections
    
    # https://github.com/sinacloud/sae-python-dev-guide/blob/master/examples/apibus/apibus_handler.py
    from apibus_handler import SaeApibusAuthHandler
    
    ACCESSKEY = '应用的AccessKey'
    SECRETKEY = '应用的SecretKey'
    
    counter = collections.Counter()
    
    apibus_handler = SaeApibusAuthHandler(ACCESSKEY, SECRETKEY)
    opener = urllib2.build_opener(apibus_handler)
    # 第3、10、11个字段分别是请求的时长、method、url
    for line in opener.open('http://g.sae.sina.com.cn/log/http/2015-12-06/2-access.log?tail,0,10000|fields,%20,3,10,11').readlines():
        fields = line.strip().split(' ')
        # Method放在栈底
        stack = [fields[1],]
        parsed_url = urlparse.urlparse(fields[2])
        # 将请求路径按照 `/` 分隔开
        stack.extend(re.findall('/[^/]*', parsed_url.path))
        if parsed_url.query:
            # 如果有query_string,将query_string按照&分开。
            stack.extend('?')
            stack.extend(parsed_url.query.split('&'))
        # 请求时长的单位为s,将其转换为ms,这样在flame graph里显示的一个sample就是1ms。
        counter[tuple(stack)] += int(float(fields[0])*1000)
    
    for k, v in counter.items():
        print ';'.join(k), v
    

    将上面代码的输出传给flamegraph.pl程序,生成最终的火焰图。

    $ python request-time-flamegraph.py | flamegraph.pl > flamegraph.svg
    

    下面是SAE上某个应用的最后10000次请求生成的火焰图,从这个图里我们可以很明显的看出哪些请求的整体时长比较多,比如: GET /index.php?name=user&handle=getUserStateNums&ajax=1 这些请求占了整个大约25%的请求时长(108,806,631,000ms)。

    request-time-flamegrap

    得到了请求的时长分布分析后,我们接下来就可以对这些请求做进一步的的分析和优化了。

  • 一周更新 #49

    • 跨应用授权

      很多开发者有跨应用访问其它应用的服务的需求,本周我们提供了Memcache、KVDB两个服务的跨应用访问支持。

      现在开发者可以在 应用/应用设置/跨应用授权 页面直接授权其它应用访问本应用的MySQL、Memcache、KVDB等服务。

      关于如何使用API跨应用访问Memcache、KVDB服务,可以参考:

    • 容器云

      • 增加了MySQL、Memcache、Cron服务的支持。 文档
      • 优化了容器云的UI部分。
      • 添加了自动部署选项。目前在应用的部署页面勾选了自动部署后,下次git push的时候,服务端会自动开始部署应用到线上环境。
    • Java运行环境去除版本、上下文,升级OpenJDK,Jetty,对Java框架提供了更好的支持。 详情

  • Java运行环境全新升级

    本次更新的内容如下:

    • JDK升级到1.7、Jetty升级到9(支持Servlet3.1规范)。

    • 去掉版本的概念,对大部分框架提供更好的支持。

      之前为了提供对版本的支持,我们修改了上下文路径,但是很多Java的Web框架对于非 / 的上下文路径支持不是很好。针对这个问题我们决定删除Java的版本支持。

      所有新创建的应用将只能部署一个war包。现有的应用还可以继续运行,但是继续无法创建新的版本。

    • 自动添加了JVM监控应用。

      我们将之前应用创建时运行的默认监控应用直接集成进了开发者的应用之中,所有应用不需要修改任何代码即可通过 http://$appname.sinaapp.com/jconsole 访问。

      在这个监控应用中,开发者可以看到当前JVM运行的各种状态信息(内存,gc,等等),下面是其中一个页面的截图:

      java-jconsole

  • Hello, World!

    >>> print 'Hello, World!'
    Hello, World!
    

subscribe via RSS