2024年8月

1. Rust 简介

Rust 的历史

  • 起源
    :Rust 语言最初由 Mozilla 研究员 Graydon Hoare 于 2006 年开始设计,并于 2009 年首次公开。
  • 开发
    :Rust 是 Mozilla 实验室的一个项目,目的是创建一种能够保证内存安全同时又不牺牲性能的系统编程语言。
  • 发布
    :Rust 1.0 稳定版于 2015 年发布,标志着语言的成熟和稳定。

设计目标

  • 内存安全
    :Rust 的设计核心是提供内存安全,通过所有权(ownership)、借用(borrowing)和生命周期(lifetimes)的概念来避免空指针解引用和数据竞争等问题。
  • 并发编程
    :Rust 旨在简化并发编程,通过所有权和类型系统来帮助开发者编写无数据竞争的多线程代码。
  • 性能
    :Rust 旨在提供与 C/C++ 相当的性能,没有运行时垃圾收集器,编译为机器码,适合系统编程和性能敏感型应用。
  • 表达性
    :Rust 提供了丰富的类型系统和模式匹配,使得代码既安全又具有表现力。

Rust 与 C/C++ 的比较

  • 内存安全
    :与 C/C++ 相比,Rust 通过所有权和借用规则在编译时避免了内存泄漏和野指针问题,而 C/C++ 需要开发者手动管理内存。
  • 并发性
    :Rust 的所有权模型天然支持无数据竞争的并发,而 C++11 引入了线程库来支持并发编程,但依然需要开发者小心处理数据同步问题。
  • 语法
    :Rust 的语法类似于 C++,但更简洁,且提供了模式匹配等特性,使得代码更易于编写和理解。
  • 错误处理
    :Rust 使用
    Result
    类型来显式处理可能的错误,而 C++ 使用异常处理。
  • 编译器友好
    :Rust 的编译器提供详尽的错误信息和有用的提示,帮助开发者快速定位和解决问题。

Rust 的应用领域

  • 系统编程
    :由于其性能和内存安全特性,Rust 适合用于操作系统、文件系统、设备驱动等底层系统开发。
  • WebAssembly
    :Rust 可以编译为 WebAssembly,用于开发 Web 应用的高性能前端逻辑。
  • 嵌入式编程
    :Rust 的资源管理特性使其适合用于嵌入式设备编程。
  • 工具开发
    :Rust 用于开发命令行工具,如
    cargo
    (Rust 的包管理器和构建工具)。

Rust 的生态系统

  • Cargo
    :Rust 的包管理器和构建工具,用于依赖管理和项目构建。
  • crates.io
    :Rust 的包注册表,类似于 npm 或 Maven,用于共享和重用代码。
  • 社区
    :Rust 拥有一个活跃的社区,提供大量的库和框架,以及持续的技术支持。

学习资源

  • The Rust Programming Language
    (又称 "The Book"):Rust 官方教程,适合初学者。
  • Rust by Example
    :通过实例学习 Rust,覆盖了 Rust 的大部分特性。
  • Rustlings
    :一个练习项目,通过小练习帮助学习 Rust。

Rust 作为一种现代系统编程语言,以其内存安全、并发性和性能优势,正在获得越来越多的关注和应用。随着 Rust 生态的不断发展,我们可以预见它将在未来的软件开发中扮演更重要的角色。

2. 环境搭建

搭建 Rust 编程环境主要包括安装 Rust 编译器和一些辅助工具。以下步骤将引导你完成环境搭建:

步骤 1: 安装 Rust 编译器

Rust 编译器可以通过 Rustup 安装,Rustup 是 Rust 的官方安装程序和版本管理器。

  1. 访问 Rustup 官网
    :打开
    rustup.rs
  2. 遵循安装指南
    :根据你的操作系统(Windows、macOS、Linux),网页会提供相应的安装指令。
  3. 自动安装脚本
    :对于大多数用户,只需复制网页上提供的命令并在终端或命令提示符中运行即可。例如,在 Linux 或 macOS 上,你可以使用以下命令:
   curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

在终端执行以上命令后的效果:

到这个界面,回车继续安装和配置环境变量。

看到这个界面,安装就完成了。

