SkyWalking拓樸圖group功能改造

  用過SkyWalking的拓樸圖功能都知道,裏面有個組功能,見左下角Create Group按鈕,我稱之爲域,一個組就是一個子域,這個功能還是很重要的,因爲如果一個月內活躍的服務器很多,整體的拓樸圖就會密密麻麻,點擊左上角All services的下拉框的值又只能看到這個服務器相關的鏈路信息,這樣拓樸圖又不夠直觀,所以需要group來切分爲一個個相關的域(子集合)。但是SkyWalking原有的分組功能又很操蛋,分好的組數據依賴於localStorage和Vuex,這臺機子的分組數據就只存在於本地,別人的機子看不到,所以架構要求我在現有的接口上模仿做一個類似的功能。

  先在guyhub找到SkyWalking前端項目代碼:https://github.com/apache/skywalking-rocketbot-ui。看了看這部分的源碼,有了一個想法,既然沒有後臺配合,那首先,利用外部化配置文件配置域之間的包含關係,頁面初始化時讀取這個json文件,然後模仿Create group按鈕的執行邏輯直接一步步走下去,相當於初始化給你建好組。按着這個思路寫好了,這個初始化的組可以被編輯,而且由於沒寫好控制邏輯,刷新的時候仍然會被新增已有的組,寫了個按鈕主動觸發初始化新增的動作,感覺也不是很完美,於是,我就有有了一個思路,模仿All services的功能來做一個All domains,見圖中左上角All domains下拉框。

  以SkyWalking7.0.0分支的代碼爲例:

首先public下新建config.json,請嚴格遵守json格式

{
   "domains" :[{
       "name":"第一個域",
       "children":["service1","service2","service3"]
   },{
       "name":"第二個域",
       "children":["service4","service5"]
   }]
}

頁面上給新的All domains下拉框騰個位置,src/views/components/topology/topo-aside.vue

<template>
    <svg class="link-topo-aside-btn icon cp lg" @click="showRadial()" :style="`position:absolute;left:290px;`">
      <use xlink:href="#issues" />
    </svg>
    <svg
      v-if="showServerInfo"
      class="link-topo-aside-btn icon cp lg"
      @click="show = !show"
      :style="`position:absolute;left:290px;transform: rotate(${show ? 0 : 180}deg);top:45px;`"
    >
      <use xlink:href="#chevron-left" />
    </svg>
</template>

<style>
.link-topo-aside-box {
    border-radius: 4px;
    position: absolute;
    width: 280px;
    z-index: 101;
    color: #ddd;
    background-color: #2b3037;
    padding: 15px 20px 10px;
}
</style>

將上述代碼template部分中的left:290px修改爲410px,style部分中的width: 280px修改爲400px,此處自行調整,開心就好。

接下來修改src/views/components/topology/topo-services.vue,其中的請求本地文件用的是原生xhr對象,如果樂意也可以npm安裝axios,此處不展開。

<template>
  <div class="link-topo-aside-box" style="padding:0;">
    <TopoSelect :current="service" :data="services" @onChoose="handleChange" style="float:left;width:50%"/>
    <TopoSelect :current="domain" :data="domains" @onChoose="domainHandleChange" style="float:left;width:50%"/>
  </div>
