前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springboot第70集:字节跳动后端三面经,一文让你走出微服务迷雾架构周刊

springboot第70集:字节跳动后端三面经,一文让你走出微服务迷雾架构周刊

作者头像
达达前端
发布2024-04-29 08:18:00
1300
发布2024-04-29 08:18:00
举报
文章被收录于专栏:达达前端达达前端

创建一个使用Kubernetes (K8s) 和 Jenkins 来自动化 GitLab 前端项目打包的CI/CD流水线,需要配置多个组件。下面,我将概述一个基本的设置步骤和示例脚本,以帮助你理解如何使用这些工具整合一个自动化流程。

前提条件

确保你已经有:

  1. Kubernetes 集群:用于部署 Jenkins 和可能的其他相关服务。
  2. Jenkins:安装在 Kubernetes 集群上,并配置好相关插件(例如 GitLab 插件、Kubernetes 插件等)。
  3. GitLab:托管你的前端代码。
步骤一:在 Kubernetes 上部署 Jenkins

首先,你需要在 Kubernetes 集群上部署 Jenkins。你可以使用 Helm chart 来简化部署过程。以下是一个基本的 Helm 安装命令:

代码语言:javascript
复制
helm repo add jenkins https://charts.jenkins.io helm repo update helm install jenkins jenkins/jenkins

helm repo add jenkins https://charts.jenkins.io
helm repo update
helm install jenkins jenkins/jenkins

确保配置 Jenkins 的持久卷和服务,以便它能稳定运行并保持数据。

步骤二:配置 Jenkins 与 GitLab 的集成

在 Jenkins 中安装并配置 GitLab 插件:

  1. 在 Jenkins 中安装 GitLab Plugin。
  2. 在 GitLab 中创建一个具有适当权限的访问令牌。
  3. 在 Jenkins 的系统配置中配置 GitLab 连接,输入 GitLab 的URL和创建的访问令牌。
步骤三:创建 Jenkins Pipeline

在 Jenkins 中创建一个新的 Pipeline 项目,你可以使用 Jenkinsfile 来定义流水线。这个 Jenkinsfile 需要放在你的前端项目的根目录下:

代码语言:javascript
复制
pipeline {
    agent {
        kubernetes {
            yaml """
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: node
    image: node:14-buster
    command:
    - cat
    tty: true
"""
        }
    }
    stages {
        stage('Checkout') {
            steps {
                git branch: 'main', url: 'https://gitlab.example.com/your-username/your-project.git'
            }
        }
        stage('Install') {
            steps {
                script {
                    container('node') {
                        sh 'yarn install'
                    }
                }
            }
        }
        stage('Build') {
            steps {
                script {
                    container('node') {
                        sh 'yarn build'
                    }
                }
            }
        }
        stage('Deploy') {
            steps {
                // 添加部署脚本或 Kubernetes 部署命令
            }
        }
    }
}
说明
  • Jenkins Pipeline 使用 Kubernetes 插件在 Kubernetes Pod 内运行。
  • 使用 Node.js 容器来执行前端构建任务(例如 yarn install 和 yarn build)。
  • 这个示例基于 Node.js 容器,你需要确保镜像版本与你的项目兼容。
  • 根据需要调整 GitLab 仓库 URL 和分支。
步骤四:触发器和部署
  • 在 Jenkins 中配置触发器,以便在 GitLab 中推送更新时自动启动构建。
  • 在 "Deploy" 阶段,你可以添加部署到 Kubernetes 的步骤,如使用 kubectl 命令或 Helm chart。

在GitLab CI/CD流水线中,当你使用Yarn来安装依赖,这些依赖通常会被安装在项目的node_modules目录下。这是Node.js和Yarn的标准行为。GitLab CI/CD流水线使用的是GitLab Runner来执行定义在.gitlab-ci.yml文件中的作业。

GitLab Runner工作目录

GitLab Runner通常会为每一个CI/CD作业创建一个隔离的环境,这通常是在Runner的系统上的一个临时目录。这个目录通常会在作业完成后被清理掉,除非你特别配置了缓存或者工件(artifacts)来存储这些文件。

使用 npm 安装 CLI 到开发依赖

$ npm install --save-dev @tarojs/cli@1.3.9

使用 yarn 安装 CLI 到开发依赖

$ yarn add --dev @tarojs/cli@1.3.9

