当前位置: 首页 > news >正文

【一起学Rust | Tauri2.0框架】基于 Rust 与 Tauri 2.0 框架实现全局状态管理

前言

在现代应用程序开发中,状态管理是构建复杂且可维护应用的关键。随着应用程序规模的增长,组件之间共享和同步状态变得越来越具有挑战性。如果处理不当,状态管理可能会导致代码混乱、难以调试,并最终影响应用程序的性能和可扩展性。

Tauri 2.0 作为一个基于 Rust 的跨平台应用程序开发框架,为我们提供了一个强大的工具集来构建高性能、安全且易于维护的桌面应用程序。结合 Rust 语言的优势,我们可以实现高效且可靠的全局状态管理。

本文将深入探讨如何在 Tauri 2.0 应用程序中实现全局状态管理。我们将从基本概念开始,逐步介绍不同的状态管理方法,并通过实际代码示例演示如何在 Tauri 2.0 项目中应用这些方法。无论你是 Rust 和 Tauri 的新手,还是有经验的开发者,相信都能从本文中获得有价值的知识和实践经验。

文章目录

  • 前言
  • 一、全局状态管理概述
    • 1.1 全局状态管理的挑战
    • 1.2 全局状态管理的重要性
  • 二、Rust 与 Tauri 2.0 中的状态管理
    • 2.1 Rust 的所有权和借用机制
        • 2.2 Tauri 2.0 的架构
  • 三、Tauri 2.0 内置状态管理
    • 3.1 使用 `tauri::State`
    • 3.2 状态更新与事件
    • 3.3 使用异步互斥锁 (async mutex)
    • 3.4 使用`Arc`
    • 3.5 使用 Manager Trait来访问状态
    • 3.6 修复`Mismatching Types`
  • 四、使用第三方状态管理库
    • 4.1 `Redux` 启发的状态管理:`Yewdux`
    • 4.2 其他状态管理库
  • 五、状态管理的最佳实践
  • 六、实战案例:构建一个简单的计数器应用
    • 6.1 项目设置
    • 6.2 后端代码
    • 6.3 前端代码
    • 6.4 运行应用
  • 总结

一、全局状态管理概述

全局状态管理是指在应用程序的多个组件之间共享和同步数据的一种机制。这些数据可以是用户界面状态、应用程序配置、用户数据等。全局状态管理的目标是确保应用程序中的所有组件都能访问和更新相同的状态,从而保持数据的一致性和应用程序的整体协调性。

1.1 全局状态管理的挑战

在大型应用程序中,全局状态管理面临着以下挑战:

  1. 数据一致性: 确保所有组件都能访问和更新相同的状态,避免数据不一致导致的问题。
  2. 组件通信: 在不同的组件之间传递状态更新,确保所有相关组件都能及时响应状态变化。
  3. 性能优化: 避免不必要的状态更新和渲染,提高应用程序的性能。
  4. 代码可维护性: 保持状态管理代码的清晰和简洁,便于理解和维护。
  5. 可扩展性: 随着应用程序的增长,状态管理方案能够适应新的需求和变化。

1.2 全局状态管理的重要性

良好的全局状态管理可以带来以下好处:

  1. 简化组件开发: 组件无需关心状态的来源和更新,只需专注于自身的渲染和逻辑。
  2. 提高代码可维护性: 将状态管理逻辑集中处理,减少代码重复和冗余。
  3. 增强应用程序可预测性: 状态变化可追踪、可预测,便于调试和问题排查。
  4. 提升用户体验: 确保应用程序在不同组件之间保持一致的状态,提供流畅的用户体验。

二、Rust 与 Tauri 2.0 中的状态管理

Rust 语言的特性和 Tauri 2.0 框架的架构为我们提供了多种实现全局状态管理的方式。

2.1 Rust 的所有权和借用机制

Rust 的所有权和借用机制是其内存安全和并发安全的基础。在状态管理中,我们可以利用这些机制来确保状态数据在不同组件之间的安全共享和访问。

  • 所有权(Ownership): Rust 中的每个值都有一个被称为其所有者的变量。在任何给定时间,一个值只能有一个所有者。当所有者超出作用域时,该值将被丢弃。
  • 借用(Borrowing): 我们可以通过引用(&)来借用一个值,而无需获取其所有权。引用可以是可变的(&mut)或不可变的(&)。
  • 生命周期(Lifetime): 生命周期是 Rust 编译器用来确保引用始终有效的机制。
2.2 Tauri 2.0 的架构

Tauri 2.0 采用了一种基于 Web 技术(HTML、CSS、JavaScript)构建前端界面,并使用 Rust 编写后端逻辑的架构。这种架构使得我们可以利用 Web 生态系统中丰富的状态管理库,同时也能利用 Rust 的性能和安全性优势。

Tauri 2.0 提供了以下机制来实现前端和后端之间的通信和状态共享:

  1. 命令(Commands): 前端可以通过调用 Tauri 提供的命令来与后端进行交互。命令可以接收参数并返回结果。
  2. 事件(Events): 后端可以向前端发送事件,前端可以监听这些事件并做出响应。
  3. 状态(State): Tauri 2.0 提供了一个内置的状态管理机制,允许我们在后端管理全局状态,并在前端访问和更新这些状态。

三、Tauri 2.0 内置状态管理

