tauri学习(7)-事件(event)

2022-09-26 15:33:56 浏览数 (1)

接上节继续,今天来研究tauri的事件(event),假设老板提了个需求,希望能实时监控cpu、内存等性能指标,你会怎么做?

思路1:

后端Rust暴露1个command,前端js不停去轮询(参考前文:tauri学习(3)-前端调用Rust代码),即传统的pull模型。

思路2:

后端不停对外喷数据,谁需要谁拿(类似发布-订阅模型)。

理论上二种思路都可以,今天讨论的是第2种,通过tauri的事件触发与监听来实现,而且event机制不仅仅限制于前端与后端通讯,还可以在前端-前端,后端与后端(多窗口应用,窗口之间)交换数据)。

一、准备工作:

1.1、如何在Rust中获取到CPU等实时监控指标?

代码语言:javascript复制
perf_monitor = "0.2.0"

这里借助了perf_monitor这个开源第3方库,基本用法如下:

代码语言:javascript复制
use perf_monitor::cpu::{processor_numbers, ProcessStat, ThreadStat};
use perf_monitor::fd::fd_count_cur;
use perf_monitor::io::get_process_io_stats;
use perf_monitor::mem::get_process_memory_info;

/**
 * 获取【cpu/内存/文件描述符数量/io】监控值
 */
fn monitor() -> Vec<String> {
  // cpu
  let core_num = processor_numbers().unwrap();
  let mut stat_p = ProcessStat::cur().unwrap();
  let mut stat_t = ThreadStat::cur().unwrap();

  let usage_p = stat_p.cpu().unwrap() * 100f64;
  let usage_t = stat_t.cpu().unwrap() * 100f64;

  let mut monitor_message: Vec<String> = Vec::with_capacity(3);

  monitor_message.push(format!(
    "[CPU] core Number: {}, process usage: {:.2}%, current thread usage: {:.2}%",
    core_num, usage_p, usage_t
  ));

  // mem
  let mem_info = get_process_memory_info().unwrap();

  monitor_message.push(format!(
    "[Memory] memory used: {} bytes, virtural memory used: {} bytes ",
    mem_info.resident_set_size, mem_info.virtual_memory_size
  ));

  // fd
  let fd_num = fd_count_cur().unwrap();
  monitor_message.push(format!("[FD] fd number: {}", fd_num));
  // println!("[FD] fd number: {}", fd_num);

  // io
  let io_stat = get_process_io_stats().unwrap();
  monitor_message.push(format!(
    "[IO] io-in: {} bytes, io-out: {} bytes",
    io_stat.read_bytes, io_stat.write_bytes
  ));

  monitor_message
}

1.2 设计事件的消息体

另:为了获取系统时间戳,从网上找了段代码

二、后端发送事件

2.1  发送事件代码

触发事件的核心就是emit方法(上图95行),事件名称可以随便取,但是要与前端监听指定的事件名保持一致。

说明一下:这里后端暴露了1个command,允许用户在前端通过按钮之类的,来触发后端吐数据(当然,大家也可以改成应用一启动,就直接开始监控cpu,无需前端触发)

另外,还演示了rust中的线程使用,创建1个独立线程来不停监控系统指标,然后1秒1次不停向外触发事件,这就带来另1个小问题,如果前端不停调用这个command,后端每次都会创建1个线程,容易引导其它问题,所以这里借助了1个全局变量来做辅助控制(当然,仅仅出于演示目的,应该有更优雅的做法)

2.2 暴露command

三、前端监听事件

代码语言:javascript复制
import React from 'react';
import { invoke } from "@tauri-apps/api/tauri";
//监听事件
import { listen } from "@tauri-apps/api/event";
//用于格式化date
import format from 'date-fns/format';

import './index.css';

//用于取消监听
let unlisten: any = null

//事件的消息体
interface Payload {
    message: Array<string>,
    timestamp: number,
}

class Home extends React.Component {

    //初始状态
    state = {
        message: [],
        timestamp: "",
        time: ""
    }

    //开始监听
    start = () => {
        invoke('init_process');
        //防止重复监听
        if (unlisten != null) {
            console.log("already listen");
            return;
        }

        const start_listen = async () => {
            //注意这里的my-event名称,要与后端保持一致
            return await listen<Payload>('my-event', (event) => {
                const { message, timestamp } = event.payload;
                console.log("message:", message,
                    "timestamp:", timestamp, "time:",
                    format(new Date(timestamp), 'yyyy-MM-dd HH:mm:ss.SSS'));
                this.setState({ message, timestamp, "time": format(new Date(timestamp), 'yyyy-MM-dd HH:mm:ss.SSS') })

            });
        };
        unlisten = start_listen();
    }

    //停止监听
    stop = () => {
        console.log("is_listening:", unlisten != null);
        if (unlisten != null) {
            unlisten.then((ok: any) => {
                ok();
                unlisten = null;
                console.log("stop success");
            }).catch((err: any) => {
                console.log("stop fail", err);
            })
        }
    }

    render() {
        return (
            <div>
                <button onClick={() => this.start()}>start</button> 
                <button onClick={() => this.stop()}>stop</button><br />
                <h4>{this.state.time}</h4>
                <div >
                    {
                        this.state.message.map((item, index) => {
                            return (<span className="monitor" key={`${this.state.timestamp}_${index}`}> {item}</span>)
                        })
                    }
                </div>
            </div >
        )
    }
}

export default Home

核心部分都加了注释,应该不难看懂,运行效果如下:

代码示例:

https://github.com/yjmyzz/tauri-visited-solution/tree/event

参考文章:

https://tauri.app/v1/guides/features/events

0 人点赞