This commit is contained in:
Spirale 2026-07-13 23:12:38 +08:00
commit e6fe90a7c0
25 changed files with 3877 additions and 1 deletions

6
.gitignore vendored
View File

@ -27,4 +27,8 @@ build/
# ---- 操作系统 ----
.DS_Store
Thumbs.db
Thumbs.db
# ---- Note 目录 ----
note/assets/
note/项目/

48
go/testing/normal_test.go Normal file
View File

@ -0,0 +1,48 @@
package testing
import (
"sort"
"testing"
"time"
)
func TestNormal(t *testing.T) {
numbers := map[int][]int{
1: {8, 2, 4, 4},
2: {0, 5, 3},
3: {1},
}
for _, numbers := range numbers {
middle := GetMiddNumber(numbers)
t.Log(middle)
}
}
func GetMiddNumber(numbers []int) float64 {
sort.Ints(numbers)
length := len(numbers)
isOddNUmber := length%2 == 1
if !isOddNUmber {
return float64(numbers[length/2-1]+numbers[length/2]) / 2.0
}
return float64(numbers[length/2])
}
func TestChannel(t *testing.T) {
var ball = make(chan string)
kickBall := func(playerName string) {
for {
t.Log(<-ball, "传球", "\n")
time.Sleep(time.Second)
ball <- playerName
}
}
go kickBall("张三")
go kickBall("李四")
go kickBall("王二麻子")
go kickBall("刘大")
ball <- "裁判" // 开球
var c chan bool // 一个零值nil通道
<-c // 永久阻塞在此
}

View File

