跳到主要内容

Go 切片缩容

· 阅读需 3 分钟
素明诚
Full stack development

在 Go 中,由于切片底层使用数组实现,真正的缩容通常指的是减少切片的容量,以释放不需要的内存。这通常通过创建一个新的切片并复制所需数据来实现。

创建新切片并拷贝数据

通过创建一个新的切片,并且将旧切片的数据复制到这个新的切片中。新切片的容量和长度被精确控制。

注意,这里使用make([]Type, len, cap)创建新的切片时,Go 运行时会分配一个全新的底层数组。

这种方式是在你永远不会让这个切片再扩容的时候使用。

func ShrinkSlice(original []int, newLength int) []int {
if newLength > len(original) {
newLength = len(original)
}
newSlice := make([]int, newLength, newLength) // 设置新切片的长度和容量
copy(newSlice, original[:newLength])
return newSlice
}

利用 append 函数

append 函数可以在增加切片长度的同时控制其容量。如果在使用 append 时开始一个新的切片(nil 切片),Go 的编译器和运行时会优化内存分配,通常只会分配到所需的容量。

这里append函数在添加元素超过原始切片容量时会自动分配新的底层数组,并复制原始数据到新数组。即使开始时指定了容量,append操作仍可能触发新数组的创建,这是为了保证切片的扩展不会影响到其他依赖原数组的切片。

func ShrinkSliceUsingAppend(original []int, newLength int) []int {
if newLength > len(original) {
newLength = len(original)
}
newSlice := make([]int, 0, newLength) // 创建一个容量为newLength的空切片
newSlice = append(newSlice, original[:newLength]...)
return newSlice
}

使用全局切片重新切片

如果不担心原始数据的保护,可以通过重新切片(reslicing)操作直接在原切片上操作,这种方法不需要额外的内存分配,但是需要手动管理原始数据。(使用全局切片重新切片)会保持与原始底层数组的直接联系,而前两种方法都会创建一个新的底层数组。

func ShrinkSliceInPlace(original []int, newLength int) []int {
if newLength > len(original) {
newLength = len(original)
}
return original[:newLength:newLength] // 设置新切片的长度和容量相同
}

缩容后,尽量少的使用 CPU

整个实现的核心是希望在后续少触发扩容的前提下,一次性释放尽可能多的内存

func calCapacity(c, l int) (int, bool) { // usage = Deng Ming
// 容量 <=64 缩不缩都无所谓, 因为浪费内存也浪费不了多少
// 你可以考虑调大这个阈值, 或者调小这个阈值
if c <= 64 {
return c, false
}
// 如果容量大于 2048, 但是元素不足一半,
// 降低为 0.625, 也就是 5/8
// 也就是比一半多一点, 和正向扩容的 1.25 倍相呼应
if c > 2048 && (c/l >= 2) {
factor := 0.625
return int(float32(c) * float32(factor)), true
}
// 如果在 2048 以内, 并且元素不足 1/4, 那么直接缩减为一半
if c <= 2048 && (c/l >= 4) {
return c / 2, true
}
return c, false
}

Vault 初始化与配置

· 阅读需 2 分钟
素明诚
Full stack development

docker-compose 启动

https://github.com/sumingcheng/dev-tools/blob/main/vault/docker-compose.yml### 初始化 Vault

在首次启动 Vault 后,需要进行初始化。这个过程会生成解封密钥和根令牌。

使用以下命令进行初始化,也可以进入容器进行初始化

docker exec -it vault vault operator init

记录下显示的解封密钥和根令牌。这些信息非常重要,必须安全保存。

a116a96b7b77fa3b896cce801dbc7ca7### 解封 Vault

使用初始化时获得的解封密钥解封 Vault。需要至少提供初始化时指定的阈值数量的密钥。

docker exec -it vault vault operator unseal <Unseal_Key>

8fa7c1f92c01241f46656fcf3431b123

图片里给了 5 个 KEY,只要使用其中 3 个即可,重复上述命令,直到 Vault 显示为解封状态。如果 docker 完全重启的话,可能还需要重新解封。

配置环境变量

确保设置正确的环境变量,特别是 VAULT_ADDR,以便您的应用和服务能够正确地与 Vault 通信。

登录到 Vault UI