Tauri 2.0 提供了一个简单而强大的内置状态管理机制,可以满足大多数应用程序的需求。

3.1 使用 tauri::State

tauri::State 是 Tauri 2.0 中用于管理全局状态的核心类型。它是一个泛型类型,可以存储任何实现了 SendSync trait 的类型。

  1. 定义状态类型:

    #[derive(Default)]
    struct AppState {counter: std::sync::Mutex<i32>,
    }
    

    这里我们定义了一个名为 AppState 的结构体,其中包含一个名为 counter 的字段。counter 的类型是 std::sync::Mutex<i32>,表示一个受互斥锁保护的 32 位整数。使用互斥锁可以确保多个线程安全地访问和修改 counter 的值。

  2. 初始化状态:

    fn main() {tauri::Builder::default().manage(AppState::default()).invoke_handler(tauri::generate_handler![increment_counter, get_counter]).run(tauri::generate_context!()).expect("error while running tauri application");
    }
    

    main 函数中,我们使用 tauri::Builder::manage 方法将 AppState 的一个实例注册为全局状态。

  3. 在命令中访问状态:

    #[tauri::command]fn increment_counter(state: tauri::State<AppState>) -> Result<(), String> {let mut counter = state.counter.lock().map_err(|e| e.to_string())?;*counter += 1;Ok(())}#[tauri::command]fn get_counter(state: tauri::State<AppState>) -> Result<i32, String> {let counter = state.counter.lock().map_err(|e| e.to_string())?;Ok(*counter)}
  1. 在异步命令中访问状态:
    #[tauri::command]async fn increment_counter(state: tauri::State<AppState>) -> Result<(), String> {let mut counter = state.counter.await;*counter += 1;Ok(())}#[tauri::command]async fn get_counter(state: tauri::State<AppState>) -> Result<i32, String> {let counter = state.counter.await;Ok(*counter)}

我们定义了两个命令:increment_counterget_counter。这两个命令都接收一个 tauri::State<AppState> 类型的参数,表示对全局状态的引用。

  • increment_counter 命令获取 counter 的互斥锁,将其值加 1,然后释放锁。
  • get_counter 命令获取 counter 的互斥锁,读取其值,然后释放锁并返回该值。
  1. 在前端访问状态:
   import { invoke } from '@tauri-apps/api/tauri';async function incrementCounter() {await invoke('increment_counter');updateCounter();}async function updateCounter() {const counter = await invoke('get_counter');document.getElementById('counter').textContent = counter;}// 在页面加载时更新计数器updateCounter();

在前端,我们使用 @tauri-apps/api/tauri 提供的 invoke 函数来调用后端命令。

  • incrementCounter 函数调用 increment_counter 命令,然后在状态更新后调用 updateCounter 函数。
  • updateCounter 函数调用 get_counter 命令获取计数器的当前值,并将其显示在页面上。

3.2 状态更新与事件

在上面的示例中,我们通过调用 get_counter 命令来获取状态的更新。这种方式在状态更新不频繁的情况下是可行的。但是,如果状态更新非常频繁,或者我们需要在状态更新时立即通知前端,那么使用事件机制会更有效。

  1. 在后端发送事件:

    #[tauri::command]
    fn increment_counter(state: tauri::State<AppState>, window: tauri::Window) -> Result<(), String> {let mut counter = state.counter.lock().map_err(|e| e.to_string())?;*counter += 1;window.emit("counter-updated", *counter).map_err(|e| e.to_string())?;Ok(())
    }
    

    increment_counter 命令中,我们在更新 counter 的值后,使用 window.emit 方法向前端发送一个名为 "counter-updated" 的事件,并将 counter 的当前值作为事件的负载。

  2. 在前端监听事件:

    import { invoke } from '@tauri-apps/api/tauri';
    import { listen } from '@tauri-apps/api/event';async function incrementCounter() {await invoke('increment_counter');
    }// 监听 counter-updated 事件
    listen('counter-updated', (event) => {document.getElementById('counter').textContent = event.payload;
    });// 在页面加载时更新计数器
    updateCounter();
    

    在前端,我们使用 @tauri-apps/api/event 提供的 listen 函数来监听 "counter-updated" 事件。当事件发生时,事件处理函数会被调用,并将事件的负载(即 counter 的当前值)显示在页面上。

3.3 使用异步互斥锁 (async mutex)

该功能必须tokio启用sync特征。

引用Tokio的文档,使用标准库的互斥体而不是Tokio提供的异步互斥体通常是可以的:

与普遍看法相反,在异步代码中使用标准库中的普通互斥体是可以的,而且通常是首选的……异步互斥体的主要用例是提供对IO资源(如数据库连接)的共享可变访问。

你需要充分阅读Tokio的文档以了解两者之间的权衡。需要使用异步互斥的一个原因是,如果您需要在await之间保持MutexGuard。

这种类型的作用类似于 std::sync::Mutex,有两个主要区别:lock 是一个异步方法,因此不会阻塞,并且锁保护被设计为跨 .await 点持有。

Tokio的Mutex在保证FIFO的基础上运行。 这意味着任务调用锁方法的顺序就是它们获取锁的顺序。