stable-x86_64-apple-darwin installed - rustc 1.80.1
表示安装成功,版本号为1.80.1。

对于 Windows 用户,下载并运行提供的
rustup-init.exe
安装程序。

步骤 2: 检查安装

安装完成后,你可以通过在终端运行以下命令来检查 Rust 编译器是否正确安装(V哥提醒:一定要重启一下终端):

rustc --version

这将显示安装的 Rust 编译器版本。

步骤 3: 安装 Cargo

Cargo 不仅是 Rust 的包管理器,还是构建工具。它与 Rust 编译器一起安装,所以你不需要单独安装。

  1. 检查 Cargo 安装
    :运行以下命令来检查 Cargo 是否已安装:
   cargo --version

步骤 4: 更新 Rust

Rust 和 Cargo 会定期更新。使用以下命令来更新到最新版本:

rustup update

步骤 5: 配置环境变量(如果需要)

在某些系统中,可能需要将 Rust 编译器和 Cargo 添加到 PATH 环境变量中。通常,Rustup 会自动处理这一步,但如果没有,你可以手动添加。

  1. 找到 Rustup 安装目录
    :Rustup 通常安装在以下路径:
    • Windows:
      C:\Users\你的用户名\.cargo\bin
    • macOS 和 Linux:
      ~/.cargo/bin
  2. 添加到 PATH
    :根据你的操作系统,将上述路径添加到 PATH 环境变量中。

步骤 6: 创建第一个项目

使用 Cargo 创建一个新项目来测试你的环境。

  1. 打开终端或命令提示符
  2. 运行以下命令
   cargo new myproject

这将在当前目录下创建一个名为
myproject
的新文件夹,包含一个新的 Rust 项目模板。

步骤 7: 构建和运行项目

  1. 进入项目目录
   cd myproject
  1. 构建项目
   cargo build

这将编译你的项目,生成的可执行文件在
target/debug/
目录下。

  1. 运行项目
   cargo run

运行成功。

步骤 8: 探索项目结构

新创建的 Rust 项目包含以下文件和目录:

  • Cargo.toml
    :项目的配置文件,包含元数据和依赖信息。
  • src
    :源代码目录,包含
    main.rs
    — 项目的入口点。

按照这些步骤,你将拥有一个基本的 Rust 开发环境,可以开始编写和运行 Rust 程序。如果你在安装过程中遇到任何问题,可以联系 V 哥一对一帮你解决。

前言

大家在平时工作中,是不是经常为了找某个文件或者应用而在电脑桌面上来回翻找?桌面图标乱七八糟,每次找东西都像在大海捞针一样。

今天给大家介绍一个开源项目
GeekDesk
,它能够让桌面焕然一新,工作效率翻倍!GeekDesk 是一个小巧、美观的桌面快速启动工具,它集成了强大的搜索功能,让我们能够快速的找到所需的应用程序和文件。

项目介绍

小巧、
美观
的桌面快速启动管理工具, 同时集成了Everything搜索。

免费 / 美观 / 高度定制化
是从 GeekDesk诞生开始就有的需求和方向, 未来也会向着这些方向发展。

GeekDesk 使用 WPF、.NET 4.7.2 和 HandyControl 构建,确保了软件的稳定性和易用性。

这些技术背景,让 GeekDesk 不仅功能强大,而且运行流畅。

最大的特点就是可以自定义各种快捷方式,让你分分钟找到自己需要的工具和文件。

目前在 Github上面收获了1.9K star!


安装使用

1、下载安装

直接在发行版本页面下载
GeekDesk
的最新版本,解压后双击
GeekDesk.exe
文件即可。


2、启动界面

软件包括设置、待办还有新手引导等功能,大家快去试试吧。

项目功能

打开 GeekDesk 客户端,可以看到一个简洁的界面。

  • 图标列表区:右侧高亮区域是图标列表区,将文件拖动到图标列表区将自动创建快捷方式,还可以鼠标右键单击添加系统项目。
  • 菜单栏:左侧高亮区域是菜单栏,右键单击左侧区域可以创建菜单,右键单击菜单可以对菜单进行操作。
  • 拖动区:左键按住上部高亮区域可以拖动程序窗体。
  • 设置和关闭:高亮区域的两个按钮分别是设置和关闭按钮,可以点击设置按钮重新打开引导提示设置窗口中可自定义开启或关闭众多功能,

