juijs / jui-chart

SVG-based JUI chart that can be used in the browser and Node.js. Support many types of charts. (Dashboard, Map, Topology, Full 3D, Realtime)
https://codepen.io/collection/nLydod/
57 stars 25 forks source link

안녕하세요. 차트 이용시 메모리 릭으로 문의한 GTPLUS 입니다. #154

Open HyunSik-Kim opened 6 years ago

HyunSik-Kim commented 6 years ago

안녕하세요. 유선으로 문의드렸던 GTPLUS의 김현식입니다. 말씀하신 코드를 발췌하여 올려드리겠습니다.

크롬버전 : 61.0.3163.100(공식 빌드) (64비트)

// 1.차트 생성(업무단에서 호출)
var createChart = function(chartId, dispList, height) {
    //thisPageInfo.chartCount += 1;

    // 최초 실행
    var chartInfo = {
        chartId: chartId
    };
    var dataList = new Array();

    for(var i = 0; i < dispList.length; i++) {
        var temp = dispList[i];
        //console.log('temp', temp);
        var data = {
            Lag: temp.lagAtChkptToSec,
            Chk: temp.timeSinceChkptToSec
        };

        dataList.push(data);
        data = null;
    }

    dispList = null;
    //removeChartDataByChartId(chartId);
    //console.log('dataList', dataList);

    // 전역변수에 저장
    chartInfo['dataList'] = dataList;
    //console.log('chartInfo', chartInfo);

    // 전역변수에 차트 아이디와 데이터 담아둔다.
    thisPageInfo.chartData.push(chartInfo);

    try {
        var chartOptions = {
            type: thisPageInfo.chartType,
            chartId: chartId,
            keys: ['Lag', 'Chk'],
            data: findChartDataByChartId(chartId).dataList,
            useFilter: true,
            isRealTime: true,
            useEvent: true,
            dblclick: function(data) {
                window.open('/realtime/analysis/lag?instanceid=' + pageInfo.instanceId + '&groupid=' + pageInfo.processGroupId, '_blank');
            },
            height: height * 130
        };
        //console.log('count', thisPageInfo.chartCount);
        // 갱신 인터벌을 한번만 등록하도록 한다.
        if(thisPageInfo.chartCount < 1) {
            chartOptions.refreshFunction = lagChkpntChart.getChartData;
        }

        syncMonChart.drawChart(chartOptions);            
    } catch(e) {
        console.dir(e);
    }

};

// 2.차트 생성 함수
var drawChart = function(options) {
    try {
        var chart = jui.include('chart.builder');
        var time = jui.include("util.time");
        var data = options.data;

        // jui chart 옵션 생성 함수 호출
        var juiOptions = getJuiOptions(options, time);
        //alert(options.chartId);
        //console.log('juiOptions', juiOptions);

        // 높이 설정
        if(options.hasOwnProperty('height') && options.height) {
            juiOptions.height = options.height;
        }

        var chartId = '#' + options.chartId;
        var c = chart(chartId, juiOptions);

        // 리사이즈시 리드로우 하기 위해 전역변수에 저장해둔다.
        var temp = {
            chartId: chartId,
            chart: c,
            time: time,
            isRealTime: options.isRealTime
        };

        // 리프레시 함수가 있으면 start/stop을 위해 전역변수에 담아둔다.
        if(options.refreshFunction && typeof options.refreshFunction === 'function') {
            temp['refreshFunction'] = options.refreshFunction;
        }

        // 새로 그리기를 위해 options값을 저장한다.
        options.height = juiOptions.height;
        temp['options'] = options;

        var interval = null;
        //alert(typeof options.refreshFunction);
        if(pageInfo.autoRefresh && options.refreshFunction && typeof options.refreshFunction === 'function') {
            // Auto Refresh 인터벌 설정
            interval = setInterval(function() {
                // 리얼타임 차트 인터벌
                options.refreshFunction();
                c.render();
            //}, pageInfo.chartRefreshInterval);
            }, pageInfo.refreshInterval);

            temp['intervalObj'] = interval;
            interval = null;
        }

        chartInfo[options.chartId] = temp;
    } catch(e) {
        console.log('chart.js Error', e);
    }
};

// 3.생성 옵션 반환
function getJuiOptions(options, time) {
    var result = {};

    result.axis = [
        {
            // 좌측 Y축 설정
            x: {
                type: 'dateblock',
                realtime: 'minutes',
                interval: 1,
                format: 'HH:mm',
                // X축 Min, Max값(현재 시점 - 5분 ~ 현재 시점(10개의 데이터))
                domain: [ start, current ]
            },
            y: {
                type: 'range',
                // Y축 Min, Max값
                domain: function(data) {
                    var max = Math.max(60, data[options.keys[0]]);
                    return max;
                },
                format: function(value) {
                    // Y축 초를 시분초로 변경하여 화면에 출력
                    return convertSecondToBasic(value);
                },
                step: 3,
                line: 'solid',
                color: '#3ea2f1'
            },
            data: options.data
        },
        {
            // 오른쪽 Y축에 대한 설정
            x: {
                hide: true
            },
            y: {
                type: 'range',
                // Y축 Min, Max값
                domain: function(data) {
                    return Math.max(60, data[options.keys[1]]);
                },
                format: function(value) {
                    // Y축 초를 시분초로 변경하여 화면에 출력
                    return convertSecondToBasic(value);
                },
                color: '#f31176',
                step: 3,
                orient: 'right'
            },
            extend: 0
        }
    ];

    result.brush = [
        {
            type: 'line',
            target: [ options.keys[0], options.keys[1] ],
            colors: [ '#3ea2f1', '#f31176' ]
        },
        {
            type: 'scatter',
            target: ['Lag', 'Chk'],
            colors: [ '#3ea2f1', '#f31176' ],
            size: 5
        }
    ];

    result.widget = [
        {
            type: 'legend',
            filter: options.useFilter,
            brush: [0, 1],
            brushSync: true
        },
        {
            // 차트 라인위에 마우스 오버 됐을 때 풍선 도움말 위치
            type: 'tooltip',
            brush: 1,
            orient: 'right',
            format: function(data, key) {
                return key + ': ' + data[key];
            }
        },
        {
            // 차트위에 마우스 올렸을 때 십자선으로 툴팁 표시
            type: 'cross',
            yFormat: function(d) {
                return Math.round(d);
            },
            xFormat: function(d) {
                return time.format(d, 'HH:mm');
            }
        }
    ];

    result.render = false;

    return result;
}

