This is an automated email from the ASF dual-hosted git repository.

liujun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-website.git


The following commit(s) were added to refs/heads/master by this push:
     new 32b696dfe02 Feature: add manual benchmark test running (#2971)
32b696dfe02 is described below

commit 32b696dfe02797ca707b319aa9dea835e9417385
Author: wxbty <[email protected]>
AuthorDate: Fri May 10 16:37:27 2024 +0800

    Feature: add manual benchmark test running (#2971)
---
 .github/workflows/build_and_deploy.yml             |  10 +
 .gitignore                                         |   6 +
 benchmark/babel.config.js                          |   5 +
 benchmark/jsconfig.json                            |  19 +
 benchmark/package.json                             |  49 ++
 benchmark/public/index.html                        |  17 +
 benchmark/src/App.vue                              |  42 ++
 benchmark/src/components/TriggerTraceDetail.vue    | 703 +++++++++++++++++++++
 benchmark/src/main.js                              |  16 +
 benchmark/vue.config.js                            |   5 +
 build_bh.sh                                        |  21 +
 .../performance/page-benchmarking.md               |  26 +
 package.json                                       |   6 +-
 static/fonts/element-icons.f1a45d74.ttf            | Bin 0 -> 55956 bytes
 static/fonts/element-icons.ff18efd1.woff           | Bin 0 -> 28200 bytes
 15 files changed, 922 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/build_and_deploy.yml 
b/.github/workflows/build_and_deploy.yml
index 5e68d8ee107..0d98847e93f 100644
--- a/.github/workflows/build_and_deploy.yml
+++ b/.github/workflows/build_and_deploy.yml
@@ -75,3 +75,13 @@ jobs:
           publish_branch: cn-site
           publish_dir: ./public
           keep_files: true
+      - name: Build Benchmark
+        run: sh ./build_bh.sh
+      - name: Deploy Benchmark
+        uses: peaceiris/actions-gh-pages@v3
+        if: github.ref == 'refs/heads/master'
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          publish_branch: cn-site
+          publish_dir: ./static
+          keep_files: true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 0a4ea499b46..765e8cdf6e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,9 @@ package-lock.json
 
 .DS_Store
 /.hugo_build.lock
+benchmark/dist/
+static/css/app.css
+static/css/chunk-vendors.css
+static/js/app.js
+static/js/chunk-vendors.js
+
diff --git a/benchmark/babel.config.js b/benchmark/babel.config.js
new file mode 100644
index 00000000000..e9558405fdc
--- /dev/null
+++ b/benchmark/babel.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}
diff --git a/benchmark/jsconfig.json b/benchmark/jsconfig.json
new file mode 100644
index 00000000000..4aafc5f6ed8
--- /dev/null
+++ b/benchmark/jsconfig.json
@@ -0,0 +1,19 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "module": "esnext",
+    "baseUrl": "./",
+    "moduleResolution": "node",
+    "paths": {
+      "@/*": [
+        "src/*"
+      ]
+    },
+    "lib": [
+      "esnext",
+      "dom",
+      "dom.iterable",
+      "scripthost"
+    ]
+  }
+}
diff --git a/benchmark/package.json b/benchmark/package.json
new file mode 100644
index 00000000000..be3ada62de8
--- /dev/null
+++ b/benchmark/package.json
@@ -0,0 +1,49 @@
+{
+  "name": "dubbo-benchmark",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "@vue/cli": "^5.0.8",
+    "core-js": "^3.8.3",
+    "echarts": "^5.5.0",
+    "element-ui": "^2.13.2",
+    "jquery": "^3.7.1",
+    "vue": "^2.6.14",
+    "vue-router": "^3.6.5",
+    "zrender": "^5.5.0"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.12.16",
+    "@babel/eslint-parser": "^7.12.16",
+    "@vue/cli-plugin-babel": "~5.0.0",
+    "@vue/cli-plugin-eslint": "~5.0.0",
+    "@vue/cli-service": "~5.0.0",
+    "eslint": "^7.32.0",
+    "eslint-plugin-vue": "^8.0.3",
+    "vue-template-compiler": "^2.6.14"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "node": true
+    },
+    "extends": [
+      "plugin:vue/essential",
+      "eslint:recommended"
+    ],
+    "parserOptions": {
+      "parser": "@babel/eslint-parser"
+    },
+    "rules": {}
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ]
+}
diff --git a/benchmark/public/index.html b/benchmark/public/index.html
new file mode 100644
index 00000000000..5b3eb170de0
--- /dev/null
+++ b/benchmark/public/index.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <link rel="icon" href="<%= BASE_URL %>dubbo.png">
+    <title><%= htmlWebpackPlugin.options.title %></title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't 
work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>
diff --git a/benchmark/src/App.vue b/benchmark/src/App.vue
new file mode 100644
index 00000000000..1557d63c2e7
--- /dev/null
+++ b/benchmark/src/App.vue
@@ -0,0 +1,42 @@
+<template>
+  <div id="app">
+    <el-row>
+      <el-col :span="24"><div style="font-size: 30px">Dubbo 基准测试</div></el-col>
+    </el-row>
+    <el-row>
+      <el-col :span="24"><div style="font-size: 15px; text-align: right;">
+        <a target="_blank"
+           
href="https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories";>
+          32并发线程 Linux 4C8T 16GB JDK1.8</a>
+      </div>
+      </el-col>
+    </el-row>
+
+    <el-row style="margin-top: 100px">
+      <el-divider><span style="font-size: 20px;">手动触发对比</span></el-divider>
+      <el-col :span="24"><trigger-trace-detail></trigger-trace-detail></el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import TriggerTraceDetail from "@/components/TriggerTraceDetail.vue";
+
+export default {
+  name: 'App',
+  components: {
+    TriggerTraceDetail
+  }
+}
+</script>
+
+<style>
+#app {
+  font-family: Avenir, Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-align: center;
+  color: #2c3e50;
+  //margin-top: 60px;
+}
+</style>
diff --git a/benchmark/src/components/TriggerTraceDetail.vue 
b/benchmark/src/components/TriggerTraceDetail.vue
new file mode 100644
index 00000000000..37df54acb7f
--- /dev/null
+++ b/benchmark/src/components/TriggerTraceDetail.vue
@@ -0,0 +1,703 @@
+<template>
+  <div>
+    <div class="form-layout">
+      <el-form label-width="100px" class="left-form">
+        <el-form-item label="仓库地址" prop="repo_url">
+          <el-input v-model="REPO_URL" placeholder="仓库地址"></el-input>
+        </el-form-item>
+
+        <el-form-item label="Github Token" prop="PUSH_TOKEN">
+          <el-input v-model="PUSH_TOKEN" placeholder="token"></el-input>
+        </el-form-item>
+
+        <!-- 使用一个新的form-item来包裹后三个需要放在一行的元素,但这样做并不是标准的。通常,form-item直接放在form下面 
-->
+        <el-row :gutter="20" class="form-row">
+          <el-col :span="8">
+            <el-form-item label="左侧配置" prop="leftSelectedOptions" 
class="form-item-in-row">
+              <el-cascader v-model="leftSelectedOptions" 
:options="cascaderOptions" clearable></el-cascader>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="右侧配置" prop="rightSelectedOptions" 
class="form-item-in-row">
+              <el-cascader v-model="rightSelectedOptions" 
:options="cascaderOptions" clearable></el-cascader>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item class="form-item-in-row">
+              <el-button type="primary" @click="open">开始运行</el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div class="right-text">
+        <div
+            style="font-size: 16px; line-height: 1.5; border: 1px solid #ccc; 
padding: 10px; margin-bottom: 20px; text-decoration: none; text-align: left;">
+          <p style="text-align: left;">用户需提供一个自己的GitHub仓库来存储数据,可以新创建一个,
+            也可使用现有的。您只需要参照示例仓库(<a href="https://github.com/dyjjack/jmh_result"; 
target="_blank">jmh_result</a>可直接fork)
+            的workflow的配置即可。此外,为确保有权限推送数据,还需配置用户的GitHub Token。</p>
+        </div>
+      </div>
+    </div>
+
+    <el-row>
+      <el-col :span="6">
+        <div id="TriggerP99" style="width:100%;height:400px;margin-top: 
30px"></div>
+      </el-col>
+      <el-col :span="6">
+        <div id="TriggerQps" style="width:100%;height:400px;margin-top: 
30px"></div>
+      </el-col>
+      <el-col :span="6">
+        <el-header>
+          <h1 style="overflow: hidden;  white-space: nowrap;  text-overflow: 
ellipsis">{{ leftTableTitle }}</h1>
+        </el-header>
+        <el-table
+            :data="leftTableDate"
+            style="width: 100%"
+            row-key="spanId_"
+            border
+            lazy
+            default-expand-all
+            :tree-props="{children: 'children'}"
+        >
+          <el-table-column prop="operationName_" label="方法名" 
min-width="82%"></el-table-column>
+          <el-table-column prop="cost" label="耗时(ms)" 
min-width="18%"></el-table-column>
+        </el-table>
+      </el-col>
+
+      <el-col :span="6">
+        <el-header>
+          <h1>{{ rightTableTitle }}</h1>
+        </el-header>
+        <el-table
+            :data="rightTableDate"
+            style="width: 100%"
+            row-key="spanId_"
+            border
+            lazy
+            default-expand-all
+            :tree-props="{children: 'children'}"
+        >
+          <el-table-column prop="operationName_" label="方法名" 
min-width="82%"></el-table-column>
+          <el-table-column prop="cost" label="耗时(ms)" 
min-width="18%"></el-table-column>
+        </el-table>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'TriggerTraceDetail',
+  data() {
+    return {
+      REPO_URL: null,
+      PUSH_NAME: null,
+      REPO_NAME: null,
+      PUSH_TOKEN: null,
+
+      triggerTable: [],
+
+      leftTableTitle: '',
+      leftTableDate: [],
+      rightTableDate: [],
+      rightTableTitle: '',
+
+      leftSelectedOptions: [],
+      rightSelectedOptions: [],
+
+      resultList: [],
+
+      cascaderOptions: [{
+        value: 'dubbo',
+        label: 'Dubbo协议',
+        children: [{
+          value: 'hessian2',
+          label: 'Hessian2'
+        }, {
+          value: 'fastjson2',
+          label: 'Fastjson2'
+        }, {
+          value: 'fastjson',
+          label: 'Fastjson'
+        }, {
+          value: 'avro',
+          label: 'Avro'
+        }, {
+          value: 'fst',
+          label: 'Fst'
+        }, {
+          value: 'gson',
+          label: 'Gson'
+        }, {
+          value: 'kryo',
+          label: 'Kryo'
+        }, {
+          value: 'msgpack',
+          label: 'Msgpack'
+        }]
+      }, {
+        value: 'rmi',
+        label: 'Rmi协议',
+        children: [{
+          value: 'hessian2',
+          label: 'Hessian2'
+        }, {
+          value: 'fastjson2',
+          label: 'Fastjson2'
+        }, {
+          value: 'fastjson',
+          label: 'Fastjson'
+        }, {
+          value: 'avro',
+          label: 'Avro'
+        }, {
+          value: 'fst',
+          label: 'Fst'
+        }, {
+          value: 'gson',
+          label: 'Gson'
+        }, {
+          value: 'kryo',
+          label: 'Kryo'
+        }, {
+          value: 'msgpack',
+          label: 'Msgpack'
+        }]
+      }, {
+        value: 'tri',
+        label: 'Triple协议',
+        children: [{
+          value: 'hessian2',
+          label: 'Hessian2'
+        }, {
+          value: 'fastjson2',
+          label: 'Fastjson2'
+        }, {
+          value: 'fastjson',
+          label: 'Fastjson'
+        }, {
+          value: 'avro',
+          label: 'Avro'
+        }, {
+          value: 'fst',
+          label: 'Fst'
+        }, {
+          value: 'gson',
+          label: 'Gson'
+        }, {
+          value: 'kryo',
+          label: 'Kryo'
+        }, {
+          value: 'msgpack',
+          label: 'Msgpack'
+        }]
+      }],
+    };
+  },
+
+  mounted() {
+    try {
+      this.init();
+      this.sampleEcharts();
+      this.thrptEcharts();
+    } catch (error) {
+      console.error("init:", error);
+    }
+    try {
+      this.initTable();
+    } catch (error) {
+      console.error("initTable:", error);
+    }
+  },
+
+  methods: {
+    init() {
+      this.REPO_URL = localStorage.getItem('REPO_URL') || ''
+      this.PUSH_NAME = localStorage.getItem('PUSH_NAME') || ''
+      this.REPO_NAME = localStorage.getItem('REPO_NAME') || ''
+      this.PUSH_TOKEN = localStorage.getItem('PUSH_TOKEN') || ''
+
+
+      if (this.PUSH_NAME && this.REPO_NAME) {
+        let jmh;
+
+        const gitUrlPattern = 
/^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git$/; // 正则表达式匹配带.git后缀的GitHub仓库URL
+        const match = this.REPO_URL.match(gitUrlPattern);
+        if (match) {
+          this.PUSH_NAME = match[1]; // 用户名是第一个捕获组
+          this.REPO_NAME = match[2]; // 仓库名是第二个捕获组
+        }
+
+        this.$.ajax({
+          type: "GET",
+          async: false,
+          url: "https://raw.githubusercontent.com/"; + this.PUSH_NAME + "/" + 
this.REPO_NAME + "/main/test-results/scenario/merged_prop_results.json",
+          success: function (res) {
+            jmh = res
+          }
+        });
+
+        try {
+          this.resultList = JSON.parse(jmh);
+        } catch (error) {
+          console.error("解析JMH结果字符串出错:", error);
+          throw error;
+        }
+      }
+    },
+
+    sampleEcharts() {
+      // 基于准备好的dom,初始化echarts实例
+      const myChart = 
this.$echarts.init(document.getElementById('TriggerP99'));
+
+      let time = this.resultList[0].params.time
+// 转换数据结构,按serialization属性分类并收集Item对象
+      let collect = this.resultList
+          .filter((a) => a.mode === 'sample')
+          .map((result) => {
+            // 注意这里只用一个参数接收当前元素
+            let protocol = 
JSON.parse(result.params.prop)['dubbo.protocol.name'];
+            let serialization = 
JSON.parse(result.params.prop)['dubbo.protocol.serialization']
+            return {
+              score: Number((result.primaryMetric.scorePercentiles['99.0'] * 
1000).toFixed(1)),
+              protocol: protocol + "-" + serialization
+            };
+          });
+
+      // let seriesDate = collect.map((result) => {
+      //   // 注意这里只用一个参数接收当前元素
+      //   return {
+      //     type: 'bar'
+      //   };
+      // });
+      //
+      // console.log(collect);
+      // console.log(seriesDate);
+
+      let option = {
+        title: {
+          text: 'P99对比',
+          x: 'center',
+          subtext: this.timestampToTime(time)
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'none'
+          },
+          formatter: function (params) {
+            return params[0].data.score + 'ms';
+          }
+        },
+        toolbox: {
+          feature: {
+            saveAsImage: {}
+          }
+        },
+        grid: {
+          // top: '3%',
+          left: '3%',
+          right: '3%',
+          bottom: '3%',
+          containLabel: true
+        },
+        xAxis: {
+          type: 'category'
+        },
+        yAxis: {
+          type: 'value',
+          name: '耗时(ms)'
+        },
+        dataset: {
+          dimensions: ['protocol', 'score'],
+          source: collect
+        },
+        series: [
+          {
+            barWidth: '25%',
+            type: 'bar',
+            label: {
+              //柱体上显示数值
+              show: true, //开启显示
+              position: 'top', //在上方显示
+              textStyle: {
+                //数值样式
+                fontSize: '15px',
+                color: '#666'
+              },
+            }
+          }
+        ]
+      };
+
+      // 使用刚指定的配置项和数据显示图表。
+      myChart.setOption(option);
+    },
+
+    thrptEcharts() {
+      // 基于准备好的dom,初始化echarts实例
+      const myChart = 
this.$echarts.init(document.getElementById('TriggerQps'));
+
+      let time = this.resultList[0].params.time
+// 转换数据结构,按serialization属性分类并收集Item对象
+      let collect = this.resultList
+          .filter((a) => a.mode === 'thrpt')
+          .map((result) => {
+            // 注意这里只用一个参数接收当前元素
+            let protocol = 
JSON.parse(result.params.prop)['dubbo.protocol.name'];
+            let serialization = 
JSON.parse(result.params.prop)['dubbo.protocol.serialization']
+            return {
+              score: Math.round(result.primaryMetric.scorePercentiles['99.0']),
+              protocol: protocol + "-" + serialization
+            };
+          });
+
+      // let seriesDate = collect.map((result) => {
+      //   // 注意这里只用一个参数接收当前元素
+      //   return {
+      //     type: 'bar'
+      //   };
+      // });
+      //
+      // console.log(collect);
+      // console.log(seriesDate);
+
+      let option = {
+        title: {
+          text: 'QPS对比',
+          x: 'center',
+          subtext: this.timestampToTime(time)
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'none'
+          },
+          formatter: function (params) {
+            return params[0].data.score + 'ops/s';
+          }
+        },
+        toolbox: {
+          feature: {
+            saveAsImage: {}
+          }
+        },
+        grid: {
+          // top: '3%',
+          left: '3%',
+          right: '3%',
+          bottom: '3%',
+          containLabel: true
+        },
+        xAxis: {
+          type: 'category'
+        },
+        yAxis: {
+          type: 'value',
+          name: 'ops/s'
+        },
+        dataset: {
+          dimensions: ['protocol', 'score'],
+          source: collect
+        },
+        series: [
+          {
+            barWidth: '25%',
+            type: 'bar',
+            label: {
+              //柱体上显示数值
+              show: true, //开启显示
+              position: 'top', //在上方显示
+              textStyle: {
+                //数值样式
+                fontSize: '15px',
+                color: '#666'
+              },
+            }
+          }
+        ]
+      };
+
+      // 使用刚指定的配置项和数据显示图表。
+      myChart.setOption(option);
+    },
+
+    timestampToTime(timestamp) {
+      let date = new Date(Number(timestamp));
+      let Y = date.getFullYear() + '-';
+      let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : 
date.getMonth() + 1) + '-';
+      let D = date.getDate() + ' ';
+      let h = date.getHours() + ':';
+      let m = date.getMinutes() + ':';
+      let s = date.getSeconds();
+
+      return Y + M + D + h + m + s;
+    },
+    initTable() {
+
+      if (this.PUSH_NAME && this.REPO_NAME) {
+        let jmh;
+
+        this.$.ajax({
+          type: "GET",
+          async: false,
+          url: "https://raw.githubusercontent.com/"; + this.PUSH_NAME + "/" + 
this.REPO_NAME + "/main/test-results/scenario/merged_prop_traces.json",
+          success: function (res) {
+            jmh = res
+          }
+        });
+
+        try {
+          this.triggerTable = JSON.parse(jmh);
+        } catch (error) {
+          console.error("解析JMH结果字符串出错:", error);
+        }
+
+        this.leftTableDate = this.createSpanTree(this.triggerTable != null && 
this.triggerTable.length > 0 ? this.triggerTable[0].spans_ : [])
+        this.rightTableDate = this.createSpanTree(this.triggerTable != null && 
this.triggerTable.length > 1 ? this.triggerTable[1].spans_ : [])
+
+        this.leftTableTitle = this.triggerTable != null && 
this.triggerTable.length > 0 ? 
JSON.parse(this.triggerTable[0].prop)['dubbo.protocol.name'] + "-" + 
JSON.parse(this.triggerTable[0].prop)['dubbo.protocol.serialization'] : ""
+        this.rightTableTitle = this.triggerTable != null && 
this.triggerTable.length > 1 ? 
JSON.parse(this.triggerTable[1].prop)['dubbo.protocol.name'] + "-" + 
JSON.parse(this.triggerTable[1].prop)['dubbo.protocol.serialization'] : ""
+      }
+
+    }
+    ,
+
+    createSpanTree(spans) {
+      console.log(spans)
+      let spanMap = new Map();
+      let rootSpans = [];
+
+      // 遍历原始spans,初始化每个span,创建映射表和寻找根span
+      for (let span of spans) {
+        spanMap.set(span.spanId_, {
+          ...span,
+          spanId_: span.spanId_.toString(),
+          cost: span.endTime_ - span.startTime_,
+          children: []
+        });
+        if (span.parentSpanId_ === -1) {
+          rootSpans.push(spanMap.get(span.spanId_));
+        }
+      }
+
+      // 根据 parentSpanId_ 属性构建树结构
+      for (let span of spans) {
+        if (span.parentSpanId_ !== -1) {
+          let parentSpan = spanMap.get(span.parentSpanId_);
+          if (parentSpan) {
+            parentSpan.children.push(spanMap.get(span.spanId_));
+          }
+        }
+      }
+
+      console.log(rootSpans)
+      return rootSpans;
+    },
+
+    open() {
+      if ((this.leftSelectedOptions == null || this.leftSelectedOptions.length 
=== 0) && (this.rightSelectedOptions == null || 
this.rightSelectedOptions.length === 0)) {
+        this.$message({
+          type: 'warning',
+          message: '请选择至少一个'
+        });
+        return
+      }
+      if (!this.PUSH_TOKEN) {
+        this.$message({
+          type: 'warning',
+          message: 'token为空'
+        });
+        return
+      }
+      if (!this.REPO_URL) {
+        this.$message({
+          type: 'warning',
+          message: '仓库地址为空'
+        });
+        return
+      }
+
+      let leftRpc = null;
+      let leftSerialization = null;
+
+      console.log(this.leftSelectedOptions)
+      if (this.leftSelectedOptions.length > 0) {
+        leftRpc = this.leftSelectedOptions[0]
+        leftSerialization = this.leftSelectedOptions[1]
+      }
+
+      let rightRpc = null;
+      let rightSerialization = null;
+      if (this.rightSelectedOptions.length > 0) {
+        rightRpc = this.rightSelectedOptions[0]
+        rightSerialization = this.rightSelectedOptions[1]
+      }
+
+      const h = this.$createElement;
+
+      this.$msgbox({
+        title: '消息',
+        message: h('p', null, [
+          h('p', null, "左边内容:rpc协议:" + (leftRpc == null ? "" : leftRpc) + 
"序列化:" + (leftSerialization == null ? "" : leftSerialization)),
+          h('p', null, "右边内容:rpc协议:" + (rightRpc == null ? "" : rightRpc) + 
"序列化:" + (rightSerialization == null ? "" : rightSerialization)),
+        ]),
+        showCancelButton: true,
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        beforeClose: (action, instance, done) => {
+          if (action === 'confirm') {
+            let leftSendDate = ""
+            if (leftRpc) {
+              leftSendDate += "dubbo.protocol.name|" + leftRpc;
+            }
+            if (leftSerialization) {
+              if (leftRpc) {
+                leftSendDate += "|";
+              }
+              leftSendDate += "dubbo.protocol.serialization|" + 
leftSerialization;
+            }
+
+            let rightSendDate = ""
+            if (rightRpc) {
+              rightSendDate += "dubbo.protocol.name|" + rightRpc;
+            }
+            if (rightSerialization) {
+              if (rightRpc) {
+                rightSendDate += "|";
+              }
+              rightSendDate += "dubbo.protocol.serialization|" + 
rightSerialization;
+            }
+
+            let prop = leftSendDate + (leftSendDate ? "@" : "") + 
rightSendDate;
+
+            instance.confirmButtonLoading = true;
+            instance.confirmButtonText = '执行中...';
+
+            const gitUrlPattern = 
/^https:\/\/github\.com\/([^/]+)\/([^/]+)\.git$/; // 正则表达式匹配带.git后缀的GitHub仓库URL
+            const match = this.REPO_URL.match(gitUrlPattern);
+            if (match) {
+              this.PUSH_NAME = match[1]; // 用户名是第一个捕获组
+              this.REPO_NAME = match[2]; // 仓库名是第二个捕获组
+            } else {
+              this.PUSH_NAME = '';
+              this.REPO_NAME = '';
+              this.$message({
+                type: 'error',
+                message: '输入的URL格式不正确,请确保它是带有.git后缀的GitHub仓库URL'
+              });
+            }
+
+            this.$.ajax({
+              url: "https://api.github.com/repos/"; + this.PUSH_NAME + "/" + 
this.REPO_NAME + "/dispatches",
+              type: "POST",
+              beforeSend: (xhr) => {
+                xhr.setRequestHeader("Authorization", "Basic " + 
btoa("username:" + this.PUSH_TOKEN));
+                xhr.setRequestHeader("Content-Type", "application/json");
+                xhr.setRequestHeader("Accept", 
"application/vnd.github.everest-preview+json");
+              },
+              data: JSON.stringify({
+                "event_type": "manual-trigger",
+                "client_payload": {
+                  "prop": prop,
+                  "PUSH_NAME": this.PUSH_NAME,
+                  "REPO_NAME": this.REPO_NAME,
+                  "PUSH_TOKEN": this.PUSH_TOKEN,
+                  "RESULTS_REPO_BRANCH": 'main'
+                }
+              }),
+
+              PUSH_NAME: null,
+              REPO_NAME: null,
+              PUSH_TOKEN: null,
+
+              success: (data) => {
+                instance.confirmButtonLoading = false;
+                console.log("Success:", data);
+                localStorage.setItem('PUSH_NAME', this.PUSH_NAME)
+                localStorage.setItem('REPO_NAME', this.REPO_NAME)
+                localStorage.setItem('PUSH_TOKEN', this.PUSH_TOKEN)
+                localStorage.setItem('REPO_URL', this.REPO_URL)
+                done();
+              },
+              error: (xhr, status, error) => {
+                instance.confirmButtonLoading = false;
+                console.error("Error:", error);
+                this.$message({
+                  type: 'error',
+                  message: '触发失败'
+                });
+              }
+            });
+          } else {
+            done();
+          }
+        }
+      }).then(() => {
+        this.$message({
+          type: 'success',
+          message: '触发成功!结果将在一小时内显示'
+        });
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消'
+        });
+      });
+    }
+  }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+h3 {
+  margin: 40px 0 0;
+}
+
+ul {
+  list-style-type: none;
+  padding: 0;
+}
+
+li {
+  display: inline-block;
+  margin: 0 10px;
+}
+
+a {
+  color: #42b983;
+}
+
+.form-layout {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start; /* 根据需要调整垂直对齐方式 */
+}
+
+.left-form {
+  flex: 1; /* 占据剩余空间的一部分 */
+  max-width: calc(50% - 20px); /* 假设两边间隔为20px,则左侧表单最大宽度为50%减去间隔 */
+  margin-right: 20px; /* 右边距,与.right-text保持间隔 */
+}
+
+.right-text {
+  flex-shrink: 0; /* 防止.right-text被压缩 */
+  width: calc(50% - 20px); /* 右侧文本区域宽度 */
+  /* 其他样式,如字体大小、颜色等 */
+}
+
+.left-form .el-form-item__label {
+  text-align: left; /* 确保标签左对齐 */
+}
+
+.left-form .el-row {
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中 */
+}
+
+.left-form .el-col {
+  display: flex;
+  flex-direction: column; /* 子元素垂直排列 */
+}
+</style>
\ No newline at end of file
diff --git a/benchmark/src/main.js b/benchmark/src/main.js
new file mode 100644
index 00000000000..3e93f1615e7
--- /dev/null
+++ b/benchmark/src/main.js
@@ -0,0 +1,16 @@
+import Vue from 'vue'
+import App from './App.vue'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import * as echarts from 'echarts';
+import $ from 'jquery'
+
+Vue.prototype.$=$
+Vue.prototype.$echarts = echarts
+
+Vue.use(ElementUI);
+Vue.config.productionTip = false
+
+new Vue({
+  render: h => h(App),
+}).$mount('#app')
diff --git a/benchmark/vue.config.js b/benchmark/vue.config.js
new file mode 100644
index 00000000000..54761742d76
--- /dev/null
+++ b/benchmark/vue.config.js
@@ -0,0 +1,5 @@
+const { defineConfig } = require('@vue/cli-service')
+module.exports = defineConfig({
+  publicPath: './',
+  transpileDependencies: true
+})
diff --git a/build_bh.sh b/build_bh.sh
new file mode 100644
index 00000000000..a04bf4ef68a
--- /dev/null
+++ b/build_bh.sh
@@ -0,0 +1,21 @@
+(cd ./benchmark && npm install && npm run build)
+
+for file in benchmark/dist/css/app.*.css; do
+    cp "$file" "static/css/app.css"
+    break
+done
+
+for file in benchmark/dist/css/chunk-vendors.*.css; do
+    cp "$file" "static/css/chunk-vendors.css"
+    break
+done
+
+for file in benchmark/dist/js/app.*.js; do
+    cp "$file" "static/js/app.js"
+    break
+done
+
+for file in benchmark/dist/js/chunk-vendors.*.js; do
+    cp "$file" "static/js/chunk-vendors.js"
+    break
+done
diff --git 
a/content/zh-cn/overview/mannual/java-sdk/reference-manual/performance/page-benchmarking.md
 