赶紧探索使用吧!


1、快速搜索程序和文件

GeekDesk 2.5.14 及之后版本集成了Everything功能。

首先启动软件,然后在设置打开Everything功能,使用快捷键
Ctrl+F
,弹出搜索框,输入自己要搜索的应用或者文件。

具体如下图所示:


2、显示设置

全局热键:自定义自己习惯的快捷键,一键呼出。

鼠标跟随:自动追踪鼠标位置,优化用户体验。


3、自定义壁纸

自选壁纸:自由选择自己喜欢的壁纸。


4、毛玻璃效果

背景毛玻璃效果

界面透明度调整

圆角界面


5、自定义菜单图标

80多种系统图标供选择

支持在线导入阿里巴巴 Icon 图标


6、定时提醒

快捷键快速新建待办事项

总结

体验后发现,GeekDesk 是一款极为实用的桌面管理工具,特别适合程序员和打工人。

它强大的自定义能力和快捷启动功能能够显著提高工作效率。

项目地址

GitHub :
https://github.com/BookerLiu/GeekDesk

Gitee :
https://gitee.com/BookerLiu/GeekDesk

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号
[DotNet技术匠]
社区,与其他热爱技术的同行一起交流心得,共同成长!

《软件性能测试分析与调优实践之路》(第2版)  是清华大学出版社出版的一本图书,作者为张永清,全书共分为9章,如下图所示

图书介绍:
《软件性能测试分析与调优实践之路》(第2版)

本文是接着

《软件性能测试分析与调优实践之路》(第2版) 读书笔记(一)总体介绍(上)-真正从性能分析与调优来看性能测试

继续往下讲。

6)、性能分析与调优实践(索引与分库分表)

分库分表时,插入数据时,可以按照如下图所示的方式来进行分库分表的写入数据。

查询数据时可以按照如下图所示的方式来进行查询,下图中的路由表非常的关键,用于能快速的定位到需要查询的数据是在哪个分库和分表中。

7)、性能分析与调优实践(层层过滤)

层层过滤的重点如下:

  • 在不同的层级尽可能过滤掉对应层级的所有该过滤的无效请求,让最末端进入到数据库中的请求都是有效的请求。
  • 错误前置,提前抛出异常。对于异常的请求,越早抛出异常,越有利于减轻系统的处理能力和节省资源的占用。
  • 避免重复请求以及通过机器人的恶意请求,从而降低系统的处理压力,更好的保护系统。《软件性能测试分析与调优实践之路》(第2版) 读书笔记

7、性能分析与调优实践(常见性能问题)

1)、性能指标曲线频繁出现大幅度抖动

  • (1)系统可能在频繁的出现Full GC。Full GC是Java 应用程序垃圾回收的一种机制,一般如果出现了Full GC,应用程序就会出现短暂的停顿。关于Full GC的介绍,可以参考纸质书5.1.7小节中的介绍。此时可以先去看一下应用程序的GC日志,如果是Full GC 非常频繁,并且又没有出现内存泄漏,那么可以参考纸质书5.4.1 小节中介绍的如何减少GC 来解决这个问题。《软件性能测试分析与调优实践之路》(第2版) 读书笔记
  • (2)系统某一次查询、修改或者删除数据耗时很长,导致了整体性能的不稳定。比如,在性能压测查询时,大部分参数化传入的参数,查询出来的结果数据都很少,但是可能某几个参数查询出来的数据量非常大,导致系统在处理这些数据量大的数据时耗时较长。《软件性能测试分析与调优实践之路》(第2版) 读书笔记
  • (3)系统在查询时,可能有时候能命中缓存,有时候命不中缓存。命中缓存时,查询会很快;不能命中缓存时,需要去查询数据库,但是查询数据库的时间肯定比缓存长,所以就会造成系统性能的不稳定。通常情况下数据库也会有缓存,如果命中了数据库的缓存,查询也会更快;如果没有命中,那查询的耗时肯定也会变久。《软件性能测试分析与调优实践之路》(第2版) 读书笔记
  • (4)如果系统是分布式部署,那么可以检查一下分布式处理系统中每个节点的处理能力是否一致,如果不一致,可能也会导致系统频繁抖动。《软件性能测试分析与调优实践之路》(第2版) 读书笔记
  • (5)服务器连接不够用导致的连接批量释放然后再突然批量连接,一旦批量释放连接后,系统TPS马上就会上涨,因为此时可以建立连接了。当连接满了后,请求就无法处理了,从而不得不等待,进而造成TPS突然下降。