// 4.차트 데이터 갱신
var refreshData = function(processId, dispList) {
    var chartId = thisPageInfo.chartType + '-' + pageInfo.processGroupId + '-' + processId + '-chart';

    // chartId의 차트 데이터
    var chartData = findChartDataByChartId(chartId);

    // jui 차트 정보
    var chartInfo = syncMonChart.getChart(chartId);
    //console.log('찾은 차트 정보', chartInfo);

    var axis = chartInfo.chart.axis(0);

    // 두번째 요청부터는 최근 데이터 한건만 보내준다.
    var data = {
            Lag: dispList[0].lagAtChkptToSec,
            Chk: dispList[0].timeSinceChkptToSec
    };

    //console.log(processId + ' : 만든 데이터', data);

    if(chartData.dataList.length >= 10) {
        chartData.dataList.shift();
        chartData.dataList.push(data);
        axis.update(chartData.dataList);
    }

    data = null;
    //console.dir(chartInfo.time);

    axis.set("x", {
        domain : [ new Date() - chartInfo.time.MINUTE * 5, new Date() ]
    });

    chartInfo.chart.render();

};

// 최초 차트 생성시 데이터
{
  "resultCount": "4",
  "resultCode": "I100001",
  "resultList": [
    {
      "processId": "process_3292",
      "dispProcessList": [
        {
          "timeSinceChkptToSec": 0,
          "lagAtChkptToSec": 0
        },
        {
          "timeSinceChkptToSec": 10,
          "lagAtChkptToSec": 5
        },
        {
          "timeSinceChkptToSec": 11,
          "lagAtChkptToSec": 3
        },
        {
          "timeSinceChkptToSec": 20,
          "lagAtChkptToSec": 10
        },
        {
          "timeSinceChkptToSec": 9,
          "lagAtChkptToSec": 8
        },
        {
          "timeSinceChkptToSec": 20,
          "lagAtChkptToSec": 40
        },
        {
          "timeSinceChkptToSec": 30,
          "lagAtChkptToSec": 41
        },
        {
          "timeSinceChkptToSec": 22,
          "lagAtChkptToSec": 43
        },
        {
          "timeSinceChkptToSec": 29,
          "lagAtChkptToSec": 49
        },
        {
          "timeSinceChkptToSec": 15,
          "lagAtChkptToSec": 30
        },
        {
          "timeSinceChkptToSec": 13,
          "lagAtChkptToSec": 25
        },
        {
          "timeSinceChkptToSec": 10,
          "lagAtChkptToSec": 20
        },
        {
          "timeSinceChkptToSec": 5,
          "lagAtChkptToSec": 
        },
        {
          "timeSinceChkptToSec": 0,
          "lagAtChkptToSec": 0
        },
      ],
      "startCount": 0,
      "endNum": 10,
      "startNum": 1,
      "endCount": 10
    }
  ],
  "resultMsg": "\uc694\uccad\uc744 \uc131\uacf5\uc801\uc73c\ub85c \uc218\ud589\ud588\uc2b5\ub2c8\ub2e4."
}

// 리얼타임 사용시 갱신할 한건의 데이터
{
  "resultCount": "4",
  "resultCode": "I100001",
  "resultList": [
    {
      "processId": "process_3292",
      "dispProcessList": [
        {
          "timeSinceChkptToSec": 0,
          "lagAtChkptToSec": 0
        }
      ],
      "startCount": 0,
      "endNum": 10,
      "startNum": 1,
      "endCount": 10
    }
  ],
  "resultMsg": "\uc694\uccad\uc744 \uc131\uacf5\uc801\uc73c\ub85c \uc218\ud589\ud588\uc2b5\ub2c8\ub2e4."
}

확인에 더 필요한 정보가 있으시면 말씀해주시기 바랍니다. 감사합니다.

seogi1004 commented 6 years ago

동일한 버전의 크롬 브라우저에서 24시간 정도 모니터링을 해봤는데, 메모리릭이 재현되지 않았습니다. (샘플 : http://jui.io/?p=gallery.realtime)

올려주신 샘플에는 글로벌 변수에 대한 코드가 없는데, 글로벌 변수 어딘가에 데이터가 누적되면 메모리가 증가할 수는 있습니다.

관련해서 한번 체크해주세요.