</template>
<script lang="ts">
  import { DurationTime } from '@/types/global';
  import compareObj from '@/utils/comparison';
  import Axios, { AxiosResponse } from 'axios';
  import { Component, Vue, Watch } from 'vue-property-decorator';
  import { Action, Getter, Mutation } from 'vuex-class';
  import TopoSelect from './topo-select.vue';

  @Component({ components: { TopoSelect } })
  export default class TopoServices extends Vue {
    @Getter('durationTime') public durationTime: any;
    @Action('rocketTopo/GET_TOPO') public GET_TOPO: any;
    @Mutation('rocketTopoGroup/UNSELECT_GROUP') private UNSELECT_GROUP: any;
    private services = [{ key: 0, label: 'All services' }];
    private service = { key: 0, label: 'All services' };
    private domains = [{ key: 0, label: 'All domains' }];
    private domain = { key: 0, label: 'All domains' };
    private servicesMap=[];
    private domainsInJSON=[];

    private fetchData() {
      Axios.post('/graphql', {
        query: `
      query queryServices($duration: Duration!) {
        services: getAllServices(duration: $duration) {
          key: id
          label: name
        }
      }`,
        variables: {
          duration: this.durationTime,
        },
      }).then((res: AxiosResponse) => {
        this.services = res.data.data.services
          ? [{ key: 0, label: 'All services' }, ...res.data.data.services]
          : [{ key: 0, label: 'All services' }];
          this.servicesMap=res.data.data.services ? res.data.data.services:[];
      });
    }

    @Watch('durationTime')
    private watchDurationTime(newValue: DurationTime, oldValue: DurationTime) {
      // Avoid repeating fetchData() after enter the component for the first time.
      if (compareObj(newValue, oldValue)) {
        this.fetchData();
      }
    }

    private handleChange(i: any) {
      this.service = i;
      this.domain = { key: 0, label: 'All domains' };
      this.UNSELECT_GROUP();
      this.GET_TOPO({
        serviceId: this.service.key,
        duration: this.durationTime,
      });
    }

    private domainHandleChange(i: any) {
      this.domains = i;
      this.service = { key: 0, label: 'All services' };
      this.UNSELECT_GROUP();
      if(i.key==0){
        this.GET_TOPO({
          serviceId: this.service.key,
          duration: this.durationTime,
        });
      }else{
        let params:string[]=[];
        let domains:any = this.domainsInJSON[i.key-1];
        this.servicesMap.forEach((item:any)=>{
          domains.children.forEach((subItem:any)=>{
            if(item.label == subItem){
              params.push(item.key);
            }
          })
        })
        this.GET_TOPO({
          serviceId: this.service.key,
          duration: this.durationTime,
        });
      }
    }

    private created() {
      this.fetchData();
      var that=this;
      var request=new XMLHttpRequest();
      request.open("get","./config.json");
      request.send(null);
      request.onload = function(){
        if(request.status==200){
          console.log("讀取外部化配置文件成功>>>>>>>>>>");
          const json = JSON.parse(request.responseText);
          that.domainsInJSON = json.domains;
          const domains = json.domains;
          for(let i=1 ; i< (domains.length+1) ;i++){
            that.domains.push({ key:i , label:domains[i-1].name })
          }
        }else{
          console.log("讀取外部化配置文件失敗,請檢查JSON文件是否符合格式>>>>>>>>>>");
        }
      }
    }
  }
</script>

  最後修改vue.config.js文件,需要注意的是原本SkyWalking前端的包是會被打到SkyWalking後端包中,如果還是想這樣子的話可以在skywalking官網下載的tar包解壓後的根目錄webapp下的skywalking-webapp.jar解壓,然後路徑爲BOOT-INF/classes/public,然後把我們的剛打出來的dist包下的文件覆蓋進去替換全部,然後再把skywalking-webapp壓縮還原爲jar包即可,那下面的步驟就可以不用看了。

  如果是把前端項目單獨分離出來維護,就需要修改proxy字段中轉發的地址修改爲自己SkyWalking後端地址,端口爲默認的12800,否則本地聯調不通。

let baseUrl='/skywalking-app' // 頁面地址,根據自己需要進行修
module.exports = {
  publicPath : baseUrl,
  lintOnsave : true,
  outputDir : dist, //包名,根據自己需要進行修改
  devServer: {
    proxy: {
      '/graphql': {
        target: `${process.env.SW_PROXY_TARGET || 'http://XXX.XX.XX.XX:12800'}`,
        changeOrigin: true,
      },
    },
  },
};

然後在nginx配置文件nginx.conf文件中進行配置,需要注意的是Skywalking的路由採用history模式,頁面刷新會404,try_files語句就是爲了解決這個問題。

        location /skywalking-app {
            alias html/dist;
            index  index.html index.htm;
            try_files $uri $uri/ /skywalking-app/index.html
            add_header 'Access-Control-Allow-Origin' '*'; #允許來自所有的訪問地址
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, DELETE, OPTIONS'; #支持請求方式
            add_header 'Access-Control-Allow-Headers' 'Content-Type,*';
        }

        #skywalking-app的反向代理
        location /graphql{
           proxy_set_header   Host             $host;
           proxy_set_header   x-forwarded-for  $remote_addr;
           proxy_set_header   X-Real-IP        $remote_addr;
           proxy_pass  http://XXXX.XX.XX.XX:12800;
        }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章