也就是说,你通常使用标准库的mutex基本上就能实现你的需求,因为async mutex实现起来成本高,因此Tokio官方直接就推荐使用标准库的mutex了。tokio文档中推荐使用:

  1. Arc<Mutex<...>>定义状态
  2. 生成一个task线程来与主线程通信

并且给出了样例代码

use tokio::sync::Mutex;
use std::sync::Arc;#[tokio::main]
async fn main() {let count = Arc::new(Mutex::new(0));for i in 0..5 {let my_count = Arc::clone(&count);tokio::spawn(async move {for j in 0..10 {let mut lock = my_count.lock().await;*lock += 1;println!("{} {} {}", i, j, lock);}});}loop {if *count.lock().await >= 50 {break;}}println!("Count hit 50.");
}

在这个例子中,有几件事需要注意。

  1. 互斥体被包裹在Arc中,以允许在线程之间共享。
  2. 每个生成的任务都会获得一个锁,并在每次迭代时释放它
  3. Mutex保护的数据的突变是通过取消引用所获得的锁来完成的。

Tokio的Mutex采用简单的FIFO(先进先出)风格,所有锁定调用都按照执行顺序完成。这样,互斥体在如何将锁分配给内部数据方面是“公平的”和可预测的。每次迭代后都会释放并重新获取锁,因此基本上,每个线程在递增一次值后都会转到行的后面。请注意,线程启动之间的时间存在一些不可预测性,但一旦它们启动,它们就会可预测地交替。最后,由于在任何给定时间只有一个有效的锁,因此在改变内部值时不可能出现竞争条件。

请注意,与std::sync::Mutex相反,当持有MutexGuard的线程崩溃时,此实现不会破坏互斥量。 在这种情况下,互斥锁将被解锁。 如果panic被捕获,这可能会使受互斥锁保护的数据处于不一致的状态。

3.4 使用Arc

在 Rust 中,常见用法是使用 Arc 在多个线程之间共享一个值的所有权(通常与 Mutex 配对使用,形式为 Arc<Mutex<T>>)。但是,你不需要对存储在 State 中的内容使用 Arc,因为 Tauri 会为你完成这项工作。

如果 State 的生命周期要求阻止你将状态移动到新线程中,你可以改为将 AppHandle 移动到线程中,然后检索你的状态,如下面“使用 Manager trait 访问状态”部分所示。AppHandle 特意设计成易于克隆,以用于此类用例。

3.5 使用 Manager Trait来访问状态

有时你可能需要在命令之外访问状态,例如在不同的线程中或在像 on_window_event 这样的事件处理程序中。在这种情况下,你可以使用实现了 Manager 特征的类型(例如 AppHandle)的 state() 方法来获取状态:

use tauri::{Builder, GlobalWindowEvent, Manager};#[derive(Default)]
struct AppState {counter: u32,
}// In an event handler:
fn on_window_event(event: GlobalWindowEvent) {// Get a handle to the app so we can get the global state.let app_handle = event.window().app_handle();let state = app_handle.state::<Mutex<AppState>>();// Lock the mutex to mutably access the state.let mut state = state.lock().unwrap();state.counter += 1;
}fn main() {Builder::default().setup(|app| {app.manage(Mutex::new(AppState::default()));Ok(())}).on_window_event(on_window_event).run(tauri::generate_context!()).unwrap();
}

当你不能依赖命令注入时,此方法非常有用。例如,如果你需要将状态移动到使用 AppHandle 更容易的线程中,或者你不在命令上下文中。

3.6 修复Mismatching Types

如果你为 State 参数使用了错误的类型,你将得到一个运行时 panic,而不是编译时错误。

例如,如果你使用 State<'_, AppState> 而不是 State<'_, Mutex<AppState>>,则不会有任何状态使用该类型进行管理。

如果你愿意,你可以用类型别名包装你的状态以防止这个错误:

use std::sync::Mutex;#[derive(Default)]
struct AppStateInner {counter: u32,
}type AppState = Mutex<AppStateInner>;

但是,请确保按原样使用类型别名,而不是再次将其包装在 Mutex 中,否则你将遇到同样的问题。

四、使用第三方状态管理库

除了 Tauri 2.0 的内置状态管理机制,我们还可以使用 Rust 生态系统中的第三方状态管理库来实现更复杂的状态管理需求。

4.1 Redux 启发的状态管理:Yewdux

