October 14, 2024
为 Zed IDE 设置自定义 LLM provider
"\u003cp\u003e在\u003ccode\u003eZed\u003c/code\u003e IDE中,默认只支持以下几种 providers :\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://zed.dev/docs/assistant/configuration#zed-ai\"\u003eZed AI (Configured by default when signed in)\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://zed.dev/docs/assistant/configuration#anthropic\"\u003eAnthropic\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://zed.dev/docs/assistant/configuration#github-copilot-chat\"\u003eGitHub Copilot Chat\u003c/a\u003e \u003ca href=\"https://zed.dev/docs/assistant/configuration#1\"\u003e1\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://zed.dev/docs/assistant/configuration#google-ai\"\u003eGoogle AI\u003c/a\u003e \u003ca href=\"https://zed.dev/docs/assistant/configuration#1\"\u003e1\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://zed.dev/docs/assistant/configuration#ollama\"\u003eOllama\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://zed.dev/docs/assistant/configuration#openai\"\u003eOpenAI\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e这对于国内开发者来说,由于政策原因,想使用起来可能需要借助一些科学上网的方法,这就有点麻烦了。另外国内几个大模型公司也都提供了一定的免费额度的 tokens,如果可以在 Zed 里集成国内几家的大模型,也是一个不错的主意。\u003c/p\u003e\n\u003cp\u003e几个月前试图通过自定义\u003ccode\u003eEndpoint\u003c/code\u003e 的方法绕过官方对使用区域的限制,但一直没有成功。今天重新试了一下仍是无效,本想打算在官方仓库里开发一个自定义provider的功能,就是感觉着有点麻烦,另外更担心个人电脑过旧,编译是一个大问题,于是重新在issue里找到了一个解决办法 \u003ca href=\"https://github.com/zed-industries/zed/pull/13276\"\u003ehttps://github.com/zed-industries/zed/pull/13276\u003c/a\u003e,就是设置起来有点麻烦。\u003c/p\u003e\n\u003cp\u003e本文将其设置方法整理如下。\u003c/p\u003e\n\u003ch1 id=\"配置-settingsjson\"\u003e配置 settings.json\u003c/h1\u003e\n\u003cp\u003e首先配置 settings.json …\u003c/p\u003e"
September 4, 2024
一款基于LLM实现的IDE翻译插件 AI Translate
"\u003cp\u003e\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2024/09/logo.png\" alt=\"logo.png\"\u003e\u003c/p\u003e\n\u003cp\u003eAI Translate 是一款基于LLM实现的IDE专用翻译插件,主要为开发人员在查看项目源码时,可以快速对一些代码英文注释进行中文翻译,这对于英文有些弱的同学很有帮助,下面讲一下为什么开发这款IDE插件,以及它比其它同类翻译插件有什么好处。\u003c/p\u003e\n\u003ch1 id=\"为什么开发这款翻译插件\"\u003e为什么开发这款翻译插件\u003c/h1\u003e\n\u003cp\u003e在以前使用IDE看项目源码时,经常遇到长段的英文段落注释信息,理解起来经常会吃力。当时找了一些翻译插件,但都感觉多多少少有些缺陷。\u003c/p\u003e\n\u003cp\u003e主要表现以下几点:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e翻译质量差,有些翻译软件可能将一些专业术语翻译成了其它名词,整体翻译后的语句读起来很让人头疼\u003c/li\u003e\n\u003cli\u003e由于在源码文件里,多数源码说明 信息都是以注释方式出现的,多数翻译软件无法做到忽略注释符后连贯起来翻译。\u003c/li\u003e\n\u003cli\u003e目前市面比较好用的翻译插件“沉浸式翻译”,可惜只支持浏览器,并不支持IDE\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e曾有一段时间的做法是手动从IDE里复制信息、删除注释符、重新整理段落,再到网页翻译里进行翻译,有时翻译的效果不太好,可能同时会用两个翻译软件。\u003c/p\u003e\n\u003cp\u003e去年看kubernets源码时,再次被折腾一番,当时花了两三个小时开发了一款基于chrome浏览器的插件,主要实现功能是在软件翻译前,将注释符移除和重新 …\u003c/p\u003e"
August 10, 2024
Rust学习教程清单
"\u003cp\u003e今年又一次重新学习RUST这门编程语言,并从零开发了一个kv存储系统 \u003ca href=\"https://github.com/cfanbo/minkv\"\u003eminKV\u003c/a\u003e,慢慢的越来越有感觉了。\u003c/p\u003e\n\u003cp\u003e本篇主要将日常学习中收集的一些入门教程进行一下汇总,希望对于一些想学习这门开发语言的同学有所帮助。\u003c/p\u003e\n\u003cp\u003e下面教程按照推荐顺序,由浅到深依次列出。以下内容将不定期的更新,请自行收藏。 如果您有更多好的教程的话,也可以在评论区列出,大家相互学习。\u003c/p\u003e\n\u003ch1 id=\"入门教程\"\u003e入门教程\u003c/h1\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://doc.rust-lang.org/book/\"\u003eRust 程序设计语言 https://doc.rust-lang.org/book/\u003c/a\u003e / (\u003ca href=\"https://kaisery.github.io/trpl-zh-cn/\"\u003e中文版\u003c/a\u003e)\u003c/p\u003e\n\u003cp\u003e官方教程,强烈推荐,同时还有非官方翻译的中文版。遗憾的是这个教程有许多概念介绍的都有点不清不楚,只能通过下方的一些资料自行补习。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://play.rust-lang.org/\"\u003ehttps://play.rust-lang.org/\u003c/a\u003e 在线 RUST 程序 Playground,类似golang的 Playground,非常的方便\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://cheats.rs/#data-layout\"\u003eRust Language Cheat Sheet\u003c/a\u003e 看完官方的教程后,紧接着就看这篇,先了解一些内存布局,后面再看其它教程就更容易理解了\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://doc.rust-lang.org/cargo/index.html#the-cargo-book\"\u003eThe Cargo Book\u003c/a\u003e Cargo 是RUST 中的包管理工具, …\u003c/p\u003e\u003c/li\u003e\u003c/ol\u003e"
August 10, 2024
在rust中实现自定义错误
"\u003cp\u003e\u003ca href=\"https://blog.haohtml.com/posts/error-hanlding-unwrap-and-expect-in-rust/#google_vignette\"\u003e上一篇\u003c/a\u003e 我们介绍了一些错误处理的最基本的用法,主要是指对 \u003ccode\u003epanic!\u003c/code\u003e 、\u003ccode\u003eunwrap\u003c/code\u003e、\u003ccode\u003eexpect\u003c/code\u003e 和 \u003ccode\u003e?\u003c/code\u003e 这些宏或函数的介绍。但这仅仅是一些最基本的处理方法,对于自定义错误这一块并没有做任何介绍。\u003c/p\u003e\n\u003cp\u003e实际开发中可能默认的错误类型,并无法满足我们的业务需求,这时一般需要通过定义自己的错误类型来实现。在rust中错误类型是通过 \u003ccode\u003eenum\u003c/code\u003e 枚举定义的,对此官方文档也做了一些简介,本文主要介绍一些业务开发过程中对错误的处理方案,当然主要是一些最基本的用法。\u003c/p\u003e\n\u003ch1 id=\"自定义-error\"\u003e自定义 Error\u003c/h1\u003e\n\u003cp\u003e在 Rust 中,自定义错误类型是一种常见的类型,特别是当你需要提供比标准错误类型更具体的错误信息时。Rust 中的错误处理是通过 \u003ccode\u003eResult\u003c/code\u003e 和 \u003ccode\u003eError\u003c/code\u003e trait 来实现的。以下是如何实现一个自定义错误的示例:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e定义一个错误枚举类型。\u003c/li\u003e\n\u003cli\u003e实现 \u003ccode\u003estd::fmt::Display\u003c/code\u003e 为自定义错误提供用户友好的错误信息。\u003c/li\u003e\n\u003cli\u003e实现 \u003ccode\u003estd::error::Error\u003c/code\u003e trait,这通常是通过派生 \u003ccode\u003eError\u003c/code\u003e trait 来完成的。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e下面是一个简单的示例:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003euse\u003c/span\u003e std::fmt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003euse …\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"
July 27, 2024
一款管理 .gitignore 的CLI工具- gitig
"\u003cp\u003e\u003ccode\u003egitig\u003c/code\u003e 是一款基于 \u003ca href=\"https://github.com/github/gitgnore\"\u003ehttps://github.com/github/gitgnore\u003c/a\u003e 仓库开发的\u003ccode\u003e.gitignore\u003c/code\u003e 客户端CLI 管理工具,也是每个开发者必不可少的提高工作效率的必具工具。\u003c/p\u003e\n\u003cp\u003e它基于官方仓库 \u003ca href=\"https://github.com/github/gitgnore\"\u003ehttps://github.com/github/gitgnore\u003c/a\u003e 丰富的 \u003ccode\u003e.gitignore\u003c/code\u003e 数据源,帮助开发者快速实现添加各类开发项目的git版本控制忽略文件清单。\u003c/p\u003e\n\u003ch1 id=\"开发背景\"\u003e开发背景\u003c/h1\u003e\n\u003cp\u003e工作中,经常需要开发各类项目,如基于 vscode 编写 rust 项目,这时为了方便进行Git管理控制,有些项目文件可能并不需要提交到git仓库,需要将一些文件写入 \u003ccode\u003e.gitignore\u003c/code\u003e 文件进行忽略。\u003c/p\u003e\n\u003cp\u003e如果手动编辑 \u003ccode\u003e.gitignore\u003c/code\u003e文件可能有些麻烦,另外也能会有一些文件项被遗忘或写错,这时如果有一些工具可以将行业能用的忽略配置项一键写入 \u003ccode\u003e.gitignore\u003c/code\u003e 文件似乎是一个不错的主意。\u003c/p\u003e\n\u003cp\u003e其中著名的 \u003ca href=\"https://github.com/github/gitignore\"\u003ehttps://github.com/github/gitignore\u003c/a\u003e 就是一个专门收集各类开发语句或IDE 需要忽略的 \u003ccode\u003e.gitignore\u003c/code\u003e 推荐配置的仓库,目前star …\u003c/p\u003e"
July 15, 2024
Rust 中常见的几种错误处理方法
"\u003cp\u003eRust 中错误可分为两大类:\u003cstrong\u003e可恢复的\u003c/strong\u003e(\u003cem\u003erecoverable\u003c/em\u003e)和 \u003cstrong\u003e不可恢复的\u003c/strong\u003e(\u003cem\u003eunrecoverable\u003c/em\u003e)错误。\u003c/p\u003e\n\u003cp\u003e对于一个可恢复的错误,比如文件未找到或权限不足的错误,我们很可能只想向用户报告问题,让用户来决定后续操作。\u003c/p\u003e\n\u003cp\u003e不可恢复的错误总是 bug 出现的征兆,比如试图访问一个超过数组末端的位置,因此我们要立即停止程序,主要通过 \u003ccode\u003epanic!\u003c/code\u003e 实现。\u003c/p\u003e\n\u003ch1 id=\"panic\"\u003epanic!\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003epanic!\u003c/code\u003e属于不可恢复错误类型,一旦发生程序将立即退出。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#a6e22e\"\u003epanic!\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;crash and burn\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这里由用户调用 \u003ccode\u003epanic!\u003c/code\u003e 宏来实现程序的中止,同时自定义错误信息。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e➜ cargo run\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e Compiling hello-world v0.1.0 \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e/Users/sxf/workspace/rust/hello-world\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e Finished \u003cspan style=\"color:#e6db74\"\u003e`\u003c/span\u003edev\u003cspan style=\"color:#e6db74\"\u003e`\u003c/span\u003e profile \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eunoptimized + debuginfo\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e target\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003es\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e in 0.68s\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e Running \u003cspan style=\"color:#e6db74\"\u003e`\u003c/span\u003etarget/debug/hello-world\u003cspan style=\"color:#e6db74\"\u003e` …\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"
July 10, 2024
在Rust中如何调用一个模块或方法
"\u003cp\u003e在 Rust 中有 \u003ccode\u003e包\u003c/code\u003e、\u003ccode\u003ecrate\u003c/code\u003e、\u003ccode\u003e模块\u003c/code\u003e 概念,本文我们介绍一下它们之间的关系和调用方法。\u003c/p\u003e\n\u003ch1 id=\"包-和-crate\"\u003e包 和 Crate\u003c/h1\u003e\n\u003cp\u003e在Rust中,\u003cem\u003e包\u003c/em\u003e(\u003cem\u003epackage\u003c/em\u003e)是提供一系列功能的一个或者多个 crate。一个包会包含一个 \u003ccode\u003eCargo.toml\u003c/code\u003e 文件,阐述如何去构建这些 crate。\u003c/p\u003e\n\u003cp\u003e我们先看一下通过 \u003ccode\u003ecargo new\u003c/code\u003e 创建一个 \u003ccode\u003emy_project\u003c/code\u003e 包。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e➜ cargo new my_project \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e Creating binary \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eapplication\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`\u003c/span\u003emy_project\u003cspan style=\"color:#e6db74\"\u003e`\u003c/span\u003e package\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enote: see more \u003cspan style=\"color:#e6db74\"\u003e`\u003c/span\u003eCargo.toml\u003cspan style=\"color:#e6db74\"\u003e`\u003c/span\u003e keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e➜ rust tree my_project \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emy_project\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e├── Cargo.toml\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e└── src\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e └── main.rs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e directories, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e files\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e它将创建一个 \u003ccode\u003eCargo.toml\u003c/code\u003e文件,内容:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[ …\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"
June 25, 2024
Rust中将一个结构体拆分成多个文件
"\u003cp\u003e官方文档将\u003ca href=\"https://kaisery.github.io/trpl-zh-cn/ch07-05-separating-modules-into-different-files.html\"\u003e一个模块拆分成多个文件\u003c/a\u003e时,介绍的是将原来多个模块写在同一个文件中,拆分成了每个模块一个文件。不过还有一种情况没有提到,如果一个模块中的某个 struct 实现代码过多时,仍写在同一个模块文件的话,维护成本就显的比较高了,这时我们可能还需要对这个 struct 的实现按某种粒度拆分成多个文件来实现。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e✗ tree\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e├── main.rs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e├── model\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e│ ├── article.rs // 文章相关\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e│ └── user.rs // 用户相关\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e└── model.rs\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这里是按官方教程拆分后的样子\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003earticle.rs\u003c/code\u003e 是文件模块相关实现 -\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003euser.rs\u003c/code\u003e 是与用户相关的实现\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003emodel.rs\u003c/code\u003e 公开模块\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ccode\u003emodel.rs \u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-rust\" data-lang=\"rust\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// src/model.rs\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003emod\u003c/span\u003e article;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epub\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003emod\u003c/span\u003e user;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003epub\u003c/code\u003e 关键字表示该模块是公开的,可以被其他模块访问。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003emod article\u003c/code\u003e 声明了一个名为 \u003ccode\u003earticle\u003c/code\u003e 的模块,并且 Rust 编译器会在同文件名的目录下( \u003ccode\u003esrc/model/\u003c/code\u003e )找到一个名为 \u003ccode\u003earticle.rs …\u003c/code\u003e\u003c/p\u003e"
May 13, 2024
记录一次排查vpn无法通讯的过程
"\u003cp\u003e今天收到一个项目重构的需求,项目源码使用私有 gitlab 托管平台,但由于考虑安全原因,必须通过vpn软件才可以访问。公司用的vpn客户端是 \u003ca href=\"https://github.com/pritunl/pritunl-client-electron\"\u003ePritunl\u003c/a\u003e, 看了一下项目主页 \u003ca href=\"https://github.com/pritunl/pritunl-client-electron\"\u003ehttps://github.com/pritunl/pritunl-client-electron\u003c/a\u003e,发现它是一个 OpenVPN 客户端,是基于Golang+Electron 框架开发的一个跨平台的客户端。\u003c/p\u003e\n\u003cp\u003e以前主要接触的是 \u003ca href=\"https://github.com/WireGuard/wireguard-go\"\u003ewireGuard\u003c/a\u003e 和 \u003ca href=\"https://github.com/OpenVPN/openvpn\"\u003eopenvpn\u003c/a\u003e 这两个项目,这个客户端还是第一次听说。不过后面发现使用这个客户端会经常出现DNS无法解析的情况,不清楚具体是什么原因引起的,因此不推荐这个客户端。另外从性能方面考虑openvpn也不是推荐方案,推荐优先考虑 wireGuard 这个项目,它的客户端也是跨平台的,而且性能要比openvpn好太多,代码也少很好,无论维护和开发成本都要低的多。个人有很长一段时间一直用wireGuard作为内网穿透方案,不过此方案需要一个公网IP地址,所以后期换成了更节约成本的解决方案,当然这是另一个话题了,不是本文要介绍的内容。\u003c/p\u003e\n\u003ch1 id=\"遇到问题\"\u003e遇到问题\u003c/h1\u003e\n\u003cp\u003e当在macOS上安装好客户 …\u003c/p\u003e"
May 12, 2024
git 操作中那些常常被忽略的用法
"\u003ch1 id=\"git-merge-与-git-rebase--的区别\"\u003egit merge 与 git rebase 的区别\u003c/h1\u003e\n\u003cp\u003e对于两者的区别,网上已经有很多文章做了介绍,不过有些初学者没有亲自实验过,多数也是作为八股文死记硬背而已。本文为了让大家彻底搞懂两者的区别,所以搞了一个实验环境并模拟了一些真实环境的操作。\u003c/p\u003e\n\u003cp\u003e实验环境主要用到两个分支,其中 main 分支做了主要分支,而 dev 作为开发功能分支。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e在生产环境中需要选择合适的分支,这里只是实验环境,所以分支名不是本文关注的重点。\u003c/p\u003e\u003c/blockquote\u003e\n\u003ctable\u003e\n \u003cthead\u003e\n \u003ctr\u003e\n \u003cth\u003emain\u003c/th\u003e\n \u003cth\u003edev\u003c/th\u003e\n \u003cth\u003e说明\u003c/th\u003e\n \u003c/tr\u003e\n \u003c/thead\u003e\n \u003ctbody\u003e\n \u003ctr\u003e\n \u003ctd\u003emkdir git-demo \u0026amp;\u0026amp; cd git-demo \u0026amp;\u0026amp; git init\u003c/td\u003e\n \u003ctd\u003e\u003c/td\u003e\n \u003ctd\u003e\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n \u003ctd\u003etouch 1.txt \u0026amp;\u0026amp; git add . \u0026amp;\u0026amp; git commit -m \u0026lsquo;add 1.txt\u0026rsquo; …\u003c/td\u003e\u003c/tr\u003e\u003c/tbody\u003e\u003c/table\u003e"
April 29, 2024
kubernetes 网络中DNS解析原理
"\u003cp\u003e当我们通过域名(例如 \u003ca href=\"https://www.example.com\"\u003ewww.example.com\u003c/a\u003e)访问一个网站时,第一步就是通过DNS服务器找到目的服务器IP地址(例如 93.184.215.14),接着再将请求数据包发送到这个 IP 服务器。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2024/04/watermark%2Ctype_ZmFuZ3poZW5naGVpdGk%2Cshadow_10%2Ctext_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDMwNzcyMQ%3D%3D%2Csize_16%2Ccolor_FFFFFF%2Ct_70.jpeg\" alt=\"img\"\u003e\u003c/p\u003e\n\u003cp\u003e而要想通过 DNS 服务器进行域名,必须得先知道 DNS 服务器地址才行,而这一般是通过读取配置文件实现,在 \u003ccode\u003e*nux\u003c/code\u003e 操作系统中,DNS 服务器一般配置在 \u003ccode\u003e/etc/resolv.conf\u003c/code\u003e文件,如\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-ini\" data-lang=\"ini\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esearch default.svc.cluster.local svc.cluster.local cluster.local\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003enameserver 10.96.0.10\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eoptions ndots:5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e用户可以通过 nameserver 指定多个 DNS 服务器,依次对域名进行解析,如果解析成功,则解析操作立即中止,如果解析不到的话,则将回退到公网 DNS 服务器进行解析,这个公网 DNS 服务器一般是由网络运营商来定的,用户不需要关心。如果公网 DNS 仍解析失败的话,则直接响应域名无法解析,此时用户将无法正常访问域名。\u003c/p\u003e\n\u003ch1 id=\"什么是-fqdn\"\u003e什么是 FQDN\u003c/h1\u003e\n\u003cp\u003e在介绍域名解析前, …\u003c/p\u003e"
April 22, 2024
Kubernetes集群扩缩容方案
"\u003cp\u003e动态扩缩容主要包括两个层级的动态扩缩容。一个层级是应用本身级别的扩缩容,如HPA、VPA。当应用负载过高时,可以通过HPA多部署几个Pods副本;或者通过VPA对当前Pod硬件资源进行扩容,以此来减少应用负载。\u003c/p\u003e\n\u003cp\u003e另一层是对集群自身的扩容,如 worker 节点的扩容。如部署Pods应用时,如果出现无可用节点资源可用时,则通过 Cluster Autoscaler 加入一些新的节点,并在新节点上重建Pods。\u003c/p\u003e\n\u003cp\u003e本文主要看一下应用这个层级的扩缩容方案。\u003c/p\u003e\n\u003ch1 id=\"水平扩展hpa--垂直扩展vpa\"\u003e水平扩展HPA \u0026amp;\u0026amp; 垂直扩展VPA\u003c/h1\u003e\n\u003ch2 id=\"hpa\"\u003eHPA\u003c/h2\u003e\n\u003cp\u003e在 Kubernetes 中,\u003ca href=\"https://kubernetes.io/zh-cn/docs/tasks/run-application/horizontal-pod-autoscale/\"\u003eHPA\u003c/a\u003e(HorizontalPodAutoscaler)也称为水平扩缩容,它将根据当前应用程序工作负载,自动更新工作负载资源 (例如 \u003ca href=\"https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/deployment/\"\u003eDeployment\u003c/a\u003e 或者 \u003ca href=\"https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/statefulset/\"\u003eStatefulSet\u003c/a\u003e)以满足当前需求。简单讲的话,就是如果集群检测到当前应用程序的n个Pod负载如果比较高的话,就再创建几个Pod副本,以减少当前负载,也就是我们平时说的水平扩容。相反如果应用程序Pod负载比较低的话,则将Pod副本数量进行减少,节省服务器资源,这个就是水平缩容。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://blogstatic.haohtml.com//uploads/2024/04/image-20240424145020517.png\" alt=\"image-20240424145020517\"\u003e\u003c/p\u003e\n\u003cp\u003e它的工 …\u003c/p\u003e"
April 10, 2024
kubernetes中overlay网络与underlay网络的区别
"\u003cp\u003eKubernetes 中的 overlay 网络和 underlay 网络是两个不同的网络层面。\u003c/p\u003e\n\u003ch1 id=\"underlay-网络\"\u003eUnderlay 网络\u003c/h1\u003e\n\u003cp\u003e在 Kubernetes 网络架构中,Underlay 网络是指承载 Kubernetes 网络流量的物理网络或底层网络。这个网络通常由物理交换机、路由器和其他网络硬件组成,它们之间通过各种路由协议(例如 OSPF、BGP 等)连接在一起组成的传统网络。\u003c/p\u003e\n\u003cp\u003eUnderlay 网络负责为 Kubernetes 节点提供基本的网络连接,它为上层的 overlay 网络提供支持。\u003c/p\u003e\n\u003cp\u003e总之,Kubernetes 的网络流量,例如 Pod 到 Pod、Pod 到 Service 等都将在这个 Underlay 网络上进行传输。\u003c/p\u003e\n\u003ch1 id=\"overlay-网络\"\u003eOverlay 网络\u003c/h1\u003e\n\u003cp\u003e对于 \u003ccode\u003eOverlay\u003c/code\u003e 网络也被称为 \u003ccode\u003e覆盖网络\u003c/code\u003e,想必只要接触过一点 kubernetes 网络知识的同学都不陌生,它主用来解决 \u003cstrong\u003e不同节点\u003c/strong\u003e 中 \u003cstrong\u003ePod\u003c/strong\u003e 之间通讯的一种网络解决方案。\n\u003cimg src=\"https://blogstatic.haohtml.com//uploads/2024/04/image_1chvp7s4g15n0134iv1tvag1mkd8g.png\" alt=\"Overlay\"\u003e\u003c/p\u003e\n\u003cp\u003e在上图可以看到 \u003ccode\u003eOverlay\u003c/code\u003e 网络是构建在 \u003ccode\u003eunderlay\u003c/code\u003e 网络之上的一层虚拟网络,它通过封装数据包(如VXLAN)通过物理网络进行传输,到达目标网络 …\u003c/p\u003e"
March 19, 2024
如何实现访问k8s集群服务之原理
"\u003cp\u003e当我们想将 k8s 集群里的服务向外暴露时,一般是将 k8s service 指定 \u003ccode\u003eLoadBalancer\u003c/code\u003e类型。目前大多数云厂商会绑定云平台的负载均衡器,并为其分配一个固定的公网 IP 从而向外提供服务。而对于我们自建的 kubernetes 裸机集群则只能选择类似 MetalLB 这类解决方案,这种情况下如何让用户可以通过这个 IP 访问到自建 k8s 的服务呢,本文来分析一下其实现原理。\u003c/p\u003e\n\u003cp\u003e本文的环境安装了 MetallB,它指定分配 IP Pool 为内网 IP 地址,实验环境为 \u003ca href=\"https://blog.haohtml.com/posts/install-kubernetes-in-raspberry-pi/\"\u003ehttps://blog.haohtml.com/posts/install-kubernetes-in-raspberry-pi/\u003c/a\u003e。\u003c/p\u003e\n\u003ch1 id=\"将外部请求流入集群节点\"\u003e将外部请求流入集群节点\u003c/h1\u003e\n\u003cp\u003e当我们需要访问一台机器时,无论是使用 IP 地址还是域名,最终都需要将其解析为 IP 地址。而要真正建立连接并传输数据,我们必须获取目标 IP 地址对应的 MAC 地址,这是由于 TCP/IP 协议分层机制决定的。\u003c/p\u003e\n\u003cp\u003e在 TCP/IP 协议栈的链路层,数据是通过封装成数据帧的方式在局域网内传输的。数据帧中包含了目标 MAC 地址,用于标识应该将数据 …\u003c/p\u003e"
February 2, 2024
Raspberry Pi 安装Kubernetes
"\u003cp\u003e这里是 arm64 架构,\u003cstrong\u003e树莓派 4B\u003c/strong\u003e, 四核八 G 内存 配置,系统为 Ubuntu 22.04.1 LTS\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ uname -a\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eLinux ubuntu 5.15.0-1049-raspi \u003cspan style=\"color:#75715e\"\u003e#52-Ubuntu SMP PREEMPT Thu Mar 14 08:39:42 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ cat /etc/issue\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eUbuntu 22.04.1 LTS \u003cspan style=\"color:#ae81ff\"\u003e\\n\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\l\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"环境检查\"\u003e环境检查\u003c/h2\u003e\n\u003cp\u003e由于 k8s 会使用 8080 和 6443 这两个端口,因此要保证端口可用,然后禁用 swap。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esudo swapoff -a\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e最后对安装环境初始化,参考 \u003ca href=\"https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/\"\u003ehttps://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"安装-docker\"\u003e安装 Docker\u003c/h2\u003e\n\u003cp\u003e参考 \u003ca href=\"https://docs.docker.com/engine/install/ubuntu/\"\u003ehttps://docs.docker.com/engine/install/ubuntu/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e安装成功后,修改 \u003ccode\u003ecgroupdriver\u003c/code\u003e 为 \u003ccode\u003esystemd\u003c/code\u003e, …\u003c/p\u003e"