b/content/zh-cn/overview/mannual/java-sdk/reference-manual/performance/page-benchmarking.md
new file mode 100644
index 00000000000..97d3c6594f9
--- /dev/null
+++ 
b/content/zh-cn/overview/mannual/java-sdk/reference-manual/performance/page-benchmarking.md
@@ -0,0 +1,26 @@
+---
+description: ""
+linkTitle: RPC 控制台
+title: RPC 基准测试 控制台
+type: html
+weight: 1
+---
+
+<html lang="">
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1">
+    <title>dubbo-benchmark</title>
+    <script defer="defer" src="/js/chunk-vendors.js"></script>
+    <script defer="defer" src="/js/app.js"></script>
+    <link href="/css/chunk-vendors.css" rel="stylesheet">
+    <link href="/css/app.css" rel="stylesheet">
+</head>
+<body>
+<noscript><strong>We're sorry but dubbo-benchmark doesn't work properly 
without JavaScript enabled. Please enable it to
+    continue.</strong></noscript>
+<div id="app"></div>
+</body>
+</html>
+
diff --git a/package.json b/package.json
index 59b56836322..289965c3f8b 100644
--- a/package.json
+++ b/package.json
@@ -13,8 +13,8 @@
   },
   "homepage": "https://dubbo.apache.org";,
   "devDependencies": {
-    "autoprefixer": "^9.8.8",
-    "postcss": "^8.4.21",
-    "postcss-cli": "^7.1.2"
+    "autoprefixer": "^10.4.19",
+    "postcss": "^8.4.38",
+    "postcss-cli": "^11.0.0"
   }
 }
diff --git a/static/fonts/element-icons.f1a45d74.ttf 
b/static/fonts/element-icons.f1a45d74.ttf
new file mode 100644
index 00000000000..91b74de3677
Binary files /dev/null and b/static/fonts/element-icons.f1a45d74.ttf differ
diff --git a/static/fonts/element-icons.ff18efd1.woff 
b/static/fonts/element-icons.ff18efd1.woff
new file mode 100644
index 00000000000..02b9a2539e4
Binary files /dev/null and b/static/fonts/element-icons.ff18efd1.woff differ

Reply via email to