Yewdux 是一个受 Redux 启发的 Rust 状态管理库,它提供了一种基于单向数据流的状态管理模式。

  1. 安装 Yewdux

    cargo add yewdux
    
  2. 定义状态和 Reducer

    use yewdux::prelude::*;#[derive(Default, Clone, PartialEq, Eq, Store)]
    struct AppState {counter: i32,
    }#[derive(Clone, PartialEq, Eq)]
    enum Action {Increment,
    }impl Reducer<AppState> for Action {fn apply(&self, mut state: Rc<AppState>) -> Rc<AppState> {let state = Rc::make_mut(&mut state);match self {Action::Increment => state.counter += 1,}state.clone().into()}
    }
    
    • 我们定义了一个名为 AppState 的结构体,其中包含一个 counter 字段。
    • 我们定义了一个名为 Action 的枚举,表示可以对状态执行的操作。
    • 我们为 Action 实现了 Reducer<AppState> trait,定义了如何根据不同的 Action 来更新状态。
  3. 在 Tauri 中使用 Yewdux

    use yewdux::prelude::*;#[tauri::command]
    fn increment_counter(dispatch: Dispatch<AppState>) -> Result<(), String> {dispatch.apply(Action::Increment);Ok(())
    }#[tauri::command]
    fn get_counter(dispatch: Dispatch<AppState>) -> Result<i32, String> {Ok(dispatch.get().counter)
    }fn main() {tauri::Builder::default().setup(|app| {let dispatch = Dispatch::<AppState>::new();app.manage(dispatch);Ok(())}).invoke_handler(tauri::generate_handler![increment_counter, get_counter]).run(tauri::generate_context!()).expect("error while running tauri application");
    }
    
    • main 函数中,我们使用 Dispatch::<AppState>::new() 创建一个 Dispatch 实例,并将其注册为 Tauri 的全局状态。
    • 在命令中,我们通过 Dispatch<AppState> 类型的参数来访问和修改状态。
    • increment_counter 命令使用 dispatch.apply(Action::Increment) 来触发状态更新。
    • get_counter 命令使用 dispatch.get().counter 来获取状态的当前值。
  4. 在前端使用 Yewdux

    与 Tauri 内置状态管理类似,我们可以使用 invoke 函数来调用后端命令,并通过事件或轮询来获取状态更新。

4.2 其他状态管理库

除了 Yewdux,Rust 生态系统中还有其他一些状态管理库可供选择,例如:

  • Relm4 一个基于 Elm 架构的 GUI 库,它内置了状态管理机制。
  • Iced 一个跨平台的 GUI 库,它也提供了自己的状态管理方案。

五、状态管理的最佳实践

在 Tauri 2.0 应用程序中实现全局状态管理时,可以遵循以下最佳实践:

  1. 选择合适的状态管理方案: 根据应用程序的复杂度和需求选择合适的状态管理方案。对于简单的应用程序,Tauri 2.0 的内置状态管理机制可能就足够了。对于更复杂的应用程序,可以考虑使用第三方状态管理库。
  2. 保持状态的单一数据源: 避免在多个地方维护相同的状态,确保状态的唯一性和一致性。
  3. 使用不可变数据: 尽可能使用不可变数据来表示状态,避免意外的状态修改。
  4. 最小化状态更新: 仅在必要时更新状态,避免不必要的状态更新和渲染。
  5. 使用选择器(Selectors): 如果状态数据比较复杂,可以使用选择器来从状态中提取所需的数据,避免在组件中直接访问原始状态。
  6. 使用调试工具: 利用 Tauri 2.0 和状态管理库提供的调试工具来跟踪状态变化和调试问题。
  7. 编写测试: 为状态管理逻辑编写单元测试和集成测试,确保状态管理的正确性和稳定性。

六、实战案例:构建一个简单的计数器应用

为了更好地理解如何在 Tauri 2.0 应用程序中实现全局状态管理,我们将构建一个简单的计数器应用。

6.1 项目设置

  1. 创建新的 Tauri 项目:

    cargo tauri init
    

    按照提示输入项目名称、窗口标题等信息。

  2. 安装 Tauri API:

    npm install @tauri-apps/api
    

6.2 后端代码

  1. 定义状态类型:

    // src-tauri/src/main.rs#[derive(Default)]
    struct AppState {counter: std::sync::Mutex<i32>,
    }
    
  2. 实现命令:

    // src-tauri/src/main.rs#[tauri::command]
    fn increment_counter(state: tauri::State<AppState>, window: tauri::Window) -> Result<(), String> {let mut counter = state.counter.lock().map_err(|e| e.to_string())?;*counter += 1;window.emit("counter-updated", *counter).map_err(|e| e.to_string())?;Ok(())
    }#[tauri::command]
    fn get_counter(state: tauri::State<AppState>) -> Result<i32, String> {let counter = state.counter.lock().map_err(|e| e.to_string())?;Ok(*counter)
    }
    
  3. 注册状态和命令:

    // src-tauri/src/main.rsfn main() {tauri::Builder::default().manage(AppState::default()).invoke_handler(tauri::generate_handler![increment_counter, get_counter]).run(tauri::generate_context!()).expect("error while running tauri application");
    }
    

6.3 前端代码

  1. 创建 HTML 结构:

    <!-- src/index.html --><!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Tauri Counter App</title></head><body><h1>Counter: <span id="counter">0</span></h1><button id="increment-button">Increment</button><script src="main.js"></script></body>
    </html>
    
  2. 编写 JavaScript 代码:

    // src/main.jsimport { invoke } from '@tauri-apps/api/tauri';
    import { listen } from '@tauri-apps/api/event';async function incrementCounter() {await invoke('increment_counter');
    }// 监听 counter-updated 事件
    listen('counter-updated', (event) => {document.getElementById('counter').textContent = event.payload;
    });// 在页面加载时更新计数器
    async function updateCounter() {const counter = await invoke('get_counter');document.getElementById('counter').textContent = counter;
    }
    updateCounter()// 绑定按钮点击事件
    document.getElementById('increment-button').addEventListener('click', incrementCounter);
    

6.4 运行应用

cargo tauri dev

现在,你应该可以看到一个简单的计数器应用。点击 “Increment” 按钮,计数器的值会增加,并且界面会实时更新。

总结