2)、在提高并发用户数时,系统的TPS和平均响应时间一直无法提升

通常,检查后会发现应用服务器的CPU、内存等资源都没有达到使用的上限,但是系统却出现了处理的瓶颈,那么说明系统一定是有地方“堵住了”。此时需要继续做如下检查:

  • (1)性能压测时,点击率是否真的上来了。如果点击率或者单位时间内的请求数没有上来,那说明是压测机器无法提供更大压测能力。尤其在大型的分布式系统中,单台压测机往往是不够用的,因为单台压测机不论是网络连接,还是带宽以及自身CPU、内存等都会存在很大限制,性能压测不止是服务器资源会有很大消耗,提供压测能力的压测机也会很大的资源消耗。
  • (2)检查网络带宽的使用情况,排查瓶颈是否因为网络带宽限制而导致。此时,需要检查网络带宽的环节包括压测机到Web服务器、Web服务器到应用服务器、应用服务器到数据库服务器等所有存在网络请求交互的地方。如下图所示。(《软件性能测试、分析与调优实践之路》(第2版),作者张永清,转载请注明出处)

  • (3)参考纸质书5.3.2小节中使用jstack命令行工具,查看Java系统的线程堆栈,从线程堆栈中直接分析当前系统的瓶颈是因为在等待什么资源,而且该资源可能是一个隐形的不容易发现的资源。
  • (4)如果对于第(3)点运用不熟的话,可以用最笨的方式就是根据请求处理的链路过程,从上而下或者从下而上按顺序去排查。此时需要坚信一点,系统肯定是“堵在什么地方了”,仔细通过checklist去检查,一定能够找到这个“堵住”的位置。这就如同自来水的供水系统一样,如果某个用户突然反馈说,我家自来水水压很小,水压一直都上不去,那么自来水公司的维修人员上门之后,肯定是从这个用户家作为起点,然后对供水链路中的每个环节进行排查,直到找到是哪个环节出现了拥堵。 (《软件性能测试、分析与调优实践之路》(第2版),作者张永清,转载请注明出处)
  • (5)如果按照前面四点还是找不到问题原因的话,那么可以尝试减少中间环节从而减少不确定因子的影响,再进行压测对比,先确定问题可能的范围,然后再在某个明确的范围内查找具体的原因。比如如图所示,将Web服务器去掉,让压测机的请求直接对应用服务器进行压测。如下图所示。

3)、提高并发用户数时,系统的TPS缓慢下降且平均响应时间缓慢上升

当系统出现
TPS下降而平均响应时间缓慢上升,可能是系统已经出现了性能的拐点,达到了最大的处理能力了。此时需要做一下如下检查


  • 1)应用服务器资源,比如
    CPU、内存、
    IO等是否已经达到了使用上限。

  • 2)数据库服务器的资源以及数据库的链接数等是否已经达到了使用上限。

  • 3)如果第(
    1)点或者第(
    2)点中的资源使用已经达到了上限,可以对服务器资源进行扩容后,再重新继续压测。通常情况下,性能出现拐点时,服务器中某项资源也达到了使用的上限。

4)、性能压测过程中,服务器内存资源使用率一直在逐步缓慢上升,随着性能压测的持续进行,从来不会出现下降或者在一定范围内小幅度波动,并且此时TPS也在缓慢下降

