-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfeed.json
More file actions
237 lines (237 loc) · 180 KB
/
feed.json
File metadata and controls
237 lines (237 loc) · 180 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
{
"version": "https://jsonfeed.org/version/1",
"title": "翼舞成梦",
"subtitle": "翼舞成梦",
"icon": "https://costalong.com/images/favicon.ico",
"description": "翼舞成梦的个人博客,专注于后端开发、K8s 容器化技术、Linux 运维以及人工智能学习笔记分享。",
"home_page_url": "https://costalong.com",
"items": [
{
"id": "https://costalong.com/2026/03/01/llama/llama-install/",
"url": "https://costalong.com/2026/03/01/llama/llama-install/",
"title": "llama 安装",
"date_published": "2026-03-01T14:59:10.099Z",
"content_html": "<h2 id=\"安装环境\"><a href=\"#安装环境\" class=\"headerlink\" title=\"安装环境\"></a>安装环境</h2><pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">sudo</span> add-apt-repository ppa:deadsnakes/ppa\n<span class=\"token function\">sudo</span> apt update\n<span class=\"token function\">sudo</span> apt <span class=\"token function\">install</span> python3.11 python3.11-venv -y\n</code></pre>\n<p>安装虚拟环境</p>\n<pre class=\" language-bash\"><code class=\"language-bash\">/usr/bin/python3.11 -m venv venv\n</code></pre>\n<p>使用虚拟</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">source</span> venv/bin/activate\n<span class=\"token comment\" spellcheck=\"true\"># 如果使用的 fish</span>\n<span class=\"token function\">source</span> venv/bin/activate.fish\n</code></pre>\n<p>安装 Install PyTorch:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"> pip <span class=\"token function\">install</span> --pre torch torchvision --extra-index-url https://download.pytorch.org/whl/nightly/cpu\n</code></pre>\n<h2 id=\"下载\"><a href=\"#下载\" class=\"headerlink\" title=\"下载\"></a>下载</h2><p>1. 下载 llama.cp</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> clone https://github.com/ggerganov/llama.cpp.git \n</code></pre>\n<ol>\n<li>下载通义千问1.5-7B模型 或 1.5-32B 模型&#x20;</li>\n</ol>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token comment\" spellcheck=\"true\"># 使用 huggingface 仓库</span>\n<span class=\"token function\">git</span> clone https://huggingface.co/Qwen/Qwen1.5-7B-Chat\n<span class=\"token comment\" spellcheck=\"true\"># 或</span>\n<span class=\"token function\">git</span> clone https://huggingface.co/qwen/Qwen1.5-32B-Chat.git\n\n\n<span class=\"token comment\" spellcheck=\"true\"># 使用 modelscope 仓库</span>\n<span class=\"token function\">git</span> clone https://www.modelscope.cn/Qwen/Qwen1.5-7B-Chat\n<span class=\"token comment\" spellcheck=\"true\"># 或</span>\n<span class=\"token function\">git</span> clone https://www.modelscope.cn/qwen/Qwen1.5-32B-Chat.git\n</code></pre>\n<h2 id=\"编译-llama-cp\"><a href=\"#编译-llama-cp\" class=\"headerlink\" title=\"编译 llama.cp\"></a>编译 llama.cp</h2><pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">cd</span> llama.cpp \n<span class=\"token function\">make</span>\n</code></pre>\n<h3 id=\"安装llama-依赖\"><a href=\"#安装llama-依赖\" class=\"headerlink\" title=\"安装llama 依赖\"></a>安装llama 依赖</h3><pre class=\" language-bash\"><code class=\"language-bash\">python3 -m pip <span class=\"token function\">install</span> -r requirements.txt\n</code></pre>\n<h3 id=\"转换-Qwen-模型为-GGUF\"><a href=\"#转换-Qwen-模型为-GGUF\" class=\"headerlink\" title=\"转换 Qwen 模型为 GGUF\"></a>转换 Qwen 模型为 GGUF</h3><p>什么是 GGUF? GGUF是一种用于存储用于GGML推断和基于GGML的执行器的模型的文件格式。GGUF是一种二进制格式,旨在快速加载和保存模型,并易于阅读。传统上,模型是使用PyTorch或其他框架开发的,然后转换为GGUF以在GGML中使用。</p>\n<p>GGUF是GGML、GGMF和GGJT的后继文件格式,旨在通过包含加载模型所需的所有信息来消除歧义。它还设计为可扩展的,因此可以向模型添加新信息而不会破坏兼容性,更多信息访问<a href=\"https://github.com/ggerganov/ggml/blob/master/docs/gguf.md\" title=\"官方说明文档\">官方说明文档</a>。</p>\n<p>查看文件结构</p>\n<pre class=\" language-bash\"><code class=\"language-bash\">$ tree -L 1\n<span class=\"token keyword\">.</span>\n├── llama.cpp \n├── Qwen1.5-32B-Chat\n└── venv\n</code></pre>\n<p>执行这转换命令</p>\n<pre class=\" language-bash\"><code class=\"language-bash\">python3 convert-hf-to-gguf.py <span class=\"token punctuation\">..</span>/Qwen1.5-32B-Chat\n\n<span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">..</span><span class=\"token punctuation\">..</span>.\nINFO:hf-to-gguf:Model successfully exported to <span class=\"token string\">'../Qwen1.5-32B-Chat/ggml-model-f16.gguf'</span>\n</code></pre>\n<p>最后的结果表示已经转为 F16 的 gguf 格式的模型了</p>\n<h3 id=\"量化模型\"><a href=\"#量化模型\" class=\"headerlink\" title=\"量化模型\"></a>量化模型</h3><p>将gguf 的模型量化到INT4&#x20;</p>\n<pre class=\" language-bash\"><code class=\"language-bash\">./llama-quantize <span class=\"token punctuation\">..</span>/Qwen1.5-32B-Chat/ggml-model-f16.gguf ./models/qwen1.5-chat-ggml-model-Q4_K_M.gguf Q4_K_M\n<span class=\"token operator\">=</span><span class=\"token operator\">></span> <span class=\"token punctuation\">..</span><span class=\"token punctuation\">..</span>\n<span class=\"token punctuation\">[</span> 767/ 771<span class=\"token punctuation\">]</span> blk.63.attn_q.bias - <span class=\"token punctuation\">[</span> 5120, 1, 1, 1<span class=\"token punctuation\">]</span>, <span class=\"token function\">type</span> <span class=\"token operator\">=</span> f32, size <span class=\"token operator\">=</span> 0.020 MB\n<span class=\"token punctuation\">[</span> 768/ 771<span class=\"token punctuation\">]</span> blk.63.attn_q.weight - <span class=\"token punctuation\">[</span> 5120, 5120, 1, 1<span class=\"token punctuation\">]</span>, <span class=\"token function\">type</span> <span class=\"token operator\">=</span> f16, converting to q4_K <span class=\"token punctuation\">..</span> size <span class=\"token operator\">=</span> 50.00 MiB -<span class=\"token operator\">></span> 14.06 MiB\n<span class=\"token punctuation\">[</span> 769/ 771<span class=\"token punctuation\">]</span> blk.63.attn_v.bias - <span class=\"token punctuation\">[</span> 1024, 1, 1, 1<span class=\"token punctuation\">]</span>, <span class=\"token function\">type</span> <span class=\"token operator\">=</span> f32, size <span class=\"token operator\">=</span> 0.004 MB\n<span class=\"token punctuation\">[</span> 770/ 771<span class=\"token punctuation\">]</span> blk.63.attn_v.weight - <span class=\"token punctuation\">[</span> 5120, 1024, 1, 1<span class=\"token punctuation\">]</span>, <span class=\"token function\">type</span> <span class=\"token operator\">=</span> f16, converting to q6_K <span class=\"token punctuation\">..</span> size <span class=\"token operator\">=</span> 10.00 MiB -<span class=\"token operator\">></span> 4.10 MiB\n<span class=\"token punctuation\">[</span> 771/ 771<span class=\"token punctuation\">]</span> output_norm.weight - <span class=\"token punctuation\">[</span> 5120, 1, 1, 1<span class=\"token punctuation\">]</span>, <span class=\"token function\">type</span> <span class=\"token operator\">=</span> f32, size <span class=\"token operator\">=</span> 0.020 MB\nllama_model_quantize_internal: model size <span class=\"token operator\">=</span> 62014.27 MB\nllama_model_quantize_internal: quant size <span class=\"token operator\">=</span> 18780.70 MB\n\nmain: quantize <span class=\"token function\">time</span> <span class=\"token operator\">=</span> 421046.36 ms\nmain: total <span class=\"token function\">time</span> <span class=\"token operator\">=</span> 421046.36 ms\n\n\n</code></pre>\n<p>../Qwen1.5-32B-Chat/ggml-model-f16.gguf 是转换的源文件路径</p>\n<p>./models/qwen1.5-chat-ggml-model-Q4_K_M.gguf 是转换成功生成的文件路径</p>\n<p>Q4_K_M 是量化方法</p>\n<p>新版量化方法包括:</p>\n<ul>\n<li>Q2_K</li>\n<li>Q3_K_S, Q3_K_M, Q3_K_L</li>\n<li>Q4_K_S, Q4_K_M</li>\n<li>Q5_K_S, Q5_K_M</li>\n<li>Q6_K</li>\n</ul>\n<h3 id=\"运行\"><a href=\"#运行\" class=\"headerlink\" title=\"运行\"></a>运行</h3><h3 id=\"运行测试\"><a href=\"#运行测试\" class=\"headerlink\" title=\"运行测试\"></a>运行测试</h3><pre class=\" language-bash\"><code class=\"language-bash\">./llama-cli -m ./models/qwen1.5-chat-ggml-model-Q4_K_M.gguf -n 128\n<span class=\"token operator\">=</span><span class=\"token operator\">></span> \n\n1<span class=\"token punctuation\">)</span> 甲乙丙丁四地中,最可能出现水土流失的是\nA. 甲\nB0 乙\nC0 丙\nD0 丁\n答案:C\n关键点:中国分区地理,水土流失及治理,区域农业生产的条件、布局特点、问题\n\n\nllama_print_timings: load <span class=\"token function\">time</span> <span class=\"token operator\">=</span> 17027.80 ms\nllama_print_timings: sample <span class=\"token function\">time</span> <span class=\"token operator\">=</span> 22.98 ms / 128 runs <span class=\"token punctuation\">(</span> 0.18 ms per token, 5571.27 tokens per second<span class=\"token punctuation\">)</span>\nllama_print_timings: prompt <span class=\"token function\">eval</span> <span class=\"token function\">time</span> <span class=\"token operator\">=</span> 0.00 ms / 0 tokens <span class=\"token punctuation\">(</span> -nan ms per token, -nan tokens per second<span class=\"token punctuation\">)</span>\nllama_print_timings: <span class=\"token function\">eval</span> <span class=\"token function\">time</span> <span class=\"token operator\">=</span> 867086.12 ms / 128 runs <span class=\"token punctuation\">(</span> 6774.11 ms per token, 0.15 tokens per second<span class=\"token punctuation\">)</span>\nllama_print_timings: total <span class=\"token function\">time</span> <span class=\"token operator\">=</span> 867381.32 ms / 128 tokens\nLog end\n</code></pre>\n<p>注意: 这个运行等待的时间比较长</p>\n<p>接下来我们进入对话模型。如何启动呢?我们这里查看 examples/chat.sh的启动方式,来编写启动 Qwen 模型命令。</p>\n<pre class=\" language-bash\"><code class=\"language-bash\">./llama-cli -m ./models/qwen1.5-chat-ggml-model-Q4_K_M.gguf -n 256 --grammar-file grammars/json.gbnf -p <span class=\"token string\">'Request: schedule a call at 8pm; Command:'</span>\n</code></pre>\n<h2 id=\"错误\"><a href=\"#错误\" class=\"headerlink\" title=\"错误\"></a>错误</h2><ol>\n<li>通义千问safetensors_rust.SafetensorError: Error while deserializing header: HeaderTooLarge解决方法</li>\n</ol>\n<p>安装 gi-lfs</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">sudo</span> <span class=\"token function\">apt-get</span> <span class=\"token function\">install</span> git-lfs\n</code></pre>\n<p>进入 Qwen1.5-32B-Chat 执行 git lfs pull</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">cd</span> Qwen1.5-32B-Chat <span class=\"token operator\">&&</span> <span class=\"token function\">git</span> lfs pull\n</code></pre>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"llama",
"ggml"
]
},
{
"id": "https://costalong.com/2026/03/01/moment-2026-03-01-book/",
"url": "https://costalong.com/2026/03/01/moment-2026-03-01-book/",
"title": "周末读了一本好书",
"date_published": "2026-03-01T02:30:00.000Z",
"content_html": "<p>周末花了一下午读完了《设计心理学》,诺曼对日常物品的洞察力太强了。好的设计应该是不需要说明书的。</p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"moments",
"读书"
]
},
{
"id": "https://costalong.com/2026/02/25/moment-2026-02-25-coffee/",
"url": "https://costalong.com/2026/02/25/moment-2026-02-25-coffee/",
"title": "尝试了新的咖啡豆",
"date_published": "2026-02-25T00:15:00.000Z",
"content_html": "<p>今天开了一包来自云南的日晒豆,风味很惊喜 – 有明显的蓝莓和巧克力调。国产精品豆越来越棒了。</p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"moments",
"咖啡",
"生活"
]
},
{
"id": "https://costalong.com/2026/02/20/moment-2026-02-20-k8s/",
"url": "https://costalong.com/2026/02/20/moment-2026-02-20-k8s/",
"title": "终于搞定了 K8s 网络策略",
"date_published": "2026-02-20T14:45:00.000Z",
"content_html": "<p>折腾了两天的 NetworkPolicy 终于通了。关键是 <code>namespaceSelector</code> 和 <code>podSelector</code> 要同时匹配,不能只写一个。记个笔记以防下次再踩坑。</p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"moments",
"k8s",
"技术"
]
},
{
"id": "https://costalong.com/2026/01/03/claude-code-cc-switch-antigravity-tools-zu-he-shi-yong-zhi-nan/",
"url": "https://costalong.com/2026/01/03/claude-code-cc-switch-antigravity-tools-zu-he-shi-yong-zhi-nan/",
"title": "Claude Code + CC-Switch + Antigravity Tools 组合使用指南",
"date_published": "2026-01-03T15:42:42.000Z",
"content_html": "<p><img src=\"/medias/loading.gif\" data-original=\"https://file.costalong.com/img/20260103224723137.png\" alt=\"image.png\"></p>\n<h2 id=\"概述\"><a href=\"#概述\" class=\"headerlink\" title=\"概述\"></a>概述</h2><p>结合了 Claude Code (强大的 AI 编程助手)、CC-Switch (假设为上下文/配置快速切换工具) 和 Antigravity Tools</p>\n<table>\n<thead>\n<tr>\n<th>工具</th>\n<th>功能</th>\n<th>作用</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><strong>Claude Code</strong></td>\n<td>Anthropic 官方 CLI 工具</td>\n<td>在终端中与 Claude 交互,进行代码编写、调试、重构等</td>\n</tr>\n<tr>\n<td><strong>CC-Switch</strong></td>\n<td>账号切换管理器</td>\n<td>管理多个 Claude 账号,一键切换,支持使用量统计</td>\n</tr>\n<tr>\n<td><strong>Antigravity Tools</strong></td>\n<td>API 反代 & 账号管理</td>\n<td>提供本地 API 代理服务,支持 OAuth 授权管理多账号</td>\n</tr>\n</tbody></table>\n<h3 id=\"工作原理\"><a href=\"#工作原理\" class=\"headerlink\" title=\"工作原理\"></a>工作原理</h3><pre><code>Claude Code CLI → Antigravity Tools (本地反代) → Claude API\n ↑\n CC-Switch (账号管理)\n</code></pre>\n<ul>\n<li><strong>Antigravity Tools</strong> 作为本地代理服务器,转发 Claude Code 的请求</li>\n<li><strong>CC-Switch</strong> 可以在 Antigravity 之上提供更便捷的账号切换功能</li>\n<li>三者配合使用,实现多账号管理和无缝切换</li>\n</ul>\n<hr>\n<h2 id=\"安装\"><a href=\"#安装\" class=\"headerlink\" title=\"安装\"></a>安装</h2><h3 id=\"1-Claude-Code\"><a href=\"#1-Claude-Code\" class=\"headerlink\" title=\"1. Claude Code\"></a>1. Claude Code</h3><p><a href=\"https://github.com/anthropics/claude-code\">GitHub 仓库</a></p>\n<p><strong>前提条件</strong>:</p>\n<ul>\n<li>Node.js 18+</li>\n<li>npm 或其他包管理器</li>\n</ul>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> -g @anthropic-ai/claude-code\n</code></pre>\n<p>安装后验证:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\">claude --version\n</code></pre>\n<h3 id=\"2-CC-Switch\"><a href=\"#2-CC-Switch\" class=\"headerlink\" title=\"2. CC-Switch\"></a>2. CC-Switch</h3><p><a href=\"https://github.com/farion1231/cc-switch\">GitHub 仓库</a></p>\n<p><strong>macOS 安装</strong>:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\">brew tap farion1231/ccswitch\nbrew <span class=\"token function\">install</span> --cask cc-switch\n</code></pre>\n<p><strong>Linux 安装</strong>:<br>前往 <a href=\"https://github.com/farion1231/cc-switch/releases\">Releases 页面</a> 下载适合的安装包:</p>\n<ul>\n<li><code>.deb</code> - Debian/Ubuntu 系</li>\n<li><code>.rpm</code> - RedHat/Fedora 系</li>\n<li><code>AppImage</code> - 通用格式</li>\n</ul>\n<h3 id=\"3-Antigravity-Tools\"><a href=\"#3-Antigravity-Tools\" class=\"headerlink\" title=\"3. Antigravity Tools\"></a>3. Antigravity Tools</h3><p><a href=\"https://github.com/lbjlaq/Antigravity-Manager\">GitHub 仓库</a></p>\n<p>前往 <a href=\"https://github.com/lbjlaq/Antigravity-Manager/releases\">GitHub Releases</a> 下载对应系统的包:</p>\n<table>\n<thead>\n<tr>\n<th>系统</th>\n<th>格式</th>\n<th>说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><strong>macOS</strong></td>\n<td><code>.dmg</code></td>\n<td>支持 Apple Silicon & Intel</td>\n</tr>\n<tr>\n<td><strong>Windows</strong></td>\n<td><code>.msi</code> 或 <code>.zip</code></td>\n<td>安装版或便携版</td>\n</tr>\n<tr>\n<td><strong>Linux</strong></td>\n<td><code>.deb</code> 或 <code>AppImage</code></td>\n<td>根据发行版选择</td>\n</tr>\n</tbody></table>\n<hr>\n<h2 id=\"配置\"><a href=\"#配置\" class=\"headerlink\" title=\"配置\"></a>配置</h2><h3 id=\"CC-Switch-配置\"><a href=\"#CC-Switch-配置\" class=\"headerlink\" title=\"CC-Switch 配置\"></a>CC-Switch 配置</h3><p><img src=\"/medias/loading.gif\" data-original=\"https://file.costalong.com/img/20260103205511054.png\" alt=\"CC-Switch 配置界面\"></p>\n<p><strong>主要功能</strong>:</p>\n<ul>\n<li>管理多个 Claude 账号</li>\n<li>快速切换当前使用的账号</li>\n<li>查看各账号的使用量统计</li>\n<li>支持快捷键切换</li>\n</ul>\n<h3 id=\"Antigravity-Tools-配置\"><a href=\"#Antigravity-Tools-配置\" class=\"headerlink\" title=\"Antigravity Tools 配置\"></a>Antigravity Tools 配置</h3><p><img src=\"/medias/loading.gif\" data-original=\"https://file.costalong.com/img/20260103205426774.png\" alt=\"Antigravity Tools 主界面\"></p>\n<p><img src=\"/medias/loading.gif\" data-original=\"https://file.costalong.com/img/20260103205632292.png\" alt=\"Antigravity Tools API 反代设置\"></p>\n<p><strong>关键配置项</strong>:</p>\n<ol>\n<li><strong>API 反代端口</strong>:默认 <code>8045</code>,可自定义</li>\n<li><strong>账号管理</strong>:通过 OAuth 添加多个 Claude 账号</li>\n<li><strong>负载均衡</strong>:可配置多账号轮询或指定账号</li>\n</ol>\n<hr>\n<h2 id=\"快速接入\"><a href=\"#快速接入\" class=\"headerlink\" title=\"快速接入\"></a>快速接入</h2><h3 id=\"步骤一:添加-Claude-账号(OAuth-授权)\"><a href=\"#步骤一:添加-Claude-账号(OAuth-授权)\" class=\"headerlink\" title=\"步骤一:添加 Claude 账号(OAuth 授权)\"></a>步骤一:添加 Claude 账号(OAuth 授权)</h3><ol>\n<li>打开 Antigravity Tools → “账号 / Accounts” → “添加账号” → “OAuth”</li>\n<li>点击生成的授权链接,链接会自动复制到剪贴板</li>\n<li>在浏览器中打开链接,完成 Claude 授权</li>\n<li>授权成功后浏览器显示 “✅ 授权成功!”</li>\n<li>应用自动完成后续流程;如未自动完成,点击”我已授权,继续”</li>\n</ol>\n<blockquote>\n<p><strong>注意</strong>:授权链接包含一次性回调端口,请使用弹窗里生成的最新链接。如果授权时应用未运行,浏览器可能会提示 <code>localhost refused connection</code>。</p>\n</blockquote>\n<h3 id=\"步骤二:启动-API-反代服务\"><a href=\"#步骤二:启动-API-反代服务\" class=\"headerlink\" title=\"步骤二:启动 API 反代服务\"></a>步骤二:启动 API 反代服务</h3><ol>\n<li>在 Antigravity Tools 中打开 “API 反代” 页面</li>\n<li>点击 “开启服务”</li>\n<li>确认服务状态为 “运行中”</li>\n</ol>\n<h3 id=\"步骤三:配置-Claude-Code-环境变量\"><a href=\"#步骤三:配置-Claude-Code-环境变量\" class=\"headerlink\" title=\"步骤三:配置 Claude Code 环境变量\"></a>步骤三:配置 Claude Code 环境变量</h3><p>在终端中设置环境变量:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">export</span> ANTHROPIC_API_KEY<span class=\"token operator\">=</span><span class=\"token string\">\"sk-antigravity\"</span>\n<span class=\"token function\">export</span> ANTHROPIC_BASE_URL<span class=\"token operator\">=</span><span class=\"token string\">\"http://127.0.0.1:8045\"</span>\n</code></pre>\n<p><strong>持久化配置</strong>(添加到 shell 配置文件):</p>\n<p><strong>Zsh</strong> (~/.zshrc):</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token keyword\">echo</span> <span class=\"token string\">'export ANTHROPIC_API_KEY=\"sk-antigravity\"'</span> <span class=\"token operator\">>></span> ~/.zshrc\n<span class=\"token keyword\">echo</span> <span class=\"token string\">'export ANTHROPIC_BASE_URL=\"http://127.0.0.1:8045\"'</span> <span class=\"token operator\">>></span> ~/.zshrc\n<span class=\"token function\">source</span> ~/.zshrc\n</code></pre>\n<p><strong>Bash</strong> (~/.bashrc):</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token keyword\">echo</span> <span class=\"token string\">'export ANTHROPIC_API_KEY=\"sk-antigravity\"'</span> <span class=\"token operator\">>></span> ~/.bashrc\n<span class=\"token keyword\">echo</span> <span class=\"token string\">'export ANTHROPIC_BASE_URL=\"http://127.0.0.1:8045\"'</span> <span class=\"token operator\">>></span> ~/.bashrc\n<span class=\"token function\">source</span> ~/.bashrc\n</code></pre>\n<h3 id=\"步骤四:启动-Claude-Code\"><a href=\"#步骤四:启动-Claude-Code\" class=\"headerlink\" title=\"步骤四:启动 Claude Code\"></a>步骤四:启动 Claude Code</h3><pre class=\" language-bash\"><code class=\"language-bash\">claude\n</code></pre>\n<p>现在 Claude Code 会通过 Antigravity Tools 的本地代理服务连接到 Claude API。</p>\n<hr>\n<h2 id=\"工作流程示例\"><a href=\"#工作流程示例\" class=\"headerlink\" title=\"工作流程示例\"></a>工作流程示例</h2><h3 id=\"日常使用流程\"><a href=\"#日常使用流程\" class=\"headerlink\" title=\"日常使用流程\"></a>日常使用流程</h3><pre><code>1. 启动 Antigravity Tools → 开启 API 反代\n2. (可选) 使用 CC-Switch 选择要使用的账号\n3. 打开终端 → 运行 claude\n4. 开始编程!\n</code></pre>\n<h3 id=\"切换账号\"><a href=\"#切换账号\" class=\"headerlink\" title=\"切换账号\"></a>切换账号</h3><p><strong>使用 CC-Switch</strong>:</p>\n<ul>\n<li>点击托盘图标,选择要切换的账号</li>\n<li>或使用配置的快捷键快速切换</li>\n</ul>\n<p><strong>使用 Antigravity Tools</strong>:</p>\n<ul>\n<li>在账号管理页面选择默认账号</li>\n<li>或配置负载均衡策略</li>\n</ul>\n<hr>\n<h2 id=\"常见问题排查\"><a href=\"#常见问题排查\" class=\"headerlink\" title=\"常见问题排查\"></a>常见问题排查</h2><h3 id=\"macOS-提示”应用已损坏,无法打开”\"><a href=\"#macOS-提示”应用已损坏,无法打开”\" class=\"headerlink\" title=\"macOS 提示”应用已损坏,无法打开”\"></a>macOS 提示”应用已损坏,无法打开”</h3><p>由于 macOS 的安全机制,非 App Store 下载的应用可能触发此提示。</p>\n<p><strong>解决方法 1 - 命令行修复</strong> (推荐):</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">sudo</span> xattr -rd com.apple.quarantine <span class=\"token string\">\"/Applications/Antigravity Tools.app\"</span>\n</code></pre>\n<p><strong>解决方法 2 - Homebrew 安装时规避</strong>:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\">brew <span class=\"token function\">install</span> --cask --no-quarantine antigravity-tools\n</code></pre>\n<h3 id=\"Claude-Code-连接失败\"><a href=\"#Claude-Code-连接失败\" class=\"headerlink\" title=\"Claude Code 连接失败\"></a>Claude Code 连接失败</h3><ol>\n<li><strong>检查 Antigravity 服务状态</strong>:确认 API 反代服务已启动</li>\n<li><strong>检查端口</strong>:确认 <code>ANTHROPIC_BASE_URL</code> 端口与 Antigravity 设置一致</li>\n<li><strong>检查环境变量</strong>:运行 <code>echo $ANTHROPIC_BASE_URL</code> 确认已设置</li>\n<li><strong>检查账号</strong>:确认至少有一个有效的 Claude 账号已授权</li>\n</ol>\n<h3 id=\"环境变量未生效\"><a href=\"#环境变量未生效\" class=\"headerlink\" title=\"环境变量未生效\"></a>环境变量未生效</h3><ul>\n<li>确认添加到正确的 shell 配置文件(<code>.zshrc</code> 或 <code>.bashrc</code>)</li>\n<li>运行 <code>source ~/.zshrc</code> 或重新打开终端</li>\n<li>使用 <code>env | grep ANTHROPIC</code> 验证设置</li>\n</ul>\n<hr>\n<h2 id=\"使用技巧\"><a href=\"#使用技巧\" class=\"headerlink\" title=\"使用技巧\"></a>使用技巧</h2><h3 id=\"1-开机自启\"><a href=\"#1-开机自启\" class=\"headerlink\" title=\"1. 开机自启\"></a>1. 开机自启</h3><ul>\n<li><strong>Antigravity Tools</strong>:在设置中开启”开机自启动”</li>\n<li><strong>CC-Switch</strong>:在设置中开启”登录时启动”</li>\n</ul>\n<h3 id=\"2-多账号负载均衡\"><a href=\"#2-多账号负载均衡\" class=\"headerlink\" title=\"2. 多账号负载均衡\"></a>2. 多账号负载均衡</h3><p>在 Antigravity Tools 中添加多个账号后,可配置:</p>\n<ul>\n<li><strong>轮询模式</strong>:请求自动分配到不同账号</li>\n<li><strong>指定模式</strong>:手动选择使用的账号</li>\n</ul>\n<h3 id=\"3-快捷操作\"><a href=\"#3-快捷操作\" class=\"headerlink\" title=\"3. 快捷操作\"></a>3. 快捷操作</h3><ul>\n<li>使用 CC-Switch 的托盘图标快速切换账号</li>\n<li>配置全局快捷键实现一键切换</li>\n</ul>\n<h3 id=\"4-使用量监控\"><a href=\"#4-使用量监控\" class=\"headerlink\" title=\"4. 使用量监控\"></a>4. 使用量监控</h3><p>通过 CC-Switch 或 Antigravity Tools 查看:</p>\n<ul>\n<li>各账号的 API 调用次数</li>\n<li>Token 使用量统计</li>\n<li>剩余配额</li>\n</ul>\n<hr>\n<h2 id=\"相关链接\"><a href=\"#相关链接\" class=\"headerlink\" title=\"相关链接\"></a>相关链接</h2><ul>\n<li><a href=\"https://docs.anthropic.com/en/docs/claude-code\">Claude Code 官方文档</a></li>\n<li><a href=\"https://github.com/farion1231/cc-switch\">CC-Switch GitHub</a></li>\n<li><a href=\"https://github.com/lbjlaq/Antigravity-Manager\">Antigravity Tools GitHub</a></li>\n<li><a href=\"https://docs.anthropic.com/en/api\">Anthropic API 文档</a></li>\n</ul>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"ai"
]
},
{
"id": "https://costalong.com/2025/12/31/moment-2026-01-01-newyear/",
"url": "https://costalong.com/2025/12/31/moment-2026-01-01-newyear/",
"title": "新年第一天",
"date_published": "2025-12-31T16:05:00.000Z",
"content_html": "<p>2026,新的开始。今年的目标:多写博客、坚持运动、学一门新语言。</p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"moments"
]
},
{
"id": "https://costalong.com/2024/11/19/git/git-commit/",
"url": "https://costalong.com/2024/11/19/git/git-commit/",
"title": "Git Commit",
"date_published": "2024-11-19T02:28:21.000Z",
"content_html": "<h2 id=\"Git-Commit-介绍\"><a href=\"#Git-Commit-介绍\" class=\"headerlink\" title=\"Git Commit 介绍\"></a><strong>Git Commit 介绍</strong></h2><p><code>git commit</code> 命令用于将工作区或暂存区的更改提交到版本库,形成可追踪的历史记录点。<br>它是 Git 工作流的核心操作,常用来保存代码的进展或完成阶段性开发。</p>\n<hr>\n<h2 id=\"基本用法\"><a href=\"#基本用法\" class=\"headerlink\" title=\"基本用法\"></a><strong>基本用法</strong></h2><pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token comment\" spellcheck=\"true\"># 将暂存区内容提交到版本库,进入编辑器填写提交信息</span>\n<span class=\"token function\">git</span> commit\n\n<span class=\"token comment\" spellcheck=\"true\"># 将特定文件提交到版本库(需已被跟踪)</span>\n<span class=\"token function\">git</span> commit <span class=\"token punctuation\">[</span>file1<span class=\"token punctuation\">]</span> <span class=\"token punctuation\">[</span>file2<span class=\"token punctuation\">]</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">..</span>.<span class=\"token punctuation\">]</span>\n\n<span class=\"token comment\" spellcheck=\"true\"># 将暂存区内容提交到版本库,同时直接添加提交信息(无需打开编辑器)</span>\n<span class=\"token function\">git</span> commit -m <span class=\"token string\">\"<message>\"</span>\n\n<span class=\"token comment\" spellcheck=\"true\"># 跳过 git add,将所有已跟踪文件的更改提交到版本库</span>\n<span class=\"token function\">git</span> commit -am <span class=\"token string\">\"<message>\"</span>\n\n<span class=\"token comment\" spellcheck=\"true\"># 替代上一次提交:</span>\n<span class=\"token comment\" spellcheck=\"true\"># - 若代码无更改,用于修改提交信息</span>\n<span class=\"token comment\" spellcheck=\"true\"># - 若代码有更改,将其合并到上一次提交</span>\n<span class=\"token function\">git</span> commit --amend -m <span class=\"token string\">\"<message>\"</span>\n</code></pre>\n<hr>\n<h2 id=\"常用选项\"><a href=\"#常用选项\" class=\"headerlink\" title=\"常用选项\"></a><strong>常用选项</strong></h2><table>\n<thead>\n<tr>\n<th>参数</th>\n<th>说明</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>-m <message></code></td>\n<td>指定提交信息,避免打开编辑器</td>\n</tr>\n<tr>\n<td><code>-a</code></td>\n<td>提交所有已修改或删除的文件(限已被 Git 跟踪的文件)</td>\n</tr>\n<tr>\n<td><code>--amend</code></td>\n<td>修改最近一次提交(包括内容或提交信息)</td>\n</tr>\n<tr>\n<td><code>--no-edit</code></td>\n<td>不修改提交信息,直接沿用上次的提交消息</td>\n</tr>\n</tbody></table>\n<hr>\n<h2 id=\"修改历史提交\"><a href=\"#修改历史提交\" class=\"headerlink\" title=\"修改历史提交\"></a><strong>修改历史提交</strong></h2><h3 id=\"修改最近一次提交\"><a href=\"#修改最近一次提交\" class=\"headerlink\" title=\"修改最近一次提交\"></a><strong>修改最近一次提交</strong></h3><p>如果需要修改最近一次提交的内容或提交信息:</p>\n<ol>\n<li>编辑文件,完成更改后添加到暂存区:<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> add <span class=\"token operator\"><</span>file-name<span class=\"token operator\">></span>\n</code></pre>\n</li>\n<li>使用 <code>--amend</code> 更新提交:<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> commit --amend\n</code></pre>\n</li>\n</ol>\n<h3 id=\"修改更早的提交\"><a href=\"#修改更早的提交\" class=\"headerlink\" title=\"修改更早的提交\"></a><strong>修改更早的提交</strong></h3><p>通过交互式变基(<code>git rebase -i</code>)修改历史提交:</p>\n<ol>\n<li><p>启动交互式变基<br>例如,修改最近 3 次提交:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> rebase -i HEAD~3\n</code></pre>\n</li>\n<li><p>编辑器中显示提交历史:</p>\n<pre class=\" language-plaintext\"><code class=\"language-plaintext\">pick 1234567 feat: 添加新功能\npick 89abcde fix: 修复启动问题\npick def7890 docs: 更新文档\n</code></pre>\n</li>\n<li><p>将需要修改的提交改为 <code>edit</code>:</p>\n<pre class=\" language-plaintext\"><code class=\"language-plaintext\">pick 1234567 feat: 添加新功能\nedit 89abcde fix: 修复启动问题\npick def7890 docs: 更新文档\n</code></pre>\n</li>\n<li><p>保存并退出。</p>\n</li>\n<li><p>修改文件并更新提交:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> add <span class=\"token operator\"><</span>file-name<span class=\"token operator\">></span>\n<span class=\"token function\">git</span> commit --amend\n</code></pre>\n</li>\n<li><p>完成修改后继续变基:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> rebase --continue\n</code></pre>\n</li>\n</ol>\n<hr>\n<h2 id=\"最佳实践\"><a href=\"#最佳实践\" class=\"headerlink\" title=\"最佳实践\"></a><strong>最佳实践</strong></h2><ol>\n<li><p><strong>编写清晰的提交信息</strong><br>遵循 <a href=\"https://www.conventionalcommits.org/\">Conventional Commits</a> 格式:</p>\n<pre class=\" language-plaintext\"><code class=\"language-plaintext\"><type>(<scope>): <subject>\n<空行>\n<body>\n<空行>\n<footer>\n</code></pre>\n<p><strong>示例:</strong></p>\n<pre class=\" language-plaintext\"><code class=\"language-plaintext\">feat(app): 添加用户登录功能\n\n实现用户登录和身份验证。解决了之前登录状态丢失的问题。\n\nCloses #123\n</code></pre>\n</li>\n<li><p><strong>保持提交单一职责</strong><br>每次提交只包含一种逻辑更改(例如,修复一个 bug 或实现一个功能),避免混合提交。</p>\n</li>\n<li><p><strong>修改已推送提交时的注意事项</strong></p>\n<ul>\n<li>若已推送到远程分支,需使用 <code>git push --force</code> 强制覆盖。</li>\n<li>与团队协作时应谨慎,确保其他人未基于该提交工作。</li>\n</ul>\n</li>\n</ol>\n<hr>\n<h2 id=\"总结\"><a href=\"#总结\" class=\"headerlink\" title=\"总结\"></a><strong>总结</strong></h2><ul>\n<li><strong><code>git commit</code> 是 Git 的核心命令</strong>,通过合适的参数选项可以更高效地管理提交。</li>\n<li>选择适合的用法(如 <code>--amend</code> 或 <code>rebase</code>)来优化历史提交,同时注意团队协作中的代码一致性。</li>\n</ul>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"Tool",
"git"
]
},
{
"id": "https://costalong.com/2024/11/19/git/git-rollback/",
"url": "https://costalong.com/2024/11/19/git/git-rollback/",
"title": "git 回滚",
"date_published": "2024-11-19T02:28:21.000Z",
"content_html": "<p>在 Git 中,<code>revert</code>、<code>reset</code> 和 <code>rebase</code> 都是用来修改提交记录或状态的,但它们的使用场景和效果各不相同。</p>\n<hr>\n<h2 id=\"恢复提交:git-revert\"><a href=\"#恢复提交:git-revert\" class=\"headerlink\" title=\"恢复提交:git revert\"></a><strong>恢复提交:<code>git revert</code></strong></h2><p><code>git revert</code> 命令的实际结果类似于 <code>reset</code>,但它的方法不同。<code>reset</code> 是通过移动分支指针撤销更改,而 <code>revert</code> 是通过添加一个新的提交取消更改。</p>\n<h3 id=\"用法\"><a href=\"#用法\" class=\"headerlink\" title=\"用法\"></a><strong>用法</strong></h3><pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> revert <span class=\"token operator\"><</span>commit-id<span class=\"token operator\">></span>\n</code></pre>\n<p>例如,撤销最近一次提交:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> revert HEAD\n</code></pre>\n<h3 id=\"特点\"><a href=\"#特点\" class=\"headerlink\" title=\"特点\"></a><strong>特点</strong></h3><ul>\n<li>安全,不会修改历史记录。</li>\n<li>适合用于团队协作和已推送到远程的分支。</li>\n</ul>\n<hr>\n<h2 id=\"回退提交:git-reset\"><a href=\"#回退提交:git-reset\" class=\"headerlink\" title=\"回退提交:git reset\"></a><strong>回退提交:<code>git reset</code></strong></h2><p><code>git reset</code> 用于改变分支指针,并决定是否保留工作区和暂存区的更改。</p>\n<h3 id=\"步骤-1:查看提交历史\"><a href=\"#步骤-1:查看提交历史\" class=\"headerlink\" title=\"步骤 1:查看提交历史\"></a><strong>步骤 1:查看提交历史</strong></h3><p>使用 <code>git log</code> 查询需要退回的提交 ID:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> log\n</code></pre>\n<h3 id=\"步骤-2:选择回退模式\"><a href=\"#步骤-2:选择回退模式\" class=\"headerlink\" title=\"步骤 2:选择回退模式\"></a><strong>步骤 2:选择回退模式</strong></h3><ol>\n<li><p><strong>软回退 (<code>--soft</code>)</strong><br>仅回退分支指针,保留暂存区和工作区的更改。</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> reset --soft <span class=\"token operator\"><</span>commit-id<span class=\"token operator\">></span>\n</code></pre>\n</li>\n<li><p><strong>混合回退 (<code>--mixed</code>)</strong><br>回退分支指针,清除暂存区,保留工作区的更改。(这是默认模式)</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> reset --mixed <span class=\"token operator\"><</span>commit-id<span class=\"token operator\">></span>\n</code></pre>\n</li>\n<li><p><strong>硬回退 (<code>--hard</code>)</strong><br>回退分支指针,同时清除暂存区和工作区的所有更改。</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> reset --hard <span class=\"token operator\"><</span>commit-id<span class=\"token operator\">></span>\n</code></pre>\n</li>\n</ol>\n<h3 id=\"推送到远程\"><a href=\"#推送到远程\" class=\"headerlink\" title=\"推送到远程\"></a><strong>推送到远程</strong></h3><p>如果需要将本地的变更覆盖远程分支:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> push --force\n</code></pre>\n<hr>\n<h2 id=\"变基:git-rebase\"><a href=\"#变基:git-rebase\" class=\"headerlink\" title=\"变基:git rebase\"></a><strong>变基:<code>git rebase</code></strong></h2><p><code>git rebase</code> 用于重新整理提交历史,将当前分支的更改重新应用到另一个基础提交上。</p>\n<h3 id=\"场景:分支变基\"><a href=\"#场景:分支变基\" class=\"headerlink\" title=\"场景:分支变基\"></a><strong>场景:分支变基</strong></h3><p>假设有以下分支:</p>\n<ul>\n<li><strong>master</strong>: <code>C4 -> C2 -> C1 -> C0</code></li>\n<li><strong>feature</strong>: <code>C5 -> C3 -> C2 -> C1 -> C0</code></li>\n</ul>\n<p>要将 <code>feature</code> 分支的提交重新基于 <code>master</code> 分支:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> checkout feature\n<span class=\"token function\">git</span> rebase master\n</code></pre>\n<p>变基后的结果:</p>\n<ul>\n<li><strong>master</strong>: <code>C4 -> C2 -> C1 -> C0</code></li>\n<li><strong>feature</strong>: <code>C5' -> C3' -> C4 -> C2 -> C1 -> C0</code></li>\n</ul>\n<h3 id=\"交互式变基\"><a href=\"#交互式变基\" class=\"headerlink\" title=\"交互式变基\"></a><strong>交互式变基</strong></h3><p>可以通过 <code>-i</code> 参数合并、修改或删除提交:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> rebase -i <span class=\"token operator\"><</span>base-branch<span class=\"token operator\">></span>\n</code></pre>\n<p>编辑器中提供以下选项:</p>\n<ul>\n<li><code>pick</code>: 保留提交。</li>\n<li><code>squash</code>: 合并当前提交到上一个提交。</li>\n<li><code>edit</code>: 修改提交内容。</li>\n</ul>\n<hr>\n<h2 id=\"恢复历史:git-reflog\"><a href=\"#恢复历史:git-reflog\" class=\"headerlink\" title=\"恢复历史:git reflog\"></a><strong>恢复历史:<code>git reflog</code></strong></h2><p><code>git reflog</code> 用于查看分支操作历史。它记录了所有分支指针的变动,帮助找回丢失的提交。</p>\n<h3 id=\"用法-1\"><a href=\"#用法-1\" class=\"headerlink\" title=\"用法\"></a><strong>用法</strong></h3><pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> reflog\n</code></pre>\n<p>示例输出:</p>\n<pre class=\" language-plaintext\"><code class=\"language-plaintext\">e309ee8 (HEAD -> master) HEAD@{0}: pull: Fast-forward\n30c0e5e HEAD@{1}: commit: fix: 优化代码\n9429f03 HEAD@{2}: commit: fix: 优化代码\n1bdcafe HEAD@{3}: commit: fix: 修改配置\n</code></pre>\n<p>如果误操作(如 <code>git reset --hard</code>),可以通过 <code>git reflog</code> 找到目标提交并恢复:</p>\n<pre class=\" language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> reset --hard <span class=\"token operator\"><</span>commit-id<span class=\"token operator\">></span>\n</code></pre>\n<hr>\n<h2 id=\"对比总结\"><a href=\"#对比总结\" class=\"headerlink\" title=\"对比总结\"></a><strong>对比总结</strong></h2><table>\n<thead>\n<tr>\n<th>功能</th>\n<th><code>git revert</code></th>\n<th><code>git reset</code></th>\n<th><code>git rebase</code></th>\n</tr>\n</thead>\n<tbody><tr>\n<td><strong>作用</strong></td>\n<td>撤销提交,创建新提交</td>\n<td>改变提交历史</td>\n<td>重组提交历史</td>\n</tr>\n<tr>\n<td><strong>安全性</strong></td>\n<td>安全,适合已推送分支</td>\n<td>高风险,慎用于远程分支</td>\n<td>高风险,慎用于远程分支</td>\n</tr>\n<tr>\n<td><strong>适用场景</strong></td>\n<td>团队协作,回滚指定提交</td>\n<td>修改本地提交</td>\n<td>清理历史、线性合并</td>\n</tr>\n</tbody></table>\n<p>选择合适的工具,确保团队协作无缝进行!</p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"Tool",
"git"
]
},
{
"id": "https://costalong.com/2024/07/16/go/tests/gomock/",
"url": "https://costalong.com/2024/07/16/go/tests/gomock/",
"title": "Gomock进行单元测试",
"date_published": "2024-07-16T15:10:12.000Z",
"content_html": "<h2 id=\"用法\"><a href=\"#用法\" class=\"headerlink\" title=\"用法\"></a>用法</h2><p>在 mockgen 命令中,支持两种生成模式:</p>\n<ol>\n<li>source:从源文件生成 mock 接口(通过 -source 启用)</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">mockgen -source=foo.go [other options]\n</code></pre>\n<ol start=\"2\">\n<li>reflect:通过使用反射程序来生成 mock 接口.它通过传递两个非标志参数来启用:导入路径和逗号分隔的接口列表</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">mockgen database/sql/driver Conn,Driver\n</code></pre>\n<p>两种方式生成的 mock 代码并没有什么区别.因此选择合适的就可以了</p>\n<h2 id=\"写测试用例\"><a href=\"#写测试用例\" class=\"headerlink\" title=\"写测试用例\"></a>写测试用例</h2><p><strong>1. 步骤:</strong></p>\n<ol>\n<li>想清楚整体逻辑</li>\n<li>定义想要(模拟)依赖项的 interface(接口)</li>\n<li>使用 mockgen 命令对所需 mock 的 interface 生成 mock 文件</li>\n<li>编写单元测试的逻辑,在测试中使用 mock</li>\n<li>进行单元测试的验证</li>\n</ol>\n<p><strong>2. 目录:</strong></p>\n<pre class=\" language-yaml\"><code class=\"language-yaml\">├── mock\n├── person\n│ └── male.go\n└── user\n ├── user.go\n └── user_test.go\n</code></pre>\n<p><strong>2. 编写:</strong></p>\n<ol>\n<li><p>定义 interface </p>\n<p> 打开 person/male.go 文件,写入以下内容:</p>\n</li>\n</ol>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">package</span> person\n\n<span class=\"token keyword\">type</span> Male <span class=\"token keyword\">interface</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">Get</span><span class=\"token punctuation\">(</span>id <span class=\"token builtin\">int64</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<ol start=\"2\">\n<li><p>调用方法</p>\n<p>打开 user/user.go 文件,写入以下内容</p>\n</li>\n</ol>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">package</span> user\n\n<span class=\"token keyword\">import</span> <span class=\"token string\">\"github.com/EDDYCJY/mockd/person\"</span>\n\n<span class=\"token keyword\">type</span> User <span class=\"token keyword\">struct</span> <span class=\"token punctuation\">{</span>\n Person person<span class=\"token punctuation\">.</span>Male\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">func</span> <span class=\"token function\">NewUser</span><span class=\"token punctuation\">(</span>p person<span class=\"token punctuation\">.</span>Male<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span>User <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> <span class=\"token operator\">&</span>User<span class=\"token punctuation\">{</span>Person<span class=\"token punctuation\">:</span> p<span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>u <span class=\"token operator\">*</span>User<span class=\"token punctuation\">)</span> <span class=\"token function\">GetUserInfo</span><span class=\"token punctuation\">(</span>id <span class=\"token builtin\">int64</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> u<span class=\"token punctuation\">.</span>Person<span class=\"token punctuation\">.</span><span class=\"token function\">Get</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<ol start=\"3\">\n<li>生成 mock 文件</li>\n</ol>\n<p>回到 mockd/ 的根目录下,执行以下命令</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">$ mockgen -source=./person/male.go -destination=./mock/male_mock.go -package=mock\n</code></pre>\n<p>在执行完毕后,可以发现 mock/ 目录下多出了 male_mock.go 文件,这就是 mock 文件.那么命令中的指令又分别有什么用呢?如下:</p>\n<pre><code>-source:设置需要模拟(mock)的接口文件\n\n-destination:设置 mock 文件输出的地方,若不设置则打印到标准输出中\n\n-package:设置 mock 文件的包名,若不设置则为 mock_ 前缀加上文件名(如本文的包名会为 mock_person)\n</code></pre>\n<p>想了解更多的指令符,可参见 官方文档: <a href=\"https://github.com/golang/mock#running-mockgen\">https://github.com/golang/mock#running-mockgen</a></p>\n<p><strong>输出的 mock 文件</strong></p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token comment\" spellcheck=\"true\">// Code generated by MockGen. DO NOT EDIT.</span>\n<span class=\"token comment\" spellcheck=\"true\">// Source: ./person/male.go</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// Package mock is a generated GoMock package.</span>\n<span class=\"token keyword\">package</span> mock\n\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">(</span>\n gomock <span class=\"token string\">\"github.com/golang/mock/gomock\"</span>\n reflect <span class=\"token string\">\"reflect\"</span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// MockMale is a mock of Male interface</span>\n<span class=\"token keyword\">type</span> MockMale <span class=\"token keyword\">struct</span> <span class=\"token punctuation\">{</span>\n ctrl <span class=\"token operator\">*</span>gomock<span class=\"token punctuation\">.</span>Controller\n recorder <span class=\"token operator\">*</span>MockMaleMockRecorder\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// MockMaleMockRecorder is the mock recorder for MockMale</span>\n<span class=\"token keyword\">type</span> MockMaleMockRecorder <span class=\"token keyword\">struct</span> <span class=\"token punctuation\">{</span>\n mock <span class=\"token operator\">*</span>MockMale\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// NewMockMale creates a new mock instance</span>\n<span class=\"token keyword\">func</span> <span class=\"token function\">NewMockMale</span><span class=\"token punctuation\">(</span>ctrl <span class=\"token operator\">*</span>gomock<span class=\"token punctuation\">.</span>Controller<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span>MockMale <span class=\"token punctuation\">{</span>\n mock <span class=\"token operator\">:=</span> <span class=\"token operator\">&</span>MockMale<span class=\"token punctuation\">{</span>ctrl<span class=\"token punctuation\">:</span> ctrl<span class=\"token punctuation\">}</span>\n mock<span class=\"token punctuation\">.</span>recorder <span class=\"token operator\">=</span> <span class=\"token operator\">&</span>MockMaleMockRecorder<span class=\"token punctuation\">{</span>mock<span class=\"token punctuation\">}</span>\n <span class=\"token keyword\">return</span> mock\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// EXPECT returns an object that allows the caller to indicate expected use</span>\n<span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>m <span class=\"token operator\">*</span>MockMale<span class=\"token punctuation\">)</span> <span class=\"token function\">EXPECT</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span>MockMaleMockRecorder <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> m<span class=\"token punctuation\">.</span>recorder\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// Get mocks base method</span>\n<span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>m <span class=\"token operator\">*</span>MockMale<span class=\"token punctuation\">)</span> <span class=\"token function\">Get</span><span class=\"token punctuation\">(</span>id <span class=\"token builtin\">int64</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span> <span class=\"token punctuation\">{</span>\n ret <span class=\"token operator\">:=</span> m<span class=\"token punctuation\">.</span>ctrl<span class=\"token punctuation\">.</span><span class=\"token function\">Call</span><span class=\"token punctuation\">(</span>m<span class=\"token punctuation\">,</span> <span class=\"token string\">\"Get\"</span><span class=\"token punctuation\">,</span> id<span class=\"token punctuation\">)</span>\n ret0<span class=\"token punctuation\">,</span> <span class=\"token boolean\">_</span> <span class=\"token operator\">:=</span> ret<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">error</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">return</span> ret0\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// Get indicates an expected call of Get</span>\n<span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>mr <span class=\"token operator\">*</span>MockMaleMockRecorder<span class=\"token punctuation\">)</span> <span class=\"token function\">Get</span><span class=\"token punctuation\">(</span>id <span class=\"token keyword\">interface</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span>gomock<span class=\"token punctuation\">.</span>Call <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">return</span> mr<span class=\"token punctuation\">.</span>mock<span class=\"token punctuation\">.</span>ctrl<span class=\"token punctuation\">.</span><span class=\"token function\">RecordCallWithMethodType</span><span class=\"token punctuation\">(</span>mr<span class=\"token punctuation\">.</span>mock<span class=\"token punctuation\">,</span> <span class=\"token string\">\"Get\"</span><span class=\"token punctuation\">,</span> reflect<span class=\"token punctuation\">.</span><span class=\"token function\">TypeOf</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token operator\">*</span>MockMale<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">nil</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Get<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> id<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p><strong>4. 测试用例</strong></p>\n<p>打开 user/user_test.go 文件,写入以下内容:</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">package</span> user\n\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">(</span>\n <span class=\"token string\">\"testing\"</span>\n\n <span class=\"token string\">\"github.com/EDDYCJY/mockd/mock\"</span>\n\n <span class=\"token string\">\"github.com/golang/mock/gomock\"</span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">func</span> <span class=\"token function\">TestUser_GetUserInfo</span><span class=\"token punctuation\">(</span>t <span class=\"token operator\">*</span>testing<span class=\"token punctuation\">.</span>T<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n ctl <span class=\"token operator\">:=</span> gomock<span class=\"token punctuation\">.</span><span class=\"token function\">NewController</span><span class=\"token punctuation\">(</span>t<span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">defer</span> ctl<span class=\"token punctuation\">.</span><span class=\"token function\">Finish</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n <span class=\"token keyword\">var</span> id <span class=\"token builtin\">int64</span> <span class=\"token operator\">=</span> <span class=\"token number\">1</span>\n mockMale <span class=\"token operator\">:=</span> mock<span class=\"token punctuation\">.</span><span class=\"token function\">NewMockMale</span><span class=\"token punctuation\">(</span>ctl<span class=\"token punctuation\">)</span>\n gomock<span class=\"token punctuation\">.</span><span class=\"token function\">InOrder</span><span class=\"token punctuation\">(</span>\n mockMale<span class=\"token punctuation\">.</span><span class=\"token function\">EXPECT</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">Get</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">Return</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">nil</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n <span class=\"token punctuation\">)</span>\n\n user <span class=\"token operator\">:=</span> <span class=\"token function\">NewUser</span><span class=\"token punctuation\">(</span>mockMale<span class=\"token punctuation\">)</span>\n err <span class=\"token operator\">:=</span> user<span class=\"token punctuation\">.</span><span class=\"token function\">GetUserInfo</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">if</span> err <span class=\"token operator\">!=</span> <span class=\"token boolean\">nil</span> <span class=\"token punctuation\">{</span>\n t<span class=\"token punctuation\">.</span><span class=\"token function\">Errorf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"user.GetUserInfo err: %v\"</span><span class=\"token punctuation\">,</span> err<span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<ol>\n<li><p>gomock.NewController:返回 gomock.Controller,它代表 mock 生态系统中的顶级控件.定义了 mock 对象的范围,生命周期和期待值.另外它在多个 goroutine 中是安全的</p>\n</li>\n<li><p>mock.NewMockMale:创建一个新的 mock 实例</p>\n</li>\n<li><p>gomock.InOrder:声明给定的调用应按顺序进行(是对 gomock.After 的二次封装)</p>\n</li>\n<li><p>mockMale.EXPECT().Get(id).Return(nil):这里有三个步骤,EXPECT()返回一个允许调用者设置期望和返回值的对象.Get(id) 是设置入参并调用 mock 实例中的方法.Return(nil) 是设置先前调用的方法出参.简单来说,就是设置入参并调用,最后设置返回值</p>\n</li>\n<li><p>NewUser(mockMale):创建 User 实例,值得注意的是,在这里注入了 mock 对象,因此实际在随后的 user.GetUserInfo(id) 调用(入参:id 为 1)中.它调用的是我们事先模拟好的 mock 方法</p>\n</li>\n<li><p>ctl.Finish():进行 mock 用例的期望值断言,一般会使用 defer 延迟执行,以防止我们忘记这一操作</p>\n</li>\n</ol>\n<h2 id=\"测试\"><a href=\"#测试\" class=\"headerlink\" title=\"测试\"></a>测试</h2><p>回到 mockd/ 的根目录下,执行以下命令</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">$ go test ./user\n</code></pre>\n<p>看到这样的结果,就大功告成啦!您可以自己调整一下 Return() 的返回值,以此得到不一样的测试结果哦 </p>\n<ol>\n<li>测试覆盖率</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">go test -cover ./user\n</code></pre>\n<p>可通过设置 -cover 标志符来开启覆盖率的统计,展示内容为 coverage: 100.0%.</p>\n<ol start=\"2\">\n<li>可视化界面</li>\n</ol>\n<p>生成测试覆盖率的 profile 文件</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">go test ./... -coverprofile=cover.out\n</code></pre>\n<p>利用 profile 文件生成可视化界面</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">go tool cover -html=cover.out\n</code></pre>\n<h2 id=\"常用-mock-方法\"><a href=\"#常用-mock-方法\" class=\"headerlink\" title=\"常用 mock 方法\"></a>常用 mock 方法</h2><ol>\n<li><p>调用方法<br> Call.Do():声明在匹配时要运行的操作</p>\n<p> Call.DoAndReturn():声明在匹配调用时要运行的操作,并且模拟返回该函数的返回值</p>\n<p> Call.MaxTimes():设置最大的调用次数为 n 次</p>\n<p> Call.MinTimes():设置最小的调用次数为 n 次</p>\n<p> Call.AnyTimes():允许调用次数为 0 次或更多次</p>\n<p> Call.Times():设置调用次数为 n 次</p>\n</li>\n<li><p>参数匹配</p>\n<p> gomock.Any():匹配任意值</p>\n<p> gomock.Eq():通过反射匹配到指定的类型值,而不需要手动设置<br> gomock.Nil():返回 nil</p>\n</li>\n</ol>\n<h2 id=\"生成多个-mock-文件\"><a href=\"#生成多个-mock-文件\" class=\"headerlink\" title=\"生成多个 mock 文件\"></a>生成多个 mock 文件</h2><p> 可能会想一条条命令生成 mock 文件,岂不得崩溃?</p>\n<p>当然,官方提供了更方便的方式,我们可以利用 go:generate 来完成批量处理的功能</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]\n</code></pre>\n<ol>\n<li>修改 interface 方法</li>\n</ol>\n<p>打开 person/male.go 文件,修改为以下内容:</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">package</span> person\n\n<span class=\"token comment\" spellcheck=\"true\">//go:generate mockgen -destination=../mock/male_mock.go -package=mock github.com/EDDYCJY/mockd/person Male</span>\n\n<span class=\"token keyword\">type</span> Male <span class=\"token keyword\">interface</span> <span class=\"token punctuation\">{</span>\n <span class=\"token function\">Get</span><span class=\"token punctuation\">(</span>id <span class=\"token builtin\">int64</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>关注到 go:generate 这条语句,可分为以下部分:</p>\n<ol>\n<li>声明 //go:generate (注意不要留空格)</li>\n<li>使用 mockgen 命令</li>\n<li>定义 -destination</li>\n<li>定义 -package</li>\n<li>定义 source,此处为 person 的包路径</li>\n<li>定义 interfaces,此处为 Male</li>\n</ol>\n<p> <strong>注意:</strong> 上面注释中的mockgen命令因为在调用时其当前目录是person,所以在-destination参数中我们需要指定../mocks/作为mock生成代码的输出目录。</p>\n<p>有了上面的go:generate注释,我们要生成所有mock只需在项目的根目录下运行下面这条命令即可,是不是非常方便?</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">go generate ./...\n</code></pre>\n<p><strong>注意:</strong> 注释中的//和go:generate之间不能有空格,这样go generate才可以把注释当做一条命令来处理。</p>\n<p><strong>总结:</strong><br>在单元测试这一环,gomock 给我们提供了极大的便利.能够 mock 掉许许多多的依赖项,其中还有很多的使用方式和功能.您可以 mark 住后详细阅读下官方文档,记忆会更深刻 </p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"golang",
"golang",
"test"
]
},
{
"id": "https://costalong.com/2024/07/16/go/package/semaphore/",
"url": "https://costalong.com/2024/07/16/go/package/semaphore/",
"title": "semaphore 信号量",
"date_published": "2024-07-16T15:00:20.000Z",
"content_html": "<p>源代码: golang.org/x/sync/semaphore</p>\n<h1 id=\"semaphore-信号量\"><a href=\"#semaphore-信号量\" class=\"headerlink\" title=\"semaphore 信号量\"></a>semaphore 信号量</h1><p>源代码: golang.org/x/sync/semaphore</p>\n<ol>\n<li>创建</li>\n</ol>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token comment\" spellcheck=\"true\">// NewWeighted使用给定的值创建一个新的加权信号量</span>\n<span class=\"token comment\" spellcheck=\"true\">// 并发访问的最大组合权重。</span>\n<span class=\"token keyword\">func</span> <span class=\"token function\">NewWeighted</span><span class=\"token punctuation\">(</span>n <span class=\"token builtin\">int64</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span>Weighted <span class=\"token punctuation\">{</span>\n w <span class=\"token operator\">:=</span> <span class=\"token operator\">&</span>Weighted<span class=\"token punctuation\">{</span>size<span class=\"token punctuation\">:</span> n<span class=\"token punctuation\">}</span>\n <span class=\"token keyword\">return</span> w\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>w 结构</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token comment\" spellcheck=\"true\">// Weighted provides a way to bound concurrent access to a resource.</span>\n<span class=\"token comment\" spellcheck=\"true\">// The callers can request access with a given weight.</span>\n<span class=\"token comment\" spellcheck=\"true\">// NewWeighted使用给定的值创建一个新的加权信号量</span>\n<span class=\"token comment\" spellcheck=\"true\">// 并发访问的最大组合权重。</span>\n<span class=\"token keyword\">type</span> Weighted <span class=\"token keyword\">struct</span> <span class=\"token punctuation\">{</span>\n size <span class=\"token builtin\">int64</span> <span class=\"token comment\" spellcheck=\"true\">//权重总数量</span>\n cur <span class=\"token builtin\">int64</span> <span class=\"token comment\" spellcheck=\"true\">//当前权重数量</span>\n mu sync<span class=\"token punctuation\">.</span>Mutex <span class=\"token comment\" spellcheck=\"true\">//全局互斥锁</span>\n waiters list<span class=\"token punctuation\">.</span>List <span class=\"token comment\" spellcheck=\"true\">//双向链表,存waiter</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>waiter 结构</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">type</span> waiter <span class=\"token keyword\">struct</span> <span class=\"token punctuation\">{</span>\n n <span class=\"token builtin\">int64</span> <span class=\"token comment\" spellcheck=\"true\">//需要权重数量</span>\n ready <span class=\"token keyword\">chan</span><span class=\"token operator\"><-</span> <span class=\"token keyword\">struct</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span> <span class=\"token comment\" spellcheck=\"true\">// Closed when semaphore acquired. //通信channel ,无缓冲</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Acquire 方法:</p>\n<p>阻塞的获取指定权种的资源,如果没有空闲的资源,会进去休眠等待。</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token comment\" spellcheck=\"true\">// Acquire获取权重为n的信号量,阻塞直到资源可用或ctx完成。</span>\n<span class=\"token comment\" spellcheck=\"true\">// 成功时,返回nil。失败时返回 ctx.Err()并保持信号量不变。</span>\n<span class=\"token comment\" spellcheck=\"true\">// 如果ctx已经完成,则Acquire仍然可以成功执行而不会阻塞</span>\n<span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>s <span class=\"token operator\">*</span>Weighted<span class=\"token punctuation\">)</span> <span class=\"token function\">Acquire</span><span class=\"token punctuation\">(</span>ctx context<span class=\"token punctuation\">.</span>Context<span class=\"token punctuation\">,</span> n <span class=\"token builtin\">int64</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">error</span> <span class=\"token punctuation\">{</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Lock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token comment\" spellcheck=\"true\">// fast path, 如果有足够的资源,都不考虑ctx.Done的状态,将cur加上n就返回</span>\n <span class=\"token keyword\">if</span> s<span class=\"token punctuation\">.</span>size<span class=\"token operator\">-</span>s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\">>=</span> n <span class=\"token operator\">&&</span> s<span class=\"token punctuation\">.</span>waiters<span class=\"token punctuation\">.</span><span class=\"token function\">Len</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span> <span class=\"token number\">0</span> <span class=\"token punctuation\">{</span>\n s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\">+=</span> n\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">return</span> <span class=\"token boolean\">nil</span>\n <span class=\"token punctuation\">}</span>\n \n <span class=\"token comment\" spellcheck=\"true\">// 如果是不可能完成的任务,请求的资源数大于能提供的最大的资源数</span>\n <span class=\"token keyword\">if</span> n <span class=\"token operator\">></span> s<span class=\"token punctuation\">.</span>size <span class=\"token punctuation\">{</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token comment\" spellcheck=\"true\">// 依赖ctx的状态返回,否则一直等待</span>\n <span class=\"token operator\"><-</span>ctx<span class=\"token punctuation\">.</span><span class=\"token function\">Done</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">return</span> ctx<span class=\"token punctuation\">.</span><span class=\"token function\">Err</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n \n <span class=\"token comment\" spellcheck=\"true\">// 否则就需要把调用者加入到等待队列中</span>\n <span class=\"token comment\" spellcheck=\"true\">// 创建了一个ready chan,以便被通知唤醒</span>\n ready <span class=\"token operator\">:=</span> <span class=\"token function\">make</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">chan</span> <span class=\"token keyword\">struct</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n <span class=\"token comment\" spellcheck=\"true\">// 组装waiter</span>\n w <span class=\"token operator\">:=</span> waiter<span class=\"token punctuation\">{</span>n<span class=\"token punctuation\">:</span> n<span class=\"token punctuation\">,</span> ready<span class=\"token punctuation\">:</span> ready<span class=\"token punctuation\">}</span>\n <span class=\"token comment\" spellcheck=\"true\">// 插入waiters中</span>\n elem <span class=\"token operator\">:=</span> s<span class=\"token punctuation\">.</span>waiters<span class=\"token punctuation\">.</span><span class=\"token function\">PushBack</span><span class=\"token punctuation\">(</span>w<span class=\"token punctuation\">)</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n \n\n <span class=\"token comment\" spellcheck=\"true\">// 等待</span>\n <span class=\"token keyword\">select</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">case</span> <span class=\"token operator\"><-</span>ctx<span class=\"token punctuation\">.</span><span class=\"token function\">Done</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token comment\" spellcheck=\"true\">// context的Done被关闭</span>\n err <span class=\"token operator\">:=</span> ctx<span class=\"token punctuation\">.</span><span class=\"token function\">Err</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Lock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">select</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">case</span> <span class=\"token operator\"><-</span>ready<span class=\"token punctuation\">:</span> <span class=\"token comment\" spellcheck=\"true\">// 如果被唤醒了,忽略ctx的状态</span>\n err <span class=\"token operator\">=</span> <span class=\"token boolean\">nil</span>\n <span class=\"token keyword\">default</span><span class=\"token punctuation\">:</span> 通知waiter\n isFront <span class=\"token operator\">:=</span> s<span class=\"token punctuation\">.</span>waiters<span class=\"token punctuation\">.</span><span class=\"token function\">Front</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span> elem\n s<span class=\"token punctuation\">.</span>waiters<span class=\"token punctuation\">.</span><span class=\"token function\">Remove</span><span class=\"token punctuation\">(</span>elem<span class=\"token punctuation\">)</span>\n <span class=\"token comment\" spellcheck=\"true\">// 通知其它的waiters,检查是否有足够的资源</span>\n <span class=\"token keyword\">if</span> isFront <span class=\"token operator\">&&</span> s<span class=\"token punctuation\">.</span>size <span class=\"token operator\">></span> s<span class=\"token punctuation\">.</span>cur <span class=\"token punctuation\">{</span>\n s<span class=\"token punctuation\">.</span><span class=\"token function\">notifyWaiters</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">return</span> err\n <span class=\"token keyword\">case</span> <span class=\"token operator\"><-</span>ready<span class=\"token punctuation\">:</span> <span class=\"token comment\" spellcheck=\"true\">// 被唤醒了</span>\n <span class=\"token keyword\">return</span> <span class=\"token boolean\">nil</span>\n <span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span>\n</code></pre>\n<h4 id=\"TryAcquire\"><a href=\"#TryAcquire\" class=\"headerlink\" title=\"TryAcquire\"></a>TryAcquire</h4><p>非阻塞地获取指定权重的资源,如果当前没有空闲资源,会直接返回<code>false</code>。</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token comment\" spellcheck=\"true\">// TryAcquire获取权重为n的信号量而不阻塞。</span>\n<span class=\"token comment\" spellcheck=\"true\">// 成功时返回true。 失败时,返回false并保持信号量不变。</span>\n<span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>s <span class=\"token operator\">*</span>Weighted<span class=\"token punctuation\">)</span> <span class=\"token function\">TryAcquire</span><span class=\"token punctuation\">(</span>n <span class=\"token builtin\">int64</span><span class=\"token punctuation\">)</span> <span class=\"token builtin\">bool</span> <span class=\"token punctuation\">{</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Lock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n success <span class=\"token operator\">:=</span> s<span class=\"token punctuation\">.</span>size<span class=\"token operator\">-</span>s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\">>=</span> n <span class=\"token operator\">&&</span> s<span class=\"token punctuation\">.</span>waiters<span class=\"token punctuation\">.</span><span class=\"token function\">Len</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span> <span class=\"token number\">0</span>\n <span class=\"token keyword\">if</span> success <span class=\"token punctuation\">{</span>\n s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\">+=</span> n\n <span class=\"token punctuation\">}</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">return</span> success\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h4 id=\"Release-方法\"><a href=\"#Release-方法\" class=\"headerlink\" title=\"Release 方法\"></a>Release 方法</h4><p>用于释放指定权重的资源,如果有<code>waiters</code>则尝试去一一唤醒<code>waiter</code>。</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token comment\" spellcheck=\"true\">// Release释放权值为n的信号量。</span>\n<span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>s <span class=\"token operator\">*</span>Weighted<span class=\"token punctuation\">)</span> <span class=\"token function\">Release</span><span class=\"token punctuation\">(</span>n <span class=\"token builtin\">int64</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Lock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\">-=</span> n\n <span class=\"token comment\" spellcheck=\"true\">// cur的范围在[0 - size]</span>\n <span class=\"token keyword\">if</span> s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\"><</span> <span class=\"token number\">0</span> <span class=\"token punctuation\">{</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token function\">panic</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"semaphore: bad release\"</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n s<span class=\"token punctuation\">.</span><span class=\"token function\">notifyWaiters</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n s<span class=\"token punctuation\">.</span>mu<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>s <span class=\"token operator\">*</span>Weighted<span class=\"token punctuation\">)</span> <span class=\"token function\">notifyWaiters</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token comment\" spellcheck=\"true\">// 如果有阻塞的waiters,尝试去进行一一唤醒 </span>\n <span class=\"token comment\" spellcheck=\"true\">// 唤醒的时候,先进先出,避免被资源比较大的waiter被饿死</span>\n <span class=\"token keyword\">for</span> <span class=\"token punctuation\">{</span>\n next <span class=\"token operator\">:=</span> s<span class=\"token punctuation\">.</span>waiters<span class=\"token punctuation\">.</span><span class=\"token function\">Front</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token comment\" spellcheck=\"true\">// 已经没有waiter了</span>\n <span class=\"token keyword\">if</span> next <span class=\"token operator\">==</span> <span class=\"token boolean\">nil</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">break</span>\n <span class=\"token punctuation\">}</span>\n\n w <span class=\"token operator\">:=</span> next<span class=\"token punctuation\">.</span>Value<span class=\"token punctuation\">.</span><span class=\"token punctuation\">(</span>waiter<span class=\"token punctuation\">)</span>\n <span class=\"token comment\" spellcheck=\"true\">// waiter需要的资源不足</span>\n <span class=\"token keyword\">if</span> s<span class=\"token punctuation\">.</span>size<span class=\"token operator\">-</span>s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\"><</span> w<span class=\"token punctuation\">.</span>n <span class=\"token punctuation\">{</span>\n <span class=\"token comment\" spellcheck=\"true\">// 没有足够的令牌供下一个waiter使用。我们可以继续(尝试</span>\n <span class=\"token comment\" spellcheck=\"true\">// 查找请求较小的waiter),但在负载下可能会导致</span>\n <span class=\"token comment\" spellcheck=\"true\">// 饥饿的大型请求;相反,我们留下所有剩余的waiter阻塞</span>\n <span class=\"token comment\" spellcheck=\"true\">//</span>\n <span class=\"token comment\" spellcheck=\"true\">// 考虑一个用作读写锁的信号量,带有N个令牌,N个reader和一位writer</span>\n <span class=\"token comment\" spellcheck=\"true\">// 每个reader都可以通过Acquire(1)获取读锁。</span>\n <span class=\"token comment\" spellcheck=\"true\">// writer写入可以通过Acquire(N)获得写锁定,但不包括所有的reader。</span>\n <span class=\"token comment\" spellcheck=\"true\">// 如果我们允许读者在队列中前进,writer将会饿死-总是有一个令牌可供每个读者。</span>\n <span class=\"token keyword\">break</span>\n <span class=\"token punctuation\">}</span>\n\n s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\">+=</span> w<span class=\"token punctuation\">.</span>n\n s<span class=\"token punctuation\">.</span>waiters<span class=\"token punctuation\">.</span><span class=\"token function\">Remove</span><span class=\"token punctuation\">(</span>next<span class=\"token punctuation\">)</span>\n <span class=\"token function\">close</span><span class=\"token punctuation\">(</span>w<span class=\"token punctuation\">.</span>ready<span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>notifyWaiters 方法</p>\n<p>在<code>Acquire</code>和<code>Release</code>方法中都调用了<code>notifyWaiters</code></p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">func</span> <span class=\"token punctuation\">(</span>s <span class=\"token operator\">*</span>Weighted<span class=\"token punctuation\">)</span> <span class=\"token function\">notifyWaiters</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">for</span> <span class=\"token punctuation\">{</span>\n <span class=\"token comment\" spellcheck=\"true\">// 获取等待调用者队列中的队员</span>\n next <span class=\"token operator\">:=</span> s<span class=\"token punctuation\">.</span>waiters<span class=\"token punctuation\">.</span><span class=\"token function\">Front</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token comment\" spellcheck=\"true\">// 没有要通知的调用者了</span>\n <span class=\"token keyword\">if</span> next <span class=\"token operator\">==</span> <span class=\"token boolean\">nil</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">break</span> <span class=\"token comment\" spellcheck=\"true\">// No more waiters blocked.</span>\n <span class=\"token punctuation\">}</span>\n\n <span class=\"token comment\" spellcheck=\"true\">// 断言出waiter信息</span>\n w <span class=\"token operator\">:=</span> next<span class=\"token punctuation\">.</span>Value<span class=\"token punctuation\">.</span><span class=\"token punctuation\">(</span>waiter<span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">if</span> s<span class=\"token punctuation\">.</span>size<span class=\"token operator\">-</span>s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\"><</span> w<span class=\"token punctuation\">.</span>n <span class=\"token punctuation\">{</span>\n <span class=\"token comment\" spellcheck=\"true\">// 没有足够资源为下一个调用者使用时,继续阻塞该调用者,遵循先进先出的原则,</span>\n <span class=\"token comment\" spellcheck=\"true\">// 避免需要资源数比较大的waiter被饿死</span>\n <span class=\"token comment\" spellcheck=\"true\">//</span>\n <span class=\"token comment\" spellcheck=\"true\">// 考虑一个场景,使用信号量作为读写锁,现有N个令牌,N个reader和一个writer</span>\n <span class=\"token comment\" spellcheck=\"true\">// 每个reader都可以通过Acquire(1)获取读锁,writer写入可以通过Acquire(N)获得写锁定</span>\n <span class=\"token comment\" spellcheck=\"true\">// 但不包括所有的reader,如果我们允许reader在队列中前进,writer将会饿死-总是有一个令牌可供每个reader</span>\n <span class=\"token keyword\">break</span>\n <span class=\"token punctuation\">}</span>\n\n <span class=\"token comment\" spellcheck=\"true\">// 获取资源</span>\n s<span class=\"token punctuation\">.</span>cur <span class=\"token operator\">+=</span> w<span class=\"token punctuation\">.</span>n\n <span class=\"token comment\" spellcheck=\"true\">// 从waiter列表中移除</span>\n s<span class=\"token punctuation\">.</span>waiters<span class=\"token punctuation\">.</span><span class=\"token function\">Remove</span><span class=\"token punctuation\">(</span>next<span class=\"token punctuation\">)</span>\n <span class=\"token comment\" spellcheck=\"true\">// 使用channel的close机制唤醒waiter</span>\n <span class=\"token function\">close</span><span class=\"token punctuation\">(</span>w<span class=\"token punctuation\">.</span>ready<span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>需要注意一个点:唤醒<code>waiter</code>采用先进先出的原则,避免需要资源数比较大的waiter被饿死。</p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"golang",
"golang",
"package"
]
},
{
"id": "https://costalong.com/2024/07/16/linux/netstat/",
"url": "https://costalong.com/2024/07/16/linux/netstat/",
"title": "Linux 网络工具 netstat",
"date_published": "2024-07-16T02:30:48.000Z",
"content_html": "<h1 id=\"netstat\"><a href=\"#netstat\" class=\"headerlink\" title=\"netstat\"></a>netstat</h1><h2 id=\"1-netstat-命令\"><a href=\"#1-netstat-命令\" class=\"headerlink\" title=\"1. netstat 命令\"></a>1. netstat 命令</h2><p>netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Memberships) 等等。</p>\n<h3 id=\"语法\"><a href=\"#语法\" class=\"headerlink\" title=\"语法\"></a>语法</h3><pre class=\" language-sh\"><code class=\"language-sh\">netstat [-acCeFghilMnNoprstuvVwx][-A<网络类型>][--ip]\n</code></pre>\n<h3 id=\"参数\"><a href=\"#参数\" class=\"headerlink\" title=\"参数\"></a>参数</h3><pre class=\" language-sh\"><code class=\"language-sh\">-a或--all 显示所有连线中的Socket。\n-A<网络类型>或--<网络类型> 列出该网络类型连线中的相关地址。\n-c或--continuous 持续列出网络状态。\n-C或--cache 显示路由器配置的快取信息。\n-e或--extend 显示网络其他相关信息。\n-F或--fib 显示路由缓存。\n-g或--groups 显示多重广播功能群组组员名单。\n-h或--help 在线帮助。\n-i或--interfaces 显示网络界面信息表单。\n-l或--listening 显示监控中的服务器的Socket。\n-M或--masquerade 显示伪装的网络连线。\n-n或--numeric 直接使用IP地址,而不通过域名服务器。\n-N或--netlink或--symbolic 显示网络硬件外围设备的符号连接名称。\n-o或--timers 显示计时器。\n-p或--programs 显示正在使用Socket的程序识别码和程序名称。\n-r或--route 显示Routing Table。\n-s或--statistics 显示网络工作信息统计表。\n-t或--tcp 显示TCP传输协议的连线状况。\n-u或--udp 显示UDP传输协议的连线状况。\n-v或--verbose 显示指令执行过程。\n-V或--version 显示版本信息。\n-w或--raw 显示RAW传输协议的连线状况。\n-x或--unix 此参数的效果和指定\"-A unix\"参数相同。\n--ip或--inet 此参数的效果和指定\"-A inet\"参数相同。\n</code></pre>\n<h3 id=\"实例\"><a href=\"#实例\" class=\"headerlink\" title=\"实例\"></a>实例</h3><h4 id=\"1-显示网络状态\"><a href=\"#1-显示网络状态\" class=\"headerlink\" title=\"1. 显示网络状态\"></a>1. 显示网络状态</h4><pre class=\" language-sh\"><code class=\"language-sh\">netstat -a\n</code></pre>\n<p>结果:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">Proto Recv-Q Send-Q Local Address Foreign Address State\ntcp 0 0 192.168.122.1:domain 0.0.0.0:* LISTEN\ntcp 0 0 localhost:ipp 0.0.0.0:* LISTEN\ntcp 0 0 localhost:35600 0.0.0.0:* LISTEN\ntcp 0 0 127.0.0.54:domain 0.0.0.0:* LISTEN\ntcp 0 0 localhost:31080 0.0.0.0:* LISTEN\ntcp 0 0 localhost:31055 0.0.0.0:* LISTEN\ntcp 0 0 hellotalk:domain 0.0.0.0:* LISTEN\ntcp 0 0 127.0.0.53:domain 0.0.0.0:* LISTEN\ntcp 0 0 172.16.0.112:50006 113.240.72.111:https TIME_WAIT\ntcp 0 0 10.100.100.164:56010 10.40.2.11:27017 ESTABLISHED\ntcp 0 0 10.100.100.164:43846 10.40.2.5:27017 ESTABLISHED\n</code></pre>\n<p>字段说明:</p>\n<p>1.Proto:传输层协议 TCP 或 UDP</p>\n<p>2.Recv-Q:接收队列</p>\n<p>3.Send-Q:发送队列</p>\n<p>4.Local Address:本地地址</p>\n<p>5.Foreign Address:远程地址</p>\n<p>6.State:状态 LISTEN:侦听,ESTABLISHED:已建立,TIME_WAIT:关闭</p>\n<h4 id=\"2-显示当前户籍-UDP-连接状况\"><a href=\"#2-显示当前户籍-UDP-连接状况\" class=\"headerlink\" title=\"2. 显示当前户籍 UDP 连接状况\"></a>2. 显示当前户籍 UDP 连接状况</h4><pre class=\" language-sh\"><code class=\"language-sh\">netstat -nu\n</code></pre>\n<p>结果:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">激活Internet连接 (w/o 服务器)\nProto Recv-Q Send-Q Local Address Foreign Address State\nudp 0 0 127.0.0.1:56871 127.0.1.1:53 ESTABLISHED\nudp 0 0 172.16.0.112:42799 106.38.222.156:443 ESTABLISHED\nudp 0 0 172.16.0.112:68 172.16.0.1:67 ESTABLISHED\n</code></pre>\n<h4 id=\"3-显示当前户籍-TCP-连接状况\"><a href=\"#3-显示当前户籍-TCP-连接状况\" class=\"headerlink\" title=\"3. 显示当前户籍 TCP 连接状况\"></a>3. 显示当前户籍 TCP 连接状况</h4><pre class=\" language-sh\"><code class=\"language-sh\">netstat -nt\n</code></pre>\n<p>结果:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">激活Internet连接 (w/o 服务器)\nProto Recv-Q Send-Q Local Address Foreign Address State\ntcp 0 0 10.100.100.164:56010 10.40.2.11:27017 ESTABLISHED\ntcp 0 0 10.100.100.164:43846 10.40.2.5:27017 ESTABLISHED\ntcp 0 0 172.16.0.112:60087 43.159.193.150:8002 TIME_WAIT\ntcp 0 0 172.16.0.112:57712 113.240.72.99:443 TIME_WAIT\ntcp 0 0 10.100.100.164:55986 10.40.2.11:27017 ESTABLISHED\ntcp 0 0 10.100.100.164:43722 10.40.2.5:27017 ESTABLISHED\n</code></pre>\n<h4 id=\"4-显示当前系统中所有正在监听的-TCP-端口、相关的进程以及其状态\"><a href=\"#4-显示当前系统中所有正在监听的-TCP-端口、相关的进程以及其状态\" class=\"headerlink\" title=\"4. 显示当前系统中所有正在监听的 TCP 端口、相关的进程以及其状态\"></a>4. 显示当前系统中所有正在监听的 TCP 端口、相关的进程以及其状态</h4><pre class=\" language-sh\"><code class=\"language-sh\">netstat -ntlp\n</code></pre>\n<p>结果:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">Active Internet connections (only servers)\nProto Recv-Q Send-Q Local Address Foreign Address State PID/Program name\ntcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1234/nginx\ntcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 2345/mysqld\ntcp6 0 0 :::22 :::* LISTEN 3456/sshd\n</code></pre>\n<p>字段解析:</p>\n<ol>\n<li><p>Proto:协议</p>\n<ul>\n<li>tcp:表示 TCP 协议。</li>\n<li>tcp6:表示 IPv6 的 TCP 协议。</li>\n</ul>\n</li>\n<li><p>Recv-Q 和 Send-Q:</p>\n<ul>\n<li>Recv-Q:表示接收队列</li>\n<li>Send-Q:表示发送队列</li>\n</ul>\n</li>\n<li><p>Local Address:本地地址</p>\n<ul>\n<li>0.0.0.0:80:本地地址和端口号,0.0.0.0 表示监听所有接口的 IP 地址,80 是端口号。</li>\n<li>127.0.0.1:3306:本地环回地址(localhost)上的端口 3306。</li>\n<li>:::22:IPv6 地址的端口 22。</li>\n</ul>\n</li>\n<li><p>Foreign Address:远程地址</p>\n<ul>\n<li>0.0.0.0:* 和 :::*:表示监听所有外部地址。</li>\n</ul>\n</li>\n<li><p>State:状态</p>\n<ul>\n<li>LISTEN:表示服务器正在监听 TCP 连接请求。</li>\n<li>ESTABLISHED:表示连接已经建立。</li>\n<li>TIME_WAIT:表示主动关闭连接的一方在关闭连接后,等待 2MSL 后依然没有收到对方的 FIN 报文,处于等待关闭状态。</li>\n<li>CLOSE_WAIT:表示被动关闭连接的一方在收到对方的 FIN 报文后,等待应用进程关闭连接。</li>\n<li>LAST_ACK:表示被动关闭连接的一方在发送完 FIN 报文后,等待对方的 ACK 报文,如果收到 ACK 报文,则处于 CLOSED 状态,否则处于 TIME_WAIT 状态。</li>\n<li>CLOSED:表示连接已经关闭。</li>\n<li>SYN_RECV:表示正在等待处理的请求数目。</li>\n<li>SYN_SENT:表示正在等待远程连接请求。</li>\n<li>CLOSING:表示正在等待远程连接关闭请求。</li>\n<li>UNKNOWN:表示未知状态。</li>\n<li>LISTENING:表示正在等待远程连接请求。</li>\n<li>IDLE:表示连接处于空闲状态。</li>\n<li>BOUND:表示正在等待远程连接请求。</li>\n<li>FIN_WAIT1: 套接字已经发出了一个 FIN 请求,表示它已经完成了发送数据,正在等待对方的 ACK 响应。</li>\n<li>FIN_WAIT2: 套接字收到了对 FIN 请求的 ACK 响应,正在等待对方发出 FIN 请求。</li>\n</ul>\n</li>\n<li><p>PID/Program name:进程 ID 和进程名称</p>\n<ul>\n<li>1234/nginx:进程 ID 为 1234 的 nginx 进程正在监听 80 端口。</li>\n<li>2345/mysqld:进程 ID 为 2345 的 mysqld 进程正在监听 3306 端口。</li>\n<li>3456/sshd:进程 ID 为 3456 的 sshd 进程正在监听 22 端口。</li>\n</ul>\n</li>\n</ol>\n<h4 id=\"5-显示网卡列表\"><a href=\"#5-显示网卡列表\" class=\"headerlink\" title=\"5. 显示网卡列表\"></a>5. 显示网卡列表</h4><pre class=\" language-sh\"><code class=\"language-sh\"> netstat -i\n</code></pre>\n<p>结果:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg\nbr-717e070443f8 1500 0 0 0 0 0 0 0 0 BMU\nbr-7f56e5476213 1500 0 0 0 0 0 0 0 0 BMU\ndocker0 1500 0 0 0 0 0 0 0 0 BMU\neno1 1500 3838424 0 0 0 3615697 0 0 0 BMRU\nlo 65536 1115368 0 0 0 1115368 0 0 0 LRU\nutun 1400 2667684 0 0 0 2598307 0 0 0 MOPRU\nvirbr0 1500 0 0 0 0 0 0 0 0 BMU\n</code></pre>\n<p>字段解析:</p>\n<ol>\n<li>Iface:网卡名称</li>\n<li>MTU:最大传输单元</li>\n<li>RX-OK:接收数据包总数</li>\n<li>RX-ERR:接收数据包错误总数</li>\n<li>RX-DRP:接收数据包丢弃总数</li>\n<li>RX-OVR:接收数据包溢出总数</li>\n<li>TX-OK:发送数据包总数</li>\n<li>TX-ERR:发送数据包错误总数</li>\n<li>TX-DRP:发送数据包丢弃总数</li>\n<li>TX-OVR:发送数据包溢出总数</li>\n<li>Flg:标志</li>\n</ol>\n<ul>\n<li>BMU:广播地址</li>\n<li>BMRU:广播地址和多播地址</li>\n<li>MOPRU:多播地址</li>\n<li>LRU:本地地址</li>\n<li>MPRU:多播地址和本地地址</li>\n<li>OPRU:多播地址和本地地址和广播地址</li>\n<li>ALL:所有地址</li>\n</ul>\n<p>#### 6. 显示组播组的关系</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">netstat -g\n</code></pre>\n<p>结果:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">IPv6/IPv4 Group Memberships\nInterface RefCnt Group\n--------------- ------ ---------------------\nlo 1 mdns.mcast.net\nlo 1 all-systems.mcast.net\n</code></pre>\n<p>字段解析:</p>\n<ol>\n<li>Interface:网卡名称</li>\n<li>RefCnt:引用计数</li>\n<li>Group:组播组</li>\n</ol>\n<p>#### 7. 显示网络统计信息</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">netstat -s\n</code></pre>\n<p>结果:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">Ip:\n Forwarding: 1\n 7269919 total packets received\n 0 forwarded\n 0 incoming packets discarded\n 7265662 incoming packets delivered\n 7078118 requests sent out\n 28 outgoing packets dropped\n 7895 dropped because of missing route\n 8416 reassemblies required\n 4208 packets reassembled ok\n 6 outgoing packets failed fragmentation\n OutTransmits: 7078115\nIcmp:\n 2022 ICMP messages received\n 6 input ICMP message failed\n ICMP接收历史\n destination unreachable: 2020\n echo requests: 2\n 2176 ICMP messages sent\n 0 ICMP messages failed\n OutRateLimitHost: 253\n ICMP发出历史\n destination unreachable: 2174\n echo replies: 2\nIcmpMsg:\n InType3: 2020\n InType8: 2\n OutType0: 2\n OutType3: 2174\nTcp:\n 202192 active connection openings\n 10375 passive connection openings\n 120686 failed connection attempts\n 3047 connection resets received\n 77 connections established\n 6857878 segments received\n 7083293 segments sent out\n 16799 segments retransmitted\n 1119 bad segments received\n 131681 resets sent\nUdp:\n 1456958 packets received\n 2421 packets to unknown port received\n 32 packet receive errors\n 556388 packets sent\n 0 receive buffer errors\n 8 send buffer errors\n InCsumErrors: 32\n IgnoredMulti: 12191\nUdpLite:\nTcpExt:\n 3 ICMP packets dropped because they were out-of-window\n 30122 TCP sockets finished time wait in fast timer\n 1 packets rejected in established connections because of timestamp\n 119999 delayed acks sent\n 39 delayed acks further delayed because of locked socket\n Quick ack mode was activated 34511 times\n 1065795 packet headers predicted\n 687289 acknowledgments not containing data payload received\n 1455607 predicted acknowledgments\n TCPSackRecovery: 327\n Detected reordering 676 times using SACK\n TCPDSACKUndo: 97\n 87 congestion windows recovered without slow start after partial ack\n TCPLostRetransmit: 8875\n TCPSackFailures: 8\n 3 timeouts in loss state\n 466 fast retransmits\n 133 retransmits in slow start\n TCPTimeouts: 11823\n TCPLossProbes: 5420\n TCPLossProbeRecovery: 82\n TCPSackRecoveryFail: 10\n TCPBacklogCoalesce: 2369\n TCPDSACKOldSent: 34608\n TCPDSACKOfoSent: 51\n TCPDSACKRecv: 1827\n TCPDSACKOfoRecv: 16\n 1537 connections reset due to unexpected data\n 2171 connections reset due to early user close\n 484 connections aborted due to timeout\n 10 times unable to send RST due to no memory\n TCPSACKDiscard: 2\n TCPDSACKIgnoredOld: 7\n TCPDSACKIgnoredNoUndo: 815\n TCPSackShifted: 26\n TCPSackMerged: 342\n TCPSackShiftFallback: 1984\n IPReversePathFilter: 22\n TCPRcvCoalesce: 203651\n TCPOFOQueue: 50737\n TCPOFOMerge: 54\n TCPChallengeACK: 879\n TCPSYNChallenge: 1140\n TCPSpuriousRtxHostQueues: 18\n TCPAutoCorking: 458417\n TCPFromZeroWindowAdv: 35\n TCPToZeroWindowAdv: 35\n TCPWantZeroWindowAdv: 167\n TCPSynRetrans: 7312\n TCPOrigDataSent: 3398432\n TCPHystartTrainDetect: 22\n TCPHystartTrainCwnd: 1073\n TCPHystartDelayDetect: 42\n TCPHystartDelayCwnd: 1831\n TCPACKSkippedSeq: 286\n TCPACKSkippedChallenge: 265\n TCPKeepAlive: 97079\n TCPDelivered: 3475194\n TCPAckCompressed: 14499\n TcpTimeoutRehash: 11471\n TCPDSACKRecvSegs: 1841\n TCPDSACKIgnoredDubious: 2\nIpExt:\n InNoRoutes: 17\n InMcastPkts: 118643\n OutMcastPkts: 8410\n InBcastPkts: 17704\n OutBcastPkts: 1960\n InOctets: 3807905209\n OutOctets: 1523626052\n InMcastOctets: 53391437\n OutMcastOctets: 1586713\n InBcastOctets: 9013152\n OutBcastOctets: 152880\n InNoECTPkts: 7512863\n InECT0Pkts: 18\nMPTcpExt:\n</code></pre>\n<h4 id=\"显示监听的套接口\"><a href=\"#显示监听的套接口\" class=\"headerlink\" title=\"显示监听的套接口\"></a>显示监听的套接口</h4><pre class=\" language-sh\"><code class=\"language-sh\">netstat -l\n</code></pre>\n<pre class=\" language-sh\"><code class=\"language-sh\">激活Internet连接 (仅服务器)\nProto Recv-Q Send-Q Local Address Foreign Address State \ntcp 0 0 hellotalk:domain 0.0.0.0:* LISTEN \ntcp6 0 0 [::]:3100 [::]:* LISTEN \nudp 0 0 0.0.0.0:45010 0.0.0.0:* \nudp 0 0 0.0.0.0:mdns 0.0.0.0:* \nudp6 0 0 [::]:49287 [::]:* \n 7 \n活跃的UNIX域套接字 (仅服务器)\nProto RefCnt Flags Type State I-Node 路径\nunix 2 [ ACC ] 流 LISTENING 1010159 /tmp/.java_pid169241.tmp\n</code></pre>\n<p>字段解析:</p>\n<ul>\n<li>Proto:协议名,如tcp、udp、unix等</li>\n<li>Recv-Q:接收队列,即收到的但未处理的数据包数</li>\n<li>Send-Q:发送队列,即发送但未确认的数据包数</li>\n<li>Local Address:本地地址</li>\n<li>Foreign Address:远程地址</li>\n<li>State:套接字状态,如LISTEN、ESTABLISHED等</li>\n<li>I-Node:套接字inode号</li>\n<li>路径:unix域套接字路径</li>\n<li>RefCnt:引用计数</li>\n<li>Flags:套接字标志,如S表示套接字是被动打开的,即服务器端,而A表示套接字是主动打开的,即客户端</li>\n<li>Type:套接字类型,如流套接字、数据报套接字等</li>\n<li>路径:unix域套接字路径</li>\n</ul>\n<h4 id=\"查看路由表\"><a href=\"#查看路由表\" class=\"headerlink\" title=\"查看路由表\"></a>查看路由表</h4><pre class=\" language-sh\"><code class=\"language-sh\">netstat -r\n</code></pre>\n<pre class=\" language-sh\"><code class=\"language-sh\">内核 IP 路由表\nDestination Gateway Genmask Flags MSS Window irtt Iface\ndefault 172.16.0.1 0.0.0.0 UG 0 0 0 eno1\none.one.one.one 0.0.0.0 255.255.255.255 UH 0 0 0 utun\n10.10.0.0 0.0.0.0 255.255.0.0 U 0 0 0 utun\n10.40.0.0 0.0.0.0 255.255.0.0 U 0 0 0 utun\n10.100.100.0 0.0.0.0 255.255.255.0 U 0 0 0 utun\n30.100.0.0 0.0.0.0 255.252.0.0 U 0 0 0 utun\nfeilian.hellota 172.16.0.1 255.255.255.255 UGH 0 0 0 eno1\nlink-local 0.0.0.0 255.255.0.0 U 0 0 0 utun\n172.16.0.0 0.0.0.0 255.255.254.0 U 0 0 0 eno1\n172.16.6.20 172.16.0.1 255.255.255.255 UGH 0 0 0 eno1\n172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0\n192.168.49.0 0.0.0.0 255.255.255.0 U 0 0 0 br-717e070443f8\n192.168.58.0 0.0.0.0 255.255.255.0 U 0 0 0 br-7f56e5476213\n192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0\n</code></pre>\n<p>字段解析:</p>\n<ul>\n<li>Destination:目的网络或目的主机</li>\n<li>Gateway:指定用于转发包的网关。</li>\n<li>Genmask:子网掩码</li>\n<li>Flags:路由标志,如U表示路由是活动的,H表示目的地址是一个主机,G表示使用网关</li>\n<li>MSS:最大分段大小</li>\n<li>Window:TCP窗口大小</li>\n<li>irtt:初始RTT</li>\n<li>Iface:路由接口</li>\n</ul>\n<h4 id=\"只列出监听中的连接\"><a href=\"#只列出监听中的连接\" class=\"headerlink\" title=\"只列出监听中的连接\"></a>只列出监听中的连接</h4><pre class=\" language-sh\"><code class=\"language-sh\">netstat -tnl\n</code></pre>\n<pre class=\" language-sh\"><code class=\"language-sh\">激活Internet连接 (仅服务器)\nProto Recv-Q Send-Q Local Address Foreign Address State \ntcp 0 0 192.168.122.1:53 0.0.0.0:* LISTEN \ntcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN \ntcp 0 0 127.0.0.1:35600 0.0.0.0:* LISTEN \ntcp 0 0 127.0.0.54:53 0.0.0.0:* LISTEN \ntcp 0 0 127.0.0.1:31080 0.0.0.0:* LISTEN \ntcp 0 0 127.0.0.1:31055 0.0.0.0:* LISTEN \ntcp 0 0 127.0.1.1:53 0.0.0.0:* LISTEN \ntcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN \ntcp6 0 0 :::41499 :::* LISTEN \ntcp6 0 0 127.0.0.1:52829 :::* LISTEN \ntcp6 0 0 127.0.0.1:35955 :::* LISTEN \ntcp6 0 0 127.0.0.1:63343 :::* LISTEN \ntcp6 0 0 127.0.0.1:63342 :::* LISTEN \n</code></pre>\n<h4 id=\"获取进程名、进程号以及用户-ID\"><a href=\"#获取进程名、进程号以及用户-ID\" class=\"headerlink\" title=\"获取进程名、进程号以及用户 ID\"></a>获取进程名、进程号以及用户 ID</h4><pre class=\" language-sh\"><code class=\"language-sh\">netstat -tunlp\n</code></pre>\n<pre class=\" language-sh\"><code class=\"language-sh\">Active Internet connections (only servers)\nProto Recv-Q Send-Q Local Address Foreign Address State PID/Program name\ntcp 0 0 127.0.1.1:53 0.0.0.0:* LISTEN 1144/dnsmasq \ntcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 661/cupsd \ntcp6 0 0 ::1:631 :::* LISTEN 661/cupsd\n</code></pre>\n<h4 id=\"查看进程的拥有者会更有用。使用-ep-选项可以同时查看进程名和用户名\"><a href=\"#查看进程的拥有者会更有用。使用-ep-选项可以同时查看进程名和用户名\" class=\"headerlink\" title=\"查看进程的拥有者会更有用。使用 -ep 选项可以同时查看进程名和用户名\"></a>查看进程的拥有者会更有用。使用 -ep 选项可以同时查看进程名和用户名</h4><pre class=\" language-sh\"><code class=\"language-sh\">netstat -tunlep\n</code></pre>\n<pre class=\" language-sh\"><code class=\"language-sh\">Proto Recv-Q Send-Q Local Address Foreign Address State User Inode PID/Program name \ntcp 0 0 192.168.122.1:domain 0.0.0.0:* LISTEN root 14887 1567/dnsmasq \ntcp 0 0 localhost:ipp 0.0.0.0:* LISTEN root 1946474 310640/cupsd \n</code></pre>\n<p>参考:<br><a href=\"https://docs.oracle.com/cd/E26926_01/html/E25874/ipconfig-142.html\">https://docs.oracle.com/cd/E26926_01/html/E25874/ipconfig-142.html</a></p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"Linux",
"Network",
"Linux",
"Network"
]
},
{
"id": "https://costalong.com/2024/07/11/git/no-sensitive/",
"url": "https://costalong.com/2024/07/11/git/no-sensitive/",
"title": "如何在 Git 提交中禁止包含敏感信息",
"date_published": "2024-07-11T10:58:20.000Z",
"content_html": "<p>为了防止在 Git 提交中包含敏感信息(如数据库密码),可以采取以下几种方法:</p>\n<h2 id=\"1-使用-gitignore\"><a href=\"#1-使用-gitignore\" class=\"headerlink\" title=\"1. 使用 .gitignore\"></a>1. 使用 .gitignore</h2><p>将包含敏感信息的文件添加到 <code>.gitignore</code> 文件中,这样 Git 就不会跟踪这些文件。例如,如果你的敏感信息存储在 <code>config.json</code> 文件中,你可以在 <code>.gitignore</code> 中添加:</p>\n<pre><code>config.json\n</code></pre>\n<h2 id=\"2-使用环境变量\"><a href=\"#2-使用环境变量\" class=\"headerlink\" title=\"2. 使用环境变量\"></a>2. 使用环境变量</h2><p>将敏感信息存储在环境变量中,而不是直接在代码中。例如,在代码中读取环境变量:</p>\n<pre class=\" language-python\"><code class=\"language-python\"><span class=\"token keyword\">import</span> os\n\ndb_password <span class=\"token operator\">=</span> os<span class=\"token punctuation\">.</span>getenv<span class=\"token punctuation\">(</span><span class=\"token string\">'DB_PASSWORD'</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<p>在运行应用程序之前,设置环境变量:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">export DB_PASSWORD=\"your_database_password\"\n</code></pre>\n<h2 id=\"3-使用-Git-钩子(pre-commit-钩子)\"><a href=\"#3-使用-Git-钩子(pre-commit-钩子)\" class=\"headerlink\" title=\"3. 使用 Git 钩子(pre-commit 钩子)\"></a>3. 使用 Git 钩子(pre-commit 钩子)</h2><p>可以编写一个 Git pre-commit 钩子,在每次提交之前检查提交内容中是否包含敏感信息。如果包含,则拒绝提交。</p>\n<h3 id=\"方法1:手动编写-pre-commit-钩子\"><a href=\"#方法1:手动编写-pre-commit-钩子\" class=\"headerlink\" title=\"方法1:手动编写 pre-commit 钩子\"></a>方法1:手动编写 pre-commit 钩子</h3><p>首先,创建一个 pre-commit 钩子脚本:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">#!/bin/sh\n# 定义敏感信息模式\nSENSITIVE_PATTERNS=(\"your_database_password\" \"another_sensitive_pattern\")\n\n# 获取已暂存的文件\nSTAGED_FILES=$(git diff --cached --name-only)\n\nfor FILE in $STAGED_FILES; do\n for PATTERN in \"${SENSITIVE_PATTERNS[@]}\"; do\n if git grep -q \"$PATTERN\" \"$FILE\"; then\n echo \"Error: Sensitive information found in $FILE\"\n exit 1\n fi\n done\ndone\n\nexit 0\n</code></pre>\n<p>将这个脚本保存到 <code>.git/hooks/pre-commit</code> 文件中,并确保它具有可执行权限:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">chmod +x .git/hooks/pre-commit\n</code></pre>\n<h3 id=\"方法2:使用-pre-commit-工具-禁止已经提交的文件\"><a href=\"#方法2:使用-pre-commit-工具-禁止已经提交的文件\" class=\"headerlink\" title=\"方法2:使用 pre-commit 工具 禁止已经提交的文件\"></a>方法2:使用 pre-commit 工具 禁止已经提交的文件</h3><ol>\n<li>安装 pre-commit</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">pip install pre-commit\n</code></pre>\n<p>或者</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">brew install pre-commit # mac \napt install pre-commit # linux\n</code></pre>\n<ol start=\"2\">\n<li>添加默认配置文件</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">pre-commit sample-config > .pre-commit-config.yaml\n</code></pre>\n<p>在项目根目录下生成文件 <code>.pre-commit-config.yaml</code>,内容如下:</p>\n<pre class=\" language-yaml\"><code class=\"language-yaml\"><span class=\"token comment\" spellcheck=\"true\"># See https://pre-commit.com for more information</span>\n<span class=\"token comment\" spellcheck=\"true\"># See https://pre-commit.com/hooks.html for more hooks</span>\n<span class=\"token key atrule\">repos</span><span class=\"token punctuation\">:</span>\n<span class=\"token punctuation\">-</span> <span class=\"token key atrule\">repo</span><span class=\"token punctuation\">:</span> https<span class=\"token punctuation\">:</span>//github.com/pre<span class=\"token punctuation\">-</span>commit/pre<span class=\"token punctuation\">-</span>commit<span class=\"token punctuation\">-</span>hooks\n <span class=\"token key atrule\">rev</span><span class=\"token punctuation\">:</span> v3.2.0\n <span class=\"token key atrule\">hooks</span><span class=\"token punctuation\">:</span>\n <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">id</span><span class=\"token punctuation\">:</span> trailing<span class=\"token punctuation\">-</span>whitespace\n <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">id</span><span class=\"token punctuation\">:</span> end<span class=\"token punctuation\">-</span>of<span class=\"token punctuation\">-</span>file<span class=\"token punctuation\">-</span>fixer\n <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">id</span><span class=\"token punctuation\">:</span> check<span class=\"token punctuation\">-</span>yaml\n <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">id</span><span class=\"token punctuation\">:</span> check<span class=\"token punctuation\">-</span>added<span class=\"token punctuation\">-</span>large<span class=\"token punctuation\">-</span>files\n</code></pre>\n<ol start=\"3\">\n<li>配置 pre-commit 钩子:</li>\n</ol>\n<p>在 <code>.pre-commit-config.yaml</code> 文件中添加一个自定义钩子来检查指定文件是否被修改。例如,假设你想保护文件 <code>protected_file.txt</code>,可以这样配置:</p>\n<pre class=\" language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">repos</span><span class=\"token punctuation\">:</span>\n <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">repo</span><span class=\"token punctuation\">:</span> local\n <span class=\"token key atrule\">hooks</span><span class=\"token punctuation\">:</span>\n <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">id</span><span class=\"token punctuation\">:</span> protect<span class=\"token punctuation\">-</span>protected<span class=\"token punctuation\">-</span>file\n <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> Protect protected_file.txt\n <span class=\"token key atrule\">entry</span><span class=\"token punctuation\">:</span> bash protect<span class=\"token punctuation\">-</span>protected<span class=\"token punctuation\">-</span>file.sh\n <span class=\"token key atrule\">language</span><span class=\"token punctuation\">:</span> system\n <span class=\"token key atrule\">stages</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span>commit<span class=\"token punctuation\">]</span>\n</code></pre>\n<ol start=\"4\">\n<li>创建自定义钩子脚本</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">#!/bin/sh\n\n# 要保护的文件列表\nPROTECTED_FILES=(\n \"protected_file.txt\" \n)\n\n# 检查是否有文件被修改\nfor FILE in \"${PROTECTED_FILES[@]}\"; do\n if git diff --cached --name-only | grep -q \"^$FILE$\"; then\n echo \"Error: You are trying to modify $FILE, which is protected.\"\n exit 1\n fi\ndone\n\n# 允许提交\nexit 0\n</code></pre>\n<ol start=\"5\">\n<li>使脚本可执行:</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">chmod +x protect-protected-file.sh\n</code></pre>\n<ol start=\"6\">\n<li>安装 pre-commit 钩子:</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">pre-commit install\n</code></pre>\n<h2 id=\"4-使用工具(如-git-secrets)\"><a href=\"#4-使用工具(如-git-secrets)\" class=\"headerlink\" title=\"4. 使用工具(如 git-secrets)\"></a>4. 使用工具(如 git-secrets)</h2><p><code>git-secrets</code> 是一个专门用于防止在 Git 仓库中提交敏感信息的工具。它可以在提交之前扫描提交内容,并阻止包含敏感信息的提交。</p>\n<h3 id=\"安装-git-secrets\"><a href=\"#安装-git-secrets\" class=\"headerlink\" title=\"安装 git-secrets\"></a>安装 git-secrets</h3><h4 id=\"对于-macOS\"><a href=\"#对于-macOS\" class=\"headerlink\" title=\"对于 macOS\"></a>对于 macOS</h4><pre class=\" language-sh\"><code class=\"language-sh\">brew install git-secrets\n</code></pre>\n<h4 id=\"对于-Linux\"><a href=\"#对于-Linux\" class=\"headerlink\" title=\"对于 Linux\"></a>对于 Linux</h4><pre class=\" language-sh\"><code class=\"language-sh\">git clone https://github.com/awslabs/git-secrets.git\ncd git-secrets\nsudo make install\n</code></pre>\n<p>然后在你的 Git 仓库中初始化 git-secrets 并添加敏感信息模式:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">cd your-repo\ngit secrets --install\ngit secrets --add 'your_database_password'\ngit secrets --add 'another_sensitive_pattern'\n</code></pre>\n<p>这样,每次提交时,git-secrets 都会扫描提交内容并阻止包含敏感信息的提交。</p>\n<h2 id=\"5-使用-Secret-Scanning-服务\"><a href=\"#5-使用-Secret-Scanning-服务\" class=\"headerlink\" title=\"5. 使用 Secret Scanning 服务\"></a>5. 使用 Secret Scanning 服务</h2><p>一些代码托管平台(如 GitHub)提供了 Secret Scanning 服务,可以自动扫描仓库中的敏感信息,并在发现敏感信息时发出警告。</p>\n<p>通过以上方法,可以有效防止在 Git 提交中包含敏感信息,保护你的代码库安全。</p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"Tool",
"git"
]
},
{
"id": "https://costalong.com/2024/07/03/linux/wechat/",
"url": "https://costalong.com/2024/07/03/linux/wechat/",
"title": "ubuntu24.04 微信客户端",
"date_published": "2024-07-03T07:14:42.000Z",
"content_html": "<h2 id=\"使用终端命令行安装铜豌豆软件源。注意需要用到sudo权限\"><a href=\"#使用终端命令行安装铜豌豆软件源。注意需要用到sudo权限\" class=\"headerlink\" title=\"使用终端命令行安装铜豌豆软件源。注意需要用到sudo权限\"></a>使用终端命令行安装铜豌豆软件源。注意需要用到sudo权限</h2><pre class=\" language-sh\"><code class=\"language-sh\">wget -c -O atzlinux-v12-archive-keyring_lastest_all.deb https://www.atzlinux.com/atzlinux/pool/main/a/atzlinux-archive-keyring/atzlinux-v12-archive-keyring_lastest_all.deb\nsudo apt -y install ./atzlinux-v12-archive-keyring_lastest_all.deb\n</code></pre>\n<h2 id=\"使用终端命令行安装微信原生版本。注意需要用到sudo权限\"><a href=\"#使用终端命令行安装微信原生版本。注意需要用到sudo权限\" class=\"headerlink\" title=\"使用终端命令行安装微信原生版本。注意需要用到sudo权限\"></a>使用终端命令行安装微信原生版本。注意需要用到sudo权限</h2><pre class=\" language-sh\"><code class=\"language-sh\">sudo apt update\nsudo cp /etc/lsb-release /etc/lsb-release.Ubuntu\nsudo apt -y install electronic-wechat-icons-atzlinux\nsudo apt -y install com.tencent.wechat\nsudo cp /etc/lsb-release /etc/lsb-release.wechat\n</code></pre>\n<h2 id=\"登录微信并使用\"><a href=\"#登录微信并使用\" class=\"headerlink\" title=\"登录微信并使用\"></a>登录微信并使用</h2><pre class=\" language-sh\"><code class=\"language-sh\">wechat\n</code></pre>\n<p>登录成功以后,下次就可以直接在终端使用wechat启动微信,或者在显示应用程序里面找到微信linux原生版图标启动了。本次安装的微信版本号:1.0.0.241</p>\n<p><img src=\"/medias/loading.gif\" data-original=\"/images/linux/wechat/image.png\" alt=\"start wechat\"></p>\n<p><a href=\"https://link.zhihu.com/?target=https://www.atzlinux.com/\">铜豌豆 Linux</a>是个开源网站</p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"linux",
"ubuntu",
"微信"
]
},
{
"id": "https://costalong.com/2024/07/01/linux/awk/",
"url": "https://costalong.com/2024/07/01/linux/awk/",
"title": "awk 常用的命令",
"date_published": "2024-07-01T08:14:42.000Z",
"content_html": "<h2 id=\"处理字符串\"><a href=\"#处理字符串\" class=\"headerlink\" title=\"处理字符串\"></a>处理字符串</h2><p><strong>提取字符串第三字符串</strong></p>\n<pre class=\" language-sh\"><code class=\"language-sh\">echo 'this is a test' |awk '{print $3}'\n</code></pre>\n<p><strong>替换 字符串中一个</strong></p>\n<pre class=\" language-sh\"><code class=\"language-sh\"> echo \"Hello Tom\" | awk '{$2=\"Adam\"; print $0}'\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">Hello Adam\n</code></pre>\n<h3 id=\"利用变量-菜鸟\"><a href=\"#利用变量-菜鸟\" class=\"headerlink\" title=\"利用变量 菜鸟\"></a>利用变量 <a href=\"https://www.runoob.com/w3cnote/8-awesome-awk-built-in-variables.html\">菜鸟</a></h3><ol>\n<li><p>$NF 表示当前行有多少个字段,</p>\n<p>因此<code>$NF</code>就代表最后一个字段。</p>\n</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">echo 'this is a test' | awk '{print $NF}'\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">test\n</code></pre>\n<p> <code>$(NF-1)</code>代表倒数第二个字段</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">echo 'this is a test' | awk '{print $(NF-1)}'\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">a\n</code></pre>\n<p> 2 .变量<code>NR</code>表示当前处理的是第几行。</p>\n<h2 id=\"处理文件\"><a href=\"#处理文件\" class=\"headerlink\" title=\"处理文件\"></a>处理文件</h2><p>处理文件 logs.txt , 文件内容:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">07.46.199.184 [28/Sep/2010:04:08:20] \"GET /robots.txt HTTP/1.1\" 200 0 \"msnbot\"\n123.125.71.19 [28/Sep/2010:04:20:11] \"GET / HTTP/1.1\" 304 - \"Baiduspider\"\n</code></pre>\n<p> <strong>获取第一列数据</strong></p>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk '{print $1}' logs.txt\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">07.46.199.184\n123.125.71.19\n</code></pre>\n<p><strong>这个文件的字段分隔符是冒号(<code>:</code>),所以要用<code>-F</code>参数指定分隔符为冒号。然后,才能提取到它的第一个字段。</strong></p>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk -F ':' '{print $1}' logs.txt\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">07.46.199.184 [28/Sep/2010\n123.125.71.19 [28/Sep/2010\n</code></pre>\n<p><strong>提取log 的时间</strong></p>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk '{print $2}' logs.txt | awk 'BEGIN{FS=\":\"}{print $1}' | sed 's/\\[//'\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">28/Sep/2010\n28/Sep/2010\n</code></pre>\n<p>**统计某一个字段的相加 ** </p>\n<p>每次都大于结果</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk '{a+=$(NF-2); print \"Total so far:\", a}' logs.txt\n</code></pre>\n<pre class=\" language-sh\"><code class=\"language-sh\">Total so far: 200\nTotal so far: 504\n</code></pre>\n<p>执行完后在打印结果</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk '{a+=$(NF-2)}END{print \"Total:\", a}' logs.txt\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">Total: 504\n</code></pre>\n<h3 id=\"利用变量\"><a href=\"#利用变量\" class=\"headerlink\" title=\"利用变量\"></a>利用变量</h3><ol>\n<li><strong>OFS: 输出字段分隔符变量</strong></li>\n</ol>\n<p><strong>OFS</strong>(Output Field Separator) 相当与输出上的 <strong>FS</strong>, 默认是以一个空格字符作为输出分隔符的,下面是一个 <strong>OFS</strong> 的例子:</p>\n<p>正常空格命令:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk '{print $1, $3;}' logs.txt\n</code></pre>\n<p>输出结果:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">07.46.199.184 \"GET\n123.125.71.19 \"GET\n</code></pre>\n<p>注意命令中的 print 语句的, 表示的使用一个空格连接两个参数,也就是默认的OFS的值。因此 <strong>OFS</strong> 可以像下面那样插入到输出的字段之间:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk 'BEGIN{OFS=\"=>\";}{print $1, $3;}' logs.txt\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">07.46.199.184=>\"GET\n123.125.71.19=>\"GET\n</code></pre>\n<p>注意: GET 前面多一个双引号,我们需要去掉</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk '{print $1, $3;}' logs.txt |sed 's/\\\"//'\n\nawk 'BEGIN{OFS=\"=>\";}{print $1, $3;}' logs.txt |sed 's/\\\"//'\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">07.46.199.184 GET\n123.125.71.19 GET\n\n07.46.199.184=>GET\n123.125.71.19=>GET\n</code></pre>\n<ol start=\"2\">\n<li><strong>变量<code>NR</code>表示当前处理的是第几行。</strong></li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk '{print NR\") \"$1}' logs.txt\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">1) 07.46.199.184\n2) 123.125.71.19\n</code></pre>\n<ol start=\"3\">\n<li><p>变量 NF 表示一列的最后一个字段 </p>\n<p>$(NF-2) 表示倒数第三个字段 </p>\n<p>利用 if 判断倒数第三个字段 是否 等于 200</p>\n<pre class=\" language-sh\"><code class=\"language-sh\"> awk '条件 动作' 文件名\n</code></pre>\n</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk '{if ($(NF-2) == \"200\") {print $0}}' logs.txt\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">07.46.199.184 [28/Sep/2010:04:08:20] \"GET /robots.txt HTTP/1.1\" 200 0 \"msnbot\"\n</code></pre>\n<h2 id=\"处理进程\"><a href=\"#处理进程\" class=\"headerlink\" title=\"处理进程\"></a>处理进程</h2><ol>\n<li>根据 lsof 命令获取进程 PID</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">lsof -i:3100|awk 'NR>1' |awk '{print $2}' \n</code></pre>\n<p> kill -9 进程</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">lsof -i:3100|awk 'NR>1' |awk '{print $2}' | xargs kill -9\n</code></pre>\n<p>NR 大于 1 表示从第二行开始</p>\n<p>输出:</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">2299757\n2299757\n2299757\n2299757\n2299757\n2299757\n2299757\n2299757\n2299757\n2410946\n</code></pre>\n<h2 id=\"统计文件中的数量\"><a href=\"#统计文件中的数量\" class=\"headerlink\" title=\"统计文件中的数量\"></a>统计文件中的数量</h2><p>查询 file 文件中 haha 的数量</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">awk -v RS='haha' 'END {print --NR}' file \n</code></pre>\n<p>或者使用 grep </p>\n<pre class=\" language-sh\"><code class=\"language-sh\">grep -o 'haha' file | wc -l\n</code></pre>\n<h2 id=\"查询使用端口\"><a href=\"#查询使用端口\" class=\"headerlink\" title=\"查询使用端口\"></a>查询使用端口</h2><pre class=\" language-bash\"><code class=\"language-bash\">ss -nutlp <span class=\"token operator\">|</span> <span class=\"token function\">awk</span> <span class=\"token string\">'{print <span class=\"token variable\">$1</span>,<span class=\"token variable\">$5</span>}'</span> <span class=\"token operator\">|</span> <span class=\"token function\">awk</span> -F<span class=\"token string\">\"[: ]\"</span> <span class=\"token string\">'{print \"协议:\"<span class=\"token variable\">$1</span>, \"端口号:\"<span class=\"token variable\">$NF</span>}'</span><span class=\"token operator\">|</span><span class=\"token function\">grep</span> <span class=\"token string\">\"[0-9]\"</span><span class=\"token operator\">|</span><span class=\"token function\">uniq</span>\n</code></pre>\n<h2 id=\"参考链接:\"><a href=\"#参考链接:\" class=\"headerlink\" title=\"参考链接:\"></a>参考链接:</h2><p><a href=\"https://gregable.com/2010/09/why-you-should-know-just-little-awk.html\">Awk Example</a></p>\n<p><a href=\"https://www.runoob.com/linux/linux-comm-awk.html\">菜鸟</a></p>\n<p><a href=\"https://www.ruanyifeng.com/blog/2018/11/awk.html\">awk 入门教程</a></p>\n<p><a href=\"https://likegeeks.com/awk-command/\">30 Examples for Awk Command in Text Processing</a></p>\n<p><a href=\"https://zhuanlan.zhihu.com/p/34946663\">查找grep、提取awk、sed、重定向</a></p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"linux",
"awk"
]
},
{
"id": "https://costalong.com/2024/07/01/linux/tcpdump/",
"url": "https://costalong.com/2024/07/01/linux/tcpdump/",
"title": "tcpdump 常用命令",
"date_published": "2024-07-01T08:14:42.000Z",
"content_html": "<p>tcpdump 支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用的信息</p>\n<h2 id=\"命令格式:\"><a href=\"#命令格式:\" class=\"headerlink\" title=\"命令格式:\"></a>命令格式:</h2><pre class=\" language-sh\"><code class=\"language-sh\">tcpdump [ -DenNqvX ] [ -c count ] [ -F file ] [ -i interface ] [ -r file ]\n [ -s snaplen ] [ -w file ] [ expression ]\n</code></pre>\n<p>抓包选项:<br>-c:指定要抓取的包数量。</p>\n<p>-i interface:指定tcpdump需要监听的接口。默认会抓取第一个网络接口</p>\n<p>-n:对地址以数字方式显式,否则显式为主机名,也就是说-n选项不做主机名解析。</p>\n<p>-nn:除了-n的作用外,还把端口显示为数值,否则显示端口服务名。</p>\n<p>-P:指定要抓取的包是流入还是流出的包。可以给定的值为”in”、”out”和”inout”,默认为”inout”。</p>\n<p>-s len:设置tcpdump的数据包抓取长度为len,如果不设置默认将会是65535字节。对于要抓取的数据包较大时,长度设置不够可能会产生包截断,若出现包截断,<br>:输出行中会出现”[|proto]”的标志(proto实际会显示为协议名)。但是抓取len越长,包的处理时间越长,并且会减少tcpdump可缓存的数据包的数量,<br>:从而会导致数据包的丢失,所以在能抓取我们想要的包的前提下,抓取长度越小越好。</p>\n<p>输出选项:<br>-e:输出的每行中都将包括数据链路层头部信息,例如源MAC和目标MAC。</p>\n<p>-q:快速打印输出。即打印很少的协议相关信息,从而输出行都比较简短。</p>\n<p>-X:输出包的头部数据,会以16进制和ASCII两种方式同时输出。</p>\n<p>-XX:输出包的头部数据,会以16进制和ASCII两种方式同时输出,更详细。</p>\n<p>-v:当分析和打印的时候,产生详细的输出。</p>\n<p>-vv:产生比-v更详细的输出。<br>-vvv:产生比-vv更详细的输出。</p>\n<h2 id=\"tcpdump示例\"><a href=\"#tcpdump示例\" class=\"headerlink\" title=\"tcpdump示例\"></a>tcpdump示例</h2><p>==**tcpdump只能抓取流经本机的数据包 **==</p>\n<ol>\n<li>默认启动</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump\n</code></pre>\n<p>默认情况下,直接启动tcpdump将监视第一个网络接口(非lo口)上所有流通的数据包。这样抓取的结果会非常多,滚动非常快。</p>\n<p>2 . 监视指定网络接口的数据包</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -i ens33\n</code></pre>\n<ol start=\"3\">\n<li>监视指定主机的数据包,例如所有进入或离开node1的数据包</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -i ens33 host node1\n</code></pre>\n<ol start=\"4\">\n<li>打印node1<–>node2或node1<–>node3之间通信的数据包</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -i ens33 host node1 and \\(node2 or node3\\)\n</code></pre>\n<ol start=\"5\">\n<li>打印node1与任何其他主机之间通信的IP数据包,但不包括与node4之间的数据包</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -i ens33 host node1 and not node4\n</code></pre>\n<ol start=\"6\">\n<li>截获主机node1 发送的所有数据</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -i ens33 src host node1\n</code></pre>\n<ol start=\"7\">\n<li>监视所有发送到主机node1 的数据包</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -i ens33 dst host node1\n</code></pre>\n<ol start=\"8\">\n<li>监视指定主机和端口的数据包</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -i ens33 port 8080 and host node1\n</code></pre>\n<ol start=\"9\">\n<li>监视指定网络的数据包,如本机与192.168网段通信的数据包,”-c 10”表示只抓取10个包</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -i ens33 -c 10 net 192.168\n</code></pre>\n<ol start=\"10\">\n<li>打印所有通过网关snup的ftp数据包</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump 'gateway snup and (port ftp or ftp-data)'\n</code></pre>\n<p>注意,表达式被单引号括起来了,这可以防止shell对其中的括号进行错误解析</p>\n<ol start=\"11\">\n<li>抓取ping包</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -c 5 -nn -i ens33 \n</code></pre>\n<p>==指定主机抓ping包==</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -c 5 -nn -i eth0 icmp and src 192.168.100.62\n</code></pre>\n<ol start=\"12\">\n<li>抓取到本机22端口包</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -c 10 -nn -i ens33 tcp dst port 22\n</code></pre>\n<ol start=\"13\">\n<li>解析包数据</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">tcpdump -c 2 -q -XX -vvv -nn -i ens33 tcp dst port 22\n</code></pre>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"linux",
"tcpdump"
]
},
{
"id": "https://costalong.com/2024/07/01/linux/date/",
"url": "https://costalong.com/2024/07/01/linux/date/",
"title": "linux date 命令",
"date_published": "2024-07-01T07:14:42.000Z",
"content_html": "<h2 id=\"date-命令参数说明\"><a href=\"#date-命令参数说明\" class=\"headerlink\" title=\"date 命令参数说明\"></a>date 命令参数说明</h2><!-- 转换表格 -->\n\n<table>\n<thead>\n<tr>\n<th>字符</th>\n<th>描述</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>%%</td>\n<td>一个文字的 %</td>\n</tr>\n<tr>\n<td>%a</td>\n<td>当前locale 的星期名缩写(例如: 日,代表星期日)</td>\n</tr>\n<tr>\n<td>%A</td>\n<td>当前locale 的星期名全称 (如:星期日)</td>\n</tr>\n<tr>\n<td>%b</td>\n<td>当前locale 的月名缩写 (如:一,代表一月)</td>\n</tr>\n<tr>\n<td>%B</td>\n<td>当前locale 的月名全称 (如:一月)</td>\n</tr>\n<tr>\n<td>%c</td>\n<td>当前locale 的日期和时间 (如:2005年3月3日 星期四 23:05:25)</td>\n</tr>\n<tr>\n<td>%C</td>\n<td>世纪;比如 %Y,通常为省略当前年份的后两位数字(例如:20)</td>\n</tr>\n<tr>\n<td>%Y</td>\n<td>年</td>\n</tr>\n<tr>\n<td>%y</td>\n<td>年份后两位 (00..99)</td>\n</tr>\n<tr>\n<td>%m</td>\n<td>月</td>\n</tr>\n<tr>\n<td>%d</td>\n<td>日</td>\n</tr>\n<tr>\n<td>%D</td>\n<td>按月计的日期;等于%m/%d/%y</td>\n</tr>\n<tr>\n<td>%e</td>\n<td>按月计的日期,添加空格,等于%_d</td>\n</tr>\n<tr>\n<td>%F</td>\n<td>完整日期格式,等价于 %Y-%m-%d</td>\n</tr>\n<tr>\n<td>%H</td>\n<td>小时 24 小时制 hour (00..23)</td>\n</tr>\n<tr>\n<td>%I</td>\n<td>小时 12 小时制 hour (01..12)</td>\n</tr>\n<tr>\n<td>%M</td>\n<td>分钟</td>\n</tr>\n<tr>\n<td>%S</td>\n<td>秒钟</td>\n</tr>\n<tr>\n<td>%s</td>\n<td>当前时间秒数</td>\n</tr>\n<tr>\n<td>%T</td>\n<td>时钟 等于 %H:%M:%S</td>\n</tr>\n<tr>\n<td>%c</td>\n<td>本地时间和日期</td>\n</tr>\n<tr>\n<td>%j</td>\n<td>一年中的第几天</td>\n</tr>\n<tr>\n<td>%W</td>\n<td>一年中的第几周 星期一为一周的第一天</td>\n</tr>\n<tr>\n<td>%w</td>\n<td>星期几 week(0..6) ; 0 是星期天</td>\n</tr>\n<tr>\n<td>%u</td>\n<td>星期几 week(1..7) ; 1 是星期一</td>\n</tr>\n<tr>\n<td>%U</td>\n<td>一年中的第几周 星期日为一周的第一天</td>\n</tr>\n<tr>\n<td>%V</td>\n<td>ISO 周数 星期一为一周的第一天, ISO 周编号</td>\n</tr>\n<tr>\n<td>%x</td>\n<td>日期 (e.g., 12/31/99)</td>\n</tr>\n<tr>\n<td>%X</td>\n<td>时间 (e.g., 23:13:48)</td>\n</tr>\n<tr>\n<td>%z</td>\n<td>时区 数字格式 (e.g., +0800)</td>\n</tr>\n<tr>\n<td>%:z</td>\n<td>时区 +08:00</td>\n</tr>\n<tr>\n<td>%::z</td>\n<td>时区 +08:00:00</td>\n</tr>\n<tr>\n<td>%:::z</td>\n<td>时区 +08</td>\n</tr>\n<tr>\n<td>%Z</td>\n<td>时区缩写 CST</td>\n</tr>\n<tr>\n<td>%n</td>\n<td>换行</td>\n</tr>\n<tr>\n<td>%N</td>\n<td>纳秒(000000000-999999999)</td>\n</tr>\n</tbody></table>\n<h2 id=\"时间戳与时间互转\"><a href=\"#时间戳与时间互转\" class=\"headerlink\" title=\"时间戳与时间互转\"></a>时间戳与时间互转</h2><ol>\n<li>时间戳转换成时间格式</li>\n</ol>\n<pre class=\" language-Bash\"><code class=\"language-Bash\">date -d @1718866413 \"+%Y-%m-%d %H:%M:%S\"\n=> 2024-06-20 14:53:33\n</code></pre>\n<ol>\n<li>获取当前时间戳</li>\n</ol>\n<pre class=\" language-Bash\"><code class=\"language-Bash\"> date +%s\n => 1718869999\n</code></pre>\n<ol>\n<li>获取当天的时钟</li>\n</ol>\n<pre class=\" language-Bash\"><code class=\"language-Bash\">date +%T \n=> 15:54:22\n</code></pre>\n<ol>\n<li>获取某个时间节点对应的时间戳</li>\n</ol>\n<pre class=\" language-Bash\"><code class=\"language-Bash\">date -d \"2021-12-20\" +%s\n=> 1639929600\n\ndate -d \"2021-12-20 20:20:10\" +%s\n=> 1640002810\n</code></pre>\n<ol>\n<li>获取当前时间或指定时间是全年的第几天</li>\n</ol>\n<pre class=\" language-Bash\"><code class=\"language-Bash\"># 当前时间\ndate +%j\n=> 172 \n# 指定时间\ndate -d \"2021-12-20 20:20:10\" +%j\n=> 354\n</code></pre>\n<ol start=\"2\">\n<li>当前时间是第几周</li>\n</ol>\n<pre class=\" language-Bash\"><code class=\"language-Bash\">date +%W\n=> 25\n</code></pre>\n<ol>\n<li>查看当前时间日前与时间</li>\n</ol>\n<pre class=\" language-Bash\"><code class=\"language-Bash\"># \ndate \"+%x %X\"\n=> 06/20/2024 04:09:04 PM\n</code></pre>\n<ol>\n<li>查看当前时区</li>\n</ol>\n<pre class=\" language-Bash\"><code class=\"language-Bash\">date +%z\n=> +0800\n</code></pre>\n<ol>\n<li>设置时区获取时间</li>\n</ol>\n<pre class=\" language-Bash\"><code class=\"language-Bash\"># 当前时间戳转换\nTZ='America/Los_Angeles' date \"+%Y-%m-%d %H:%M:%S\"\n=> 2024-06-20 01:22:26\n\nTZ=\"Asia/Shanghai\" date \"+%Y-%m-%d %H:%M:%S\"\n=> 2024-06-20 16:26:38\n\n# 指定时间戳转换\nTZ='America/Los_Angeles' date -d @1718866413 \"+%Y-%m-%d %H:%M:%S\"\n=> 2024-06-19 23:53:33\n\nTZ=\"Asia/Shanghai\" date -d @1718866413 \"+%Y-%m-%d %H:%M:%S\"\n=> 2024-06-20 14:53:33\n</code></pre>\n<p>参考时区表 <a href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\">https://en.wikipedia.org/wiki/List_of_tz_database_time_zones</a></p>\n<p>通过 <code>timedatectl list-timezones</code> 列出可用的时区</p>\n<pre class=\" language-Bash\"><code class=\"language-Bash\">timedatectl list-timezones\n\n=>Africa/Abidjan\nAfrica/Accra\nAfrica/Addis_Ababa\nAfrica/Algiers\nAfrica/Asmara\nAfrica/Asmera\nAfrica/Bamako\nAfrica/Bangui\nAfrica/Banjul\nAfrica/Bissau\nAfrica/Blantyre\n...\n</code></pre>\n<h2 id=\"设置系统日期和时间\"><a href=\"#设置系统日期和时间\" class=\"headerlink\" title=\"设置系统日期和时间\"></a>设置系统日期和时间</h2><pre class=\" language-Bash\"><code class=\"language-Bash\"> date –set=\"20140125 09:17:00\"\n</code></pre>\n<p>通过 <code>man date</code> 查看 date 更多参数</p>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"linux",
"date"
]
},
{
"id": "https://costalong.com/2024/06/18/makefile/print-variable/",
"url": "https://costalong.com/2024/06/18/makefile/print-variable/",
"title": "Makefile 打印变量",
"date_published": "2024-06-18T10:08:52.000Z",
"content_html": "<p>在编写Makefile时,调试信息的输出对于查找和解决问题至关重要。Makefile提供了几种不同的方式来输出调试信息,包括<code>info</code>、<code>warning</code>、<code>error</code>以及<code>echo</code>命令。下面我们将详细介绍这些工具的使用方法以及它们之间的区别。</p>\n<p>makefile 打印变量方式有两种情况</p>\n<ol>\n<li>使用info/warning/error增加调试信息<ol>\n<li>打印字符串</li>\n</ol>\n</li>\n</ol>\n<pre class=\" language-Makefile\"><code class=\"language-Makefile\">$(info xxx-msg) # 输出字符串xxxx-msg,不需要加\"\",info后加空格\n=> xxxx-msg \n</code></pre>\n<pre><code>2. 打印变量\n</code></pre>\n<pre class=\" language-Makefile\"><code class=\"language-Makefile\">$(info $(GOPATH)) #打印变量GOPATH,变量名用$())\n\n=> /home/user/code/go\n\n</code></pre>\n<pre><code>3. 字符串、变量混合打印\n</code></pre>\n<pre class=\" language-Makefile\"><code class=\"language-Makefile\">$(info GOPATH: $(GOPATH))\n=> GOPATH: /home/hellotalk/code/go\n</code></pre>\n<pre><code>4. info/warning/error之间区别 \n\n info只输出信息:\n</code></pre>\n<pre class=\" language-Makefile\"><code class=\"language-Makefile\">$(info GOPATH is: $(GOPATH)) \n=> GOPATH is: /home/hellotalk/code/go\n</code></pre>\n<pre><code> warning输出信息和对应的行号:\n</code></pre>\n<pre class=\" language-Makefile\"><code class=\"language-Makefile\"> $(warning ***** $(shell date))\n => scripts/make-rules/common.mk:99: ***** Tue Jun 18 05:27:20 PM CST 2024\n</code></pre>\n<pre><code> error输出信息和对应的行号,并停止makefile的编译:\n</code></pre>\n<pre class=\" language-Makefile\"><code class=\"language-Makefile\">$(error GOPATH: $(GOPATH))\n=> scripts/make-rules/golang.mk:12: *** GOPATH: /home/hellotalk/code/go。 停止。\n</code></pre>\n<ol start=\"2\">\n<li>使用echo增加调试信息(echo只能在target:后面的语句中使用,且前面是个TAB)</li>\n</ol>\n<pre class=\" language-Makefile\"><code class=\"language-Makefile\"># 假设我们有一个变量 \nMY_VARIABLE := Hello, Makefile! \n \n# 定义一个目标(target) \nall: \n # 注意这里有一个制表符(Tab),而不是空格 \n @echo \"开始构建...\" \n @echo \"MY_VARIABLE 的值是: $(MY_VARIABLE)\" \n # 假设这里有一些其他的构建命令... \n @echo \"构建完成!\" \n \n# 如果你还有其他的目标(target),也可以在这里定义 \nclean: \n # 同样是制表符(Tab)开头 \n @echo \"开始清理...\" \n # 这里应该有清理构建产物的命令,比如 rm -f ... \n @echo \"清理完成!\"\n</code></pre>\n<pre><code>执行测试命令\n</code></pre>\n<pre class=\" language-Bash\"><code class=\"language-Bash\">make -n all\n</code></pre>\n<pre><code>这将会显示`all`目标下的所有命令,但由于`@`前缀的存在,它们不会显示`echo`命令的内容。如果你想要看到`echo`的内容,你需要移除`@`前缀或者不使用`-n`选项。\n</code></pre>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"makefile",
"makefile"
]
},
{
"id": "https://costalong.com/2023/10/14/go/mutex/",
"url": "https://costalong.com/2023/10/14/go/mutex/",
"title": "golang 协程互斥锁",
"date_published": "2023-10-14T13:43:14.000Z",
"content_html": "<p>golang 在多协程的情况下,如果多个协程同时操作一个变量,会出现数据不一致的情况,这个时候就需要使用互斥锁来解决这个问题。</p>\n<h2 id=\"互斥锁-sync-Mutex\"><a href=\"#互斥锁-sync-Mutex\" class=\"headerlink\" title=\"互斥锁 (sync.Mutex)\"></a>互斥锁 (sync.Mutex)</h2><p>互斥即不可同时运行。即使用了互斥锁的两个代码片段互相排斥,只有其中一个代码片段执行完成后,另一个才能执行。</p>\n<p>Go 标准库中提供了 sync.Mutex 互斥锁类型及其两个方法:</p>\n<blockquote>\n<p>Lock 加锁<br>Unlock 释放锁</p>\n</blockquote>\n<h2 id=\"代码案例\"><a href=\"#代码案例\" class=\"headerlink\" title=\"代码案例\"></a>代码案例</h2><p>下面是没有使用锁一个例子情况:</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">var</span> setMap <span class=\"token operator\">=</span> <span class=\"token function\">make</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">map</span><span class=\"token punctuation\">[</span><span class=\"token builtin\">int</span><span class=\"token punctuation\">]</span><span class=\"token builtin\">bool</span><span class=\"token punctuation\">,</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">func</span> <span class=\"token function\">printOnce</span><span class=\"token punctuation\">(</span>num <span class=\"token builtin\">int</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">if</span> <span class=\"token boolean\">_</span><span class=\"token punctuation\">,</span> exist <span class=\"token operator\">:=</span> setMap<span class=\"token punctuation\">[</span>num<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span> <span class=\"token operator\">!</span>exist <span class=\"token punctuation\">{</span>\n fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Println</span><span class=\"token punctuation\">(</span>num<span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n setMap<span class=\"token punctuation\">[</span>num<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">func</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">for</span> i <span class=\"token operator\">:=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\"><</span> <span class=\"token number\">10</span><span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">go</span> <span class=\"token function\">printOnce</span><span class=\"token punctuation\">(</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n time<span class=\"token punctuation\">.</span><span class=\"token function\">Sleep</span><span class=\"token punctuation\">(</span>time<span class=\"token punctuation\">.</span>Second<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>运行 go run main.go 会发生什么情况呢?</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">go run main.go\n100\n100\n</code></pre>\n<p>多运行几次打印不同的结果, 有时候打印7次,有时候打印10次,有时候还触发 panic,是因为对同一个数据结构的访问冲突了。解决的方法使用 <code>Lock()</code> 与 <code>UnLock()</code> 的方法解决问题</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">var</span> m sync<span class=\"token punctuation\">.</span>Mutex\n<span class=\"token keyword\">var</span> setMap <span class=\"token operator\">=</span> <span class=\"token function\">make</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">map</span><span class=\"token punctuation\">[</span><span class=\"token builtin\">int</span><span class=\"token punctuation\">]</span><span class=\"token builtin\">bool</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">func</span> <span class=\"token function\">printOnce</span><span class=\"token punctuation\">(</span>num <span class=\"token builtin\">int</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n m<span class=\"token punctuation\">.</span><span class=\"token function\">Lock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">if</span> <span class=\"token boolean\">_</span><span class=\"token punctuation\">,</span> exist <span class=\"token operator\">:=</span> setMap<span class=\"token punctuation\">[</span>num<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span> <span class=\"token operator\">!</span>exist <span class=\"token punctuation\">{</span>\n fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Println</span><span class=\"token punctuation\">(</span>num<span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n setMap<span class=\"token punctuation\">[</span>num<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n m<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">func</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">for</span> i <span class=\"token operator\">:=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\"><</span> <span class=\"token number\">10</span><span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span> <span class=\"token punctuation\">{</span>\n <span class=\"token keyword\">go</span> <span class=\"token function\">printOnce</span><span class=\"token punctuation\">(</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span>\n <span class=\"token punctuation\">}</span>\n time<span class=\"token punctuation\">.</span><span class=\"token function\">Sleep</span><span class=\"token punctuation\">(</span>time<span class=\"token punctuation\">.</span>Second<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>运行 go run main.go</p>\n<pre class=\" language-sh\"><code class=\"language-sh\">go run main.go\n100\n</code></pre>\n<p><strong>注意</strong>:</p>\n<blockquote>\n<p>一个互斥锁只能同时被一个 goroutine 锁定,其它 goroutine 就算调用 Lock() 方法也会等待锁的释放。</p>\n</blockquote>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">func</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n ch <span class=\"token operator\">:=</span> <span class=\"token function\">make</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">chan</span> <span class=\"token keyword\">struct</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">var</span> m sync<span class=\"token punctuation\">.</span>Mutex\n <span class=\"token keyword\">go</span> <span class=\"token keyword\">func</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n m<span class=\"token punctuation\">.</span><span class=\"token function\">Lock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">defer</span> m<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Println</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"goroutine1: 大概锁定 2s\"</span><span class=\"token punctuation\">)</span>\n time<span class=\"token punctuation\">.</span><span class=\"token function\">Sleep</span><span class=\"token punctuation\">(</span>time<span class=\"token punctuation\">.</span>Second <span class=\"token operator\">*</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Println</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"goroutine1: 锁定结束,准备退出\"</span><span class=\"token punctuation\">)</span>\n ch <span class=\"token operator\"><-</span> <span class=\"token keyword\">struct</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n <span class=\"token keyword\">go</span> <span class=\"token keyword\">func</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Println</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"goroutine2: 等待解锁\"</span><span class=\"token punctuation\">)</span>\n m<span class=\"token punctuation\">.</span><span class=\"token function\">Lock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token keyword\">defer</span> m<span class=\"token punctuation\">.</span><span class=\"token function\">Unlock</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Println</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"goroutine2: 锁定结束\"</span><span class=\"token punctuation\">)</span>\n ch <span class=\"token operator\"><-</span> <span class=\"token keyword\">struct</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n <span class=\"token punctuation\">}</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n <span class=\"token comment\" spellcheck=\"true\">// 等待 goroutine 执行结束</span>\n <span class=\"token keyword\">for</span> i <span class=\"token operator\">:=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\"><</span> <span class=\"token number\">2</span><span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span> <span class=\"token punctuation\">{</span>\n <span class=\"token operator\"><-</span>ch\n <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>运行 go run main.go</p>\n<pre class=\" language-sh\"><code class=\"language-sh\"> go run main.go\ngoroutine1: 大概锁定 2s\ngoroutine2: 等待解锁\ngoroutine1: 锁定结束,准备退出\ngoroutine2: 锁定结束\n</code></pre>\n<h2 id=\"sync-Map-Go-1-9\"><a href=\"#sync-Map-Go-1-9\" class=\"headerlink\" title=\"sync.Map (Go 1.9+)\"></a>sync.Map (Go 1.9+)</h2><p>Go 1.9 标准库提供了内置并发安全的 map,可以安全地被多个 goroutine 并发使用,无需额外的加锁或协调。</p>\n<pre class=\" language-go\"><code class=\"language-go\"><span class=\"token keyword\">import</span> <span class=\"token string\">\"sync\"</span>\n\n<span class=\"token keyword\">var</span> sm sync<span class=\"token punctuation\">.</span>Map <span class=\"token comment\" spellcheck=\"true\">// 直接声明即可使用</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// 存储</span>\nsm<span class=\"token punctuation\">.</span><span class=\"token function\">Store</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"key1\"</span><span class=\"token punctuation\">,</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// 读取</span>\n<span class=\"token keyword\">if</span> value <span class=\"token punctuation\">,</span>ok <span class=\"token operator\">:=</span> sm<span class=\"token punctuation\">.</span><span class=\"token function\">Load</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"key1\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>ok <span class=\"token punctuation\">{</span>\n fmt<span class=\"token punctuation\">.</span><span class=\"token function\">PrintLn</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Loaded value\"</span><span class=\"token punctuation\">,</span>value<span class=\"token punctuation\">.</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">int</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token comment\" spellcheck=\"true\">// 注意类型断言</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// 读取或存储</span>\n\nactual<span class=\"token punctuation\">,</span> loaded <span class=\"token operator\">:=</span> sm<span class=\"token punctuation\">.</span><span class=\"token function\">LoadOrStore</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"key2\"</span><span class=\"token punctuation\">,</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span>\nfmt<span class=\"token punctuation\">.</span><span class=\"token function\">Println</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"LoadOrStore\"</span><span class=\"token punctuation\">,</span>actual<span class=\"token punctuation\">.</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">int</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span><span class=\"token string\">\"loaded?\"</span><span class=\"token punctuation\">,</span>loaded<span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// 删除</span>\nsm<span class=\"token punctuation\">.</span><span class=\"token function\">Delete</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"key1\"</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\" spellcheck=\"true\">// 遍历(注意 Range 的函数签名)</span>\nsm<span class=\"token punctuation\">.</span><span class=\"token function\">Range</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">func</span><span class=\"token punctuation\">(</span>key <span class=\"token punctuation\">,</span> value any<span class=\"token punctuation\">)</span> <span class=\"token builtin\">bool</span><span class=\"token punctuation\">{</span>\n fmt<span class=\"token punctuation\">.</span><span class=\"token function\">Println</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Range:\"</span><span class=\"token punctuation\">,</span>key<span class=\"token punctuation\">.</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>value<span class=\"token punctuation\">.</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">int</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span> <span class=\"token comment\" spellcheck=\"true\">// 返回 true 继续遍历 , false 停止</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"golang",
"golang"
]
},
{
"id": "https://costalong.com/2023/09/14/k8s/pod-elegant-stop/",
"url": "https://costalong.com/2023/09/14/k8s/pod-elegant-stop/",
"title": "pods 优雅终止",
"date_published": "2023-09-14T14:08:44.000Z",
"content_html": "<h2 id=\"概述\"><a href=\"#概述\" class=\"headerlink\" title=\"概述\"></a>概述</h2><p>Pod 销毁时,会停止容器内的进程,通常在停止的过程中我们需要执行一些善后逻辑,比如等待存量请求处理完以避免连接中断,或通知相关依赖进行清理等,从而实现优雅终止目的。本文介绍在 Kubernetes 场景下,实现容器优雅终止的最佳实践。</p>\n<h2 id=\"当-Kubernetes-杀死一个-pod-时,会发生以下-5-个步骤:\"><a href=\"#当-Kubernetes-杀死一个-pod-时,会发生以下-5-个步骤:\" class=\"headerlink\" title=\"当 Kubernetes 杀死一个 pod 时,会发生以下 5 个步骤:\"></a>当 Kubernetes 杀死一个 pod 时,会发生以下 5 个步骤:</h2><ol>\n<li>Pod 切换到终止状态并停止接收任何新流量,容器仍在 pod 内运行。</li>\n<li>preStop 钩子是一个特殊的命令或 HTTP 请求被执行,并被发送到 pod 内的容器。</li>\n<li>SIGTERM 信号被发送到 pod,容器意识到它将很快关闭。</li>\n<li>Kubernetes 等待宽限期 (terminationGracePeriodSeconds)。此等待与 preStop hook 和 SIGTERM 信号执行并行(默认 30 秒)。因此,Kubernetes 不会等待这些完成。如果这段时间结束,则直接进入下一步。正确设置宽限期的值非常重要。</li>\n<li>向 pod 发送 SIGKILL 信号,然后移除 pod。如果容器在宽限期后仍在运行,则 Pod 被 SIGKILL 强行移除,终止完成。</li>\n</ol>\n<p>总结下大致分为两步,第一步定义 preStop,一般情况下可以休眠 30s,用于处理残余流量;第二步发送 SIGTERM 信号,服务收到信号后进行服务的收尾工作处理。比如:关闭连接、通知第三方注册中心服务关闭…..</p>\n<h2 id=\"Pods-生命周期的状态\"><a href=\"#Pods-生命周期的状态\" class=\"headerlink\" title=\"Pods 生命周期的状态\"></a>Pods 生命周期的状态</h2><p>phase表示一个Pod处于其生命周期的哪个阶段,一共有以下5个可能的取值:</p>\n<ol>\n<li>Pending:Pod已经被k8s系统接受,但Pod中还有容器没有被创建。Pod被调度前和下载容器镜像的时候都处于这个阶段</li>\n<li>Running:Pod已经被调度到Node上,所有的容器都已经被创建,并且至少有一个容器还在运行中(正在启动或重启中的容器也算)</li>\n<li>Succeeded:Pod中的所有容器都成功停止,并且不会再次重启</li>\n<li>Failed:Pod中的所有容器都已经停止,并且至少有一个容器是以失败停止的(以非0状态退出或被系统强制停止)</li>\n<li>Unknown:由于某种原因无法获得Pod的状态,一般是和Pod所在的Host出现通信问题导致</li>\n</ol>\n<p>Pod phase的查看方式:</p>\n<pre class=\" language-shell\"><code class=\"language-shell\">kubectl get pods whoami-78c854646d-nhgl9 -o yaml |grep 'phase:'\n</code></pre>\n<p>输出:</p>\n<pre class=\" language-shell\"><code class=\"language-shell\"> phase: Running\n</code></pre>\n<h2 id=\"k8s-lifecycle-用法\"><a href=\"#k8s-lifecycle-用法\" class=\"headerlink\" title=\"k8s lifecycle 用法\"></a>k8s lifecycle 用法</h2><p>lifecycle 周期有两个hook钩子 postStart 与 preStop </p>\n<ol>\n<li>PostStart hook是在容器创建(created)之后立马被调用,并且PostStart跟容器的ENTRYPOINT是异步执行的,无法保证它们之间的顺序.</li>\n<li>PreStop hook是容器处于Terminated状态时立马被调用(也就是说要是Job任务的话,执行完之后其状态为completed,所以不会触发PreStop的钩子),同时PreStop是同步阻塞的,PreStop执行完才会执行删除Pod的操作</li>\n</ol>\n<p>注意:<br>PostStart 会阻塞容器成为Running状态,PreStop 会阻塞容器的删除,但是过了 terminationGracePeriodSeconds时间后,容器会被强制删除,<br>如果PreStop或者PostStart失败的话, 容器会被杀死;</p>\n<h3 id=\"钩子的回调函数支持三种方式定义动作:\"><a href=\"#钩子的回调函数支持三种方式定义动作:\" class=\"headerlink\" title=\"钩子的回调函数支持三种方式定义动作:\"></a>钩子的回调函数支持三种方式定义动作:</h3><ol>\n<li>exec:在容器内执行命令,如果命令的退出状态码是 0 表示执行成功,否则表示失败</li>\n</ol>\n<pre class=\" language-yaml\"><code class=\"language-yaml\"> lifecycle:\n <span class=\"token key atrule\">postStart</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">exec</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">command</span><span class=\"token punctuation\">:</span>\n <span class=\"token punctuation\">-</span> cat\n <span class=\"token punctuation\">-</span> /tmp/healthy\n</code></pre>\n<ol start=\"2\">\n<li>httpGet:向指定 URL 发起 GET 请求,如果返回的 HTTP 状态码在 [200, 400) 之间表示请求成功,否则表示失败</li>\n</ol>\n<pre class=\" language-yaml\"><code class=\"language-yaml\">lifecycle:\n <span class=\"token key atrule\">postStart</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">httpGet</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">path</span><span class=\"token punctuation\">:</span> /login <span class=\"token comment\" spellcheck=\"true\"># URI地址</span>\n <span class=\"token key atrule\">port</span><span class=\"token punctuation\">:</span> <span class=\"token number\">80 </span><span class=\"token comment\" spellcheck=\"true\"># 端口号</span>\n <span class=\"token key atrule\">host</span><span class=\"token punctuation\">:</span> 192.168.126.100 <span class=\"token comment\" spellcheck=\"true\"># 主机地址</span>\n <span class=\"token key atrule\">scheme</span><span class=\"token punctuation\">:</span> HTTP <span class=\"token comment\" spellcheck=\"true\"># 支持的协议,http或https</span>\n\n<span class=\"token comment\" spellcheck=\"true\"># http://192.168.126.100:80/login</span>\n</code></pre>\n<ol start=\"3\">\n<li>TCPSocket:在容器尝试访问指定的socket</li>\n</ol>\n<pre class=\" language-yaml\"><code class=\"language-yaml\"> lifecycle:\n <span class=\"token key atrule\">postStart</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">tcpSocket</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">port</span><span class=\"token punctuation\">:</span> <span class=\"token number\">8080</span>\n</code></pre>\n<p>Example:</p>\n<pre class=\" language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">apiVersion</span><span class=\"token punctuation\">:</span> apps/v1\n<span class=\"token key atrule\">kind</span><span class=\"token punctuation\">:</span> Deployment\n<span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> nginx\n <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> nginx\n<span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">replicas</span><span class=\"token punctuation\">:</span> <span class=\"token number\">1</span>\n <span class=\"token key atrule\">selector</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">matchLabels</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> nginx\n <span class=\"token key atrule\">strategy</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n <span class=\"token key atrule\">template</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">metadata</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">creationTimestamp</span><span class=\"token punctuation\">:</span> <span class=\"token null important\">null</span>\n <span class=\"token key atrule\">labels</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> nginx\n <span class=\"token key atrule\">spec</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">containers</span><span class=\"token punctuation\">:</span>\n <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">image</span><span class=\"token punctuation\">:</span> nginx\n <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> nginx\n <span class=\"token key atrule\">resources</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n <span class=\"token key atrule\">lifecycle</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">postStart</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">exec</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">command</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"/bin/sh\"</span><span class=\"token punctuation\">,</span><span class=\"token string\">\"-c\"</span><span class=\"token punctuation\">,</span><span class=\"token string\">\"echo 11 >> /usr/share/nginx/html/index.html\"</span><span class=\"token punctuation\">]</span> <span class=\"token comment\" spellcheck=\"true\"># 启动容器应用之后执行</span>\n <span class=\"token key atrule\">preStop</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">exec</span><span class=\"token punctuation\">:</span>\n <span class=\"token key atrule\">command</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"/bin/sh\"</span><span class=\"token punctuation\">,</span><span class=\"token string\">\"-c\"</span><span class=\"token punctuation\">,</span><span class=\"token string\">\"echo 'Hello from the preStop handler' >> /var/log/nginx/message\"</span><span class=\"token punctuation\">]</span> <span class=\"token comment\" spellcheck=\"true\">## 删除pod 完成之前执行</span>\n<span class=\"token key atrule\">status</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"k8s",
"k8s",
"pods"
]
},
{
"id": "https://costalong.com/2023/09/13/linux/ubuntu-install-nfs-server/",
"url": "https://costalong.com/2023/09/13/linux/ubuntu-install-nfs-server/",
"title": "ubuntu 安装 nfs",
"date_published": "2023-09-13T10:49:02.000Z",
"content_html": "<ol>\n<li>安装nfs服务</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">sudo apt install nfs-kernel-server\n</code></pre>\n<ol start=\"2\">\n<li>编辑配置文件</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">sudo vim /etc/exports\n\n# Example for NFSv4:\n# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)\n# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)\n#\n/data/nfs-share *(rw,sync,no_subtree_check,no_root_squash) \n</code></pre>\n<ol start=\"3\">\n<li>创建共享目录</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">sudo mkdir -p /data/nfs-share\n</code></pre>\n<ol start=\"4\">\n<li>重启nfs服务</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">sudo service nfs-kernel-server restart\n</code></pre>\n<ol start=\"5\">\n<li><p>常用命令工具</p>\n<p>已经安装的nfs无需安装客户端</p>\n</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\"># 显示已经mount到本机上\nsudo showmount -e localhost\n# 将配置文件中的目录全部重新 Export一次,无需重启\nsudo exportfs -rv\n# 查看nfs的运行状态\nsudo nfsstat\n#查看rpc执行信息,可以用于检测rpc运行情况\nsudo rpcinfo\n\n#查看网络端口,NFS默认是使用111端口。\nsudo netstat -tu -4\n</code></pre>\n<ol start=\"6\">\n<li>客户端的命令</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\"># 安装客户端命令\nsudo apt install nfs-common\n</code></pre>\n<ol start=\"7\">\n<li>显示指定的(192.168.2.167)NFS服务器上export出来的目录</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">sudo showmount -e 192.168.2.167\n</code></pre>\n<ol start=\"8\">\n<li>创建本地挂载目录</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">sudo mkdir -p /mnt/data\n</code></pre>\n<ol start=\"9\">\n<li>挂载共享目录</li>\n</ol>\n<pre class=\" language-sh\"><code class=\"language-sh\">sudo mount -t nfs 192.168.3.167:/data/nfs-share /mnt/data\n</code></pre>\n<link rel=\"stylesheet\" href=\"/css/spoiler.css\" type=\"text/css\"><script src=\"/js/spoiler.js\" type=\"text/javascript\" async></script>",
"tags": [
"linux",
"nfs",
"storage"
]
}
]
}