August 10, 2024
Rust学习教程清单
"今年又一次重新学习RUST这门编程语言,并从零开发了一个kv存储系统 minKV,慢慢的越来越有感觉了。\n本篇主要将日常学习中收集的一些入门教程进行一下汇总,希望对于一些想学习这门开发语言的同学有所帮助。\n下面教程按照推荐顺序,由浅到深依次列出。以下内容将不定期的更新,请自行收藏。 如果您有更多好的教程的话,也可以在评论区列出,大家相互学习。\n入门教程 Rust 程序设计语言 https://doc.rust-lang.org/book/ / (中文版)\n官方教程,强烈推荐,同时还有非官方翻译的中文版。遗憾的是这个教程有许多概念介绍的都有点不清不楚,只能通过下方的一些资料自行补习。\nhttps://play.rust-lang.org/ 在线 RUST 程序 Playground,类似golang的 Playground,非常的方便\nRust Language Cheat Sheet 看完官方的教程后,紧接着就看这篇,先了解一些内存布局,后面再看其它教程就更容易理解了\nThe Cargo Book Cargo 是RUST 中的包管理工具, …"
August 10, 2024
在rust中实现自定义错误
"上一篇 我们介绍了一些错误处理的最基本的用法,主要是指对 panic! 、unwrap、expect 和 ? 这些宏或函数的介绍。但这仅仅是一些最基本的处理方法,对于自定义错误这一块并没有做任何介绍。\n实际开发中可能默认的错误类型,并无法满足我们的业务需求,这时一般需要通过定义自己的错误类型来实现。在rust中错误类型是通过 enum 枚举定义的,对此官方文档也做了一些简介,本文主要介绍一些业务开发过程中对错误的处理方案,当然主要是一些最基本的用法。\n自定义 Error 在 Rust 中,自定义错误类型是一种常见的类型,特别是当你需要提供比标准错误类型更具体的错误信息时。Rust 中的错误处理是通过 Result 和 Error trait 来实现的。以下是如何实现一个自定义错误的示例:\n定义一个错误枚举类型。 实现 std::fmt::Display 为自定义错误提供用户友好的错误信息。 实现 std::error::Error trait,这通常是通过派生 Error trait 来完成的。 下面是一个简单的示例:\nuse std::fmt; use …"
July 27, 2024
一款管理 .gitignore 的CLI工具- gitig
"gitig 是一款基于 https://github.com/github/gitgnore 仓库开发的.gitignore 客户端CLI 管理工具,也是每个开发者必不可少的提高工作效率的必具工具。\n它基于官方仓库 https://github.com/github/gitgnore 丰富的 .gitignore 数据源,帮助开发者快速实现添加各类开发项目的git版本控制忽略文件清单。\n开发背景 工作中,经常需要开发各类项目,如基于 vscode 编写 rust 项目,这时为了方便进行Git管理控制,有些项目文件可能并不需要提交到git仓库,需要将一些文件写入 .gitignore 文件进行忽略。\n如果手动编辑 .gitignore文件可能有些麻烦,另外也能会有一些文件项被遗忘或写错,这时如果有一些工具可以将行业能用的忽略配置项一键写入 .gitignore 文件似乎是一个不错的主意。\n其中著名的 https://github.com/github/gitignore 就是一个专门收集各类开发语句或IDE 需要忽略的 .gitignore 推荐配置的仓库,目前star …"
July 15, 2024
Rust 中常见的几种错误处理方法
"Rust 中错误可分为两大类:可恢复的(recoverable)和 不可恢复的(unrecoverable)错误。\n对于一个可恢复的错误,比如文件未找到或权限不足的错误,我们很可能只想向用户报告问题,让用户来决定后续操作。\n不可恢复的错误总是 bug 出现的征兆,比如试图访问一个超过数组末端的位置,因此我们要立即停止程序,主要通过 panic! 实现。\npanic! panic!属于不可恢复错误类型,一旦发生程序将立即退出。\nfn main() { panic!(\u0026#34;crash and burn\u0026#34;); } 这里由用户调用 panic! 宏来实现程序的中止,同时自定义错误信息。\n➜ cargo run Compiling hello-world v0.1.0 (/Users/sxf/workspace/rust/hello-world) Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.68s Running `target/debug/hello-world` thread …"
July 10, 2024
在Rust中如何调用一个模块或方法
"在 Rust 中有 包、crate、模块 概念,本文我们介绍一下它们之间的关系和调用方法。\n包 和 Crate 在Rust中,包(package)是提供一系列功能的一个或者多个 crate。一个包会包含一个 Cargo.toml 文件,阐述如何去构建这些 crate。\n我们先看一下通过 cargo new 创建一个 my_project 包。\n➜ cargo new my_project Creating binary (application) `my_project` package note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html ➜ rust tree my_project my_project ├── Cargo.toml └── src └── main.rs 2 directories, 2 files 它将创建一个 Cargo.toml文件,内容:\n[package] name = …"
June 25, 2024
Rust中将一个结构体拆分成多个文件
"官方文档将一个模块拆分成多个文件时,介绍的是将原来多个模块写在同一个文件中,拆分成了每个模块一个文件。不过还有一种情况没有提到,如果一个模块中的某个 struct 实现代码过多时,仍写在同一个模块文件的话,维护成本就显的比较高了,这时我们可能还需要对这个 struct 的实现按某种粒度拆分成多个文件来实现。\n✗ tree . ├── main.rs ├── model │ ├── article.rs // 文章相关 │ └── user.rs // 用户相关 └── model.rs 这里是按官方教程拆分后的样子\narticle.rs 是文件模块相关实现 - user.rs 是与用户相关的实现 model.rs 公开模块 model.rs // src/model.rs pub mod article; pub mod user; pub 关键字表示该模块是公开的,可以被其他模块访问。\nmod article 声明了一个名为 article 的模块,并且 Rust 编译器会在同文件名的目录下( src/model/ )找到一个名为 article.rs …"
May 13, 2024
记录一次排查vpn无法通讯的过程
"今天收到一个项目重构的需求,项目源码使用私有 gitlab 托管平台,但由于考虑安全原因,必须通过vpn软件才可以访问。公司用的vpn客户端是 Pritunl, 看了一下项目主页 https://github.com/pritunl/pritunl-client-electron,发现它是一个 OpenVPN 客户端,是基于Golang+Electron 框架开发的一个跨平台的客户端。\n以前主要接触的是 wireGuard 和 openvpn 这两个项目,这个客户端还是第一次听说。不过后面发现使用这个客户端会经常出现DNS无法解析的情况,不清楚具体是什么原因引起的,因此不推荐这个客户端。另外从性能方面考虑openvpn也不是推荐方案,推荐优先考虑 wireGuard 这个项目,它的客户端也是跨平台的,而且性能要比openvpn好太多,代码也少很好,无论维护和开发成本都要低的多。个人有很长一段时间一直用wireGuard作为内网穿透方案,不过此方案需要一个公网IP地址,所以后期换成了更节约成本的解决方案,当然这是另一个话题了,不是本文要介绍的内容。\n遇到问题 当在macOS上安装好客户端 …"
May 12, 2024
git 操作中那些常常被忽略的用法
"git merge 与 git rebase 的区别 对于两者的区别,网上已经有很多文章做了介绍,不过有些初学者没有亲自实验过,多数也是作为八股文死记硬背而已。本文为了让大家彻底搞懂两者的区别,所以搞了一个实验环境并模拟了一些真实环境的操作。\n实验环境主要用到两个分支,其中 main 分支做了主要分支,而 dev 作为开发功能分支。\n在生产环境中需要选择合适的分支,这里只是实验环境,所以分支名不是本文关注的重点。\nmain dev 说明 mkdir git-demo \u0026amp;\u0026amp; cd git-demo \u0026amp;\u0026amp; git init touch 1.txt \u0026amp;\u0026amp; git add . \u0026amp;\u0026amp; git commit -m \u0026lsquo;add 1.txt\u0026rsquo; f3c82ba touch 2.txt \u0026amp;\u0026amp; git add . \u0026amp;\u0026amp; git commit -m \u0026lsquo;add 2.txt\u0026rsquo; eb061da touch 3.txt \u0026amp;\u0026amp; git add . …"
April 29, 2024
kubernetes 网络中DNS解析原理
"当我们通过域名(例如 www.example.com)访问一个网站时,第一步就是通过DNS服务器找到目的服务器IP地址(例如 93.184.215.14),接着再将请求数据包发送到这个 IP 服务器。\n而要想通过 DNS 服务器进行域名,必须得先知道 DNS 服务器地址才行,而这一般是通过读取配置文件实现,在 *nux 操作系统中,DNS 服务器一般配置在 /etc/resolv.conf文件,如\nsearch default.svc.cluster.local svc.cluster.local cluster.local nameserver 10.96.0.10 options ndots:5 用户可以通过 nameserver 指定多个 DNS 服务器,依次对域名进行解析,如果解析成功,则解析操作立即中止,如果解析不到的话,则将回退到公网 DNS 服务器进行解析,这个公网 DNS 服务器一般是由网络运营商来定的,用户不需要关心。如果公网 DNS 仍解析失败的话,则直接响应域名无法解析,此时用户将无法正常访问域名。\n什么是 FQDN 在介绍域名解析前, …"
April 22, 2024
Kubernetes集群扩缩容方案
"动态扩缩容主要包括两个层级的动态扩缩容。一个层级是应用本身级别的扩缩容,如HPA、VPA。当应用负载过高时,可以通过HPA多部署几个Pods副本;或者通过VPA对当前Pod硬件资源进行扩容,以此来减少应用负载。\n另一层是对集群自身的扩容,如 worker 节点的扩容。如部署Pods应用时,如果出现无可用节点资源可用时,则通过 Cluster Autoscaler 加入一些新的节点,并在新节点上重建Pods。\n本文主要看一下应用这个层级的扩缩容方案。\n水平扩展HPA \u0026amp;\u0026amp; 垂直扩展VPA HPA 在 Kubernetes 中,HPA(HorizontalPodAutoscaler)也称为水平扩缩容,它将根据当前应用程序工作负载,自动更新工作负载资源 (例如 Deployment 或者 StatefulSet)以满足当前需求。简单讲的话,就是如果集群检测到当前应用程序的n个Pod负载如果比较高的话,就再创建几个Pod副本,以减少当前负载,也就是我们平时说的水平扩容。相反如果应用程序Pod负载比较低的话,则将Pod副本数量进行减少,节省服务器资源,这个就是水平缩容。\n它的工作 …"
April 10, 2024
kubernetes中overlay网络与underlay网络的区别
"Kubernetes 中的 overlay 网络和 underlay 网络是两个不同的网络层面。\nUnderlay 网络 在 Kubernetes 网络架构中,Underlay 网络是指承载 Kubernetes 网络流量的物理网络或底层网络。这个网络通常由物理交换机、路由器和其他网络硬件组成,它们之间通过各种路由协议(例如 OSPF、BGP 等)连接在一起组成的传统网络。\nUnderlay 网络负责为 Kubernetes 节点提供基本的网络连接,它为上层的 overlay 网络提供支持。\n总之,Kubernetes 的网络流量,例如 Pod 到 Pod、Pod 到 Service 等都将在这个 Underlay 网络上进行传输。\nOverlay 网络 对于 Overlay 网络也被称为 覆盖网络,想必只要接触过一点 kubernetes 网络知识的同学都不陌生,它主用来解决 不同节点 中 Pod 之间通讯的一种网络解决方案。 在上图可以看到 Overlay 网络是构建在 underlay 网络之上的一层虚拟网络,它通过封装数据包(如VXLAN)通过物理网络进行传输,到达目标网络后再 …"
March 19, 2024
如何实现访问k8s集群服务之原理
"当我们想将 k8s 集群里的服务向外暴露时,一般是将 k8s service 指定 LoadBalancer类型。目前大多数云厂商会绑定云平台的负载均衡器,并为其分配一个固定的公网 IP 从而向外提供服务。而对于我们自建的 kubernetes 裸机集群则只能选择类似 MetalLB 这类解决方案,这种情况下如何让用户可以通过这个 IP 访问到自建 k8s 的服务呢,本文来分析一下其实现原理。\n本文的环境安装了 MetallB,它指定分配 IP Pool 为内网 IP 地址,实验环境为 https://blog.haohtml.com/posts/install-kubernetes-in-raspberry-pi/。\n将外部请求流入集群节点 当我们需要访问一台机器时,无论是使用 IP 地址还是域名,最终都需要将其解析为 IP 地址。而要真正建立连接并传输数据,我们必须获取目标 IP 地址对应的 MAC 地址,这是由于 TCP/IP 协议分层机制决定的。\n在 TCP/IP 协议栈的链路层,数据是通过封装成数据帧的方式在局域网内传输的。数据帧中包含了目标 MAC 地址,用于标识应该将数据 …"
February 2, 2024
Raspberry Pi 安装Kubernetes
"这里是 arm64 架构,树莓派 4B, 四核八 G 内存 配置,系统为 Ubuntu 22.04.1 LTS\n$ uname -a Linux ubuntu 5.15.0-1049-raspi #52-Ubuntu SMP PREEMPT Thu Mar 14 08:39:42 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux $ cat /etc/issue Ubuntu 22.04.1 LTS \\n \\l 环境检查 由于 k8s 会使用 8080 和 6443 这两个端口,因此要保证端口可用,然后禁用 swap。\nsudo swapoff -a 最后对安装环境初始化,参考 https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/\n安装 Docker 参考 https://docs.docker.com/engine/install/ubuntu/\n安装成功后,修改 cgroupdriver 为 systemd,同时为了国内访问 docker …"
January 12, 2024
使用kubectl create service 命令无法为pod创建service问题
"在做一个试验时,无意中发现使用 kubectl create service 命令无法为一个通过 deployment 创建出来的pod创建对应的 service, 感觉有点奇怪,经过分析才明白怎么回事,这里将过程记录一下。\n这里需要说明一下,本文操作全部是通过 kubectl create 命令来完成的,并没有使用 kubectl apply -f pod.yaml 这种方式。\n这里先创建一个实验命名空间 lab\n$ kubectl create ns lab 首先创建一个deployment 对象\n$ kubectl create deployment test --image=nginx:1.23-alpine --replicas=2 --port=80 -n lab 确认创建成功\n$ kubectl get deploy,pod -n lab NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/test 2/2 2 2 16s NAME DESIRED CURRENT READY AGE …"
December 2, 2023
pod sandbox 创建netns源码分析
"在上一篇《创建Pod源码解析》文中,我们大概介绍了Pod的整体创建过程。其中有一步很重要,就是在创建三类容器之前必须先创建一个 sandbox (源码),本篇就来分析一下sandbox这一块的 netns 实现过程。\n对 sandbox 的创建由 kubelet 组件通过调用 CRI 容器运行时服务来实现的,对于容器运行的实现目前市面上有多个,如 Docker Engine(不推荐)、 containerd、CRI-O 等,由于目前生产环境中选择 containerd 的占大多数,所以这里我们以 containerd 为例来看一下其实现过程。\nhttps://github.com/containerd/containerd/blob/32bf805e5703bc91387d047fa76625e915ac2b80/pkg/cri/server/sandbox_run.go\n对 sandbox 的创建是由 cri 服务调用 RunPodSandbox()方法来实现的。\n// RunPodSandbox creates and starts a pod-level sandbox. …"