当出现这种情况时,很有可能是出现了内存泄露,此时可以做如下检查:


  • 1)查看系统日志,看看有没有内存溢出的报错信息。

  • 2)在性能压测过程中参考使用纸质书
    5.2.1小节中的
    jconsole或者
    5.2.2小节中的
    jvisualvm来进一步定位
    Java JVM 是否存在内存泄露。(《软件性能测试、分析与调优实践之路》(第2版),作者张永清,转载请注明出处)

  • 3)如果存在
    JVM 内存泄露,可以参考使用纸质书
    5.3.3小节中的
    MemoryAnalyzer工具来进一步分析是代码中哪个地方出现了内存泄露。

  • 4)性能压测过程中,当并发用户数和点击率不变的情况下,服务器的资源消耗应该是在一个稳定的范围内,或者在一定范围内不断地小幅度波动,这才是比较正常的。

  • 4)如果第(
    3)点无法排查到具体的问题,可以参考纸质书
    1.6.1小节中的分层分析的方式来定位问题。

5)、在分布式部署环境下的性能压测过程中出现每台应用服务器CPU或者内存资源消耗相差太大

当出现这种现象时,可以做如下排查:


  • 1)检查每台应用服务器的硬件配置是否一致。(《软件性能测试、分析与调优实践之路》(第2版),作者张永清,转载请注明出处)

  • 2)检查每台应用服务器的操作系统,应用软件、数据库软件、
    JDK软件等版本以及配置信息是否一致。(《软件性能测试、分析与调优实践之路》(第2版),作者张永清,转载请注明出处)

  • 3)如果第(
    1)点和第(
    2)点都没问题,检查
    Web服务器转发请求到应用服务器的负载均衡是否均匀。比如
    Nginx配置中是否有转发的权重不一致,或者有
    ip_hash等的配置限制,具体可以参考纸质书
    3.1.1小节中的介绍。

热点随笔:

·
程序员失业日记3:工作真不好找
(
小码A梦
)
·
老弟想自己做个微信,被我一个问题劝退了。。
(
程序员鱼皮
)
·
实习第一天,不小心透露了,我是拆迁户
(
Java3y
)
·
告别卡顿,畅享GitHub:国内开发者必看的五大加速访问与下载技巧
(
汀、人工智能
)
·
C#窗体自定义快捷操作键的实现 - 开源研究系列文章
(
lzhdim
)
·
记录兼职运维的一天
(
踩刀诗人
)
·
这就是为什么你学不会DDD
(
老肖想当外语大佬
)
·
为了给Javaer落地DDD,我们不得不写开源组件
(
老肖想当外语大佬
)
·
七天.NET 8操作SQLite入门到实战详细教程(选型、开发、发布、部署)
(
追逐时光者
)
·
北漂日志第1话:惨淡销量、后续发展
(
ThiefCat
)
·
《熬夜整理》保姆级系列教程-玩转Wireshark抓包神器教程(4)-再识Wireshark
(
北京-宏哥
)
·
.NET 8 跨平台高性能边缘采集网关
(
小码编匠
)

热点新闻:

·
外卖骑手与保安,困在各自的“系统”中
·
再见,Devin!基于GPT-4o,最强「AI工程师」Genie诞生
·
大脑推理神经过程首次阐明
·
火车餐车不消费不能坐,12306 回应:属实
·
小红书告诉你,冗员是怎么来的?
·
江小白硬刚东方甄选,遭消费者「补刀」
·
曝印度制造iPhone零件仅一半合格!迫使苹果降价促销
·
钾离子电池来了,电车又要降价了?
·
大模型只是在模仿
·
作为“吃灰神器”,平板电脑为何销量逆袭?
·
iPhone 17 Air 和 SE 4 再曝光!苹果或许比用户更需要一个「大招」
·
爆火的小游戏,成了苹果 VS 腾讯的导火索

0x01 背景

Pod需要使用远程存储的PV,由同k8s集群内的服务提供的存储服务。一开始的做法是:

  1. CSI中解析Service的clusterIP。
  2. 然后使用clusterIP挂载PV卷。

但因为走clusterIP时,经过多次转换:

  1. clusterIP到Pod IP 经过了1次NAT
  2. Pod IP到最终服务。经过1次转发,具体性能损耗跟 CNI 实现相关。

导致了最终client写PV的性能损失严重。

0x02 解决方法

既然走容器网络导致性能差,修改服务端的部署形式为 hostNetwork,绕过容器网络。但带来一个问题,存储服务可能切换节点,导致 client 端无法正常重连(切换节点带来的数据不一致的问题能处理),这一点不能接受。