@ -0,0 +1,303 @@
# HTTP
## 上传文件
> **https://github.com/zeromicro/zero-examples/tree/main/http/upload**
### 前端
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<form
enctype="multipart/form-data"
action="http://localhost:8888/upload"
method="post"
>
<input type="file" name="myFile" />
<input type="submit" value="upload" />
</form>
</body>
</html>
```
> **参数说明**
- `enctype="multipart/form-data"` 用于支持文件的二进制传输
- `<input type="file">` 让用户挑选本地文件
- `action``method`,将数据以 `POST` 方式提交到后端地址
### 后端
```yaml
Name: upload-api
Host: 0.0.0.0
Port: 8888
MaxBytes: 1073741824
Timeout: 30000
```
> **yaml文件需要设置上传的最大字节并且如果为大文件需要考虑超时时间设置是否合理**
```go
func (l *UploadLogic) Upload(r *http.Request) (resp *types.Response, err error) {
err = r.ParseMultipartForm(10 << 27)
if err != nil {
fmt.Println("错误发生:", err)
return nil, err
}
file, handler, err := r.FormFile("myFile")
if err != nil {
fmt.Println(err)
return nil, err
}
defer file.Close()
fmt.Printf("Uploaded File: %+v\n", handler.Filename)
fmt.Printf("File Size: %+v\n", handler.Size)
fmt.Printf("MIME Header: %+v\n", handler.Header)
tempFile, err := os.Create(path.Join(".", handler.Filename))
if err != nil {
fmt.Println(err)
return nil, err
}
defer tempFile.Close()
io.Copy(tempFile, file)
return &types.Response{
OK: 0,
}, nil
}
```
> **API逻辑说明**
- 需要在handler里面将`*http.Request`传递到逻辑层
- `ParseMultipartForm`用于解析 `multipart/form-data` 类型的请求体
主要作用是:
解析表单字段(包括文件上传字段)
将请求体拆分成多个部分parts并填充到 `r.MultipartForm`
内存与磁盘分配:最多在内存中保留 `maxFileSize` 字节,超出的部分会写入临时文件
- `FormFile(key string)` 从已解析的 `r.MultipartForm` 中提取名为 `"myFile"` 的文件部分
- `tempFile, err := os.Create(path.Join(".", handler.Filename))`将文件上传到当前目录
# DTM
## 基础概念
- **APApplication**:应用程序,发起全局事务请求。
- **TMTransaction Manager**:事务协调者,负责协调分布式事务的执行流程。
- **RMResource Manager**:资源管理者,例如订单服务、库存服务等,执行具体业务与补偿逻辑
- **事务分支**:我们把每个服务管理的全局事务组成部分,称为事务分支,例如前面的转账,分为转出和转入两个事务分支
- **分支操作**每个事务分支在SAGA、XA、TCC等事务模式下会有多个操作例如转出事务分支包括正向操作TransOut和TransOutCompensate
- **本地事务**:转出事务分支中的正向操作,通常会开启一个事务,对余额进行扣减,我们将数据操作的这个事务,称为本地事务
- **GID**全局事务ID用于标记全局事务必须唯一。该ID可以采用UUID生成也可以使用业务上的ID例如订单ID等
| 模式 | 场景 | 典型使用 |
| ---------- | ---------------------------------------- | --------------------------------- |
| 二阶段消息 | 需要确保消息通知或缓存更新与本地事务同步 | 发短信/推送、消息投递、缓存一致性 |
| Saga | 流程复杂、长链路、不需强一致性 | 电商下单→扣库存→支付等微服务流程 |
| TCC | 强一致、高隔离资源场景 | 金融系统、库存冻结、大额支付 |
## 二阶段消息
是dtm首创的事务模式用于替换本地事务表和事务消息这两种现有的方案。它能够保证本地事务的提交和全局事务提交是“原子的”适合解决不需要回滚的分布式事务场景比如发消息、更新缓存等
- **准备阶段**Prepare ,应用先发一个准备信号给 DTM Server表示“我现在要做本地事务了”。DTM Server 接收到后先记录这个请求,但不触发后续通知。
- 执行本地事务, 应用开始执行本地事务(如写数据库、扣款),并在同一个数据库里,记录这次全局事务的 gid全局事务 ID。这保证了后续可以根据 gid 判断本地事务是否已成功。
- **提交阶段**Submit 本地事务成功后,应用再向 DTM 提交提交请求。DTM 执行这个操作后,才真正触发下游服务或消息投递。开始调用其他微服务或消息队列,实现完整的全局动作。
**优点:**
- 保证“本地事务 + 消息”业务要么同时成功,否则都不执行。(强触发保障)
- 对开发者透明,业务侵入小。
**缺点:**
- 需要开发一套 Prepare/Submit 调用,系统需定期监控、回查状态。
- 不适用于需要复杂补偿的场景。
## SAGA
核心思想是将长事务拆分为多个短事务由Saga事务协调器协调如果每个短事务都成功提交完成那么全局事务就正常完成如果某个步骤失败则根据相反顺序一次调用补偿操作。例如我们要进行一个类似于银行跨行转账的业务将A中的30元转给B根据Saga事务的原理我们将整个全局事务切分为以下服务
- **转出**TransOut服务这里转出将会进行操作A-30
- **转出补偿**TransOutCompensate服务回滚上面的转出操作即A+30
- **转入**TransIn服务转入将会进行B+30
- **转入补偿**TransInCompensate服务回滚上面的转入操作即B-30
**优点:**
- 流程自然、开发简单,只要为操作实现补偿逻辑即可。
- 对资源无锁设计,支持高并发、长流程,性能佳。
**缺点:**
- 是最终一致性,存在临时中间状态(如库存已扣但未支付)。
- 无事务隔离,可能出现脏写或并发冲突。
- 补偿逻辑可能复杂易错(幂等、防重复)。
## TCC
TCC 是一种“预留-确认-取消”的事务模式,将事务分为三个阶段,高精度控制点 的场景,比如:支付系统、资金冻结、库存预扣等
- **Try** 阶段:尝试执行,完成所有业务检查(一致性), 预留必须业务资源(准隔离性)
- **Confirm** 阶段如果所有分支的Try都成功了则走到Confirm阶段。Confirm真正执行业务不作任何业务检查只使用 Try 阶段预留的业务资源
- **Cancel** 阶段如果所有分支的Try有一个失败了则走到Cancel阶段。Cancel释放 Try 阶段预留的业务资源。
**优点:**
- 强一致性:通过 Try 预留资源Confirm/Cancel 明确执行或回滚,避免中间状态。
- 高并发友好,资源锁粒度小;性能优于传统 XA/2PC。
**缺点:**
- 业务侵入性高每个参与方需实现三个操作Try/Confirm/Cancel
- 实现复杂:需处理悬挂、空回滚、幂等、补偿重试等问题。
## SAGA实践
- 准备RM和DTM数据表
[GitHub地址](https://github.com/dtm-labs/dtm/tree/main/sqls)
- DTM配置
[GitHub地址](https://github.com/dtm-labs/dtm/blob/main/conf.sample.yml)
- docker部署参考配置
```yaml
LogLevel: 'info'
Server:
HttpPort: 36789 #HTTP port
GrpcPort: 36790 #Grpc port
Store:
Driver: "mysql" # 数据库类型
Host: "mysql" # 使用容器名
Port: 3306
User: "root"
Password: "root"
Db: "dtm"
MicroService:
Driver: "dtm-driver-gozero" #使用go-zero的注册服务发现驱动
Target: "etcd://etcd:2379/dtmservice" # 使用容器名当前dtm的server直接注册到微服务所在的etcd集群中使用
EndPoint: "dtm:36790" # 使用容器名集群中的微服务可以直接通过etcd获得此地址跟dtm交互
```
- go-zero的yaml参考配置
```yaml
#AP使用etcd的配置
Etcd:
Hosts:
- etcd:2379
Key: order.api
#RM使用etcd的配置
OrderRpc:
Etcd:
Hosts:
- etcd:2379
Key: order.rpc
#RM使用etcd的配置
StockRpc:
Etcd:
Hosts:
- etcd:2379
Key: stock.rpc
```
SAGA 事务编排使用步骤
```go
// dtm服务在etcd中的调用地址
var dtmServer = "etcd://localhost:2379/dtmservice"
// 从配置中通过BuildTarget获得对应RM的etcd调用地址
orderTarget, err := l.svcCtx.Config.OrderRpcConf.BuildTarget()
stockTarget, err := l.svcCtx.Config.StockRpcConf.BuildTarget()
// 构造对应的请求结构体,用于后面的本地事务的执行或者回滚补偿
createOrderReq := &order.CreateReq{UserId: req.UserId, GoodsId: req.GoodsId, Num: req.Num}
deductReq := &stock.DecuctReq{GoodsId: req.GoodsId, Num: req.Num}
// 使用dtmgrpc注册一个全局事务ID用于标记全局事务
gid := dtmgrpc.MustGenGid(dtmServer)
// 开启全局事务,并提交
// dtm需要从dtm服务器调用该方法所以不走强类型而是走动态的url: busiServer+"/trans.TransSvc/TransOut"
// "/trans.TransSvc/TransOut"对应的是grpc_pb.go文件中Invoke方法所使用的路径
saga := dtmgrpc.NewSagaGrpc(dtmServer, gid).
Add(
orderTarget+"/pb.order/create",
orderTarget+"/pb.order/createRollback",
createOrderReq,
).
Add(
stockTarget+"/pb.stock/deduct",
stockTarget+"/pb.stock/deductRollback",
deductReq,
)
err = saga.Submit()
```
服务实现(包含子事务屏障)
```go
// 从 gRPC 请求上下文中提取分布式事务信息,构建一个用于管理子事务屏障的对象
barrier, err := dtmgrpc.BarrierFromGrpc(l.ctx)
// 获取一个sqlx的数据库连接
db, err := sqlx.NewMysql(l.svcCtx.Config.DB.DataSource).RawDB()
if err != nil {
// 一般数据库不会错误不需要dtm回滚就让他一直重试
// 返回为status.Error(codes.Internal, err.Error())会让dtm一直重试不会回滚直到
// 返回为status.Error(codes.Aborted, dtmcli.ResultFailure)会让dtm立刻调用回滚方法
return nil, status.Error(codes.Internal, err.Error())
}
// 开启本地事务(这里没写,正常应该在本地事务中再开启子事务屏障进行业务处理
// 将业务逻辑包裹在子事务屏障的保护下执行
if err := barrier.CallWithDB(db, func(tx *sql.Tx) error {
order := new(model.Order)
order.GoodsId = in.GoodsId
order.Num = in.Num
order.UserId = in.UserId
_, err = l.svcCtx.OrderModel.Insert(tx, order)
if err != nil {
return fmt.Errorf("创建订单失败 err : %v , order:%+v \n", err, order)
}
return nil
}); err != nil {
// 同上让dtm一直重试不回滚
return nil, status.Error(codes.Internal, err.Error())
}
// 补偿操作跟上面差不多,就是业务逻辑修改
```

View File

@ -0,0 +1,70 @@
## oh-my-openagent
### Agent角色
| **Agent 名称** | **属性** | **一句话特点** |
| ----------------- | -------- | ------------------------------------------------------------ |
| Sisyphus | 台前主控 | 项目总管、日常聊天与轻量开发入口,居中调度,维持记忆的多面手 |
| Prometheus | 台前主控 | 复杂需求的规划者、Gap差距分析师只出谋划策写 Plan不动手写代码 |
| Atlas | 台前主控 | 流水线执行者、项目经理,盯着 TODO 督战的施工队包工头 |
| Hephaestus | 台前主控 | 火神、地狱级 Bug 终结者,单兵作战,不计成本死磕疑难杂症 |
| Oracle | 幕后工具 | 代码审查员、安全顾问挑刺、CR、架构安全性把关 |
| Momus | 幕后工具 | Debug 专家,专门吃报错日志并给出修复建议 |
| Metis | 幕后工具 | 质检员、对齐审计,检查代码跟计划对不对齐,防止跑偏 |
| Sisyphus-junior | 幕后工具 | 基层打工人、代码搬砖牛马,真正动手改文件的“牛马码农” |
| Explore | 幕后工具 | 代码库检索员,在代码库里疯狂乱窜、扫图、找文件 |
| Librarian | 幕后工具 | 文档与知识检索员,专门翻文档、查第三方库用法 |
| Multimodal-looker | 幕后工具 | 视觉质检员,负责看截图、核对 UI 视觉问题 |
### 内置分类预设
| 分类名称(Category) | 预设心智与应用场景 |
| ------------------ | ------------------------------------------------------------ |
| visual-engineering | **像素级前端 UI 专家**。专门处理前端组件、页面样式美化、CSS 动画调整,默认偏向调度带有视觉能力的模型。 |
| deep | **目标导向的硬核攻坚战**。针对棘手难啃的底层问题,严格遵循“单次调用:一个目标 + 一个交付物”原则,死磕到底。 |
| quick | **低成本牛马杂活线**。处理单文件小改动、错别字修复、简单注释补充。默认绑定极便宜的 Mini 或 Flash 模型来帮你疯狂省钱。 |
| ultrabrain | 高浓度脑力推演 |
| writing | **散文与技术文档写手**。专门用来编写官方 Markdown 文档、技术方案白皮书或代码内说明。 |
## oh-my-opencode-slim
### 预设
`oh-my-opencode-slim.json`中的顶级字段`presets`为预设对象数组,默认设置了两套:一套是`openai`,一套是`opencode-go`。可以自行增删预设或者修改`agent`角色的模型。
```
# 在opencode会话中输入
/preset <name>
```
### Agent角色
| **Agent 名称** | **角色** | **模型选择** |
| -------------- | ----------------------- | ------------------------------------------------------------ |
| Orchestrator | 首席委派者和战略协调员 | 选择您的默认、最强的全能型编程模型。Orchestrator 既是核心编程智能体,又是委派者,因此它需要强大的实现能力、出色的判断力和可靠的指令遵循度。 |
| Explorer | 代码库侦察 | 选择快速、低成本的模型。Explorer 处理宽泛的侦察工作,因此速度 and 效率通常比使用最强推理模型更重要。 |
| Oracle | 战略顾问和终极调试者 | 选择您最强的高推理模型,用于架构设计、疑难调试、方案权衡以及代码审查。 |
| Council | 多LLM共识与提炼 | 使用一个强大的综合提炼模型作为 Council 智能体本身并选择多样化的模型作为议员。Council 的价值在于对比不同的模型视角,而不仅仅是在所有地方都选择同一个最强的模型。 |
| Librarian | 外部知识检索 | 选择快速、低成本的模型。Librarian 处理调研和文档查询,因此速度和效率通常比使用最强推理模型更重要。 |
| Designer | UI/UX实现和极致视觉呈现 | 选择在 UI/UX 判断、前端实现和视觉打磨方面表现强劲的模型。 |
| Fixer | 快速实现专家 | 选择一个快速、可靠的编程模型来执行常规且范围明确的开发工作。Fixer 通常从 Orchestrator 接收具体的计划或受限的指令,非常适合高效执行诸如编写测试、更新测试和直接的代码更改等任务。 |
| Observer | 多模态处理 | 如果Orchestrator模型不是多模态模型可以启用 Observer 来处理图像、屏幕截图、PDF 以及其他视觉文件。 |
### Skill分配
## 技能分配
控制每个代理人可以使用的技能`~/.config/opencode/oh-my-opencode-slim.json`(或`.jsonc`
| 句法 | 意义 |
| ------------------- | ------------------------------- |
| `["*"]` | 所有已安装的技能 |
| `["*", "!codemap"]` | 除以下技能外的所有技能`codemap` |
| `["simplify"]` | 仅有的`simplify` |
| `[]` | 没有技能 |
| `["!*"]` | 拒绝所有技能 |

View File

@ -0,0 +1,70 @@
### 常用命令
#### 一、分支管理
| 操作 | 命令 | 说明 |
| -------------------- | -------------------------------------- | ---------------------------- |
| 查看本地分支 | `git branch` | 显示所有本地分支 |
| 查看远程分支 | `git branch -r` | 显示远程分支 |
| 创建新分支 | `git checkout -b feature/xxx` | 基于当前分支创建并切换新分支 |
| 基于远程创建本地分支 | `git checkout -b dev origin/dev` | 跟踪远程分支创建 |
| 删除本地分支 | `git branch -d feature/xxx` | 删除(已合并)本地分支 |
| 删除远程分支 | `git push origin --delete feature/xxx` | 删除远程分支 |
| 重命名分支 | `git branch -m old new` | 修改分支名 |
#### 二、提交相关
| 操作 | 命令 | 说明 |
| ------------------ | ------------------------------- | ------------------------------------------ |
| 添加到暂存区 | `git add .``git add <file>` | 添加改动文件 |
| 提交代码 | `git commit` | 使用文本编辑器编写提交信息后提交 |
| 提交代码 | `git commit -m "描述信息"` | 提交到本地仓库,可使用多个`-m`提交多行信息 |
| 修改上一次提交信息 | `git commit --amend` | 用于补充或改描述 |
| 查看提交历史 | `git log --oneline` | 简洁模式显示历史 |
#### 三、远程操作
| 操作 | 命令 | 说明 |
| ---------------- | --------------------------------------------------------- | ------------------- |
| 克隆仓库 | `git clone <url>` | 拉取远程项目 |
| 拉取远程代码 | `git pull` | 获取并合并远程分支 |
| 推送代码 | `git push` | 推送当前分支到远程 |
| 推送并设置上游 | `git push -u origin <branch>` | 建立跟踪关系 |
| 查看远程地址 | `git remote -v` | 显示远程仓库地址 |
| 远程查询所有标签 | git ls-remote --tags https://github.com/xxx.git \| grep X | 查看X相关的所有标签 |
#### 四、合并与变基
| 操作 | 命令 | 说明 |
| -------------- | ------------------------------------ | ------------------------------- |
| 合并分支 | `git merge feature/xxx` | 把目标分支合并进当前分支 |
| 取消合并 | `git merge --abort` | 终止合并,直接回退到合并前的状态 |
| 变基rebase | `git rebase dev` | 让你的提交在最新 `dev` 上 |
| 解决冲突后继续 | `git add . && git rebase --continue` | 继续变基或合并流程 |
#### 五、查看状态和差异
| 操作 | 命令 | 说明 |
| ---------------- | ------------------- | ---------------------------- |
| 查看状态 | `git status` | 当前分支、暂存区、工作区状态 |
| 查看修改内容 | `git diff` | 查看未暂存的更改 |
| 查看暂存内容差异 | `git diff --cached` | 查看已 add 的更改 |
#### 六、撤销操作
| 操作 | 命令 | 说明 |
| ----------------------- | ------------------------- | -------------------- |
| 撤销 add | `git reset <file>` | 把文件移出暂存区 |
| 撤销 commit保留修改 | `git reset --soft HEAD~1` | 撤销最近一次提交 |
| 丢弃修改 | `git checkout -- <file>` | 丢弃对文件的本地更改 |
| 丢弃所有工作区修改 | `git reset --hard` | 恢复到最后一次提交 |
#### 七、其他常用
| 操作 | 命令 | 说明 |
| ---------------------- | ------------------------------------------------------------ | ---- |
| 查看当前分支 | `git branch --show-current``git rev-parse --abbrev-ref HEAD` | |
| 查看某次提交内容 | `git show <commit-id>` | |
| 切换到上一个分支 | `git checkout -` | |
| 清理无效引用 | `git remote prune origin` | |
| 从远程仓库获取所有标签 | `git fetch origin --tags` | |

View File

@ -0,0 +1,117 @@
[来源网站](https://www.conventionalcommits.org/zh-hant/v1.0.0/)
## 提交说明
### 基础结构
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
# 译文
<类型>[可选 范围]: <描述>
[可选 正文]
[可选 脚注]
```
### 类型type
- feat: 表示在代码库中新增了一个功能;
- fix: 表示在代码库中修复了一个 bug
- build: 用于修改项目构建系统或外部依赖,例如升级依赖版本等;
- chore: 用于其他不修改`src`或`test`文件的杂项任务,例如更新`.gitignore`、修改编辑器配置等;
- ci: 用于修改持续集成/持续部署的配置与脚本,例如`GitHub Actions`、`Jenkins`、`Travis` 等工作流;
- docs: 用于修改文档,例如修改`README`文件、API文档等
- style: 用于修改代码格式(不影响代码逻辑),例如调整缩进、空格、分号、引号风格等;
- refactor: 用于重构代码,例如修改代码结构、变量名、函数名等但不修改功能逻辑;
- improvement: 用于对现有实现进行改进,例如优化算法、增强健壮性等(不涉及新功能或 bug 修复);
- revert: 用于回退之前的提交,描述应使用被回退提交的标题,并在脚注中用 `Refs:` 引用被回退的 commit SHA
- perf: 用于优化性能,例如提升代码的性能、减少内存占用等;
- wip: 用于保存尚未完成的工作进度Work In Progress
- test: 用于修改测试用例,例如添加、删除、修改代码的测试用例等;
> 破坏性变更**必须**在提交信息中标记出来,有两种方式:
>
> 1.在 `<类型>[可选 范围]` 后、冒号前添加 `!`,例如:`feat!: ...` 或 `refactor(api)!: ...`
>
> 2.*在脚注中添加* `BREAKING CHANGE: <描述>`
>
> 这两种方式可以同时使用,也可以只用其一。
### 范围Scope
范围用于标识本次修改影响的模块或组件,例如:
- feat(auth): ... auth 模块)
- fix(parser): ... parser 模块)
- build(deps): ... (依赖管理)
范围名称建议使用小写英文,多个单词用 `-` 连接,例如 `user-profile`
### 描述description
描述用于简要概括本次变更的内容,要求:
- 使用祈使语气、现在时态(例如用 "add" 而非 "added" 或 "adds"
- 小写开头,末尾不加句号;
- 长度建议控制在50个字符以内中文约 25 个字);
### 正文Body
正文是可选的,用于对本次变更进行更详细的说明,包括:
- 变更的动机(为什么这样做)
- 与之前行为的对比
- 实现方案的简要说明
正文与描述之间**必须**空一行,正文内部段落之间也可以空行分隔。
### 脚注Footer
脚注用于记录元信息,常见用法:
- `BREAKING CHANGE: <描述>` — 标记破坏性变更;
- `Closes #123``Fixes #456` — 关联 Issue/PR;
- `Refs: abc123` — 引用相关提交;
- `Reviewed-by: <name>` — 标记审查者;
- `Co-authored-by: <name> <email>` — 标记共同作者;
## 示例
### 基础功能提交
feat(auth): add JWT token refresh mechanism
### Bug 修复(含关联 issue
fix(parser): handle empty string input edge case
Closes #123
### 破坏性变更(使用 ! 前缀)
feat(api)!: change user endpoint response format
BREAKING CHANGE: the `GET /api/users` endpoint now returns
a paginated response instead of a flat array.
### 回退提交
revert: remove experimental cache layer
Refs: a1b2c3d