全局状态管理是构建复杂 Tauri 2.0 应用程序的关键。本文深入探讨了 Tauri 2.0 中的全局状态管理,介绍了 Tauri 2.0 的内置状态管理机制以及如何使用第三方状态管理库。通过结合 Rust 语言的优势和 Tauri 2.0 框架的功能,我们可以构建高性能、安全且易于维护的桌面应用程序。

希望本文能够帮助你更好地理解 Tauri 2.0 中的全局状态管理,并在你的项目中应用这些知识。如果你有任何问题或建议,欢迎留言讨论。

相关文章:

【一起学Rust | Tauri2.0框架】基于 Rust 与 Tauri 2.0 框架实现全局状态管理

前言 在现代应用程序开发中&#xff0c;状态管理是构建复杂且可维护应用的关键。随着应用程序规模的增长&#xff0c;组件之间共享和同步状态变得越来越具有挑战性。如果处理不当&#xff0c;状态管理可能会导致代码混乱、难以调试&#xff0c;并最终影响应用程序的性能和可扩…...

金桔网桥路由版3

上一集我们讲到了二层云交换机&#xff0c;我把在云上搭建的桥接模式的VPN服务器称为二层云交换机。 那么现在我家到办公室的网络结构就变成这样的&#xff0c; 这样的好处就是我的电视盒子通过网线看电视&#xff0c;走的是OpenWrt路由器通过二层云交换机由办公室的OpenWrt路由…...

前沿分享|处理LLM幻觉问题-CoN|笔记链:增强检索增强语言模型的鲁棒性

检索增强语言模型&#xff08;RALMs&#xff09;在大型语言模型的能力方面取得了重大进步&#xff0c;特别是在减少事实幻觉方面&#xff0c;这得益于外部知识来源的利用。 然而&#xff0c;检索到的信息的可靠性并不总是有保证。 检索到无关数据可能导致误导性回答&#xff…...

OpenWrt开发第4篇:设置开发板的IP-基于Raspberry Pi 4B开发板

文/指尖动听知识库-谷谷 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:Openwrt开发-基于Raspberry Pi 4B开发板 有时候开发过程中经常会使用其他路由器,很多时候固件烧上去之后板子IP基本都是192.168.1.1,这时就需要修改板子的IP,下面介绍一下板…...

浅谈跨平台框架的演变(H5混合开发->RN->Flutter)

引言 这里分为四个阶段&#xff1a; 第一阶段 &#xff1a; 原生开发 第二阶段 &#xff1a; H5混合开发 第三阶段&#xff1a; 跨平台RN 第四阶段&#xff1a; 跨平台Flutter 正文 第一阶段&#xff1a; 原生开发 开发成本比较大 &#xff1a; 需要Android 和ios 开发两…...

Android数据加密方案

Android数据加密方案 前言 在移动应用开发中,数据安全是一个永恒的话题。Android应用中往往需要存储和传输敏感数据,如用户密码、支付信息、个人隐私等。本文将深入介绍Android平台上的数据加密方案,帮助开发者构建安全可靠的数据保护机制。 基础知识 1. 加密算法分类 …...

深入理解traceroute命令及其原理

traceroute 是一个网络诊断工具&#xff08;Windows上叫tracert&#xff09;&#xff0c;用于显示数据包从本地主机到远程主机经过的路由&#xff08;跳数&#xff09;。它可以帮助您了解数据包在网络中的传输路径&#xff0c;以及每跳的延迟情况。这对于网络故障排除、分析网络…...

PostgreSQL_安装

目录 前置&#xff1a; 安装过程&#xff1a; 1 下载软件 2 创建安装文件夹和放置数据的文件夹 3 双击安装 4 连接服务 前置&#xff1a; PostgreSQL 15 windows 10 专业版 安装过程&#xff1a; 1 下载软件 PostgreSQL: Downloads 大小326MB 2 创建安装文件夹和放…...

leetcode684.冗余连接

依旧是并查集问题&#xff0c;这道题目正好给定顶点数目和边的数目相等&#xff0c;只要找到其中的一条边删除将图转化为树就行&#xff0c;而这个多余的边起始就是并查集的添加过程中二者是同一个根&#xff08;两个顶点早已经联通了&#xff09;&#xff0c;这时直接返回这条…...

Python学习第二十一天

爬虫 概念 网络爬虫&#xff08;Web Crawler&#xff09;&#xff0c;也称为网络蜘蛛&#xff08;Web Spider&#xff09;&#xff0c;是一种自动化程序&#xff0c;用于从互联网上抓取数据。爬虫通过模拟浏览器行为&#xff0c;访问目标网站并提取所需的信息&#xff0c;然后…...

matlab近似计算联合密度分布

在 Matlab 中&#xff0c;当A和B是两个序列数据时&#xff0c;可以通过以下步骤来近似求出A大于B的概率分布&#xff1a;数据准备&#xff1a;确保序列A和B具有相同的长度。如果长度不同&#xff0c;需要进行相应的处理&#xff08;例如截取或插值&#xff09;。计算A大于B的逻…...

OpenCV图像处理基础2

