websocket+vue+echarts实现实时动态渲染图表

前言

最近遇到一个需求,需要在前端做一个可视化的图表,并且是实时更新的。但echarts不支持图表的实时渲染。只能通过setOption重新设置。我的实现思路是在页面初始化的时候向后端发起一个websocket请求,后端收到连接后将channel保存起来,每次数据更新或隔段时间就轮询这些channel推送数据。

后端

websocket我用的是netty实现的,在handler中的handlerAdded监听add事件后将channel存入service层的channels容器。

     /**
       * 当有新的连接加入时就将channel添加到指定容器
       */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        DataChartServiceImpl.putChannel(ctx.channel());
    }

service层每隔5s就遍历channels容器,如果客户端还在连接状态就推送消息。

    /**
     * websocket客户端容器
     */
    private static List<Channel> channels = new ArrayList<>();

    /**
     * 添加客户端
     */
    public static void putChannel(Channel channel){
        channels.add(channel);
    }
    /**
     * 每隔5s获取一次
     * @return void
     * @author 黎勇炫
     * @create 2022/8/4
     * @email 1677685900@qq.com
     */
    @Override
    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void updateData(){
        if(counter.intValue() == 12){
            resetData();
        }else {
            add();
            counter.increment();
        }
        Iterator iterator = channels.iterator();
        while (iterator.hasNext()){
            Channel channel = (Channel) iterator.next();
            if(!channel.isActive()){
                iterator.remove();
                return;
            }
            channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(queueData())));
        }
    }

前端

vue初始化的时候打开websocket连接,同时先向后端controller获取一次数据。因为后端5s传一次数据,要保证视图先渲染出来。每次websocket服务端推送消息时都重新渲染一次ecahrts图表。注意:在websocket中的onmessage 不能直接使用this,需要在外出将this赋值给var _this,在onmessage 方法中操作_this.

  mounted(){
    this.initWebsocket();
    this.initCharts();
  },
  methods:{
    initWebsocket(){
      var socket;
      var _this = this;
      if(window.WebSocket){
        // go on
        socket = new WebSocket("ws://localhost:8687/connect");

        socket.onmessage = function (ev){
          var res = JSON.parse(ev.data)
          _this.setCharts(res)
        }
      }
    },
    initCharts(){
      axios.get("/xy/data/queue").then(Response=>{
        this.setCharts(Response.data)
      })
    },
    setCharts(data){
      let qLinechar = this.$refs.qLinechar
      let qPiechar = this.$refs.qPiechar

      var myChart = echarts.init(qLinechar);
      var myChart2 = echarts.init(qPiechar);

      var queueAccDetail = [];
      Object.keys(data.queueAccDetail).forEach(function (item){
        queueAccDetail.push({name:item,value:data.queueAccDetail[item]})
      })
      var option = {
        title: {
          text: '队列容器'
        },
        tooltip: {
          trigger: 'axis'
        },
        legend: {
          data: ['消息堆积','消费成功','消息总数']
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          containLabel: true
        },
        toolbox: {
          feature: {
            saveAsImage: {}
          }
        },
        xAxis: {
          type: 'category',
          boundaryGap: false,
          data: data.time
        },
        yAxis: {
          type: 'value'
        },
        series: [
          {
            name: '消息堆积',
            type: 'line',
            stack: '消息堆积',
            data: data.accQueueCount
          },
          {
            name: '消费成功',
            type: 'line',
            stack: '消费成功',
            data: data.successQueueCount
          },
          {
            name: '消息总数',
            type: 'line',
            stack: '消息总数',
            data: data.queueTotal
          }
        ]
      };
      var option2 = {
        title: {
          text: '堆积详情',
          // subtext: 'Fake Data',
          left: 'center'
        },
        tooltip: {
          trigger: 'item'
        },
        legend: {
          orient: 'vertical',
          left: 'left'
        },
        series: [
          {
            name: 'Access From',
            type: 'pie',
            radius: '50%',
            data: queueAccDetail,
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: 'rgba(0, 0, 0, 0.5)'
              }
            }
          }
        ]
      };

      myChart.setOption(option,true);
      myChart2.setOption(option2)

    }
  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值