View File

@ -0,0 +1,24 @@
### SSH
#### 快速连接服务器
在ssh的配置文件`~/.ssh/config`中可以设置常用的服务配置,这样就可以直接使用`ssh myserver`快速连接服务器了
```
Host myserver
HostName your-server.com
Port 2222
User ubuntu
IdentityFile ~/.ssh/mykey
```
#### 本地转发
```bash
# 使用示例
ssh -L 25432:localhost:5432 ubuntu@your-server.com
```
**作用**以本地端口25432请求远程服务器在远程服务器看来这是以远程`localhost:5432`发起的请求
**使用场景**当远程服务器某服务限制了只接收远程服务器的127.0.0.1连接,这时候又需要远程连接这个服务,这样的话就能和访问本地服务一样访问了

View File

@ -0,0 +1,84 @@
## 程序、进程与线程
### 程序
#### 定义
程序是存储在硬盘的一组指令集和,它是对计算任务的完整描述,是一个被动实体。类似于一本菜谱,记录了完整的烹饪步骤。
#### 核心特征
| 属性 | 说明 |
| :------------- | :-------------------------------------------------------- |
| **静态性** | 程序是一个文件(如 .exe、.go不占用 CPU 或内存执行资源 |
| **持久性** | 可长期存在于存储介质上 |
| **无生命周期** | 没有创建、运行、消亡的状态变迁 |
### 进程
#### 定义
进程是程序的一次动态执行过程,是操作系统进行资源分配的基本单位。它就像按照菜谱进行烹饪的过程,需要占用资源,有始有终。它由以下三部分构成。
| 组成部分 | 作用 |
| :-------------------- | :----------------------------------------------------- |
| **程序段** | 描述进程要完成的功能,是控制进程执行的指令集 |
| **数据段** | 程序执行时所需的数据和工作区(全局变量、堆等) |
| **进程控制块PCB** | 包含进程的描述信息和控制信息,是**进程存在的唯一标志** |
#### 核心特征
- **动态性**:进程有生命周期(创建 → 就绪 → 运行 → 阻塞 → 终止)
- **独立性**:每个进程拥有独立的地址空间(虚拟内存空间、代码、数据、系统资源的集合)
- **并发性**:多个进程可在宏观上同时运行
- **资源拥有者**进程拥有独立的文件描述符、环境变量、用户ID等资源
### 线程
#### 定义
线程是CPU进行运算调度的最小单位是进程中的一条执行路径一个进程至少有一个线程也就是主线程。它就像多位厨师共用厨房的资源水、灶台、食材等但是每人也都有自己的独有的刀具、围裙等他们需要协调使用厨房做菜。
#### 核心特征
线程虽然共享进程的资源,但每个线程拥有自己的**私有数据**
| **独有内容** | **说明** |
| :------------------ | :--------------------------------- |
| 线程 ID | 唯一标识 |
| 寄存器状态 | 包括程序计数器PC和栈指针 |
| 栈 | 独立运行栈,用于函数调用和局部变量 |
| 信号掩码 | 线程级别的信号处理设置 |
| 优先级 | 调度优先级 |
| 线程专用存储TLS | 线程私有的全局变量 |
## 用户态与内核态
### 内核态
运行操作系统内核代码,拥有对硬件的完全控制权,负责管理内存、进程、文件系统、网络等底层资源。
### 用户态
运行用户应用程序,无法直接访问硬件或内核数据结构,必须通过 系统调用System Call请求内核代为执行特权操作。
### 切换过程
为了保护系统安全与稳定操作系统会对CPU的执行权限进行分级不同的CPU架构有不同的等级但是通常都遵循应用在最低特权级用户态运行内核在次高特权级内核态运行通过CPU硬件强制隔离特权操作。当在X86架构的CPU运行时一个应用程序需要执行特权操作比如读写文件过程大致是这样的
1. **触发**
程序调用 `read()` 等库函数,最终执行 CPU 的 `syscall` 指令。这条指令告诉 CPU“我要切换到内核态干正事”。
2. **CPU 硬件自动切换**
CPU 在硬件层面做三件关键事:
- 把特权级从 **Ring 3用户态** 切换为 **Ring 0内核态**
- 将栈指针从用户栈切换到**内核栈**(从 MSR 中读取预设的内核栈指针)。
- 把返回地址(下一条指令)和标志寄存器保存到特定寄存器(如 `rcx`、`r11`),然后跳转到内核预先设置好的**入口函数**。
3. **内核软处理**
入口函数将刚才没来得及保存的全部寄存器压入内核栈(形成 `pt_regs` 结构),然后根据**系统调用号**(放在 `rax` 通用寄存器中)从系统调用表里找到对应的内核函数(如 `ksys_read`)并执行。
4. **内核执行 & 返回**
内核函数完成实际工作(检查权限、读取数据等),把结果放在 `rax` 通用寄存器中。返回前,内核检查是否有其他任务需要处理(如信号、调度),然后通过 `sysret` 指令恢复之前保存的寄存器、用户栈和返回地址,特权级自动切回 **Ring 3**,程序从当初的下一条指令继续运行。

View File

@ -0,0 +1,123 @@
配置文件位置
```
C:\Users\admin\AppData\Roaming\Code\User\keybindings.json
```
该文件保存的是用户级快捷键列表,每一个快捷键配置都是包含`key`、`command`、`when`(可选)字段
| 字段 | 作用 |
| :------------ | :------------------------------- |
| **`key`** | 你按下的键盘组合 |
| **`command`** | 要执行的命令 ID |
| **`when`** | 规定这条快捷键在什么“语境”下生效 |
完整示例:
```json
// markdown 代码块
{
"key": "ctrl+shift+`",
"command": "markdown.extension.editing.toggleCodeBlock",
"when": "editorTextFocus && editorLangId == 'markdown'"
},
```
## 按键的书写规则
### 基本格式
- **全小写**:修饰键和字母键都要小写,写 `ctrl` 而不是 `Ctrl`,写 `a` 而不是 `A`
- **用 `+` 连接**:按键之间用加号连接,不带空格。例如 `ctrl+shift+a`
- **修饰键顺序任意**`ctrl+alt+a` 和 `alt+ctrl+a` 效果相同,但习惯按 `ctrl -> shift -> alt -> 字母` 的顺序写
### 支持的修饰键
| 修饰键 | 写法 | 说明 |
| :------------ | :------ | :---------------------------------------------- |
| Ctrl | `ctrl` | Windows / Linux 上常用 `ctrl`macOS 对应 `cmd` |
| Shift | `shift` | |
| Alt | `alt` | macOS 上是 `opt`(或者也支持 `alt` |
| Cmd (Mac) | `cmd` | 在 Windows 上无效,建议用 `ctrl` 兼顾跨平台 |
| Win (Windows) | `win` | 很少用 |
| Meta (macOS) | `meta` | 通常等同 `cmd` |
### 特殊按键名称
| 按键 | 写法 |
| :----------- | :------------------------------------- |
| 方向键 | `up` `down` `left` `right` |
| 功能键 | `f1` ~ `f19` |
| 回车 | `enter` |
| 空格 | `space` |
| 退格 | `backspace` |
| Tab | `tab` |
| 反引号 | ``` (就是 backtick) |
| 逗号、句号 | `,` `.` 等直接写符号本身 |
| 数字键 | `0`~`9` |
| 计算器小键盘 | `numpad0` ~ `numpad9``numpad_add` 等 |
### 按键组合
VS Code 支持**二段按键**(一次按键序列),写法是:
```json
"key": "ctrl+k ctrl+b"
```
意思是先按 `Ctrl+K`,松开后再按 `Ctrl+B`
## 命令ID
`command` 是一个**全局唯一的字符串**,由扩展或 VS Code 自己注册。不能自己编造,必须使用确切的 ID。
通过`ctrl+k ctrl+b`按键组合或者在设置中点击“键盘快捷方式”即可查看当前能设置的所有的命令。虽然显示的是命令但是实际上是命令的名字右键可以获取命令的ID。也可以不通过修改配置文件在图形化界面设置快捷键。
## 上下文条件表达式
`when` 是一个**布尔表达式**,只有计算结果为 `true` 时,快捷键才会触发,可以组合变量使用逻辑运算符。
### 常用上下文变量
| 变量 | 说明 | 常见值 |
| :------------------------------ | :------------------------------------------------- | :------------------------------------------ |
| `editorTextFocus` | 焦点是否在编辑器的**文本区域**(光标可以输入文字) | `true` / `false` |
| `editorLangId` | 当前编辑器的语言 ID不是文件后缀 | `"markdown"`, `"javascript"`, `"python"` 等 |
| `editorHasSelection` | 编辑器里是否有文本被选中 | `true` / `false` |
| `isLinux`, `isWindows`, `isMac` | 当前操作系统 | `true` / `false` |
| `resourceExtname` | 当前文件的后缀名(带`.` | `".md"`, `".js"` 等 |
| `activeEditor` | 是否有激活的编辑器 | `true` / `false` |
| `terminalFocus` | 焦点是否在集成终端 | `true` / `false` |
| `listFocus` | 焦点在列表/树视图吗 | `true` / `false` |
### 逻辑运算符
| 运算符 | 含义 |
| :--------- | :----------------------------------------------------------- |
| `&&` | 与 |
| ` | |
| `!` | 非 |
| `==`, `!=` | 等于 / 不等于(字符串) |
| `=~` | 正则匹配(例如:`editorLangId =~ /^markdown$ |
| `in` | 测试左侧值是否在给定的右值数组里,如 `editorLangId in ['markdown', 'rmd']` |
### 例子分析
```json
"when": "editorTextFocus && editorLangId == 'markdown'"
```
表达式含义:确保你正在编辑文本,而不是在侧边栏、终端或其他地方**并且**当前文件被 VS Code 识别为 Markdown 语言
## 常见问题排查
| 现象 | 可能原因 |
| :------------------------------- | :------------------------------------------------------ |
| 按键完全没反应 | `command` 拼写错误;`when` 条件一直为 false扩展未激活 |
| 在其他语言文件里也生效了 | 缺少 `editorLangId == 'xxx'` 限制,考虑加上 |
| 一次性按了没反应,但按键序列可以 | 可能你定义成了 chord但习惯是单键 |
| 提示 “command 'xxx' not found” | 命令 ID 不存在,检查扩展是否已安装并启用 |

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,225 @@
### **小六壬时课 · 知识库**
#### **一、 简介:什么是小六壬?**
小六壬,相传为三国时期诸葛亮、唐代李淳风所创,是一种简便、快速的预测方法,因其起课方便、便于携带(仅需掐指一算),故有“袖珍式预测学”或“马前课”之称。
其核心思想是,根据事发或问事时的“时间”或“心念”所触发的即时信息,来推演事物的状态和发展趋势。它主要用于对**单一、具体、短期**的事情进行快速占断,属于“一事一占”的动念占法。
**核心特点**
- **简便快捷**:无需复杂工具,仅凭掌诀即可推算。
- **随机性强**:动念即可成课,捕捉瞬间天机。
- **应用广泛**:适用于寻物、问事、决疑、出行等日常生活的方方面面。
#### **二、 核心概念:六神宫位与掌诀**
小六壬共有六个基本“角色”,称为“六神”(或六宫),它们分布在食指、中指和无名指的指节上,形成一个固定的“掌上地盘”,即“掌诀”。
**掌诀位置图示** 您可以伸出左手,按照下图在手指上定位六神:
- **大安**:食指根部
- **留连**:食指尖部
- **速喜**:中指尖部
- **赤口**:无名指尖部
- **小吉**:无名指根部
- **空亡**:中指根部
(其顺序为顺时针循环)
**六神基础信息表**
| 宫位/六神 | 五行 | 吉凶 | 核心意象与关键词 |
| :------------------ | :--- | :--- | :--------------------------------- |
| **大安 (Dà'ān)** | 木 | 吉 | 安泰、平稳、静止、官方、身体、龙 |
| **留连 (Liúlián)** | 水 | 凶 | 延迟、纠缠、牵绊、缓慢、暧昧、暗昧 |
| **速喜 (Sùxǐ)** | 火 | 吉 | 快速、喜讯、文书、宴饮、光明、朱雀 |
| **赤口 (Chìkǒu)** | 金 | 凶 | 口舌、官非、争斗、惊恐、凶险、白虎 |
| **小吉 (Xiǎojí)** | 木 | 吉 | 合作、财利、和合、顺畅、六合 |
| **空亡 (Kōngwáng)** | 土 | 凶 | 落空、无果、忧虑、阴私、迷茫、玄武 |
#### **三、 主要起课方法**
起课,就是根据参数确定最终落在哪一个宫位上。
**1. 传统时间起课法(月、日、时)** 这是最正统的方法,以农历时间为准。
- 第一步:月上起日
- 从**大安**宫开始,顺时针将月份数代入。例如,一月为大安,二月为留连,三月为速喜……以此类推。
- 第二步:日上起时
- 从上一步月份的落宫开始,顺时针将日期数(初一、初二……)代入。
- 第三步:时上定宫
- 从上一步日期的落宫开始顺时针将时辰数子时为1丑时为2……代入最终落下的宫位即为结果。
**【示例】**占测农历八月初十下午4点申时
1. **定月**从大安宫数8个月落点在**赤口**。1大安→2留连→3速喜→4赤口→5小吉→6空亡→7大安→8赤口
2. **定日**从赤口宫数10日落点在**小吉**。1小吉→2空亡→...→6赤口→...→10小吉
3. 定时
申时是第9个地支。从上一步的小吉宫数9个时辰最终落点在
速喜
- **结果**:速喜。
**2. 报数起课法** 当不知晓具体时辰或为求心念感应时使用。
- 让问事者随意报出**三个数字**1位数到3位数均可
- 将每个数字分别**除以6取其余数**若余数为0则按6计算
- **第一步**:从“大安”宫起,按第一个余数顺时针数,定下基点。
- **第二步**:从基点宫位起,按第二个余数顺时针数,定下过程点。
- **第三步**:从过程点起,按第三个余数顺时针数,定下最终结果。
**【示例】**:报数为 7, 11, 32。
1. **第一数**`7 ÷ 6 = 1 ... 余 1`。从大安数1落在**大安**。
2. **第二数**`11 ÷ 6 = 1 ... 余 5`。从大安数5落在**小吉**。
3. 第三数
```
32 ÷ 6 = 5 ... 余 2
```
。从小吉数2落在
空亡
- **结果**:空亡。
#### **四、 六神象义详解与分类占断**
这是解卦的核心,需要结合所问之事灵活判断。
**1. 大安**
- **核心**:身不动,心安宁。静待为吉,妄动有失。
- **问事**:事可成,但需时日,不可急躁。过程平顺。
- **寻物/寻人**:人或物未走远,在家中或原地,或在东方、东北方,多在静态、稳定的环境中(如办公室、卧室)。
- **感情**:关系稳定,感情安泰,但可能缺乏激情。
- **财运**:财运稳定,宜守成,不宜投机。
- **健康**:身体安康,即使有病也易于恢复。
**2. 留连**
- **核心**:事未了,心纠结。拖延、纠缠、暧昧不清。
- **问事**:事情进展缓慢,有阻碍,难以立刻解决,容易节外生枝。
- **寻物/寻人**:难寻,或被某事/某人牵绊住。物品可能在潮湿、脏乱或纠缠之地。
- **感情**:关系纠缠不清,或处于冷战、延迟状态。有藕断丝连之象。
- **财运**:财运不畅,资金周转困难,或有债务纠纷。
- **健康**:病情迁延,难以速愈,多为慢性病或旧病复发。
**3. 速喜**
- **核心**:火光之象,喜信速至。宜速战速决,不宜拖延。
- **问事**:事能速成,有喜讯或好结果。尤其利于文书、考试、信息类事宜。
- **寻物/寻人**:能快速找到。人或物在南方,或在热闹、明亮之处。
- **感情**:进展神速,热情洋溢,或有喜事临门(如订婚、结婚)。
- **财运**:偏财运旺,有意外之财,或短期投资获利。
- **健康**:多为急症、热症(如发烧、炎症),但来得快去得也快。
**4. 赤口**
- **核心**:白虎开口,是非官讼。主凶险、争斗、惊扰。
- **问事**:事多纷争,易有口舌是非,甚至官司诉讼。过程不顺,需防小人。
- **寻物/寻人**:难寻。多有争吵或处于危险境地。物品可能已损坏。
- **感情**:关系紧张,争吵频繁,易有分离之象。
- **财运**:破财之象,谨防因纠纷、官非而损财。
- **健康**:需防意外伤害、手术、或急性恶疾。有血光之灾的警示。
**5. 小吉**
- **核心**:六合之象,诸事和合。代表合作、财利、转机。
- **问事**:事情顺利,有贵人相助,适宜合作、商议。
- **寻物/寻人**:易寻。人或物在附近,或与友人、同伴在一起。
- **感情**:关系和谐,琴瑟和鸣,是发展的良好时机。
- **财运**:正财、合伙之财运佳,交易顺利。
- **健康**:病情好转,遇到良医,康复有望。
**6. 空亡**
- **核心**:万事成空,虚无之象。信息不实,劳而无功。
- **问事**:事情没有头绪,心中没底,最终可能不了了之或毫无结果。有虚假、欺骗的成分。
- **寻物/寻人**:人已远去,物已丢失,找回希望渺茫。
- **感情**:关系空虚,前景不明,或对方心意不诚。
- **财运**:财运空空,投资无望,谨防被骗。
- **健康**:病情诊断不明,或查无此症,也可能指精神、心理层面的问题。
#### **五、 断辞内容**
1、大安身不动时属木为青龙凡谋事主一、五、七。
断辞曰:
大安事事昌,求谋在东方,失物去不远,
宅舍保安康,行人身未动,病者主无妨,
将军回田野,仔细与推详。
2、留连卒未归时属水为玄武凡谋事主二、八、十。
断辞曰:
留连事难成,求谋曰未明,官事只宜缓,
去者未回程,失物南方见,急讨方称心,
更须防口舌,人口且平平。
3、速喜人便至时属火为朱雀凡谋事主三、六、九。
断辞曰:
速喜喜来临,求财向南行,失物申午未,
逢人路上寻,官事有福德,病者无祸侵,
田家六畜吉,行人有信音。
4、赤口官事凶时属金为白虎凡谋事主四、七、十。
断辞曰:
赤口主口舌,官非切要防,失物急去寻,
行人有惊慌,鸡犬多作怪,病者出西方,
更须防咒诅,恐怕染蕴疾。
5、小吉人来喜时属木为六合凡谋事主一、五、七。
断辞曰:
小吉最吉昌,路上好商良,阳人来报喜,
失物在坤方,行人立便至,交关真是强,
凡事皆和合,病者事无仿。
6、空亡音信稀时属土为勾陈凡谋事主一、五、七。
断辞曰:
空亡事不长,阴人小乘张,求财无有利,
行人有灾殃,失物寻不见,官事主刑伤,
病人逢暗鬼,乞解保安康。
#### **六、 使用原则与心法**
- **心诚意专**:占测时心态必须虔诚、专注,意念集中于所问之事。
- **一事一占**:同一件事在短期内不应反复占问,以第一次的结果为准。所谓“再三渎,渎则不告”。
- **动念即占**:当某个问题或念头强烈地出现在脑海中时,是最佳的占测时机。
- **趋吉避凶**:小六壬提供的是一种预警和参考。得到吉卦,应顺势而为;得到凶卦,则应反思、谨慎、规避,而非坐以待毙。它是一种积极的“行为指导”,而非消极的“宿命论”。

View File

@ -0,0 +1,21 @@
## CONCAT函数
`CONCAT` 是 SQL 中用于**字符串拼接**的函数,它可以将两个或多个字符串合并成一个新的字符串。
------
### 基本语法
```sql
CONCAT(string1, string2, ..., stringN)
```
### 不同数据库的 CONCAT 行为
| 数据库 | 行为 |
| :--------------------- | :----------------------------------------------- |
| **MySQL** | 支持多参数;**任一参数为 NULL整个结果为 NULL** |
| **SQL Server** (2012+) | 支持多参数;**NULL 会被自动转为空字符串** `''` |
| **Oracle** | 只支持 **2 个参数**NULL 视为空字符串 |
| **PostgreSQL** | 支持多参数NULL 视为空字符串 |

View File

@ -0,0 +1,44 @@
## B-Tree
**适用场景**
| 场景 | 示例 |
| :------- | :-------------------------- |
| 等值查询 | WHERE user_id = 100 |
| 范围查询 | WHERE age BETWEEN 20 AND 30 |
| 排序优化 | ORDER BY created_at DESC |
| 前缀匹配 | WHERE name LIKE '张%' |
**不适用场景**
- LIKE '%关键词%'(后缀/中间模糊匹配)
- 全文搜索
- 数组包含查询
## GIN
**结构特点**
- 倒排索引Inverted Index 机制:从"内容"映射到"行"
- 索引的是复合值内部的独立元素
- 索引构建较慢,但查询性能极佳
**适用场景**
> `@>`表示包含、`<@`表示被包含、`&&`表示重叠、`@@`表示全文搜索匹配、`?`表示JSONB 键存在
| 场景 | 示例 |
| :----------- | :----------------------------------------------------- |
| 全文搜索 | WHERE to_tsvector(content) @@ to_tsquery('postgresql') |
| 数组包含 | WHERE tags @> ARRAY['java', 'sql'] |
| JSONB 查询 | WHERE data @> '{"status": "active"}' |
| 多值元素搜索 | WHERE arr && ARRAY[1, 2] |

View File

@ -0,0 +1,60 @@
## 权重抽奖
### 轮盘赌
适合奖品数量较少,一次性抽奖的场景
核心顺序遍历,逐个减权重:是用一个随机数 `r`,依次减去每个奖品的权重,当 `r` 变成负数的那一刻,说明它“落入了”当前奖品的权重区间。时间复杂度 **O(n)**,空间复杂度**O(1)**
```go
func Draw(rewards []*Reward) *Reward {
totalWeight := 0
for _, r := range rewards {
totalWeight += r.Weight
}
r := rand.Intn(totalWeight) // [0, totalWeight)
for _, reward := range rewards {
r -= reward.Weight
if r < 0 {
return reward
}
}
return rewards[len(rewards)-1] // 兜底,理论上不会走到
}
```
### 前缀和 + 二分查找
适合奖品数量较多,需要频繁抽奖的通用场景
核心是构建前缀和数组,二分查找:把权重想象成一条线段,每个奖品占据一段,生成 `[0, 100)` 的随机数,落在哪个区间就中哪个奖品。二分查找快速定位区间,时间复杂度 **O(log n)**,空间复杂度**O(n)**
```
权重: 50 25 15 7 2 1
├─────────┼────────┼──────┼────┼──┼──┤
0 50 75 90 97 99 100
← 谢谢参与 →←5元券→←10元券→←月卡→←年卡→←大奖→
```
```go
type Reward struct {
Name string
Weight int
}
func Draw(rewards []*Reward) *Reward {
prefixSumArray := make([]int, len(rewards)) // 前缀和数组
totalWeight := 0 // 总权重
for i, p := range rewards {
totalWeight += p.Weight
prefixSumArray[i] = totalWeight
fmt.Println(totalWeight)
}
r := rand.Intn(totalWeight) // r ∈ [0, totalWeight-1]
idx := sort.SearchInts(prefixSumArray, r+1) // 查找目标 ∈ [1, totalWeight]
return rewards[idx]
}
```

View File

@ -0,0 +1,123 @@
## API
### goctl代码生成
可以在doc/genCode.md查看
```powershell
# 生成多个types类型的话需要关闭这个实验性功能
goctl env -w GOCTL_EXPERIMENTAL=off
# 代码生成会自动处理引入的子api文件
goctl api go -api background.api -dir . --home D:/zhangyuxin/template/1.8.3
goctl api go -api login.api -dir . --home ../../../deploy/goctl
goctl api go -api main.api -dir . --home ../../deploy/goctl
```
### 参数校验
| 接收规则 | 说明 | 示例 |
| -------- | ------------------------------------------ | ------------------------------- |
| optional | 当前字段是可选参数,允许为零值(zero value) | `json:"foo,optional"` |
| options | 当前参数仅可接收的枚举值 | `json:"gender,options=foo|bar"` |
| default | 当前参数默认值 | `json:"gender,default=male"` |
| range | 当前参数数值有效范围,支持开/闭区间 | `json:"age,range=[0:120]"` |
#### **`optional`** **与** **`omitempty`** **的区别**
两者都写在 json/form tag 的 options 里,但作用域完全不同,不可互相替代
##### `optional` —— goctl 专用,控制 Swagger 与请求校验
- **Swagger 生成**:标了 `optional` 的字段不进 `required` 数组Apifox/Swagger UI 显示为可选;未标的字段进 `required` 数组,显示为必填
- **请求校验**goctl 生成的 logic 不会对标了 `optional` 的字段做必填校验
- **判定逻辑**goctl 遍历 tag options找到 `optional` 关键字即判定为可选
##### `omitempty` —— Go 标准库指令,控制 JSON 序列化
- **JSON 序列化**字段为零值0、""、nil、false响应 JSON 中**不输出**该字段
- **Swagger 生成****goctl 完全不识别**,字段仍按未标 `optional` 处理,即判定为 required
- **请求校验**:无影响
## RPC
### goctl代码生成
```powershell
# 主proto文件代码生成
goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. --proto_path=. -m --home ../../../deploy/goctl
# 主proto文件中引入的proto代码生成
protoc.exe -I ./ --go_out=. --go-grpc_out=. ./user_model.proto
```
当`proto`文件里面定义了多个 service的时候需要额外注意还要再main函数中手动注册一下
> 每个 proto 文件应该聚焦于一个核心概念/实体。一个文件里定义太多 service 通常是设计需要重新考虑的信号
注册示例
```go
import (
ebookServer "olive/services/product/rpc/internal/server/ebookservice"
)
var configFile = flag.String("f", "etc/product.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
// 新服务
productrpcmodel.RegisterEbookServiceServer(grpcServer, ebookServer.NewEbookServiceServer(ctx))
})
defer s.Stop()
fmt.Printf("Starting product RPC server at %s...\n", c.ListenOn)
s.Start()
}
```
### proto代码生成错误File not found错误
- 错误描述
执行 protoc 报 google/protobuf/timestamp.proto: File not found. 的错提示File not found
- 解决方式
重新下载[protoc](https://github.com/protocolbuffers/protobuf/releases)解压后将整个include目录复制到protoc执行文件同一级目录下
## model
可以在doc/genCode.md查看
**Model 层**:只做 **“业务循环”** + **“SQL 查询”** + **“结果封装”**,不关心字符串到时间的转换和 Protobuf 类型。
### goctl代码生成
```powershell
# mysql根据远程数据库表生成
goctl model mysql datasource --url="olive:xxvkikaDDx01&&99@tcp(127.0.0.1:3306)/ebming" --table="user_invitation_record" --dir="." -c --home ../../../../deploy/goctl/1.8.3
# mysql根据ddl生成
goctl model mysql ddl --src h5.sql --dir . -c --home D:/zhangyuxin/template/1.8.3
# pgsql根据远程数据库表生成
goctl model pg datasource -url "postgres://labman:tN5bWz2KpL9Fy4@127.0.0.1:15432/sern" -table user -dir ./user --cache --home ../../goctl_template/1.8.3
# pgsql根据远程数据库表生成,带模式schema
goctl model pg datasource -url "postgres://admin:zhangyuxin@127.0.0.1:25432/test?sslmode=disable" -s "users" -table "profile" -dir ./go-zero/model/users/ --cache --home ../../deploy/goctl
```

View File

@ -0,0 +1,212 @@
## 正确关闭`HTTP`响应
> **注意事项**
>
> - 先检查 `err`,再 `defer close`
>
> - 必须消费完响应体,否则连接无法复用
>
> - `defer` 会延迟到函数结束,如果需要在循环中处理大量响应,建议手动关闭(避免 `defer` 累积)
```go
resp, err := http.Get("https://example.com")
if err != nil {
// 处理请求错误(此时 resp 可能为 nil
return err
}
// 确保在函数退出时关闭响应体
defer resp.Body.Close()
// 手动关闭,不用 defer
// io.Copy(io.Discard, resp.Body)
// resp.Body.Close()
// 读取或处理响应体(即使不需要内容也建议消费完)
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
// 使用 body ...
```
## 切片内存泄漏
> 即使原切片变量本身被置为 `nil` 或超出作用域,只要**还存在任何一个指向其底层数组的子切片**,整个底层数组就不会被回收
如果新切片需要长期保存,而原切片很大且即将被丢弃,那么**直接使用原切片[:n] 会导致新切片底层仍然引用原切片的整个底层数组**,使得那个大数组无法被 GC 回收,造成内存泄漏。此时应该用 `copy` 创建一份独立的、只包含所需元素的新切片。
```go
func getFirstLines() []string {
hugeLines := readHugeFile() // 假设这个切片有 100 万行,占用 100MB 内存
// 错误做法:返回子切片
return hugeLines[:3] // 虽然只返回3行但底层数组的 100MB 内存无法被回收!
}
// 正确做法
func getFirstLines() []string {
hugeLines := readHugeFile()
// 只拷贝需要的3行
result := make([]string, 3)
copy(result, hugeLines[:3])
return result // 原来的 hugeLines 可以被GC回收只占用很小的内存
}
```
## 堆栈
### 核心区别
| 特性 | 栈 | 堆 |
| --------- | -------------- | ------------------ |
| 分配/释放 | 移动指针,极快 | 需要查找 + GC 回收 |
| 生命周期 | 函数返回即销毁 | GC 决定何时回收 |
| 内存布局 | 连续,无碎片 | 分散,有碎片 |
| 大小限制 | 较小MB级 | 较大GB级 |
### 逃逸判断规则
```
变量在以下情况逃逸到堆:
1. 返回指针 → func f() *int { x := 42; return &x }
2. 存入全局变量 → globalPtr = &x
3. 闭包捕获 → return func() { return x }
4. 发送到 channel → ch <- &x
5. 存入外部结构体 → c.Value = &x
6. 接口装箱逃逸 → return interface{}(x)
7. 反射调用 → fmt.Println(x)
8. 大对象 → make([]int, 10000)
```
### 常见误区澄清
| 误区 | 正解 |
| ------------------ | -------------------------------- |
| 指针一定在堆上 | ❌ 函数内创建和使用的指针可在栈上 |
| 接口一定在堆上 | ❌ 不逃逸的接口内部值可在栈上 |
| 结构体指针一定逃逸 | ❌ 小结构体 + 内联优化可能栈分配 |
### 验证方法
```bash
# 查看逃逸分析
go build -gcflags="-m" main.go
# 更详细信息
go build -gcflags="-m -m" main.go
```
### 优化建议
```go
// ✅ 推荐:小对象返回值
func getUser() User {
return User{
ID: 1
}
}
// ❌ 避免:小对象返回指针(增加 GC 压力)
func getUser() *User {
return &User{
ID: 1
}
}
// ✅ 推荐:传指针给只读函数
func read(p *Data) int {
return p.Value
}
// ❌ 避免:在热路径使用 fmt.Print
// 用 strconv 或 log 代替
```
### 记忆口诀
```
生命周期越界 → 堆
外部有引用 → 堆
闭包捕获 → 堆
反射调用 → 堆
函数内使用 → 栈
返回值拷贝 → 栈
只读指针 → 栈
```
### 本质理解
**栈** = 函数的私有工作区,函数结束自动清理
**堆** = 共享仓库,需要 GC 定期打扫
逃逸分析 = 编译器判断"这个变量能不能安全地放在栈上"
## 类型转换/序列化
```
//int64转换string
strconv.FormatInt(num,10)
//序列化为string
jsoniter.ConfigCompatibleWithStandardLibrary.MarshalToString(input)
```
在 encode/decode JSON 数据并且数据类型是interface时Go 默认会将数值当做 float64 处理
```go
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Height float64 `json:"height"`
}
type UserInter struct {
Name interface{} `json:"name"`
Age interface{} `json:"age"`
Height interface{} `json:"height"`
}
func TestJson(t *testing.T) {
one := &User{
Name: "tom",
Age: 18,
}
oneJson, err := jsoniter.Marshal(one)
if err != nil {
fmt.Println(err)
return
}
var two UserInter
var three map[string]interface{}
err = jsoniter.Unmarshal(oneJson, &two)
err = jsoniter.Unmarshal(oneJson, &three)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(reflect.TypeOf(two.Name))
fmt.Println(reflect.TypeOf(two.Age)) //会打印float64
fmt.Println(reflect.TypeOf(two.Height)) //会打印float64
fmt.Println(reflect.TypeOf(three["name"]))
fmt.Println(reflect.TypeOf(three["age"])) //会打印float64
fmt.Println(reflect.TypeOf(three["height"])) //会打印float64
}
```
## `HTTP`阅后即焚
处理 HTTP Body 只能读一次的问题时,根据你的**数据量大小**选择武器:
1. **小请求配置、JSON**:使用 `io.ReadAll` 存入内存,再通过 `io.NopCloser` 塞回 `r.Body` 或设置 `r.GetBody`
2. **中等请求(签名校验)**:使用 `io.TeeReader` 边读边处理,节省一次全量扫描。
3. **大请求(文件上传)****禁止 ReadAll**。直接流式 `io.Copy` 到目的地,或使用临时文件做中转。

View File

@ -0,0 +1,55 @@
## 交叉编译
### 基本用法
```bash
# Linux/macOS
GOOS=目标系统 GOARCH=目标架构 go build -o 输出文件名 源文件
# Windows (PowerShell)
$env:GOOS="目标系统"; $env:GOARCH="目标架构"; go build -o 输出文件名 源文件
# Windows (CMD)
set GOOS=目标系统 && set GOARCH=目标架构 && go build -o 输出文件名 源文件
```
查看所有支持的平台
```bash
go tool dist list
```
### 常用组合
| 目标平台 | GOOS | GOARCH |
| ------------ | ------- | ------ |
| Linux 64位 | linux | amd64 |
| Linux ARM64 | linux | arm64 |
| Windows 64位 | windows | amd64 |
| macOS Intel | darwin | amd64 |
| macOS M1/M2 | darwin | arm64 |
### 示例
```bash
# Linux/Mac (Bash)
# 编译 Linux 64位
GOOS=linux GOARCH=amd64 go build -o app ./app/api/main.go
# 编译 Windows 64位
GOOS=windows GOARCH=amd64 go build -o app.exe ./app/api/main.go
# 编译 macOS ARM64 (M1/M2)
GOOS=darwin GOARCH=arm64 go build -o app ./app/api/main.go
# Windows (PowerShell)
# 编译 Linux 64位
$env:GOOS="linux"; $env:GOARCH="amd64"; go build -o app ./app/api/main.go
# 编译 Windows 64位
$env:GOOS="windows"; $env:GOARCH="amd64"; go build -o app.exe ./app/api/main.go
# 编译 macOS ARM64 (M1/M2)
$env:GOOS="darwin"; $env:GOARCH="arm64"; go build -o app ./app/api/main.go
```

View File

@ -0,0 +1,117 @@
### validator
[GitHub地址](github.com/go-playground/validator)
包验证器,可以用于验证参数是否符合规则,比如下方的比较验证,还有其他可以查看网址的详细介绍
go-zero自带的有校验器可以使用
| Tag | Description |
| ---- | --------------------- |
| eq | Equals |
| gt | Greater than |
| gte | Greater than or equal |
| lt | Less Than |
| lte | Less Than or Equal |
| ne | Not Equal |
### goverter
[GitHub地址](https://github.com/jmattheis/goverter)
类型转换器,可以用于类型的转换,比如下面的代码
```go
func (l *CreatePopupLogic) CreatePopup(in *backgroundrpcmodel.ResPopup) (*backgroundrpcmodel.ResResult, error) {
_, err := l.svcCtx.PopupModel.Insert(l.ctx, u.Convert.SysPopupR2S(in.Info))
if err != nil {
return nil, rerr.NewFromSQL(err)
}
return &backgroundrpcmodel.ResResult{
Result: true,
}, nil
}
```
使用
```
goverter gen .
```
### asynq
[GitHub网址](https://github.com/hibiken/asynq)
- 入队流程
```go
payload := &jobtype.xxxPayload{}
payloadByte, err := jsoniter.Marshal(payload)
if err != nil {
logt.Error("xxx jsoniter.Marshal err:", err)
return nil, zerr.New(rescode.DataFormatConversionError)
}
_, err = l.svcCtx.AsynqClient.Enqueue(asynq.NewTask(jobtype.xxx, payloadBytes, asynq.Queue(consts.QueueLow)))
if err != nil {
logt.Error("xxx enqueue err:", err)
return nil, zerr.New(rescode.EnqueueErr)
}
```
- 自行定义了队列优先级,在入队的时候可以加参数进行选择
```
consts.QueueCritical: 6,
consts.QueueDefault: 3,
consts.QueueLow: 1,
```
- 处理前流程(反序列化,获取数据)
```go
var payload jobtype.xxx
err := json.Unmarshal(task.Payload(), &payload)
if err != nil {
logt.Error("xxx unmarshal payload err:", err)
return zerr.New(rescode.DataFormatConversionError)
}
```
### gorm
[官网文档](https://gorm.io/zh_CN/docs/)
**使用gorm + sqlx但是复用sql连接**
```go
func NewServiceContext(c config.Config) *ServiceContext {
logx.Infof("Cache config: %+v", c.CacheRedis)
gormDB, err := gorm.Open(postgres.Open(c.Database.DataSource), &gorm.Config{})
if err != nil {
msg := fmt.Sprintf("连接数据库失败 %s ", err.Error())
logx.Error(msg)
panic(msg)
}
sqlDB, err := gormDB.DB()
if err != nil {
msg := fmt.Sprintf("获取底层连接失败: %s", err.Error())
logx.Error(msg)
panic(msg)
}
sqlxConn := sqlx.NewSqlConnFromDB(sqlDB)
return &ServiceContext{
Config: c,
UserAuthModel: models.NewUserAuthModel(sqlxConn, c.CacheRedis, gormDB),
}
}
```

View File

@ -0,0 +1,120 @@
### URL设计规范
URL为统一资源定位器 ,接口属于服务端资源通常一个完整的URL组成由以下几个部分构成
```
URI = scheme "://" host ":" port "/" path [ "?" query ][ "#" fragment ]
```
- **scheme**: 指底层用的协议如http、https、ftp
- **host**: 服务器的IP地址或者域名
- **port**: 端口http默认为80端口
- **path**: 访问资源的路径就是各种web 框架中定义的route路由
- **query**: 查询字符串,为发送给服务器的参数,在这里更多发送数据分页、排序等参数。
- **fragment**: 锚点,定位到页面的资源
**通常一个RESTful API的path组成**如下:
```undefined
/{version}/{resources}/{resource_id}
```
- **version**API版本号有些版本号放置在头信息中也可以通过控制版本号有利于应用迭代。
- **resources**资源RESTful API推荐用小写英文单词的复数形式。
- **resource_id**资源的id访问或操作该资源。
有时候可能资源级别较大其下还可细分很多子资源也可以灵活设计URL的path例如
```undefined
/{version}/{resources}/{resource_id}/{subresources}/{subresource_id}
```
当增删改查无法满足业务要求可以在URL末尾加上action例如
```
/{version}/{resources}/{resource_id}/action
```
其中action就是对资源的操作。
**RESTful API的URL具体设计的规范**如下:
1. 不用大写字母,所有单词使用英文且小写。
2. 连字符用中杠`"-"`而不用下杠`"_"`
3. 正确使用 `"/"`表示层级关系,URL的层级不要过深并且越靠前的层级应该相对越稳定
4. 结尾不要包含正斜杠分隔符`"/"`
5. URL中不出现动词用请求方式表示动作
6. 资源表示用复数不要用单数
7. 不要使用文件扩展名
**[参考网址](https://www.cnblogs.com/bigsai/p/14099154.html)**
---
### HTTP方法
- **GET**
GET请求会向数据库发索取数据的请求从而来获取资源该请求就像数据库的select操作一样**只是用来查询数据,不会影响资源的内容**。无论进行多少次操作,结果都是一样的
- **POST**
POST请求向服务器发送数据但是该请求**会改变数据的内容(新添)**,就像数据库的`insert`操作一样会创建新的内容且POST请求的请求参数都是请求体中其大小是没有限制的
- **PUT**
PUT请求是向服务器端发送数据的 与POST请求不同的是PUT请求**侧重于数据的修改 ,就像数据库中update一样**而POST请求侧重于数据的增加
- **DELETE**
用来**删除资源**,和数据库中`delete`相对应
### 参数填写位置
- **ParamsQuery 参数)**:用于 GET 请求或其他将参数**附加在 URL 中**的情况。
- **Body请求体**:用于 POST、PUT 等请求方法,将参数**包含在请求体**中。
### 常见的请求文件类型及其适用场景
- **application/json**
用途:用于传输结构化数据,如对象和数组。
场景:客户端与服务器之间交换数据时,尤其是在前后端分离的应用中。
- **multipart/form-data**
用途:用于上传文件或包含文件的表单数据。
场景:用户上传图片、视频、文档等文件。
- **application/x-www-form-urlencoded**
用途:用于提交简单的表单数据。
场景:传统的表单提交,数据量较小且不包含文件。
- **application/octet-stream**
用途:用于传输二进制数据。
场景:下载文件或通过 API 提供文件流。

View File

@ -0,0 +1,92 @@
## Cookie
| 维度 | 说明 |
| :----------- | :----------------------------------------------------------- |
| **本质** | 存储在浏览器端的一小段文本数据(通常 4KB 以内) |
| **核心机制** | 浏览器在每次 HTTP 请求中**自动携带**符合域名/路径规则的 Cookie |
| **关键属性** | `HttpOnly`(防 JS 读取)、`Secure`(仅 HTTPS、`SameSite`(防 CSRF、`Expires/Max-Age` |
| **定位** | **传输载体,而非认证方案本身**——它负责把令牌Session ID 或 Token在客户端和服务器之间来回搬运 |
**优点**
- 简单成熟,几乎所有 Web 框架默认集成
- 服务端可随时销毁会话,控制力强
- 敏感信息存在服务端,不暴露给客户端
**缺点**
- 扩展性受限——多服务器需共享 Session引入 Redis 等外部存储)
- 依赖 Cookie纯移动端/非浏览器环境不友好
- 容易受到 CSRF 攻击(需额外防护)
## Session
| 维度 | 说明 |
| :----------- | :----------------------------------------------------------- |
| **本质** | 服务器端维护的用户会话数据(通常以 Map 结构存在内存、Redis 或数据库中) |
| **认证流程** | 用户登录 → 服务器创建 Session → 将 Session ID 通过 `Set-Cookie` 返回 → 浏览器后续请求自动携带该 Cookie → 服务器通过 Session ID 查找会话数据验证身份 |
| **核心特点** | **有状态**:服务器持有用户上下文,可随时主动吊销 |
| **定位** | 经典的**服务端主导**的认证方式,适合传统 Web 应用和前后端未彻底分离的场景 |
**优点**
- 简单成熟,几乎所有 Web 框架默认集成
- 服务端可随时销毁会话,控制力强
- 敏感信息存在服务端,不暴露给客户端
**缺点**
- 扩展性受限——多服务器需共享 Session引入 Redis 等外部存储)
- 依赖 Cookie纯移动端/非浏览器环境不友好
- 容易受到 CSRF 攻击(需额外防护)
## JWT
| **本质** | 一段经过签名(或加密)的 JSON 数据,由 Header、Payload、Signature 三部分组成,以 Base64URL 编码 |
| ------------ | ------------------------------------------------------------ |
| **认证流程** | 用户登录 → 服务器验证凭据并签发 JWT → 客户端存储 Token → 后续请求在 `Authorization: Bearer <token>` 头中携带 → 服务器**仅验证签名和有效期**,无需查库 |
| **核心特点** | **无状态**Token 自身包含用户身份信息,服务器不需要维护会话 |
| **定位** | 现代**前后端分离架构**和**多端Web/iOS/Android统一接入**的事实标准 |
**优点**
- 天然无状态,服务端可随意水平扩展
- 不依赖 Cookie可用于移动端、小程序等非浏览器环境
- 跨域友好CORS 场景下比 Cookie 更灵活)
- Payload 可携带少量业务数据,减少数据库查询
**缺点**
- Token 一旦签发,在过期前无法主动撤销(需引入黑名单/版本号等机制)
- Payload 仅 Base64 编码(非加密),敏感信息绝对不能放入
- Token 体积比 Session ID 大,每次请求都传输有一定带宽开销
## SSO
| 维度 | 说明 |
| :----------- | :----------------------------------------------------------- |
| **本质** | 一种架构模式,通过**统一的认证中心IdP**,让用户在多个独立系统间共享登录状态 |
| **认证流程** | 用户访问应用A → 重定向到 SSO 认证中心 → 登录并获取令牌 → 带回令牌访问应用A → 再访问应用B时认证中心识别已登录状态直接放行 |
| **常见实现** | CAS、SAML、OAuth 2.0 + OpenID Connect |
| **定位** | **企业级、多系统生态**的统一身份认证方案 |
**优点**
- 用户体验极佳:一次登录,访问所有关联系统
- 集中管理:统一账号体系、权限策略、安全审计
- 安全性提升减少密码泄露面可配合多因素认证MFA
**缺点**
- **单点故障风险**:认证中心一旦宕机,所有系统登录受影响
- 实现复杂:涉及协议配置、令牌传递、跨域重定向等
- 对认证中心的高可用要求极高
## OAuth 2.0
| 维度 | 说明 |
| :----------- | :----------------------------------------------------------- |
| **本质** | 一种**授权框架**(而非纯粹的认证协议),允许用户授权第三方应用在**不暴露自己密码**的情况下访问受保护资源 |
| **核心角色** | 资源拥有者(用户)、客户端(第三方应用)、授权服务器、资源服务器 |
| **核心流程** | 用户授权 → 第三方获取授权码 → 换取 Access Token → 持 Token 访问资源 |
| **定位** | **第三方接入**和**开放平台**的标准授权方案微信登录、Google 登录等) |

17
note/随手记/Windows.md Normal file
View File

@ -0,0 +1,17 @@
## 快速复制文件路径
### Shift + 右键菜单(通用)
1. 在文件资源管理器中,**按住 `Shift` 键不放**
2. 右键点击目标文件
3. 选择 **「复制为路径(A)」**
### 功能区按钮
1. 选中文件
2. 点击资源管理器顶部的 **「主页」** 选项卡
3. 点击 **「复制路径」** 按钮(剪贴板图标)
### 拖拽到终端
- 直接把文件**拖入** PowerShell 或 CMD 窗口,会自动粘贴完整路径。

50
note/随手记/go.md Normal file
View File

@ -0,0 +1,50 @@
## 句点引入
一个完整引入声明语句形式的引入名`importname`可以是一个句点(`.`)。 这样的引入称为句点引入。使用被句点引入的包中的导出代码要素时,限定标识符的前缀必须省略。
比如在下面这个例子中,`Println`和`Now`函数调用不需要带任何前缀。
```go
package main
import (
. "fmt"
. "time"
)
func main() {
Println("Current time:", Now())
}
```
> 一般来说,句点引入不推荐使用,因为它们会导致较低的代码可读性
## map的坑
go的map内部是哈希表元素会因 rehash扩容而移动到新的内存位置如果允许 `&m[key]` 拿到一个指针map 一旦扩容,这个指针就变成悬空指针(指向已失效的内存),为了避免这种内存安全问题Go 直接禁止对 map 值取地址,下面是示例代码:
```go
type User struct {
Name string
Age int
}
func main() {
m := map[int]User{1: {Name: "张三", Age: 20}}
// ❌ 编译错误cannot assign to struct field m[1].Name in map
m[1].Name = "李四"
// ✅ 正确的替代写法
temp:=m[1]
temp.Name = "李四"
m[1]=temp
}
```
> **可寻址addressable**:能通过 `&` 拿到它内存地址的操作数。
>
> **不可寻址not addressable**:不能通过 `&` 拿到地址的操作数。

View File

@ -0,0 +1,176 @@
```json
{
"$schema": "https://unpkg.com/oh-my-opencode-slim@latest/oh-my-opencode-slim.schema.json",
"preset": "zhipu",
"presets": {
"opencode-go": {
"orchestrator": {
"model": "opencode-go/glm-5.2",
"skills": [
"*"
],
"mcps": [
"*",
"!context7"
]
},
"oracle": {
"model": "deepseek/deepseek-v4-pro",
"variant": "max",
"skills": [
"simplify"
],
"mcps": []
},
"council": {
"model": "opencode-go/qwen3.7-max",
"variant": "high",
"skills": [],
"mcps": []
},
"librarian": {
"model": "opencode-go/minimax-m3",
"skills": [],
"mcps": [
"websearch",
"context7",
"gh_grep"
]
},
"explorer": {
"model": "opencode-go/mimo-v2.5-pro",
"skills": [],
"mcps": []
},
"designer": {
"model": "opencode-go/kimi-k2.7-code",
"variant": "medium",
"skills": [],
"mcps": []
},
"fixer": {
"model": "opencode-go/deepseek-v4-flash",
"variant": "high",
"skills": [],
"mcps": []
},
"observer": {
"model": "opencode-go/kimi-k2.7-code",
"skills": [],
"mcps": []
}
},
"zhipu": {
"orchestrator": {
"model": "zhipuai-coding-plan/glm-5.2",
"skills": [
"*"
],
"mcps": [
"*",
"!context7"
]
},
"oracle": {
"model": "zhipuai-coding-plan/glm-5.2",
"variant": "max",
"skills": [
"simplify"
],
"mcps": []
},
"council": {
"model": "zhipuai-coding-plan/glm-5.1",
"skills": [],
"mcps": []
},
"librarian": {
"model": "zhipuai-coding-plan/glm-5-turbo",
"skills": [],
"mcps": [
"websearch",
"context7",
"gh_grep"
]
},
"explorer": {
"model": "zhipuai-coding-plan/glm-5-turbo",
"skills": [],
"mcps": []
},
"designer": {
"model": "zhipuai-coding-plan/glm-5.2",
"variant": "medium",
"skills": [],
"mcps": []
},
"fixer": {
"model": "zhipuai-coding-plan/glm-5.2",
"skills": [],
"mcps": []
},
"observer": {
"model": "zhipuai-coding-plan/glm-4.6v",
"skills": [],
"mcps": []
}
},
"deepseek": {
"orchestrator": {
"model": "deepseek/deepseek-v4-pro",
"variant": "high",
"skills": [
"*"
],
"mcps": [
"*",
"!context7"
]
},
"oracle": {
"model": "deepseek/deepseek-v4-pro",
"variant": "max",
"skills": [
"simplify"
],
"mcps": []
},
"council": {
"model": "deepseek/deepseek-v4-pro",
"variant": "medium",
"skills": [],
"mcps": []
},
"librarian": {
"model": "deepseek/deepseek-v4-flash",
"skills": [],
"mcps": [
"websearch",
"context7",
"gh_grep"
]
},
"explorer": {
"model": "deepseek/deepseek-v4-flash",
"skills": [],
"mcps": []
},
"designer": {
"model": "deepseek/deepseek-v4-pro",
"variant": "medium",
"skills": [],
"mcps": []
},
"fixer": {
"model": "deepseek/deepseek-v4-flash",
"variant": "high",
"skills": [],
"mcps": []
}
}
},
"disabled_agents": []
}
```

56
note/随手记/算法.md Normal file
View File

@ -0,0 +1,56 @@
## 轮询算法
### 平滑加权轮询
#### 原理
每台服务器维护一个 `当前权重`currentWeight每次选择 `currentWeight` 最大的服务器,选中后将其 `currentWeight` 减去总权重,然后**所有服务器都加上自己的固定权重**。如此循环,高权重节点被选中的频率更高,但不会连续出现。
#### 实际应用
- **Nginx**`upstream` 默认负载均衡算法
- **LVS**Linux Virtual Server
- 几乎所有需要「按比例分配且平滑」的场景
#### 轮询示例
```go
type server struct {
name string
weight int
curWeight int
}
func NextServer(servers []*server) *server {
var totalWeight int
// 1.先给所有服务器加上各自的固定权重
for i := range servers {
servers[i].curWeight += servers[i].weight
totalWeight += servers[i].weight
}
// 2.再找出 curWeight 最大的服务器
maxIdx := 0
for i := 1; i < len(servers); i++ {
if servers[i].curWeight > servers[maxIdx].curWeight {
maxIdx = i
}
}
// 3.选中的服务器减去总权重
servers[maxIdx].curWeight -= totalWeight
return servers[maxIdx]
}
```
| 轮次 | 操作前 (currentWeight) | 选中 | 操作后 |
| ---- | ---------------------- | ----- | --------------- |
| 初始 | A=0, B=0, C=0 | — | — |
| 1 | A=5, B=1, C=1 | **A** | A=2, B=1, C=1 |
| 2 | A=3, B=2, C=2 | **A** | A=4, B=2, C=2 |
| 3 | A=1, B=3, C=3 | **B** | A=1, B=4, C=3 |
| 4 | A=6, B=3, C=4 | **A** | A=1, B=3, C=4 |
| 5 | A=4, B=2, C=5 | **C** | A=4, B=2, C=2 |
| 6 | A=9, B=1, C=1 | **A** | A=2, B=1, C=1 |
| 7 | A=7, B=0, C=0 | **A** | A=0, B=0, C=0 |

4
note/随手记/网址.md Normal file
View File

@ -0,0 +1,4 @@
[Typora onelight 主题](https://github.com/caolib/typora-onelight-theme?tab=readme-ov-file#4%E5%85%B3%E4%BA%8E%E5%AD%97%E4%BD%93)