使用 cnpm 安装 CLI 到开发依赖

$ cnpm install --save-dev @tarojs/cli@1.3.9

  1. 简化代码:将操作合并到单个流操作中,减少了重复的代码。
  2. 使用 flatMap() :使用 flatMap() 处理 feeRuleIds 字段,避免了在循环中手动拆分字符串。
  3. 一致的命名风格:使用了统一的命名风格(驼峰命名法),提高了代码的一致性和可读性。
  4. 使用 StringBuilder 替代字符串拼接,提高效率。

npm install echarts --save

代码语言:javascript
复制
// echarts-4.0
import echarts from 'echarts'
Vue.prototype.$echarts = echarts

// 引入echarts-5.0
import * as echarts from 'echarts'
Vue.prototype.$echarts = echarts

let myChart = this.$echarts.init(document.getElementById('map'))

import * as echarts from "echarts";

let myChart = echarts.init(document.getElementById("map"));

import "../../node_modules/echarts/map/js/china"; // 引入中国地图  注意是4.0才有 不然会报错

methods: {
    //中国地图
    chinaMap() {
      let myChart = echarts.init(document.getElementById("map")); // 初始echarts
      let option = {
        // 绘制地图
        tooltip: {
          // 提示框信息配置
          // triggerOn: "click", // 触发方式
          trigger: "item", // 对象
          // backgroundColor:'#6950a1',
          formatter: (params) => {
            // 格式化提示框信息。 若要访问 data中的数据则要用箭头函数
            return `${params.name} <br/> 
                      地区ID: ${
                        this.arrMap.find((item) => item.name === params.name)
                          ?.id ?? 0
                      }`;
          },
        },
        series: [
          // 配置地图
          {
            type: "map", // 类型
            mapType: "china", // 地图名称,要和引入的地图名一致
            roam: true, // 是否开启鼠标缩放和平移漫游
            label: {
              // 地图省份模块配置
              normal: { show: true }, // 是否显示省份名称
              position: "right", // 显示位置
            },
            emphasis: {
              // 高亮状态下的多边形和标签样式。
              label: {
                show: true, // 是否显示标签。
              },
            },
            itemStyle: {
              //地图区域的多边形图形样式
              normal: {
                areaColor: "#2a5caa", //地图区域颜色
                borderColor: "#afb4db", //图形的描边颜色
                borderWidth: 1, //描边线宽。为 0 时无描边
                borderType: "solid", // 边框样式
                opacity: 0.6, // 透明度
              },
            },
            data: this.arrMap, // 提示框的数据源
          },
        ],
      };
      myChart.setOption(option);
    },
代码语言:javascript
复制
/**
 * echarts tooltip轮播
 * @param chart ECharts实例
 * @param chartOption echarts的配置信息
 * @param options object 选项
 * {
 *  interval    轮播时间间隔,单位毫秒,默认为2000
 *  loopSeries  boolean类型,默认为false。
 *              true表示循环所有series的tooltip,false则显示指定seriesIndex的tooltip
 *  seriesIndex 默认为0,指定某个系列(option中的series索引)循环显示tooltip,
 *              当loopSeries为true时,从seriesIndex系列开始执行。
 *  updateData  自定义更新数据的函数,默认为null;
 *              用于类似于分页的效果,比如总数据有20条,chart一次只显示5条,全部数据可以分4次显示。
 * }
 * @returns {{clearLoop: clearLoop}|undefined}
 */
 export function loopShowTooltip(chart, chartOption, options) {
    let defaultOptions = {
      interval: 2000,
      loopSeries: false,
      seriesIndex: 0,
      updateData: null,
    };
  
    if (!chart || !chartOption) {
      return;
    }
  
    let dataIndex = 0; // 数据索引,初始化为-1,是为了判断是否是第一次执行
    let seriesIndex = 0; // 系列索引
    let timeTicket = 0;
    let seriesLen = chartOption.series.length; // 系列个数
    let dataLen = 0; // 某个系列数据个数
    let chartType; // 系列类型
    let first = true;
    let lastShowSeriesIndex = 0;
    let lastShowDataIndex = 0;
  
    if (seriesLen === 0) {
      return;
    }
  
    // 待处理列表
    // 不循环series时seriesIndex指定显示tooltip的系列,不指定默认为0,指定多个则默认为第一个
    // 循环series时seriesIndex指定循环的series,不指定则从0开始循环所有series,指定单个则相当于不循环,指定多个
    // 要不要添加开始series索引和开始的data索引?
  
    if (options) {
      options.interval = options.interval || defaultOptions.interval;
      options.loopSeries = options.loopSeries || defaultOptions.loopSeries;
      options.seriesIndex = options.seriesIndex || defaultOptions.seriesIndex;
      options.updateData = options.updateData || defaultOptions.updateData;
    } else {
      options = defaultOptions;
    }
  
    // 如果设置的seriesIndex无效,则默认为0
    if (options.seriesIndex < 0 || options.seriesIndex >= seriesLen) {
      seriesIndex = 0;
    } else {
      seriesIndex = options.seriesIndex;
    }
  
    /**
     * 清除定时器
     */
    function clearLoop() {
      if (timeTicket) {
        clearInterval(timeTicket);
        timeTicket = 0;
      }
  
      chart.off('mousemove', stopAutoShow);
      zRender.off('mousemove', zRenderMouseMove);
      zRender.off('globalout', zRenderGlobalOut);
    }
  
    /**
     * 取消高亮
     */
    function cancelHighlight() {
      /**
       * 如果dataIndex为0表示上次系列完成显示,如果是循环系列,且系列索引为0则上次是seriesLen-1,否则为seriesIndex-1;
       * 如果不是循环系列,则就是当前系列;
       * 如果dataIndex>0则就是当前系列。
       */
      let tempSeriesIndex =
        dataIndex === 0
          ? options.loopSeries
            ? seriesIndex === 0
              ? seriesLen - 1
              : seriesIndex - 1
            : seriesIndex
          : seriesIndex;
      let tempType = chartOption.series[tempSeriesIndex].type;
  
      if (tempType === 'pie' || tempType === 'radar' || tempType === 'map') {
        chart.dispatchAction({
          type: 'downplay',
          seriesIndex: lastShowSeriesIndex,
          dataIndex: lastShowDataIndex,
        }); // wait 系列序号为0且循环系列,则要判断上次的系列类型是否是pie、radar
      }
    }
  
    /**
     * 自动轮播tooltip
     */
    function autoShowTip() {
      let invalidSeries = 0;
      let invalidData = 0;
      function showTip() {
        // chart不在页面中时,销毁定时器
        let dom = chart.getDom();
        if (document !== dom && !document.documentElement.contains(dom)) {
          clearLoop();
          return;
        }
  
        // 判断是否更新数据
        if (
          dataIndex === 0 &&
          !first &&
          typeof options.updateData === 'function'
        ) {
          options.updateData();
          chart.setOption(chartOption);
        }
  
        let series = chartOption.series;
        let currSeries = series[seriesIndex];
        if (
          !series ||
          series.length === 0 ||
          !currSeries ||
          !currSeries.type ||
          !currSeries.data ||
          !currSeries.data.length
        ) {
          return;
        }
  
        chartType = currSeries.type; // 系列类型
        dataLen = currSeries.data.length; // 某个系列的数据个数
  
        let tipParams = {
          seriesIndex: seriesIndex,
        };
        switch (chartType) {
          case 'pie':
            // 处理饼图中数据为0或系列名为空的不显示tooltip
            if (
              !currSeries.data[dataIndex].name ||
              currSeries.data[dataIndex].name === '空' ||
              !currSeries.data[dataIndex].value
            ) {
              invalidData += 1;
              dataIndex = (dataIndex + 1) % dataLen;
              if (options.loopSeries && dataIndex === 0) {
                // 数据索引归0表示当前系列数据已经循环完
                // 无效数据个数个总数据个数相等,则该系列无效
                if (invalidData === dataLen) {
                  invalidSeries += 1;
                }
  
                // 新系列,重置无效数据个数
                invalidData = 0;
  
                // 系列循环递增1
                seriesIndex = (seriesIndex + 1) % seriesLen;
                // 系列数循环至起始值时重置无效系列数
                if (seriesIndex === options.seriesIndex) {
                  if (seriesLen !== invalidSeries) {
                    // 下一次系列轮回,重置无效系列数
                    invalidSeries = 0;
                    showTip();
                  } else {
                    // 下一次系列轮回,重置无效系列数
                    invalidSeries = 0;
                    clearLoop();
                  }
                } else {
                  showTip();
                }
              } else if (!options.loopSeries && dataIndex === 0) {
                if (dataLen !== invalidData) {
                  invalidData = 0;
                  showTip();
                } else {
                  invalidData = 0;
                  clearLoop();
                }
              } else {
                showTip();
              }
  
              return;
            }
          // eslint-disable-next-line no-fallthrough
          case 'map':
          case 'chord':
            tipParams.name = currSeries.data[dataIndex].name;
            break;
          case 'radar': // 雷达图
            tipParams.seriesIndex = seriesIndex;
            // tipParams.dataIndex = dataIndex;
            break;
          case 'lines': // 线图地图上的lines忽略
            dataIndex = 0;
            seriesIndex = (seriesIndex + 1) % seriesLen;
            invalidSeries++; // 记录无效系列数,如果无效系列数和系列总数相等则取消循环显示
            if (seriesLen !== invalidSeries) {
              showTip();
            } else {
              clearLoop();
            }
            return;
          default:
            tipParams.dataIndex = dataIndex;
            break;
        }
  
        if (chartType === 'pie' || chartType === 'radar' || chartType === 'map') {
          if (!first) {
            cancelHighlight();
          }
  
          // 高亮当前图形
          chart.dispatchAction({
            type: 'highlight',
            seriesIndex: seriesIndex,
            dataIndex: dataIndex,
          });
        }
  
        // 显示 tooltip
        tipParams.type = 'showTip';
        chart.dispatchAction(tipParams);
  
        lastShowSeriesIndex = seriesIndex;
        lastShowDataIndex = dataIndex;
  
        dataIndex = (dataIndex + 1) % dataLen;
        if (options.loopSeries && dataIndex === 0) {
          // 数据索引归0表示当前系列数据已经循环完
          invalidData = 0;
          seriesIndex = (seriesIndex + 1) % seriesLen;
          if (seriesIndex === options.seriesIndex) {
            invalidSeries = 0;
          }
        }
  
        first = false;
      }
  
      showTip();
      timeTicket = setInterval(showTip, options.interval);
    }
  
    // 关闭轮播
    function stopAutoShow() {
      if (timeTicket) {
        clearInterval(timeTicket);
        timeTicket = 0;
  
        if (chartType === 'pie' || chartType === 'radar' || chartType === 'map') {
          cancelHighlight();
        }
      }
    }
  
    let zRender = chart.getZr();
  
    function zRenderMouseMove(param) {
      if (param.event) {
        // 阻止canvas上的鼠标移动事件冒泡
        // param.event.cancelBubble = true;
      }  
      stopAutoShow();
    }
  
    // 离开echarts图时恢复自动轮播
    function zRenderGlobalOut() {
      // console.log("移出了")
      // console.log(timeTicket)
      if (!timeTicket) {
        autoShowTip();
      }
    }
  
    // 鼠标在echarts图上时停止轮播
    chart.on('mousemove', stopAutoShow);
    zRender.on('mousemove', zRenderMouseMove);
    zRender.on('globalout', zRenderGlobalOut);
  
    autoShowTip();
  
    return {
      clearLoop: clearLoop
    };
  }

getFirst() 方法是 List 接口没有的方法。它可能是某个特定实现类的方法,比如 LinkedList 或者 ArrayList 的方法,但是在标准的 List 接口中并不存在。

如果你希望获取列表中的第一个元素,可以使用 get(0) 方法,它会返回列表中索引为 0 的元素。这是通用的方法,可以用于任何实现了 List 接口的类。

问题出在List接口上,它没有getFirst()方法。如果你想获取列表中的第一个元素,可以使用get(0)方法来实现。所以,你需要将下面这行代码:

代码语言:javascript
复制
AppProductSkuSaveReqVO appProductSkuSaveReqVO = updateReqVO.getSkus().getFirst();

修改为:

代码语言:javascript
复制
AppProductSkuSaveReqVO appProductSkuSaveReqVO = updateReqVO.getSkus().get(0);

这样就可以正确获取列表中的第一个元素了。

代码语言:javascript
复制
StringBuilder url = new StringBuilder()
        .append("https://api.weixin.qq.com/sns/jscode2session")
        .append("?appid=").append(appid)
        .append("&secret=").append(secret)
        .append("&js_code=").append(code)
        .append("&grant_type=authorization_code");
03dffbb3990fb7df18b2826aa2846a68.png
03dffbb3990fb7df18b2826aa2846a68.png
b3148fddd2f3ef6ca0ce10f46040b3e3.png
b3148fddd2f3ef6ca0ce10f46040b3e3.png
38b46e3e95b655dc504a177cf7e72f04.png
38b46e3e95b655dc504a177cf7e72f04.png
代码语言:javascript
复制
var mycharts = echarts.init(this.$refs.echartsMap);
var option ={};
mycharts.setOption(option);
 
var index = 0; //播放所在下标
this.mTime = setInterval(function() {
     mycharts.dispatchAction({
          type: 'showTip',
          seriesIndex: 0,
          dataIndex: index
     });
     index++;
     if(index >= option.series[0].data.length) {
       index = 0;
     }
}, 6000);
代码语言:javascript
复制
let index = 0; //播放所在下标
const mTime = setInterval(function() {
  mycharts.dispatchAction({
    type: 'showTip',
    seriesIndex: 0,
    dataIndex: index
  });
  index++;
  if(index >= option.value.series[0].data.length) {
    index = 0;
  }
}, 6000);
2933e2d8b03c33c3940d90ef0b032321.png
2933e2d8b03c33c3940d90ef0b032321.png
9ddda1a0bfb891c28819b472e91c05bf.png
9ddda1a0bfb891c28819b472e91c05bf.png
7becfde6f7716ad884dcfa18ee2a25e9.png
7becfde6f7716ad884dcfa18ee2a25e9.png
de245bee878d18eec793bc22b2bcd910.png
de245bee878d18eec793bc22b2bcd910.png
6bee59bd9392ffa299568dba0e4fadb4.png
6bee59bd9392ffa299568dba0e4fadb4.png
7039df99e78c7893576b497fc61c7d22.png
7039df99e78c7893576b497fc61c7d22.png
c641707335aaab09994fa8b7785f19b9.png
c641707335aaab09994fa8b7785f19b9.png
697ac2015b1a15fe9573a75b96c41406.png
697ac2015b1a15fe9573a75b96c41406.png
54bcd2a4d76a2c5b3c03d62f4bfede85.png
54bcd2a4d76a2c5b3c03d62f4bfede85.png
9c26dce6f7f18bf5c359e31648b65182.png
9c26dce6f7f18bf5c359e31648b65182.png
76aab4e2cdf4ff61bc6efe34925c0382.png
76aab4e2cdf4ff61bc6efe34925c0382.png
57312471e47d9ccc73f3e716e454d32c.png
57312471e47d9ccc73f3e716e454d32c.png
c796b3379bbecef225426035c900f328.png
c796b3379bbecef225426035c900f328.png
56e6a348c716a2b12b4b4b3cbdaf0d94.png
56e6a348c716a2b12b4b4b3cbdaf0d94.png
6cbc9e801a2cd9e11ad6fefffc90a310.png
6cbc9e801a2cd9e11ad6fefffc90a310.png
a38912b2f7a09e5e136025d283823d20.png
a38912b2f7a09e5e136025d283823d20.png
1928fb72a1315196f8ff1f6fd3c30d0e.png
1928fb72a1315196f8ff1f6fd3c30d0e.png
cb25e183d5654ec629d63567853c764f.png
cb25e183d5654ec629d63567853c764f.png
be4b8a7cdaa0462c8c03c300df79bb85.png
be4b8a7cdaa0462c8c03c300df79bb85.png

发起过一次订阅申请之后用户在设置中不允许通知,则无法弹出申请对话框: 

97f8ec2249a9975ef5a40613aefcc077.jpeg
97f8ec2249a9975ef5a40613aefcc077.jpeg
30b530d5331d089cf12a9e87c00500ad.png
30b530d5331d089cf12a9e87c00500ad.png
96626baa174b3883a5d4719c4bcb6ab7.png
96626baa174b3883a5d4719c4bcb6ab7.png
3c5d2cc9bac5a4718ddd55a0fb02c85d.png
3c5d2cc9bac5a4718ddd55a0fb02c85d.png

后端获取Token:

代码语言:javascript
复制
private TokenObj getAccessToken() {
    Map<String, Object> params = new HashMap<>();
    params.put("grant_type", "client_credential");
    params.put("appid", appletsConfig.getWxAppId());
    params.put("secret", appletsConfig.getWxAppSecret());
    String wxAppletDomain = "https://api.weixin.qq.com/cgi-bin/token";
    String res = HttpClientUtils.get(HttpClientUtils.getDefaultPoolClient(),wxAppletDomain,params);
    TokenObj obj = null;
    if (StringUtils.hasText(res)) {
        obj = JSON.parseObject(res, TokenObj.class);
    }
    if (obj == null || StringUtils.isEmpty(obj.getAccess_token())) {
        throw new BusinessException("获取token失败:" + res);
    }
    return obj;
}
代码语言:javascript
复制
public String sendMsg(HttpServletRequest request){    
    //请求 微信接口 获取 accessToken
    String accessToken = getAccessToken();
    String openid = "接收消息的微信用户的openId";
    String templateId = "微信订阅消息模板";
    String page = "点击消息的跳转路径";
    // 构建订阅消息内容的JSON对象
    // 构建订阅消息内容的JSON对象
    JSONObject messageData = new JSONObject();
    messageData.put("name2", createDataItem("张三"));
    messageData.put("name3", createDataItem("李四"));
    messageData.put("time4", createDataItem("2023-06-30"));
    // 将订阅消息内容转换为JSON字符串
    String jsonData = messageData.toJSONString();
    pushMessage(accessToken,openid,templateId,page,jsonData);
    return "success";
}
private static Map<String, Object> createDataItem(String value) {
    Map<String, Object> item = new HashMap<>();
    item.put("value", value);
    return item;
}

public void pushMessage(String accessToken, String openId, String templateId, String page, Map<String, Map<String,Object>> jsonData) {
    try {
        Map<String, Object> params=new HashMap<>();
        params.put("touser",openId);
        params.put("data", jsonData);
        if(StringUtils.hasText(page)){
            params.put("page",page);
        }
        params.put("miniprogram_state", "trial");
        params.put("template_id",templateId); 
        String pushUrl = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%s";
        String result= HttpClientUtils.post(HttpClientUtils.getDefaultPoolClient(), String.format(pushUrl, accessToken), params);
        logger.info("【微信推送】微信推送返回结果 ,{}",result);        
    } catch (Exception e) {        
        logger.error("【微信推送】微信推送请求失败", e);
    }
}
ee3cc8eb41c50f2e7c592d0ec2b6f4cb.png
ee3cc8eb41c50f2e7c592d0ec2b6f4cb.png
4104704c3d3c512cf587d5ea62bb6146.png
4104704c3d3c512cf587d5ea62bb6146.png
4b0edcda21e65072853d7afa93b84c01.png
4b0edcda21e65072853d7afa93b84c01.png
b2fe36526bd9ea482354bf9850bf22dd.png
b2fe36526bd9ea482354bf9850bf22dd.png
代码语言:javascript
复制
js
@RestController
public class SendWxMessage {
    /*
     * 发送订阅消息
     * */
    @GetMapping("/pushOneUser")
    public String pushOneUser() {
        return push("o3DoL0WEdzxxx96gbjM");
    }
    public String push(String openid) {
        RestTemplate restTemplate = new RestTemplate();
        //这里简单起见我们每次都获取最新的access_token(时间开发中,应该在access_token快过期时再重新获取)
        String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + getAccessToken();
        //拼接推送的模版
        WxMssVo wxMssVo = new WxMssVo();
        wxMssVo.setTouser(openid);//用户的openid(要发送给那个用户,通常这里应该动态传进来的)
        wxMssVo.setTemplate_id("CFeSWarQL-MPyBzTU");//订阅消息模板id
        wxMssVo.setPage("pages/index/index");
        Map<String, TemplateData> m = new HashMap<>(3);
        m.put("thing1", new TemplateData("小程序1"));
        m.put("thing6", new TemplateData("小程序2"));
        m.put("thing7", new TemplateData("小程序3"));
        wxMssVo.setData(m);
        ResponseEntity<String> responseEntity =
                restTemplate.postForEntity(url, wxMssVo, String.class);
        return responseEntity.getBody();
    }
    @GetMapping("/getAccessToken")
    public String getAccessToken() {
        RestTemplate restTemplate = new RestTemplate();
        Map<String, String> params = new HashMap<>();
        params.put("APPID", "wx7c54942sssssd8");  //
        params.put("APPSECRET", "5873a729csxssssd49");  //
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(
                "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={APPID}&secret={APPSECRET}", String.class, params);
        String body = responseEntity.getBody();
        JSONObject object = JSON.parseObject(body);
        String Access_Token = object.getString("access_token");
        String expires_in = object.getString("expires_in");
        System.out.println("有效时长expires_in:" + expires_in);
        return Access_Token;
    }
}

发送订阅消息三步走

  • 1,拿到用户的openid
  • 2,获取access_token
  • 3,调用小程序消息推送的接口
使用Vite

如果你的项目是使用Vite创建的,你的build.sh脚本可以修改为:

代码语言:javascript
复制
bashCopy code
echo "===打包文件==="
# 设置环境模式
export VITE_APP_BASE_URL=your_production_url
代码语言:javascript
复制
echo "===打包文件===" # 设置环境变量 
export NODE_ENV=production 
yarn build echo "===传输文件==="
  1. Kafka:优化用于高吞吐量消息传输,尤其在消息较小时表现出色。
  2. MySQL:作为关系型数据库,适合复杂查询,但高写负载或复杂事务可能影响性能。
  3. MongoDB:作为NoSQL文档数据库,优于处理大量读写操作,但取决于数据模型和索引。
  4. Elasticsearch:搜索和分析工作负载优化,但受限于索引和查询优化。

数据库类型

吞吐量参考值(每秒操作数)

影响因素

Kafka

数十万到上百万消息

消息大小、网络带宽、磁盘I/O、分区策略

MySQL

数百到数千事务

查询优化、索引、数据模型、硬件资源

MongoDB

数百到数千读写操作

文档设计、索引、查询模式、服务器配置

Elasticsearch

数百到数千查询和索引操作

索引结构、查询类型、数据量、硬件资源

Kafka:在良好的硬件和网络条件下,单机Kafka可以处理每秒数十万到上百万消息的吞吐量,尤其在消息大小较小(几百字节到几KB)的情况下。

MySQL:对于MySQL,一个常见的参考吞吐量是每秒几百到几千个事务,但这极大地取决于事务的复杂性和数据库的优化。

数据库和消息队列系统(如Kafka、MySQL、MongoDB、Elasticsearch)的单机吞吐量受多种因素影响,因此很难给出具体的数值。实际的吞吐量取决于硬件配置、网络环境、数据模型、查询类型以及系统的配置和优化。以下是一些影响各系统吞吐量的关键因素以及如何评估各自的性能:

  1. Kafka:
  • Kafka设计用于处理高吞吐量的数据流,其性能受制于网络带宽、磁盘I/O以及分区策略。
  • Kafka吞吐量的评估通常考虑消息大小和生产者/消费者的数量。

MySQL:

  • 作为关系型数据库,MySQL的性能受到查询优化、索引、数据模型和硬件资源(如CPU、内存、磁盘I/O)的影响。
  • 评估MySQL性能时,通常考虑每秒可以处理的事务数(TPS)和查询响应时间。

MongoDB:

  • MongoDB是一个文档型数据库,其性能受到文档设计、索引、查询模式和服务器配置的影响。
  • MongoDB吞吐量的评估可以考虑每秒读写操作的数量。

Elasticsearch:

  • Elasticsearch是一个搜索引擎和分析平台,其性能取决于索引结构、查询类型、数据量和硬件资源。
  • Elasticsearch的性能通常以每秒查询数和索引操作来衡量。

对于其他数据库(如PostgreSQL、Redis、Cassandra等),同样的原则适用。它们的性能取决于特定的使用场景和系统配置。

要准确地了解这些系统的吞吐量,最佳做法是在特定的环境下进行基准测试。基准测试应模拟真实的工作负载和数据模式,以获得有意义的性能指标。此外,性能调优(如查询优化、索引调整、合理的分区和复制策略)也是提高系统吞吐量的重要方面。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前提条件
  • 步骤一:在 Kubernetes 上部署 Jenkins
  • 步骤二:配置 Jenkins 与 GitLab 的集成
  • 步骤三:创建 Jenkins Pipeline
  • 说明
  • 步骤四:触发器和部署
  • GitLab Runner工作目录
  • 使用 npm 安装 CLI 到开发依赖
  • 使用 yarn 安装 CLI 到开发依赖
  • 使用 cnpm 安装 CLI 到开发依赖
    • 后端获取Token:
      • 使用Vite
  • 发送订阅消息三步走
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档