我正在参与「启航方案」

介绍

本来呢最近是在学Rust,趁便看看Tauri相关的内容.然后刷评论区忽然看到有人说到go生态中也有类似的框架—Wails,所以下午花了点时间来着手玩一下.

首要看一下最终的运转作用,前端样式懒得调整所以界面很丑仅仅完结一下功能

wails+vue3实现一个简单Monitor

开端

这次的目标便是做一个功能类似于nvidia-smi的桌面东西,别的整合一下cpu的运用情况.阐明一下所运用到的相关库,关于CPU资源运用gopsutil来得到,GPU相关信息则是运用go-nvml(现在仅仅支持linux,原本想运用wails跨平台编译一个windows exe成果不支持就此作罢);前端界面除了wails自身的vue模板,可视化运用vue3-apexcharts.

大致思路:

  1. 完结后端逻辑,得到相关数据
  2. 完结前端界面
  3. 前后端交互完结动态数据更新

1 后端逻辑

根据相关库的介绍,咱们能够简略完结相应数据的获取,这儿拿GPU中计算运用率为例

首要咱们需求一个全局的nvml device,因为主机只要一张显卡所以没有考虑运用count循环而是直接指定index(0)作为device.

ret = nvml.Init()
if ret != nvml.SUCCESS {
log.Fatalf("init failed %v", nvml.ErrorString(ret))
}
​
device, ret = nvml.DeviceGetHandleByIndex(0)

这儿没有defer nvml.shutdown()因为是一向更新,所以当函数退出可是goroutine依旧在运转,后期就无法得到device犯错.

再来考虑数据方面,因为我想调查usage的变化,因而得规划一个类数组容器来记载每次得到的值,一起为了保证不会无限增加所以得设置最大长度.一开端考虑用数组或者切片,可是这无可避免会导致后期继续更新数据的时分每次都需求copy,无端消耗性能与内存空间.所以想到用循环行列,这样只需求更新队首与队尾,而且占用内存并不会产生增加.

type CircularQueue struct {
    slice []uint32
    front int
    size int
    count int
}
​
func newCircularQueue(maxSize int) *CircularQueue {
    return &CircularQueue{
        slice: make([]uint32, maxSize),
        front: 0,
        size: maxSize,
        count: 0,
    }
}
​
func (cq *CircularQueue) enqueue(element uint32) {
    if cq.count == cq.size {
        cq.front = (cq.front + 1) % cq.size
        cq.count--
    }
    rear := (cq.front + cq.count) % cq.size
    cq.slice[rear] = element
    cq.count++
}
​
func (cq *CircularQueue) getSlice() []uint32 {
    if cq.count == 0 {
        return nil
    }
​
    slice := make([]uint32, cq.count)
    for i := 0; i < cq.count; i++ {
        slice[i] = cq.slice[(cq.front+i)%cq.size]
    }
    return slice
}

经过循环行列,咱们每次请求元素并将元素入队,最终回来只需求运用getSlice()方法回来行列中的数据即可.具体得到GPU Usage数据的代码如下

const MAXSIZE = 10var GpuUsagecq = newCircularQueue(MAXSIZE)
​
func (a *App) GetGpuUsage() []uint32 {
    rwmutex.RLock()
    utilization, ret := device.GetUtilizationRates()
    rwmutex.RUnlock()
    if ret != nvml.SUCCESS {
        log.Fatalf("Unable to get utilization of device at index %d: %v", 0, nvml.ErrorString(ret))
    }
    GpuUsagecq.enqueue(utilization.Gpu)
    return GpuUsagecq.getSlice()
}

这儿为了保证并发安全还是用了一下读锁,不过感觉读操作的话用不用应该问题不大.这儿运用device.XXX()就能够得到GPU当前的各种信息,具体能够去看api文档.依照相似的逻辑,就能够得到咱们所需求的一切信息数据.

2 前端界面

这儿咱们运用vue3-apexcharts,不过因为我的前端技能很菜,对于vue3也仅仅大致了解过,因而这部分我也不太好具体简介,更多是对着官网中的demo修正.在frontend/src/components下创立一个组件Monitor,然后写一下template

<template>
 <div>
  <div class="chart-row">
   <apexchart
     v-for="(chartOptions, index) in areaChartOptions"
     :key="index"
     type="area"
     :options="chartOptions"
     :series="areaChartSeries[getChartId(index)]"
     class="chart-column"
   />
  </div>
​
  <div class="chart-row">
   <apexchart type="donut" :options="DonutOptions" :series="donutSeries" />
   <apexchart type="radialBar" :options="radialOptions" :series="radialSeries" />
  </div>
​
 </div>
</template>

在下面data()中设置好options和series初始值,运转wails dev就能看到一个静态的页面.

3 前后端交互

这部分我感觉wails文档中写的并不好,没有任何很具体的例子指出运转时的前后端数据怎么交互.参阅官网介绍中唯一能够参阅的Events事情配合怎么工作,自己渐渐体会写出来.

首要来看后端部分,第一部分中咱们完结了一切数据的获取,这儿咱们只需求让数据获取继续运转(改写距离自定义)而且将数据传给前端.

go func() {
  for {
    runtime.EventsEmit(a.ctx, "GetCpuUsage", a.GetCpuUsage())
    runtime.EventsEmit(a.ctx, "GetGpuMem", a.GetGpuMem())
    runtime.EventsEmit(a.ctx, "GetGpuUsage", a.GetGpuUsage())
    time.Sleep(100 * time.Millisecond)
   }
}()
​
go func() {
  for {
    runtime.EventsEmit(a.ctx, "GetFans", a.GetFans())
    runtime.EventsEmit(a.ctx, "GetTemperature", a.GetTemperature())
    time.Sleep(3 * time.Second)
   }
}()

我期望关于usage以及memory的信息改写更及时,而温度和风扇转速这些貌似不太重要的信息能够慢一点.然后经过EventsEmit将数据与事情传递给前端,而前端设置一下EventsOn事情监听,完结数据接收并完结前端界面数据更新.

EventsOn("GetGpuUsage",GetGpuUsage=>{
    if(GetGpuUsage){
     this.areaChartSeries['area-chart-1'][0].data=GetGpuUsage
    }
})

这样就最终完结了咱们一开端的界面内容.

最后

这一次尝试算是Tauri之前的一次小玩具,花了两个个小时从0学习wails以及一些前端库,最终也算拼凑出了最初规划的功能.wails最终打包出来的可执行文件巨细仅仅只要2.8M,比起一些Electron打包出来的东西来说小了不止一点.

wails+vue3实现一个简单Monitor

别的咱们能够在main.go中添加webview运用硬件加速,这样就能够让GPU usage不再一向是0%

Linux: &linux.Options{
            WebviewGpuPolicy: linux.WebviewGpuPolicyAlways,
},

最后放一张与watch -n0.1 nvidia-smi比照的图片作为结束

wails+vue3实现一个简单Monitor