zoukankan      html  css  js  c++  java
  • VUE组件——FlameGraph火焰图

    先贴个图

    火焰图的应用场景

           用于显示复杂的函数调用链,特点在于:数据量较大。

           详细介绍可以参考阮大师的博客:http://www.ruanyifeng.com/blog/2017/09/flame-graph.html

    本文主要在vue框架的基础上实现函数调用链火焰图,其它框架的实现方式类似,由于应用场景中的数据量较大,使用了三种方式来实现并测试该功能。

    数据源格式:

          函数调用链实质就是一棵树,最下层节点是根;上层节点的位置及宽度依赖于下层节点。

    {
    id: {
    id: 'id_*', //使用字符串id,避免json.parse自动排序
    name: 函数名, percentage: 百分比, parentId: 父节点id, prevId: 兄弟节点id, level: 层级,
    }
    }

      

    计算方式:

         根节点的宽度默认为100%宽度;

        子节点宽度=调用百分比*父节点宽度;

        位置信息x 从父节点的x开始,若前面有兄弟节点prev,则x = prev.x + prev.width;

        位置信息y 从最底端开始,y = level * 16(每层火焰高度为15,间隔1像素)。

    方法一:SVG多组件渲染方式

    将一个节点作为一个组件实现

    <svg width="100%" :height="height">
       <g v-show="!loading" id="graphContainer">
          <call-item v-for="item in items" :item="item" :key = "item.id" @mouseover="displayItem(item)" @mouseout="display=''" 
            :parent="items['id_' + item.parentId]" @click="repaint(item)"
            :prev="items['id_' + item.preId]" :height="cHeight" :cur = "curItem"></call-item>
          <text x="15" :y="cHeight - 15" >{{display}}</text>
        </g>
    </svg>

    mouseover事件,在下册text区域显示鼠标滑动所至节点的详细信息

    click事件,以被点击的节点为最大节点重绘调用链,其父节点均半透明,子节点根据当前节点宽度及位置信息重绘

    单组件渲染方式在节点个数过大时效果较差

    方法二:SVG单组件渲染方式

    使用js拼凑火焰图部分作为模板来渲染,绘制部分和渲染部分如下:

       drawOneCallItem (item, frag, items) {
          let g = '', color = this.calcItemColor(),
            title = `${item.name}, ${item.percentage}%)`, display = title
          display = this.calcDisplay(title, item.width)
          g = `<g class="func_g">
          <title>${title}</title>
          <rect x="${item.pos.x}" y="${item.pos.y}" width="${item.width}" height="15.0" fill="${color}" rx="2" ry="2"
          orig-x="${item.pos.x}" orig-y="${item.pos.y}" orig-width="${item.width}" ></rect>
          <text x="${item.pos.x + 5}" y="${item.pos.y + 12}" >${display}</text>
          </g>`
          frag.push(g)
        },
        initFlameGraph () {
          console.info('starting getData:', new Date().getTime())
          if (this.id > 0) {
            this.depth = 0
            let frag = []
            service.getTree().then(({data}) => {
              let res = data.data
              let graph = document.getElementById('graphContainer')
              graph.innerHTML = ''
              for (let key of Object.keys(res.records)) {
                let item = res.records[key]
                this.drawOneCallItem(item, frag, res.records)
              }
              var MyAppendTo = Vue.extend({
                template: frag.join('')
              })
              let app = new MyAppendTo()
              app.$mount().$appendTo('#graphContainer')
              frag = []
              console.info('draw finish:', new Date().getTime())
            })
          }
        }

    此方案在绘制1万+个节点时加上网络消耗大致需要2秒左右,基本符合需求。

    方法三:Canvas实现方式

    canvas 方式绘制性能非常好,实验过12万+的节点比方法二快3倍左右。但事件处理流程比较复杂,尤其是hover事件的模拟,计算量也比较大。

    计算方式与方法二类似,预先计算好节点位置,然后从最底层按照宽度和位置信息按序绘制即可。

    RoundedRect 类是canvas的单节点绘制类,原本是需要绘制圆矩形的,但宽度低时有问题,后面修改后再更新。
    export default class RoundedRect {
      constructor (ctx, x, y, width, fill, text) {
        this.ctx = ctx
        this.x = x
        this.y = y
        this.width = width
        this.fill = fill
        this.text = text
      }
    
        //
      draw () {
        let ctx = this.ctx
        this.drawRoundedRect(ctx, this.x, this.y, this.width, 15, 0, this.fill, this.text)
      }
    
      calcDisplay (title, width) {
        let cLen = Math.ceil(width / 8) - 2,
          disStr = title
        if (title.length > cLen) {
          disStr = title.substring(0, cLen - 2)
          disStr = disStr.length > 0 ? disStr + '..' : disStr
        }
        return cLen <= 4 ? '' : disStr
      }
    
      drawRoundedRect (ctx, x, y, w, h, r, bgcolor, text) {
        ctx.beginPath()
        ctx.fillStyle = bgcolor
        ctx.fillRect(x, y, w, h)
        ctx.strokeText(this.calcDisplay(text, w), x + 5, y + 12)
        ctx.closePath()
      }
    }

    canvas方案的hover和click事件需要通过鼠标位置信息找到hover或click的是哪个节点,在节点很多时计算量也是相当的大啊,最后不得不放弃canvas方案。选择方法二。

  • 相关阅读:
    【学习笔记】ASP.NET页面之间传值的方式之Application
    【学习笔记】ASP.NET页面之间传值的方式之QueryString
    【学习笔记】C#中的装箱(inboxing)和拆箱(unboxing)
    C# Func和Action用法以及区别和使用Lambda表达式
    构建ABP vNext项目并切换MySql数据库
    .Net FrameWork发布项目时报Microsoft.Net.Compilers is only supported on MSBuild v16.3 and above错误解决方案
    Docker容器与Linux主机环境获取时间不一致
    shell脚本中无法使用cd的问题解决方法
    Docker安装Mysql8.0,并配置忽略大小写
    Docker-Compose排版一些坑
  • 原文地址:https://www.cnblogs.com/yiyitong/p/8182416.html
Copyright © 2011-2022 走看看