新的方案:
为服务端创建一个 Headless Service,针对 Deployment 类型的负载Headless Service会解析提到所有Pod的 IP 地址列表
具体见官方文档
,那唯一的问题还剩下 client 重连时,这个域名怎么解析?因为使用的驱动是内核提供的,内核中无法直接使用glibc的域名解析功能,即无法使用外部的DNS Server,即使是/etc/resolv.conf中指定的。

0x03 request-key 机制

通过调查了解到内核提供了
request-key机制
,可以从内核调用到用户态的应用。request-key本来是用于内核和用户态之间的安全token管理的,后也扩展用于其他用途。以内核解析域名来说,大概流程如下:

  1. 内核发起域名解析转到dns_resolver模块【内核态】。
  2. 发起request-key请求转到key管理模块【内核态】。
  3. key管理模块调用/sbin/request-key,调用到用户态【内核态】。
  4. /sbin/request-key根据/etc/request-key.conf中的配置,分发到对应的命令调用,示例为/sbin/key.dns_resolver【用户态】。
  5. /sbin/key.dns_resolver调用glibc域名解析,完成解析,并调用request-key相关系统调用,设置好payload,即域名对应的IP地址【用户态】。

但还有新的问题:key.dns_resolver只能使用/etc/hosts和/etc/resolv.conf解析域名,不支持从额外的dns server解析域名。

0x04 具体的方案

所有的方法都要通过修改 /etc/request-key.conf配置文件指定自己的程序进行解析。

后面的流程有以下方案:

自己写脚本,通过/etc/request-key.conf配置文件指定自己的脚本,通过kubectl去查询 Pod IP地址,调用/sbin/request-key将结果写回。

问题
:C语言中对字符串的处理在dns_resolver和request-key两个模块之间发生了冲突,使用/sbin/request-key写入的IP地址被dns_resolver内核模块认为非法,这个方案行不通,详见QA部分解释。

通过C调用key-utils的SDK,可以实现同样的功能,但基本上照抄key.dns_resolver的实现。突然想到可以用Python调用so库的方法,验证了下基本可行。但又有一个新的问题:

Python标准库中的域名解析同样不支持指定域名,想要支持就要引入第3方的dns模块。

最终方案比较:

方案 优点 缺点
写Python调用key-utils的SDK so完成IP写回内核 灵活控制对coreDNS的访问。 需要调用第3方的dns解析服务、或者直接访问 kube-apiserver获取IP,加重kube-apiserver的负担。
shell脚本通过unshare mount namespace 隔离,生成临时的/etc/resolv.conf,调用/sbin/key.dns_resolver实现 不用访问 kube-apsierver,根据kubelet的配置可获取coreDNS的地址,不用感知具体的DNS解析细节。更通用,其他的 headless也可以用 无法控制调用频率

考虑这种异常切换解析并不会太频繁,最终选择了第2种方案。mount namespace 可以方便地通过 unshare -m 来实现。

0x05 补充QA

Q:/sbin/key.dns_resolver支持从/etc/hosts解析域名,为什么不修改 /etc/hosts?
A:/etc/hosts是全局配置,修改冲突不容易控制,出现冲突时影响不可控。

Q:为什么不能修改/etc/resolv.conf配置,指向coreDNS?
A:虽然coreDNS也支持将非k8s域名转向宿主中/etc/resolv.conf中的指定的DNS,但这种机制依赖 coreDNS,对整个系统的影响过大。

Q: 为什么不用/sbin/request-key回写解析到的IP地址?
A:这种实现了验证了,发现request-key和dns_resolver的实现关于C中字符串的处理有不一致的地方,前者payload长度未包含\0,后者要求包含。这一点是通过bpf钩子确认的。

0x06 总结

问题的解决过程中尝试了多种方案,最终最适合的方案巧妙运用了命名空间隔离机制,这也是了解容器底层原理的好处。
同时带来一点关于命名空间的用途回顾:

  1. 容器内不希望被宿主机影响。
  2. 容器内不期望影响宿主机(本文中的场景),可随意设置/etc/resolv.conf。