前端请求队列,解决多个请求同时请求一个接口导致阻塞的问题

1、背景

最近开发的数据大屏项目,使用echarts图表,通过拖拽的方式完成大屏的布局。
每一个图表编写一个vue文件,例如柱状图(barChart.vue):

<template>
  <div>
    <v-chart ref="barchart" :option="options" autoresize />
  </div>
</template>

<script>
export default {
  components: {},
  props: {
    value: Object,
    index: Number
  },
  data() {
    return {
      options: {
        grid: {},
        legend: {
          textStyle: {
            color: "#fff"
          }
        },
        xAxis: {
          type: "category",
          data: [],
          axisLabel: {
            show: true,
            textStyle: {
              color: "#fff"
            }
          }
        },
        yAxis: {
          type: "value",
          data: [],
          axisLabel: {
            show: true,
            textStyle: {
              color: "#fff"
            }
          }
        },
        series: [
          {
            data: [],
            type: "bar",
            barGap: "0%",
            itemStyle: {
              borderRadius: null
            }
          }
        ]
      },
      optionsStyle: {}, // 样式
      optionsData: {}, // 数据
      optionsSetup: {},
    };
  },
  mounted() {
    this.editorOptions();
  },
  methods: {
    // 修改图标options属性
    editorOptions() {
      this.setOptionsData();
    },
    // 数据解析
    setOptionsData() {
      const data = this.queryEchartsData(val);
      data.then(res => {
        this.renderingFn(optionsSetup, res);
      });
    },
  }
};
</script>

queryEchartsData的方法体如下:

queryEchartsData(params,_this) {
   return new Promise(async (resolve) => {
     const {code, data} = getData(params);
     if (code != 200) return
     resolve(data)
   })
 }

折线图等其他图表同理,都是使用queryEchartsData来获取后端接口返回的数据,queryEchartsData写在queryData.js中,然后通过全局混入的方式使用。当我一个大屏拖拽了太多的组件时,渲染时,每一个组件都同时通过queryEchartsData来获取数据,就会导致阻塞,最后一个接口返回数据的时间甚至可能达到9秒左右
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
从图上就可以看到光阻塞时长就达到了6秒,我的天啊!!!

2、思路

我实在受不了这种情况,于是就想办法解决喽,首先要弄清楚阻塞的原因到底在后端还是在前端。

  1. 后端使用ehcache来缓存接口数据
if (cacheHelper.exist(key)) {
    data = cacheHelper.stringGet(key);
} else {
	data = reportService.getChartData(dto)
	cacheHelper.stringSetExpire(datakey, JSON.toJSONString(data), 3600);
}

这样写完以后发现情况并没有任何变化,所以阻塞的情况是前端同时一次性发起太多请求导致的。

  1. 前端使用vuex来缓存接口数据
    在Vuex的store中定义一个状态,用于存储请求返回的数据。
// store.js
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    echartsData: {},
  },
  mutations: {
    setEchartsData(state, data) {
      state.echartsData[data.key] = data.data;
    },
  },
});

然后在执行queryEchartsData的时候,先判断store中是否已存在,如果存在就直接从store获取,否则就调用接口然后往store中写入。

但是在实际测试过程中,发现每一个vue文件进入到queryEchartsData后,在执行到getData的时候就不在往下走,而是跳转到下一个vue文件进入queryEchartsData方法,当所有vue文件都进入过queryEchartsData方法后,才会再继续执行getData后面的内容,无语,这个也行不通啊…

3、解决

总归是要解决,最后通过不停的喂ChatGPT,终于是找到了解决办法,使用请求队列和限流,实现一个请求队列,控制并发请求的数量,避免同时发起过多的请求。可以设置最大并发数,并根据需求进行请求的限流,以平衡请求和服务器资源之间的关系。代码如下:

// your-queue-file.js

/**
 * 创建请求队列
 * @param {number} maxConcurrentRequests 最大并发请求数量
 * @returns {object} 请求队列对象
 */
export function createRequestQueue(maxConcurrentRequests) {
  const queue = []; // 存储请求的队列
  let runningCount = 0; // 当前正在运行的请求数量

  /**
   * 处理下一个请求
   */
  async function processNextRequest() {
    if (runningCount >= maxConcurrentRequests || queue.length === 0) return;

    runningCount++;
    const request = queue.shift(); // 获取队列中的下一个请求

    try {
      const result = await request(); // 执行请求
      // 处理请求结果
      // ...
    } catch (error) {
      // 处理请求错误
      // ...
    } finally {
      runningCount--;
      processNextRequest(); // 处理下一个请求
    }
  }

  return {
    /**
     * 添加请求到队列
     * @param {function} request 请求函数,返回 Promise
     * @returns {Promise} 请求的 Promise 对象
     */
    add(request) {
      return new Promise((resolve, reject) => {
        queue.push(async () => {
          try {
            const result = await request(); // 执行请求
            resolve(result); // 请求成功,返回结果
          } catch (error) {
            reject(error); // 请求失败,返回错误信息
          }
        });

        processNextRequest(); // 添加请求后,处理下一个请求
      });
    }
  };
}
// 在 queryform.js 中
queryEchartsData(params, _this) {
  return new Promise(async (resolve) => {
    const result = await addToRequestQueue(() => getData(params));
    const { code, data } = result;
    if (code !== 200) return;
    resolve(data );
  });
}

// 在 main.js 中
import { createRequestQueue } from './your-queue-file'; // 导入请求队列的实现

const requestQueue = createRequestQueue(3); // 创建最大并发请求数为3的请求队列

Vue.mixin({
  methods: {
    addToRequestQueue(request) {
      return requestQueue.add(request);
    }
  }
});

测试结果如下:
在这里插入图片描述
除了最后一个接口时间在1.84秒,其他的都基本上在400多毫秒,而阻塞时间也控制在0.16ms左右
在这里插入图片描述
至此也算是解决了一桩心病啊,后面就可以在继续优化下去了!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_34337333

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值