打开浏览器访问 Vault UI(通常是 http://IP:8000)。

使用初始化生成的根令牌登录。如果 Vault UI 配置了默认的 VAULT_TOKEN_DEFAULT 令牌并且有效,也可以尝试使用它登录。如果无法登录可以先试使用root key 邓肯

配置访问控制和策略

通过 UI 或命令行配置访问控制列表 (ACL) 策略,以定义谁可以访问什么数据。

创建额外的用户和令牌,以便其他系统和用户能够访问 Vault。

JavaScript 的可选链操作符

· 阅读需 1 分钟
素明诚
Full stack development

可选链操作符 (?.) 允许开发者在访问深层嵌套对象属性时无需显式验证每一层的存在性。这大大简化了从复杂对象中提取值的过程,尤其是在属性可能未定义的情况下。该特性在 ECMAScript 2020 中正式引入,之前开发者需要进行多层检查以防运行时错误。

可选链操作符 (?.) 的应用场景

对象属性访问

当访问链中的某个中间属性不存在时,不会引发错误,而是使表达式短路并返回 undefined

let nestedProp = obj.first?.second;

此处,如果 obj.firstnullundefined,则 nestedProp 返回 undefined

数组索引访问

let item = arr?.[42];

表达式安全地访问数组的第 43 项,不必担心 arr 可能为 nullundefined

函数或方法调用

如果尝试调用的函数不存在,表达式会短路并返回 undefined,而不抛出错误。

obj.func?.();

如果 obj.func 存在且为函数,则执行调用;如果为 undefinednull,则不执行并返回 undefined

Linux tree 命令详解

· 阅读需 2 分钟
素明诚
Full stack development

tree 命令是一个在命令行环境中用于展示文件和目录结构的工具,它以树状图形式列出文件和目录。

选项描述
-L level限制目录树的显示层数。
-a显示所有文件和目录,包括隐藏文件。
-d仅显示目录。
-f显示每个文件或目录的完整路径。
-i不显示树状图的缩进线,只列出文件名。
-p显示文件或目录的权限。
-u显示文件或目录的所有者。
-g显示文件或目录的组。
-s显示文件大小(以字节为单位)。
-h以易读的方式(如 KB、MB)显示文件大小。
-C在终端使用颜色高亮显示。
-P pattern只显示匹配给定模式的文件或目录。
-I pattern排除匹配给定模式的文件或目录。
-J输出为 JSON 格式。

美观的方式显示结果

为了使输出结果更美观,可以使用 -C 选项来启用颜色高亮显示,这有助于区分不同类型的条目(如目录、文件等)。

tree -C

此命令将以彩色格式输出当前目录及其子目录的结构

仅查看文件夹

如果你只想查看目录结构而不包括文件

tree -d

输出到文件 tree.txt

如果你想将 tree 的输出保存到一个文件中,可以将输出重定向到一个文件。这可以通过在命令行中使用重定向操作符 > 来实现。

tree > tree.txt

这个命令将当前目录的树状结构输出到 tree.txt 文件中。如果你希望输出更具可读性,可以结合之前的选项

tree -C > tree.txt

Ubuntu 22044 LTS x86 64 手动安装 Docker

· 阅读需 1 分钟
素明诚
Full stack development

访问 Docker 的官方仓库索引页面以找到 Ubuntu 22.04 对应的软件包

https://download.docker.com/linux/ubuntu/dists/

在页面中点击 jammy 目录,这是 Ubuntu 22.04 LTS 的代码名。接下来,进入 pool/ 目录,然后选择 stable/ 目录,并选择 amd64/ 目录

https://download.docker.com/linux/ubuntu/dists/jammy/pool/stable/amd64/

在这个目录下,您需要下载以下文件,版本自己选择

  • containerd.io_<version>_amd64.deb
  • docker-ce-cli_<version>~ubuntu_jammy_amd64.deb
  • docker-ce_<version>~ubuntu_jammy_amd64.deb

下载这些 .deb 文件后,进入下载的目录后,使用下面的命令在您的 Ubuntu 系统上安装这些文件

sudo dpkg -i containerd.io_*.deb
sudo dpkg -i docker-ce-cli_*.deb
sudo dpkg -i docker-ce_*.deb

如果安装过程中遇到依赖问题,可以运行以下命令来解决

sudo apt-get install -f

完成安装后,启动 Docker 服务并设置为开机启动

sudo systemctl start docker
sudo systemctl enable docker

最后,运行一个简单的测试来验证 Docker 是否安装成功

sudo docker ps

Ubuntu 如何完全重新安装某个应用

· 阅读需 1 分钟
素明诚
Full stack development

以 fzf 这个工具为例,完全重新安装 fzf

卸载 fzf 首先,彻底卸载 fzf,包括其所有配置文件

sudo apt remove --purge fzf

清理残留的依赖 运行自动清理命令,移除无用的包和依赖

sudo apt autoremove

更新软件包列表 更新您的软件包列表,确保能够从仓库获取到最新信息

sudo apt update

重新安装 fzf 再次安装 fzf

sudo apt install fzf

验证安装 安装完成后,检查 fzf 是否可以正常运行

fzf --version

如果在重新安装过程中发现任何问题,或者问题依然存在,请检查是否有环境变量问题或路径问题影响到了可执行文件的查找。您还可以检查 /usr/bin 目录,看看 fzf 的可执行文件是否确实存在于那里

ls -l /usr/bin/fzf

如果上述步骤都不能解决问题,可能需要考虑系统环境问题更深层次的检查,例如,确认 /bin/usr/bin 是否在环境变量的 PATH

echo $PATH

Linux 配置和使用 Screen

· 阅读需 2 分钟
素明诚
Full stack development

安装 Screen

使用以下命令安装screen

# Debian/Ubuntu
sudo apt-get install screen

# CentOS/RHEL
sudo yum install screen

# Arch Linux
sudo pacman -S screen

启动和管理 Screen 会话

命令功能
screen启动新会话
screen -S session_name启动命名会话
screen -ls列出所有会话
screen -r session_name重新连接会话
Ctrl + a, d分离当前会话

窗口管理

命令功能
Ctrl + a, c新建窗口
Ctrl + a, n切换到下一个窗口
Ctrl + a, p切换到上一个窗口
Ctrl + a, w列出所有窗口
Ctrl + a, k关闭当前窗口

分割窗口

命令功能
Ctrl + a, S水平分割窗口
Ctrl + a, Tab切换分区
Ctrl + a, Q移除所有分割

复制和粘贴

命令功能
Ctrl + a, [进入复制模式
Space开始选择文本(再次按 Space 结束选择并复制)
Ctrl + a, ]粘贴文本

锁定和帮助

命令功能
Ctrl + a, x锁定会话
Ctrl + a, ?显示帮助信息

退出 Screen

命令功能
exit退出会话
Ctrl + a, K强制终止会话

自定义配置

编辑~/.screenrc文件以自定义screen的行为。示例配置启用垂直分割和设置快捷键

bind | split -v
bind - resize =

Git tag 创建提交删除

· 阅读需 1 分钟
素明诚
Full stack development

git tag 是 Git 版本控制系统中用于标记特定提交的命令。它允许你在代码仓库的历史中的某一点创建一个易于记忆的句柄,代表特定的重要版本。标签(tags)在项目管理中非常有用,尤其是在发布软件版本时。

创建并推送标签

# 创建新标签
git tag -a v1.0.1 -m "v1.0.1"

# 推送标签到远程
git push origin v1.0.1

删除本地和远程标签

# 删除本地标签
git tag -d v1.0.1

# 删除远程标签
git push --delete origin v1.0.1

查看所有标签

git tag

发布 Docker 镜像到 GitHub Packages

· 阅读需 2 分钟
素明诚
Full stack development

1.准备你的项目

确保你的项目包含所有必需的文件,例如Dockerfile(对于 Docker 镜像)、并且你已经在本地测试镜像构建成功。

确定你的项目结构,你的Dockerfile支持版本控制。

2.配置 GitHub Actions

创建工作流文件

在你的 GitHub 仓库中的 .github/workflows 目录下创建一个 YAML 工作流文件。

name: Publish Docker image

on:
push:
branches:
- main
tags:
- 'v*'

jobs:
build-and-push:
runs-on: ubuntu-22.04
steps:
- name: Check out the repo
uses: actions/checkout@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Log in to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.PAT_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
push: true
tags: ghcr.io/${{ github.repository_owner }}/gin-blog-template:${{ github.ref_name }}
build-args: VERSION=${{ github.ref_name }}

定义触发条件

配置工作流触发条件,比如在推送到特定分支或创建标签时触发。我配置的是提交tag到主分支后触发构建镜像的流程

配置环境变量,设置 GitHub Secrets

这里需要你仓库的一些权限,需要你生成PAT_TOKEN确保你有以下权限

  • write:packages (允许写入包)
  • delete:packages (可选,允许删除包)
  • repo (允许访问私有仓库,如果您的仓库是私有的)

创建令牌

3cb98ce51325c66ec3f995bf6d96319d### 添加到仓库的环境变量
238f091e27596b2f8551428ee5efed9e### 提交要构建的 tag 并推送

创建注释标签

git tag -a v1.0 -m "Release version 1.0"

查看所有标签

git tag

推送标签到远程仓库

git push origin v1.0

构建成功后你可以看到你的镜像

https://github.com/sumingcheng/gin-blog-template/pkgs/container/gin-blog-template

febd41c887cb836369ca768d26d3d3f3### 3.设置包的可见性(可选)

一旦包被推送到 GitHub Packages,你可能需要设置其可见性(公开或私有)。这可以在 GitHub 界面中完成,通过修改包的设置。

a42761d361813deed0775762a8a2bf22

修改可见性

e73a6d6c637e29d6af34554f689e8b07

修改为公开的

4.链接到仓库(可选)

如果需要,你可以将包链接到一个特定的 GitHub 仓库,以便更好地集成和展示。直接链接到你想展示的仓库,创建即可。

3b7fdbc667d8671a178d7d0600779192### 5.验证和测试

验证包是否已正确发布到 GitHub Packages,并测试是否可以正常下载和使用。

52170a3fec321082944d9904d532b1cf

Go strconv 包的主要功能

· 阅读需 2 分钟
素明诚
Full stack development

整数转换

Atoi(ASCII to Integer)将字符串转换为整数

Itoa(Integer to ASCII)将整数转换为字符串

ParseInt将字符串解析为指定基数(进制)的整数

FormatInt将整数格式化为字符串,可指定基数

浮点数转换

ParseFloat将字符串解析为浮点数,可以指定精度(32 位或 64 位)

FormatFloat将浮点数格式化为字符串,可以指定格式和精度

布尔值转换

ParseBool将字符串解析为布尔值

FormatBool将布尔值格式化为字符串

整数转换示例

package main

import (
"fmt"
"strconv"
)

func main() {
// 字符串转整数
s := "1024"
num, err := strconv.Atoi(s)
if err != nil {
fmt.Println("转换错误:", err)
} else {
fmt.Println("转换结果:", num)
}

// 整数转字符串
numStr := strconv.Itoa(num)
fmt.Println("整数转字符串:", numStr)

// 使用 ParseInt 解析十六进制字符串
hexNum, _ := strconv.ParseInt("1f4", 16, 32)
fmt.Println("十六进制 '1f4' 转为十进制:", hexNum)

// 使用 FormatInt 将数字转换为二进制表示的字符串
binStr := strconv.FormatInt(int64(hexNum), 2)
fmt.Println("数字转二进制字符串:", binStr)
}

浮点数转换示例

package main

import (
"fmt"
"strconv"
)

func main() {
// 字符串转浮点数
floatStr := "3.14159"
f, err := strconv.ParseFloat(floatStr, 64)
if err != nil {
fmt.Println("转换错误:", err)
} else {
fmt.Println("转换结果:", f)
}

// 浮点数转字符串
formattedFloat := strconv.FormatFloat(f, 'f', 3, 64)
fmt.Println("浮点数格式化:", formattedFloat)
}

布尔值转换示例

package main

import (
"fmt"
"strconv"
)

func main() {
// 字符串转布尔值
boolStr := "true"
b, err := strconv.ParseBool(boolStr)
if err != nil {
fmt.Println("转换错误:", err)
} else {
fmt.Println("转换结果:", b)
}

// 布尔值转字符串
bStr := strconv.FormatBool(b)
fmt.Println("布尔值转字符串:", bStr)
}