接着上一篇OpenCV图像处理基础1继续说。 图像阈值处理 1、简单阈值处理 ret, thresholded_image = cv2.threshold(image, thresh, maxval, cv2.THRESH_BINARY)thresh 是阈值,maxval 是最大值。 2、自适应阈值处理 thresholded_image = cv2.adaptiveThreshold(image, maxv…...

Prometheus Exporter系列-Mysql_Exporter一键部署

新项目旧项目都需要给研发配置mysql监控&#xff0c;这里mysql监控对应aws 阿里云 腾讯云 华为云的云mysql产品或开源自建mysql。 exporter安装虽然简单&#xff0c;经常手动操作不免让人心烦&#xff0c;一键完成省去繁琐的常规操作。 配置信息对的情况下测试多次都可以正常安…...

A l密码学(Deepseek)

我&#xff1a;qwertyuiopasdfghjklzxcvbnm deepseek:深度思考中&#xff0e; Okay, lets see. The user input is "qwertyuiopasdfghjklzxcvbnm". At first glance, it looks like a jumbled sequence of letters with some spaces or maybe other characters in …...

​「Java-API帮助文档」

「Java-API帮助文档」&#xff0c;链接&#xff1a;https://pan.quark.cn/s/d7ced3b48f33 java.applet提供创建 applet 所必需的类和 applet 用来与其 applet 上下文通信的类。java.awt包含用于创建用户界面和绘制图形图像的所有类。java.awt.color提供用于颜色空间的类。java…...

卷积神经网络 - 梯度和反向传播算法

在卷积网络中&#xff0c;参数为卷积核中权重以及偏置。和全连接前馈网络类似&#xff0c;卷积网络也可以通过误差反向传播算法来进行参数学习。本文我们从数学角度&#xff0c;来学习卷积神经网络梯度的推导和其反向传播算法的原理。 一、梯度&#xff1a;损失函数 L 关于第 …...

ripro 主题激活 问题写入授权Token失败,可能无文件写入权限

ripro 主题激活 问题 写入授权Token失败&#xff0c;可能无文件写入权限 找到主题下面的functions.php文件&#xff0c;给其他写入权限。就好了。...

MySQL 中,查看执行频次、慢查询日志、SHOW PROFILE和 EXPLAIN性能分析和优化

在 MySQL 中,查看执行频次、慢查询日志、SHOW PROFILE 和 EXPLAIN 是性能分析和优化的核心工具。以下是它们的详细用法和高级语法: 一、查看 SQL 执行频次 通过 SHOW STATUS 命令可以查看 SQL 的执行频次,帮助定位高频查询。 1. 查看全局 SQL 执行频次 SHOW GLOBAL STATU…...

Springdoc 全部注解一文解释清楚

文章目录 **1. 核心注解****Tag-Class类上** **2. 方法级别注解****Operation-方法描述****ApiResponse 和 ApiResponses-方法的返回结果** **3. 参数相关注解****Parameter-方法参数****Parameters方法参数&#xff08;单个&#xff09;** **4. 实体模型相关注解****Schema-描…...

1.angular介绍

初級使用视频添加链接描述 angular工具 angular.module(‘名’, [依赖模块]) 模块 angular.bind(*) : 修改this指向 angualr.copy() // a angular.copy(a, b) —a完全覆盖了b,c就是a angular.extend(a, b) a里面集成了b属性 angular.isArray angular.isDate angular.isDefin…...

StarRocks vs Doris:深度剖析与选型分析

StarRocks vs Doris&#xff1a;深度剖析与选型分析 在大数据技术蓬勃发展的当下&#xff0c;企业对于高效的数据分析工具的需求日益增长。StarRocks 和 Doris 作为两款优秀的 MPP&#xff08;大规模并行处理&#xff09;数据库&#xff0c;在数据仓库和数据分析领域备受关注。…...

Ambari、Bigtop源码编译最新支持情况汇总

以下是目前的版本情况 支持了绝大部分的组件编译及安装 版本组件名称组件版本env 版本v1.0.5Ozone1.4.11.0.5Impala4.4.11.0.5Nightingale7.7.21.0.5Categraf0.4.11.0.5VictoriaMetrics1.109.11.0.5Cloudbeaver24.3.31.0.5Celeborn0.5.31.0.5v1.0.4Doris2.1.71.0.4v1.0.3Phoen…...

【sql靶场】第23、25,25a关过滤绕过保姆级教程

目录 【sql靶场】第23、25-28关过滤绕过保姆级教程 第二十三关 第二十五关 1.爆出数据库 2.爆出表名 3.爆出字段 4.爆出账号密码 【sql靶场】第23、25&#xff0c;25a关过滤绕过保姆级教程 第二十三关 从本关开始又是get传参&#xff0c;并且还有了对某些字符或字段的过…...

coding ability 展开第五幕(二分查找算法)超详细!!!!

. . 文章目录 前言二分查找搜索插入的位置思路 x的平方根思路 山脉数组的峰顶索引思路 寻找旋转排序数组中的最小值思路 总结 前言 本专栏上篇博客已经把滑动指针收尾啦 现在还是想到核心——一段连续的区间&#xff0c;有时候加上哈希表用起来很爽 今天我们来学习新的算法知识…...

存算分离是否真的有必要?从架构之争到 Doris 实战解析

引言&#xff1a;一场关于 “存与算” 的N年辩论 在数据库与大数据领域&#xff0c;“存算一体” 与 “存算分离” 的架构之争从未停歇。有人质疑&#xff1a;“存算分离真的有必要吗&#xff1f;本地盘性能难道不够&#xff1f;” 答案并非非黑即白 —— 技术选型的关键&…...

卸载conda,poetry常用命令,vscode使用poetry虚拟环境

~/miniconda3/bin/conda init bash ~/miniconda3/bin/conda init zsh conda info 查看当前环境的配置信息 conda install package-name conda install package-nameversion 安装依赖包 conda uninstall package-nameversion 卸载依赖包 conda update package-name 更新依赖包…...

【总结】Pytest vs Behave,BDD 测试框架哪家强?

引言 在测试驱动开发(TDD)和行为驱动开发(BDD)流行的今天&#xff0c;Pytest和 Behave 成为了 Python 生态中最常见的自动化测试框架。那么&#xff0c;究竟该选择哪一个&#xff1f;它们各自有哪些优缺点&#xff1f;本篇文章将为你全面解析&#xff01; 1. 什么是 Pytest&a…...

INT202 Complexity of Algroithms 算法的复杂度 Pt.2 Search Algorithm 搜索算法

文章目录 1.树的数据结构1.1 有序数据(Ordered Data)1.1.1 有序字典&#xff08;Ordered Dictonary&#xff09;1.1.1.1 排序表&#xff08;Sorted Tables&#xff09; 1.2 二分查找&#xff08;Binary Search&#xff09;1.2.1 二分查找的时间复杂度 1.3 二叉搜索树&#xff0…...

springmvc中使用interceptor拦截

HandlerInterceptor 是Spring MVC中用于在请求处理之前、之后以及完成之后执行逻辑的接口。它与Servlet的Filter类似&#xff0c;但更加灵活&#xff0c;因为它可以访问Spring的上下文和模型数据。HandlerInterceptor 常用于日志记录、权限验证、性能监控等场景。 ### **1. 创…...

C++编译汇编八股总结

汇编的四个阶段&#xff1f; 预编译&#xff08;预处理&#xff09;&#xff1a; 预编译是源代码在编译之前进行的一些处理&#xff0c;主要包括宏定义展开、条件编译指令处理和头文件展开等。 编译&#xff1a; 编译器根据源代码的语法和语义规则&#xff0c;将源代码进行词法…...

基于ArcGIS和ETOPO-2022 DEM数据分层绘制全球海陆分布

第〇部分 前言 一幅带有地理空间参考、且包含海陆分布的DEM图像在研究区的绘制中非常常见&#xff0c;本文将实现以下图像的绘制 关键步骤&#xff1a; &#xff08;1&#xff09;NOAA-NCEI官方下载最新的ETOPO-2022 DEM数据 &#xff08;2&#xff09;在ArcGIS&#xff08;…...

【LangChain入门 4 Prompts组件】提示词追加示例 FewShotPromptTemplate和示例选择器ExampleSelector

文章目录 一、提示词追加示例 FewShotPromptTemplate二、使用示例选择器 example_selector三、关键类介绍3.1 PromptTemplate3.2 FewShotPromptTemplate3.3 SemanticSimilarityExampleSelector 提示词中包含交互样本的作用是为了帮助模型更好地理解用户的意图&#xff0c;从而更…...

Android Compose 切换按钮深度剖析:从源码到实践(六)

Android Compose 切换按钮深度剖析&#xff1a;从源码到实践 一、引言 在现代 Android 应用开发中&#xff0c;用户交互体验至关重要。切换按钮&#xff08;Toggle Button&#xff09;作为一种常见的交互组件&#xff0c;允许用户在两种状态之间进行切换&#xff0c;例如开 /…...

挖矿病毒应急响应处置手册

挖矿病毒应急响应处置手册 文章目录 挖矿病毒应急响应处置手册0x00 概述0x01 了解基本情况1.1 如何发现1.1.1 异常外联1.1.2 主机异常1.2 事件的时间节点1.3 临时处置情况1.4 网络拓扑情况0x02 判断是否属于挖矿2.1 属于挖矿2.1.1 根据告警和流量信息初步判断挖矿类型2.1.2 win…...

VSCode - 查看 PDF 文件

VSCode 原生并不支持 查看 PDF 文件&#xff0c;需要额外安装插件。 这里我使用 vscode-pdf&#xff0c;效果还不错&#xff0c;有需要的可以搜索安装。 效果&#xff1a; 2025-03-18&#xff08;二&#xff09;...

vue3:八、登录界面实现-忘记密码

该文章实现登录界面的忘记密码功能&#xff0c;点击忘记密码文本&#xff0c;打开dialog对话框 一、页面效果 加入忘记密码&#xff0c;在记住密码的同一行中&#xff0c;实现flex-between 二、对话框实现 1、新建组件页面 2、引入dialog组件到组件页面 参考路径 Dialog 对…...

Python Django入门(创建其他网页)

在本章中&#xff0c;你将学习如何使用 Django&#xff08;http://djangoproject.com/ &#xff09;来开发一个名为“学习笔记”&#xff08;Learning Log&#xff09;的项目&#xff0c;这是一个在线日志系统&#xff0c;让你能够记录所学习的有关特定主题的知识。 我们将为这…...

Windows安装MySQL5.7.26教程图解

Windows安装MySQL5.7.26教程图解 零、准备工作 下载MySQL软件包 ①、官网下载:程序员 常用 软件汇总 - 超人那个超~ - 博客园 ②、百度云下载:链接:百度网盘 请输入提取码 提取码:chao 一、彻底删除MySQL 从电脑里卸载旧的MYSQL数据库服务时,首先先在WINDOWS服务里…...

FreGS: 3D Gaussian Splatting with Progressive Frequency Regularization论文学习记录

3. 提出的方法 我们提出了FreGS&#xff0c;一种具有渐进频率正则化的新型3D高斯溅射方法&#xff0c;它是首个从频率角度缓解3D高斯溅射过度重建问题的方法。图2展示了FreGS的概览。第3.1节简要介绍了原始的3D高斯溅射方法&#xff08;3D-GS&#xff09;&#xff0c;包括高斯…...

汽车行业敏捷开发实践:基于Atlassian工具链的全流程解决方案(Jira、Confluence、Jira Service Management等)

直播回顾 在数字化浪潮席卷全球的今天&#xff0c;各行各业都在积极寻求转型与突破&#xff0c;汽车行业也不例外。 近日&#xff0c;在“Atlassian助力企业破局&#xff1a;数字化协作与全球市场拓展”的线上直播活动中&#xff0c;龙智资深顾问张晓乐深入探讨了汽车行业数字…...

遇到一个奇怪问题,页面请求不到后端

背景 页面有两个请求,第一个接口获取令牌,第二个接口根据令牌去获取数据, 突然发现获取数据接口校验令牌的时候一直报错 而且报错的时候服务器没有获取令牌请求 而且发现偶尔是正常的,正常的发现服务器ip和异常的不一样,同事定位可能是域名解析问题 解决 最后定位是腾讯cdn解…...

【C++】:C++11详解 —— 线程库

目录 线程库&#xff08;thread&#xff09; 线程对象的构造函数 构造函数的用法示例 参数传递的关键细节 构造函数的异常行为 线程对象的使用 互斥量库&#xff08;mutex&#xff09; 互斥量类型 锁管理类&#xff08;RAII 封装&#xff09; 条件变量&#xff08;…...

招聘面试季--一文顿悟,Java中字节流和字符流的区别及使用场景上的差异

‌一、核心区别‌ ‌特性‌‌字节流‌‌字符流‌‌数据单位‌以字节&#xff08;8-bit&#xff09;为单位处理数据&#xff08;如0xA1&#xff09;以字符&#xff08;16-bit Unicode&#xff09;为单位处理数据&#xff08;如A, 你&#xff09;‌基类‌InputStream / OutputSt…...

在 ARM 嵌入式 Linux 下使用 C/C++ 实现 MQTT

在 ARM 嵌入式 Linux 下使用 C/C 实现 MQTT 通信是一个常见的需求&#xff0c;尤其是在资源受限的环境中。以下是一个详细的教程&#xff0c;使用 Eclipse Paho C Client 库来实现 MQTT 客户端。 1. 安装 Eclipse Paho C Client 库 Eclipse Paho C Client 是一个轻量级的 MQTT…...

C++20 中 `constexpr` 的强大扩展:算法、工具与复数库的变革

文章目录 一、constexpr 在 <algorithm> 中的应用1. 编译时排序2. 编译时查找 二、constexpr 在 <utility> 中的应用1. 编译时交换2. 编译时条件交换 三、constexpr 在 <complex> 中的应用1. 编译时复数运算 四、总结 C20 对 constexpr 的增强是其最引人注目…...

C++ 介绍STL底层一些数据结构

c 标准模板库中&#xff0c;set和map的底层实现通常基于红黑树&#xff0c;然们都是平衡二叉搜索树(Balanceed Binary Serach Tree&#xff09;的一种,这种结构保证了 插入&#xff0c;删除&#xff0c;查找的时间复杂度为O(log n)比普通二叉搜索树更高效。 set set<T>…...

算法2--两数相加

题目描述 解题思路 题目说的很详细了&#xff0c;也就是把每个数倒序写成链表进行输入&#xff0c;然后让你计算两个倒序数组的和&#xff0c;要保证跟预期的结果一样。 首先应该考虑的是两个数组的长度问题&#xff0c;对于链表的每一位进行加法运算&#xff0c;如果两个列表…...

Docker搭建Testlink教程

1.拉取镜像 打开终端输入命令&#xff1a; #拉取mariadb镜像 docker pull bitnami/mariadb #拉取testlink镜像 docker pull bitnami/testlink-archived 执行结果&#xff1a; 2.运行容器 打开终端输入命令&#xff1a; #创建容器网络 docker network create testlink #查…...

安卓7.0以上App抓包

安卓7.0以上App抓包 导出BurpSuite证书 设置本机IP的8080端口监听 证书转换 将这个der证书下载到kali上&#xff0c;并使用以下命令进行证书转换 openssl x509 -inform der -in cacert.der -out burp.pem openssl x509 -inform PEM -subject_hash_old -in burp.pem转换成功…...

CCBCISCN复盘

AWDP – ccfrum 自己搭了一下环境, 复现一下这道题目, 之前比赛的时候完全没想到这个漏洞要怎么打, 修也不知道要怎么修, 就仅仅是对用户名的账号和密码进行了一下过滤, 完全没起到作用, 唉, 实在太菜 如果想要尝试复现的话可以尝试拉取这个镜像, 我打完之后就直接把这个容器给…...