title: no_warn
site_title: 涛叔
articles:
- { "date": "2024-10-06", "path": "/blog-as-knowledge-graph.html", "title": "将博客打造成个人知识图谱", "desc": "细心的读者可能会发现，在我的博客主页右上方和文章标题右下方多出来一个🕸️图标。点击 就会打开我的个人知识图谱(Knowledge Graph)。这是我在国庆期间鼓捣出来的。本文就跟 大家分享知识图谱功能的来龙去脉。", "tags": [], "refs": ["/unix/blog-internal.html", "/git/socks-proxy.html", "/why-blog.html", "/unix/markdown-link.html", "/unix/markdown-link.html"] }
- { "date": "2024-10-05", "path": "/dmit-2024-10-05.html", "title": "DMIT VPS 补货", "desc": "DMIT 终于补货了，有需要的朋友快点下手。", "tags": [], "refs": ["/dmit-25.html"] }
- { "date": "2024-09-21", "path": "/vim/mini-neovim-config.html", "title": "My Mini Neovim Config", "desc": "Hello, Guys. Today, I want to introduce you my minimized config of Neovim. I have used Vim/Neovim for almost 10 years. In the beginning, I tended to add more and more configuration and plugins. However, after some years, I have realized that, the more experiences about Vim/Neovim I have, the less configuration or plugins I need. As a result, I try to drop all the unnecessary configuration and plugins as far as I can. So here is my mini Neovim config.", "tags": [], "refs": ["/vim/vim-search-plugin.html", "/vim/fzf.html", "/vim/mru.html"] }
- { "date": "2024-07-26", "path": "/rfc-common-netid.html", "title": "国家网络身份认证公共服务管理办法", "desc": "公安部和国家网信办于2024年7月26日，发布公告，向全社会就《国家网络身份认证公共 服务管理办法》征求意见。这是一个可以让中国变得更好的机会。虽说小民的意见 肉食者不见得采纳。但既然是征求意见，那还是想听听社会上的声音。说的越多效果也就 越好。更何况这类提案普通人关注的很少，所以我在博客上呼吁大家，所有人都用去提意见， 都去表达自己的想法，给肉食者提供参考，为中国的互联网建议贡献一份力量。", "tags": [], "refs": [] }
- { "date": "2024-07-21", "path": "/compile-chromium.html", "title": "编译 Chromium 留念", "desc": "之前写文章介绍 DNS SVCB/HTTPS 记录，说可以用它来解决家庭宽带无法使用 443 端口的问题。前天收到网友徐嘉鑫来信，讨论相关问题。其中最核心的问题是 Chrome 是否支持使用 HTTPS 记录自定义端口。试验下来发现确实不支持🥹Safari 和 Firefox 都没有问题。而且进一步调研发现居然是原作者有意不实现这个特性。于是便有了一股想改 源码的冲动。但改之前需要先完成编译，这小小的第一步也遇到不少问题。本文先记录编译 过程中的具体问题，希望能对各位同好有所参考。", "tags": [], "refs": ["/dns/dns-svcb-https.html", "/http3-port.html", "/https-tunnel.html"] }
- { "date": "2024-07-07", "path": "/net/utun.html", "title": "UTUN:极简 VPN 软件", "desc": "最近网络环境有所变化，公网 IP 地址没有了，而且也拿不到光猫管理员密码。要想访问 内网设备，要么走内网穿透，要么走 VPN 组网。VPN 组网最灵活，不需要关心协议问题。 好还我有几台服务器，可以用来组网。但组网的软件没用使用主流的 WireGuard 或者 ZeroTier， 而是用了自已开发的 UTUN。本文就跟大家分享使用 UTUN 的原委。", "tags": [], "refs": [] }
- { "date": "2024-07-06", "path": "/unix/resize-oracle-ext4.html", "title": "扩展 Linux 云主机磁盘容量", "desc": "我之前跟大家介绍过注册 Oracle 免费云主机的经历。虽说 Oracle 主机的网络很不稳定，但提供对外 10T 流量，算是很大方了。最为可贵的一点是 提供最多 200G 的免费磁盘空间，可以存不少数据。我申请了一台 ARM 24C 主机和一台 AMD 主机，各占 50G 磁盘空间。另外 100G 空间一直闲置。今天就试着把 ARM 主机的空间扩展 到 150G，后面看看能不能做个下载站。本文梳理整个操作过程，希望能帮到大家。", "tags": [], "refs": ["/free-oracle-cloud.html", "/unix/resize-qemu-ext4.html"] }
- { "date": "2024-06-15", "path": "/nginx/if-is-evil.html", "title": "Nginx if 避坑指南", "desc": "Nginx 配置以静态规则为主。现在我们可以使用 njs 或者 lua 模块实现各种动态逻辑。 在这些模块出现之前，只能通过 rewrite 模块的 if 指令实现一定的动态规则。这个 if 在设计之初仅用来做 URL 重写或重定向。但发布之后很多用户尝试使用 if 实现种动态 配置，这导致了很多意想不到的问题。所以有 if is evil 之说。今天就来分享如何避开 if 的各类问题。", "tags": [], "refs": ["/nginx/common-conf-mistakes.html"] }
- { "date": "2024-06-14", "path": "/net/one-ddos-analysis.html", "title": "记一次线上 DDoS 攻击案例", "desc": "跟进大半年的项目终于上线了🎉虽说上线当天没有出问题，最近却受到了一次 DDoS 攻击。 说起来我还没有线上 DDoS 的相关经验，这次攻击规模不大，有些方面符合预期，有些方面 出乎预料。今天把这次攻击事件分享出来，给大家做个参考。", "tags": [], "refs": [] }
- { "date": "2024-06-10", "path": "/go/bytes-counter.html", "title": "Go语言实现流量计费", "desc": "最近相关部门发出通知📢要关闭中国大陆的 Docker Hub 镜像加速服务。一时间哀嚎遍野。 Docker 支持 Secure HTTP Proxy 协议，而我也一直使用该协议。于是就想着能不能共享 自己的代理服务。其中最大的障碍就是流量计费。现在互联网肯定是不能用爱发电，免费 就一定会被滥用。所以需要实现一套能用的流量计费功能。在 ChatGTD 的帮助下， 花了半天的时间实现了计费功能，今天分享出来供大家参考。", "tags": [], "refs": ["/docker-hub-proxy.html", "/https-tunnel.html"] }
- { "date": "2024-06-09", "path": "/docker-hub-proxy.html", "title": "简易 Docker Hub 加速服务", "desc": "最近网上传言说相关部门已经通知，要关闭所有中国大陆的 Docker Hub 镜像站。而且像是 上海交大、中科大的镜像站均已停止服务。但 Docker Hub 现在几乎成了云原生时代的基础 设施，不用又不行。那要怎样才能顺畅地访问 Docker Hub 呢？", "tags": [], "refs": ["/go/bytes-counter.html", "/https-tunnel.html"] }
- { "date": "2024-06-08", "path": "/nginx/common-conf-mistakes.html", "title": "Nginx 常见配置错误", "desc": "本文整理了 Nginx 常见的配置错误，供大家参考。如果你果你是初学者，可以先看我的另 一篇基础文章。", "tags": [], "refs": ["/nginx/quick-start.html"] }
- { "date": "2024-06-01", "path": "/nginx/quick-start.html", "title": "Nginx 配置快速入门", "desc": "今天面向初学者分享一篇 Nginx 配置的入门材料。Nginx 安装之后附带的 nginx.conf 篇幅 较长，令人望而生畏。初学者限于自身经验不足，很难分清重点。今天我尝试抛开默认配置， 从最小的配置出发，带领大家学习从静态 Web 站点到动态 API 网关等各类功能配置。希望 能给大家带来一些启发。如果有问题或者想法，欢迎留言讨论。", "tags": [], "refs": [] }
- { "date": "2024-05-26", "path": "/life/snakes-ladders.html", "title": "寓教于乐的蛇棋", "desc": "家里有一套蛇棋，女儿👧很很喜欢，经常要跟我玩几局。我发现它不仅简单有趣，而且还能 在娱乐的过程中潜移默化地教一些道理，正所谓寓教于乐。这算是我迄今为止所发现的性价 比最高的玩具了。今天就跟大家分享自己的感悟。", "tags": [], "refs": [] }
- { "date": "2024-05-20", "path": "/net/vps-he-ipv6.html", "title": "配置 HE 免费 IPv6 地址", "desc": "现在 IPv6 已经算是比较普及了，无论是民用 ISP 还是各类云厂商或者 IDC，都支持配置 IPv6 网络。但有一些小型的 VPS 厂商或者一些大厂的特定产品，如阿里云的轻量服务器， 仍然不支持接入 IPv6 网络。这时候我们就需要所谓的 tunnel broker 来接入 IPv6 网络。 在众多 broker 中，HE提供的 tunnelbroker.net 最为有名，而且支持的区域也最为 广泛。我之前换过几次 VPS，每次配置都要查一遍。今天把过程记录下来，做个备忘，也 分享给大家。", "tags": [], "refs": [] }
- { "date": "2024-05-11", "path": "/git/socks-proxy.html", "title": "为 Git 配置网络代理", "desc": "虽然 Git 用得很多，却很少碰到需要为 Git 配置网络代理的场景。最近在跟的项目网络 环境比较特别，需要为 Git 配置代理才能访问远端仓库。整理成文，权当备忘。", "tags": [], "refs": ["/https-tunnel.html"] }
- { "date": "2024-04-16", "path": "/dns/diy-doh.html", "title": "手撸纯净版 DoH 服务", "desc": "传统 DNS 协议使用明文传输，中间节点可以监听用户的查询内容，甚至还能篡改查询结果。 为了解决这类问题，IETF 制定了 RFC8484，使用 HTTPS 加密链路传输 DNS 查询。 这就是所谓的 DNS over HTTPS (DoH)。当前网上有很多 DoH 服务，但能用的却不多。海外 的服务基本都被阻断，就算能正常访问，解析出来的往往也是海外 IP 地址，从国内访问很 不稳定；国内的 DoH 可以正常访问，解析结果往往也是最优的，但奈何国内有 DNS 污染， 好多域名解析出来的 IP 都是错的。本文就分享 DoH 的工作原理并使用 Go 语言开发一套 简易的 DoH 服务，解决前面提到的两个问题。", "tags": [], "refs": ["/dns/dns-privacy.html", "/dns/edns-client-subnet.html"] }
- { "date": "2024-01-13", "path": "/net/http-download-only-changed-using-wget-or-curl.html", "title": "Download File only when Changed using wget/curl", "desc": "I am working on one ChatGPT based Product, and it need to download the cl100k_base.tiktoken file via HTTP protocol to count the token consumed in the streaming API. Although the tiktoken file is not very big (1.7M), its content rarely changes. So there is no need to wate net traffic to download the entire every time. In this article, I am going to share how to download file only if its content changed by wget or curl.", "tags": [], "refs": [] }
- { "date": "2024-01-07", "path": "/net/sony-youtube.html", "title": "国行索尼电视安装 YouTube", "desc": "2024年元旦假期总共休了五天，完成了很多拖了很久的小心愿。其中之是让家里的索尼电视 国行版用上了 YouTube。孩子们从此再也不需要忍受电脑的小屏幕了。讲真在国内环境下， 要想能在电视上用 YouTube，还真得花一番功夫。今天把自己的方案梳理出来，分享给大家。 虽然我家是索尼电视，但本文的大部分内容都跟电视机型无关，请放心食用。", "tags": [], "refs": ["/free-oracle-cloud.html", "/go/simple-vpn.html", "/vpn-over-tcp.html"] }
- { "date": "2023-12-23", "path": "/net/outlook-delist.html", "title": "Outlook/Hotmail is no longer blocking my mail server", "desc": "After some struggle, I managed to de-list the address of my mail server from Microsoft’s blocklist. Here is some experiences may help you.", "tags": [], "refs": ["/net/selfhost-email.html"] }
- { "date": "2023-12-22", "path": "/sre/prom.html", "title": "Prometheus 简介", "desc": "我最近的工作重心转到 SRE 领域，当前的主要内容是帮助部门完善监控系统。说起监控， 大多数做 SRE 的朋友都会想到 Prometheus，中文译名是普罗米修斯。我很早就知道、平时 也大量使用。但因为之前主要做开发工作，一直没有仔细研究它：基础概念比较模糊，查询 语法一知半解，采集、存储等方面则完全不懂。这次就梳理一下最近学到的内容，希望能给 新入门的朋友提供一些参考。", "tags": [], "refs": [] }
- { "date": "2023-12-10", "path": "/go/bench.html", "title": "Go语言基准测试", "desc": "基准测试英文关键词 benchmark，主要用于测试 CPU 和内存使用效率。配合一定的工具， 还能直观地展示出代码优化的效果。因为跟性能相关，有时候也叫性能测试。Go语言内置 基准测试工具包，使用非常方便。建议所有Go开发者都能熟练使用。", "tags": [], "refs": [] }
- { "date": "2023-12-08", "path": "/nginx/proxy-https.html", "title": "使用 Nginx 反向代理 HTTPS 网站", "desc": "Nginx 最常用的功能是就是反向代理。不过之前多是用 Nginx 代理 HTTP FastCGI 请求， 或者是内网的 HTTP 请求，还从来没玩过用 Nginx 代理 HTTPS 请求。惭愧。最近申请到 Oracle 的免费机器并搭建了自己的 Email 服务。搭建过程用到 了 Nginx。今天顺手用 Nginx 给博客搞了一个反代，看看访问速度快不快。如果还可以， 后面考虑退掉付费的虚拟机，进一步降低博客成本。", "tags": [], "refs": ["/free-oracle-cloud.html", "/net/selfhost-email.html", "/net/http.html"] }
- { "date": "2023-11-26", "path": "/unix/test-bracket.html", "title": "Shell 编程中的 test, [ 和 [[", "desc": "写过 Shell 的朋友一定摆脱不了被方括号支配的恐惧，Shell 分支判断语法简直是奇葩。 时而用[，时而用 [[，甚至有时还会用 test，跟常见的编程语言差距太大。我自己 也没弄明白为什么要分单括号和双括号，直到我遇到这篇文章。今天就结合原文以及 自己的理解为大家梳理一下 Shell 中的方括号语法。", "tags": [], "refs": [] }
- { "date": "2023-11-25", "path": "/net/selfhost-email.html", "title": "How to Set Up Selfhosted Email Server", "desc": "I have used email service with my own domain for several years. Gandi.net were chosen as my domain registrar, not only because its low fees, but also its free mailbox service. However, since the middle of this year, Gandi.net has announced their price increment and would cancel all free mailbox service in the end of November. Although it is a controversial decision, I have no choice but to find an alternative mail service as soon as possible. As I also own some always free instance of Oracle Cloud Computer, why not set up my own selfhosted mail service? So do I, and in this blog, I will share how to set up one selfhosted mail server securely and freely, yet without any fee.", "tags": [], "refs": [] }
- { "date": "2023-10-29", "path": "/net/email-dns.html", "title": "电子邮件系统中的 DNS 记录", "desc": "最近要搭建个人电子邮件系统，正在系统学习相关的知识。今天分享与 DNS 相关的内容。", "tags": [], "refs": ["/email-ports.html"] }
- { "date": "2023-10-22", "path": "/beian.html", "title": "为什么要备案？", "desc": "最近政府布告天下，从事互联网信息服务的 App 需要备案才能上架。这个应用备案就是从 网站备案或者 ICP 备案发展而来的。今天就来聊聊备案这个话题。", "tags": [], "refs": ["/dns/dns-svcb-https.html"] }
- { "date": "2023-10-21", "path": "/oracle-25.html", "title": "Oracle 云主机开通 25 号 SMTP 端口", "desc": "我在七月份成功注册 Oracle 云账户并创建一台 x86 虚拟主机，永久免费。本 不想凑这个热闹，但我的服务商年底下架免费邮件服务，所以想用 Oracle 云主机 做邮件服务器。注册后才发现 Oracle 默认屏蔽 25 号端口，得工单才能开通。 但是永久免费账号不能提工单。快就年底了，邮件服务必须迁移。这两天就折腾了一把， 成功给 Oracle 云主机开通了 25 号端口。", "tags": [], "refs": ["/free-oracle-cloud.html", "/email-ports.html"] }
- { "date": "2023-10-20", "path": "/grpc-clen.html", "title": "记录 gRPC GOAWAY 报错排查过程", "desc": "公司内部新服务基本都使用 gRPC 协议通信。我们的业务使用 Sniper 框架， 并没有内置 gRPC 客户端。所以我基于 Go 语言的 net/http 标准库自己撸了一个 简单版本，仅支持 gRPC Unary 接口，但足够应付绝大多数场景了。 最近在调用某部门的接口时，对方服务直接报 GOAWAY 错误。 一番排查后发现居然跟Content-Length头有关，万万没想到。今天跟大家分享排查过程。", "tags": [], "refs": ["/go/sniper.html", "/go/grpc-unary-client.html", "/grpc.html"] }
- { "date": "2023-09-21", "path": "/net/http-103.html", "title": "HTTP 103 Early Hints", "desc": "我去年写文章介绍 HTTP 协议演变时提到了最新的 103 Early Hints 状态码。 它是 HTTP/2 Server Push 的简化版本。今天就来介绍 103 状态码的工作原理。", "tags": [], "refs": ["/net/http.html"] }
- { "date": "2023-08-20", "path": "/ai/chatgtd.html", "title": "ChatGTD.net - 极简 ChatGPT 代理", "desc": "ChatGPT 想必大家都有所耳闻，它是 OpenAI 公司研发的生成式聊天机器人，代表了当前 人工智能 AI 领域的最高水平。ChatGPT 可以帮助大家完成很多工作，比如写小说、写简 历、总结工作汇报、多语言之间相互翻译、批改作文，甚至还能解鸡免同笼这类的数学题。 可以这么说，只要你把要回复的内容描述清楚，基本上都能获得正确的答案。 然而，这么强大的工具却不能在中国大陆和香港地区使用，于是就出现了形形色色的代理。 我自己也做了一款叫ChatGTD.net，简单稳定，推荐给大家。", "tags": [], "refs": ["/ai/better-chatgpt-prompts.html", "/ai/depay-chatgpt-subs.html", "/ai/onekey-chatgpt.html"] }
- { "date": "2023-07-28", "path": "/unix/apt-key-deprecated.html", "title": "修复 apt-key deprecated 警告", "desc": "我的服务器使用 Ubuntu LTS 版本。之前是 20.04，升级到 22.04 之后，每次更新系统都 会报一堆警告信息。虽然不影响功能，但肯定是哪里出了问题，作为强迫症患者的我肯定要 一查究究。查一来发现是跟 apt-key 被弃用有关，整个 apt 的签名系统有了新的配置方案。 今天就跟大家分享一下处理过程。", "tags": [], "refs": ["/unix/blog-internal.html", "/git/ssh-sign.html"] }
- { "date": "2023-07-26", "path": "/unix/dpkg-i386.html", "title": "64 位 Linux 系统安装与清理 32 位环境", "desc": "我之前 200 块收了一台华硕 RT-AC1900P 无线路由器，刷了梅林系统，用作家庭网关。这 货用的是 2.6.36.4brcmarm 内核。因为是十多年前的内核，WireGuard 模块是不用想了， 但还可以编译像 IPIP/GRE 等隧道模块，具体编译过程我后面会撰文介绍。博通 (Broadcom) 只有 32 位工具链，而我的服务器是 64 位 Ubuntu，无法直接运行。今天就分享怎样在 64 位 Linux 系统上安装和清理 32 位运行环境。", "tags": [], "refs": ["/unix/vps-rebirth.html"] }
- { "date": "2023-07-24", "path": "/life/renew-driving-license.html", "title": "记录上海驾驶证期满换证过程", "desc": "最近收到交警的短信通知，说驾驶证临期，需要办理期满换证手续。上周六跑了一趟，全程 也就半个多小时，总共花费 110 元。现在把办理过程整理出来，希望能帮到大家。", "tags": [], "refs": [] }
- { "date": "2023-07-20", "path": "/go/mock.html", "title": "Go单元测试 Mock 方案总结", "desc": "我从 2018 年转向 Go 语言开发，一晃就是五年。当年因为不了解 Go 生态，缺乏趁手的 工具，所以选了一套很朴素但能用的方案做单元测试 Mock。后来伴随业务的发展，研发 团队拆分成不同的小组。最近有的小组在做新项目，单元测试居然还在用五年前的临时方案。 这确实让我意外。是时候总结一下过去的实践经验，做一拨分享了。", "tags": [], "refs": ["/go/monkey/monkey.html", "/go/monkey/monkey-2.html"] }
- { "date": "2023-07-08", "path": "/tex/circuit.html", "title": "如何优雅地绘制电路图", "desc": "最近整理学习笔记时需要画数字电路图。幸亏之前给博客系统开发了对应的功能， 可以在 Markdown 中使用 LaTeX 的宏包来绘图。简单学习了一把，发现 LaTeX 可以绘制 非常精美的电路图，这让我的文章增色不少。今天就给大家简要介绍一下绘图方法，希望 能帮大家快速入门。", "tags": [], "refs": ["/cs/boot.html", "/unix/markdown-drawing.html"] }
- { "date": "2023-07-07", "path": "/cs/boot.html", "title": "简述计算机启动过程", "desc": "我到了小学六年级才第一次见到计算机。当时最令我惊讶不解的是它需要很长的启动过程。 启动时会在屏幕上滚动显示各种不认识的字母。上大学以后，因为不是计算机科班，只学了 操作系统和编程语言，并没有系统的学习计算机组成原理。工作之后更是鲜少需要用到相关 知识。所以到现在对这个问题的理解还很模糊。最近读到 Hackman 的文章，简明 介绍了早期计算机硬件的启动过程，虽然有点过时，但基本原理跟现代 CPU 还是相通的。 今天把学习笔记分享给大家。", "tags": [], "refs": ["/unix/markdown-drawing.html"] }
- { "date": "2023-07-04", "path": "/free-oracle-cloud.html", "title": "注册 Oracle 永久免费虚拟主机", "desc": "Oracle Cloud 推出永久免费套餐也有两三年了。我就一张招行信用卡，老是注册失败。但 很多朋友都成功了，甚至还有催生出卖号之类的灰产。今天看到有人通过 5G 网络成功注册。 我也就试了一下，没想到也成功了。在此大家分享注册过程。", "tags": [], "refs": ["/dmit-25.html", "/oracle-25.html"] }
- { "date": "2023-07-02", "path": "/life/brain-teaser.html", "title": "跟女儿玩脑筋急转弯", "desc": "今天跟大女儿视频聊天。她提议要互相提问题，让对方回答。大女儿六岁半了，我觉得可以 趁机教她玩脑筋急转弯，于是欣然答应。", "tags": [], "refs": [] }
- { "date": "2023-07-01", "path": "/net/punycode.html", "title": "Punycode 编码", "desc": "DNS 最早由美国人 Jon Postel 等人设计，所以仅支持 ASCII 编码。但很快互联网 就扩展到欧洲和世界其他地区。人们希望能够使用自己的本地语言表示 DNS 域名。但同时 又必须兼容已经部署和运行的 DNS 软件系统，所以就得设计一种用 ASCII 编码来表示世界 上所有不同语言文字的编码规则，这就是 Punycode 编码。我们的中文域名用的也是这种 编码。这么重要的编码技术，中文互联网上几乎没有详细的资料介绍。于是只能硬啃它的 RFC 文档，今天把学习成果分享给大家。", "tags": [], "refs": ["/utf-8.html"] }
- { "date": "2023-06-29", "path": "/sms-spam.html", "title": "垃圾短信治理建议", "desc": "今天清理手机短信时发现一条工信部的短信，我之前在部长信箱留言，提出一种治理垃圾 短信的建议。顺手查了一下，过了一个半月，工信部回复了。虽然都是场面话，但他们但 凡能听进去一点，也不枉废我一番功夫。既然已经回复，那就算告以段落。短信里的查询 密码也就不必保留。但我觉得可以将建议的内容分享给大家。", "tags": [], "refs": ["/dmit-25.html"] }
- { "date": "2023-06-27", "path": "/c/regexp.html", "title": "使用递归实现正则匹配", "desc": "很久之前就读到 Brain 的文章，讲怎么写优雅的代码。作者在文中讲了 Rob Pike 的故事，Rob 用一个多小时的时间实现了一个简的的正则匹配函数， 所有代码 加上注释也才三十多行。Rob 的实现不但简洁而优雅，而且还充分展示了递归函数和指针 的强大魅力。今天就结合自己的理解分享给大家。", "tags": [], "refs": ["/utf-8.html", "/hello-regexp.html"] }
- { "date": "2023-06-25", "path": "/blog-copyright.html", "title": "谈谈博客版权", "desc": "最近偶然发现某网站转载了我的文章，但没有事先跟我申请授权。于是我联系到站长要求 下线。但最终站长同意给出规范外链，此事也就作罢。今天记录完整的维权过程，也顺便 谈谈我对博客版权的思考。", "tags": [], "refs": [] }
- { "date": "2023-06-22", "path": "/unix/markdown-drawing.html", "title": "在 Markdown 中优雅绘图", "desc": "我一直用自研的博客系统。因为是自研，只实现了核心功能。像矢量绘图功能 就没有支持。技术文章在很多地方都需要添加示意图。现在只能先用外部软件画，再生成图片， 最后上传到博客来引用。这种办法不但将绘图跟写作分割开来，更大的问题是后期改起来很 麻烦。所以我尽量使用 ASCII 字符 + 代码块的方式画示意图，方便后续修改。到现在博客 的基础功能已经稳定，为了让文章更加出彩，是时候实现矢量绘图功能了。今天跟大家分享 基于 pandoc 的 lua-filter 实现在 Markdown 中直接绘制 SVG 矢量图。", "tags": [], "refs": ["/unix/blog-internal.html", "/unix/blog-internal.html"] }
- { "date": "2023-06-19", "path": "/dns/cn-no-root.html", "title": "为什么中国没有DNS根服务器", "desc": "当今互联网有13组DNS根服务器，但没有一组由中国机构运行。无论民间还是政界，一提起 此事大家都痛心疾首，感觉被美国卡住了脖子。从国际互联网和中国互联网的发展脉络看， 中国本来有多次机会运营自己的根服务器。但发展到今天为什么事与愿违呢？本文就尝试 回答这个问题。", "tags": [], "refs": ["/dns/13-roots.html"] }
- { "date": "2023-06-18", "path": "/ai/onekey-chatgpt.html", "title": "ChatGPT 付费新方案 Onekey 美国优选卡", "desc": "该来的还是来了。5月31号 Depay 发布维护公告，说自己的 Mastercard 号段被 银行风控，无法正常扣费。之前开通的 ChatGPT Plus 因无法续费被取消了。虽然我个人 不强依赖 ChatGPT Plus，可是我还开发了 ChatGTD 为大家提供服务。 功能是简陋了点，还是有一些朋友有用。如果 ChatGPT API 也不能通过 Depay 支付的话， 那么 ChatGTD 的用户就会受影响。今天检查 OpenAI 的账单，果然扣款失败了。为了不辜负 大家的信任，我得尽快解决这个问题。网上说 Onekey Card 新出了美国美国优选卡，可以 解锁 ChatGPT Plus 和支付 ChatGPT API 费用。也只能硬着头皮试试看，顺便看能不能 转移 Depay 中的余额。没想到还真可以。现在把过程记录下来，分享给大家。", "tags": [], "refs": ["/ai/onekey-card.html"] }
- { "date": "2023-06-17", "path": "/life/no-ocd.html", "title": "与自己和解", "desc": "最近发生了不少事，让我治好了自己的强迫症。以前还觉得强迫症是有追求的体现。 现在回过头来看，不过是较真、钻牛角尖罢了。根除强迫症的疗法不在外面，就在心里。 与自己和解，一切都会豁然开朗。", "tags": [], "refs": [] }
- { "date": "2023-06-06", "path": "/dns/13-roots.html", "title": "为什么只有13台根服务器", "desc": "网上很多人都说 DNS 根服务器只有 13 台，中国一台也没有。在网络世界，中国被美国 卡住了脖子。那 DNS 根服务器真的只有 13 台吗？如果是，那原因又是什么？今天就给 大家说道说道。", "tags": [], "refs": ["/dns/dns-intro.html", "/dns/cn-no-root.html"] }
- { "date": "2023-06-04", "path": "/why-blog.html", "title": "为什么还在写博客", "desc": "都 2023 年了，现在是短视频的时代，怎么还在写博客？是的，我还在写博客。从注册域名 taoshu.in 开始，到现在己经两年多了。我的创作经历还能追溯到更久，因为之前主要在 知乎创作。今天回顾一下过去两年的发展，也谈谈自己对博客创作的理解。", "tags": [], "refs": ["/beian.html", "/emoji.html", "/web/self-host-font.html", "/web/bird-button.html", "/svg-to-png.html", "/unix/blog-internal.html"] }
- { "date": "2023-05-31", "path": "/dns/us-and-cctld.html", "title": "美国删除伊拉克国家域名始末", "desc": "简中互联网流传着一则摇言，说美国政府曾经利用自己的网络霸权删除过伊拉克的国家域名 .iq，导致该国的网站一夜之间从互联网上完全蒸发。这种危言耸听的摇言本不值得一驳， 但相关内容竟出自体制内人员之口，而且还被《人民日报》和人民网报道。作为互联网从业 人员和 DNS 爱好者，我有必要站出来说两句，以正人心而靖浮言。", "tags": [], "refs": [] }
- { "date": "2023-05-30", "path": "/unix/makefile-based-blogging.html", "title": "Makefile Based Blogging", "desc": "In this article, I will build a simple static site generator using make, bash, and pandoc. All codes have been published to GitHub named after makedown.", "tags": [], "refs": [] }
- { "date": "2023-05-29", "path": "/indie-alipay.html", "title": "个人网站支付宝收款", "desc": "个人微创业最大的问题就是收款。但如果不注册公司或者个体户，就很难接入支付宝或者 微信的支付系统。也有一些第三方公司可以通过支付机构的服务商接口为个人开通 支付权限。但一方面需要向三方提供个人信息，有隐私风险；另一方面有额外的手续费。 经过一番调研，我觉得目前最靠谱的还是支付宝的当面付，可以集成到个人PC/H5网站上。", "tags": [], "refs": [] }
- { "date": "2023-05-28", "path": "/ai/better-chatgpt-prompts.html", "title": "怎样向 ChatGPT 提问", "desc": "最近几个月，ChatGPT 席卷全球，可谓是炙手可热。很多人都说 ChatGPT 是人工智能，也 就是 AI 的 iPhone 时刻。ChatGPT 就像年 iPhone 颠覆智能手机行业一样，彻底改写了 人工智能行业历史。但当大家过关斩将终于可以跟 ChatGPT 对话后才发现，它好像 也不是很智能，老是答非所问或者翻来覆去都是些空话套话。这个问题是因为大家不了解 ChatGPT 的工作方式，不能正确的提问，不能给 ChatGPT 传递正确的提示，也就是 Prompts 导致的。今天就跟大家分享一下怎样给 ChatGPT 提问题，或者说怎样写 Prompts。", "tags": [], "refs": ["/ai/pay-chatgpt.html"] }
- { "date": "2023-05-24", "path": "/unix/synology-https.html", "title": "群晖 NAS 支持 HTTPS 访问", "desc": "朋友在我这托管了一个群晖设备，正好我也有中国电信的公网IP地址，就想着做一个公网 可用的网络存储。公网访问肯定要上 HTTPS，不然密码可能会被别人截获。虽然群晖系统 默认支持通过 Let’s Encrypt 申请免费 SSL 证书，却无法在国内网络使用。因为运营商 会屏蔽443端口，导致无法验证域名控制权。今天分享基于 DNS API 的方案，绕过运营商 的端口限制，为群晖设备开启 HTTPS 支持。", "tags": [], "refs": [] }
- { "date": "2023-05-23", "path": "/unix/vps-rebirth.html", "title": "抢救 VPS 服务器", "desc": "昨天晚上例行更新 VPS，更新后重启，重启后失联了😂博客、梯子、ChatGPT全部失效。 今天早上折腾了一番终于给抢救回来了。记录一下抢救过程，供大家参考。", "tags": [], "refs": [] }
- { "date": "2023-05-16", "path": "/chenhao.html", "title": "纪念陈皓（左耳朵耗子）", "desc": "昨天惊悉知名IT技术人士陈皓（左耳朵耗子）于13号突发心梗不幸辞世，享年四十七岁。 四十不惑，五十知天命。这可能是人生最好的阶段，他也在这个阶段收获了知识、技能、 家庭、财富和名望。但所有的一切突然就化作云烟随风飘散。生命是一，其他的都是零。", "tags": [], "refs": [] }
- { "date": "2023-04-26", "path": "/go/json-any.html", "title": "一例Go语言解析JSON引发的BUG", "desc": "今天有同事反馈说某接口的的签名计算有误。而且比较签名的代码也写错了，结果负负得正， 系统居然带病运转了达五年之久。作为代码提交者我深感愧疚。痛定思痛，发现该问题跟 Go 语言的 JSON 解析行为有关。今天就把相关经验分享给大家。", "tags": [], "refs": [] }
- { "date": "2023-04-25", "path": "/go/fast-modulo.html", "title": "一种快速取模算法", "desc": "最近业务系统使用map[uint64]float64保存某种业务数据，大约有两亿多条，但实际内存 占用却将近10个G。团队最终设计了一种自定义数据结构来解决内存消耗问题。 在解决问题的过程各我们考查了 SwissTable 这种新哈希表。虽然没能解决 我们的问题，却给出了一种新的快速取模的算法，很有启发意义。本文就基于 Daniel 的 文章为大家分享这种取模算法。", "tags": [], "refs": [] }
- { "date": "2023-04-22", "path": "/ai/chatgpt-access-denied.html", "title": "解决 ChatGPT Access denied 问题", "desc": "之前分享了订阅 ChatGPT Plus 的过程，又解决了订阅续费 问题。过了几天好日子，ChatGPT Plus 也确实提高了自己 的生产力。但好景不长，突然有一天打开 ChatGPT网页后发现账号被封了😂欲哭无泪呀。 虽然还可以调 API 使用 GPT-3.5 模型，但用不了 GPT-4 确实难受。今天就跟大家分享 解决问题的方法和过程。", "tags": [], "refs": ["/ai/pay-chatgpt.html", "/ai/depay-chatgpt-subs.html", "/net/vps-he-ipv6.html"] }
- { "date": "2023-04-18", "path": "/ai/onekey-card.html", "title": "OneKey Card 虚拟卡", "desc": "之前为了开通 ChatGPT Plus，我注册了 Depay Card 虚拟卡，还专门写了介绍文章。 最近硬件钱包厂商 OneKey 也推出了一卡虚拟卡 OneKey Card，号称比 Depay Card 更强大。 出于好奇，我研究了一把。今天就说说要不要注册 OneKey Card。", "tags": [], "refs": ["/ai/pay-chatgpt.html", "/ai/onekey-chatgpt.html"] }
- { "date": "2023-04-17", "path": "/unix/linux.html", "title": "Linux 往事", "desc": "最近读到 Lars Wirzenius 写的文章，他是 Linus 的同学，参与并见证了 Linux 系统从无到有的历史。想想人家上的大学，再比较一下自己的学生时代，真是让人唏嘘呀。 今天把文章编译成中文分享给大家。", "tags": [], "refs": [] }
- { "date": "2023-03-28", "path": "/ai/depay-chatgpt-subs.html", "title": "ChatGPT Plus Depay 续费失败问题", "desc": "最近开通了 Depay 虚拟卡并订阅了 ChatGPT Plus。我把相关的经验总结出来， 帮助了不少读者。但好景不长，一周之后 Depay 收到一笔退款，ChatGPT 把之前的 20 美元 退回来了。我估计是 Depay 卡初封控了😂于是研究了一番，发现很多朋友的都无法正常扣费。 今天分享一个解决方案。", "tags": [], "refs": ["/ai/onekey-chatgpt.html"] }
- { "date": "2023-03-25", "path": "/web/target-color.html", "title": "设置超链接锚点目标样式", "desc": "我一直使用 Markdown 创作。随着创作的深入，使用脚注的情况越来越多。Pandoc 会自动 处理脚注的编号，并将所有脚注锚链接追加到文章末尾。如果脚注非常多，点击脚注编号 会跳转到文章结尾。但这里有很多脚注，读者可能分不清刚才点的是哪一个数字😂虽然可以 再点一下脚注末尾的↩︎跳回原文，但总归是不方便。如果文章内容比较短，或者同一行中 有多个脚注，读者更对不上号。今天就通过 CSS 解决这个问题，进一步优化阅读体验。", "tags": [], "refs": [] }
- { "date": "2023-03-18", "path": "/ai/pay-chatgpt.html", "title": "开通 ChatGPT Plus 攻略", "desc": "ChatGPT 已经红透海内外。虽然国人想注册免费版都得过关斩将，但用得人还是太多了。 高峰时期服务响应非常慢。而且免费版无法使用 OpenAI 最新发布的 GPT-4 模型。为了 更快、更强，必须开通 ChatGPT Plus。但是 OpenAI 不但屏蔽了中国的 IP 地址，连国内 的 Visa 信用卡也一同屏蔽。所以最难的一关是如何给 OpenAI 充值😂你看吧，好的产品 往往是用户求着付钱💰这就是差距呀。今天就给大家分享我开通的过程。", "tags": [], "refs": ["/ai/onekey-chatgpt.html", "/ai/depay-chatgpt-subs.html"] }
- { "date": "2023-03-14", "path": "/web/simple-audio-player.html", "title": "开发一个简易的电台 PWA 应用", "desc": "练习英语听力需要听大量的音频。我尝试下载离线资源保存到手机上听。但更新 或者扩展内容都不太方便。于是便改听在线广播。很多在线广播就是一个 CDN 链接，对应 特定格式的音频文件（MP3、AAC等）。它们完全可以通过 HTML 的&lt;audio&gt;播放。所以我 就想做一个 PWA 应用，把合适的广播资源都集成起来方便使用。一番折腾之后，做了一个 很简易的版本。今天把开发过程整理出来，分享给大家。今天分享的内容完全基于浏览器 标准 API 开发，没有使用 JavaScript 框架，便于初学者入门。前端大佬请轻喷。", "tags": [], "refs": ["/web/podcast-rss.html", "/svg-favicons.html"] }
- { "date": "2023-03-08", "path": "/web/push-on-ios.html", "title": "Web Push on iOS", "desc": "Finally, the first beta of Safari 16.4 packs the long awaited feature, the Web Push. I can’t wait to install the beta version iOS, and have made a demo. In this article, I will demonstrate how to send web notification to Safari, and some of its restrictions.", "tags": [], "refs": [] }
- { "date": "2023-02-21", "path": "/web/self-host-font.html", "title": "独立托管 Web 字体", "desc": "本站使用霞鹜文楷字体，最早还是在班班的频率博客上看到的，非常喜欢。本站最早 使用 CDN 加载该字体。但因为 jsDelivr 在国内日益不稳定，我最近决定用自己的 服务器独立托管霞鹜文楷。本文就梳理这其中的来龙去脉。", "tags": [], "refs": [] }
- { "date": "2023-02-21", "path": "/firefox/doh-in-org.html", "title": "在公司网络中给 Firefox 开启 DoH", "desc": "我曾专门写文章总结DNS隐私安全问题，其中最主要的问题是 上网行为监控。理论上公司可以通过 DNS 记录你在上班期间的全部网络行为。 当前最好的应对策略就是开启 DNS over HTTPS (DoH)。但是公司网络中往往有很多内部 系统，开启 DoH 后就无法正常访问。今天以 Firefox 为例分享一个比较完整的解决方案。", "tags": [], "refs": ["/dns/dns-privacy.html"] }
- { "date": "2023-02-10", "path": "/web/podcast-rss.html", "title": "使用 RSS 发布播客", "desc": "最近在练习英语听力，想试试广播剧。研究了一番后发现，英语有很多优秀的广播剧资源。 其中做的最好的就是英国，而 BBC 甚至还出品了经典的《指环王》。所以我就从网找到了 指环王的资源，放到博客服务器上。但直接使用浏览器不太方便，我就想能不能做成播客， 也就是 Podcast，然后用手机自带的播客工具播放。制作过程比我想像的要复杂，今天把 整个过程分享给大家。", "tags": [], "refs": [] }
- { "date": "2023-02-08", "path": "/life/cola-keyboard.html", "title": "可乐洒键盘的抢救办法", "desc": "最近不小心打翻了可乐瓶，快乐水一下子洒到了键盘上。因为是妙控键盘，我果断将其翻 过来控水，避免损坏。然后把残余的可乐擦干净。运气不错，还能用😄但到第二天就没法 快乐了，因为浸过可乐的键盘位都变成了粘粘的手感，非常难用。在网上查了一下，都是 些笼统的方案，估计没有实践过。于是自己动手、丰衣足食。现在把实践经验分享给大家。", "tags": [], "refs": [] }
- { "date": "2023-02-05", "path": "/merkle-tree.html", "title": "默克尔树简介", "desc": "默克尔树(Merkle Tree)广泛应用于分布式系统。我最早在学习区块链技术时了解到这一 数据结构。但因为几乎没有在工作中用到过，所以对相关的概念和特性一直都似是而非。 今天研究浏览器证书透明度日志的时候读到 Cloudflare 的一篇文章，也讲到 默克尔树。文章内容简洁明了，一下子就掌握了该数据结构的特性和要解决的问题。现在 结合自己的理解分享给大家。", "tags": [], "refs": [] }
- { "date": "2023-02-03", "path": "/go/memory-model.html", "title": "Go语言内存模型", "desc": "最近做了一个项目，需要将大量数据加载到内存，然后对外提供查询使用。同时，数据又 需要按照一定的策略更新，但更新的频率很低。原则上并发读写需要加锁。但考虑到写的 频率远低于读，为这点写加锁实在是浪费，所以就想研究一下有没有不加锁的方案。这就 需要用到本文要讲的内存模型(Memory Medel)了。", "tags": [], "refs": [] }
- { "date": "2023-01-29", "path": "/go/simple-vpn.html", "title": "Simple VPN in Golang", "desc": "In this article, I will describe how to implement one simple VPN system, using the Golang.", "tags": [], "refs": [] }
- { "date": "2023-01-14", "path": "/go/parse-binary-data.html", "title": "使用Go语言解析二进制数据踩坑总结", "desc": "最近用 Go 语言重写了推荐系统的打分服务。其中的模型数据体积很大，有 5G 字节左右， 而且是文本文件。我想着先统一转成二进制格式，加载速度会提高不少，而且解析逻辑也 不会太复杂。于是力排众议决定用二进制格式。结果上线后就出现非常诡异的问题。 宏观表现就是打分结果偏大。经过一番调试发现，原来是自认为比较简单的解析部分 有逻辑问题。今天就把踩坑的经验分享给大家😂", "tags": [], "refs": [] }
- { "date": "2022-12-20", "path": "/unix/markdown-soft-break.html", "title": "优化中文 Markdown 软换行显示效果", "desc": "Markdown 支持所谓的软换行(soft break)。连续输入的两个换行符表示新起一段，因为 段落之间会有一个空行。如果只输入一个换行符，那就表示软换行。说它软是因为它 只会影响到 Markdown 源文，转成 HTML 后就不是换行(&lt;br&gt;)了，而是对应空格。这在英文 环境中没有问题，但在中文环境中却很不方便。今天就分享如何优化中文 Markdown 中 软换行的显示效果。", "tags": [], "refs": ["/unix/blog-rune-count.html", "/unix/markdown-link.html", "/go/gc-guide.html"] }
- { "date": "2022-12-20", "path": "/go/gc-guide.html", "title": "Go语言垃圾回收指南", "desc": "本文是 Go 语言官方文档 A Guide to the Go Garbage Collector 的中译本。 我之前尝试总结 Go 语言 GC 相关问题。在准备资料的过程中我发现官方手册基本上是 最系统的学习资料了，所以尝试将其译成中文。当今时代机器翻译已经越来智能， Go 语言开发者也大都具备英语阅读能力。在这个背景下做翻译，更多是帮自己加深理解， 顺便也提高一下英语水平😄如果能帮助到大家，那更是善莫大焉。", "tags": [], "refs": [] }
- { "date": "2022-12-14", "path": "/vim/macro-find-replace.html", "title": "使用Vim宏实现复杂的查找与替换", "desc": "最近准备翻译一篇文章。原文是 HTML 格式，译文需要改成 Markdown 格式。两种格式使用不同的标题锚点，前者使用驼峰加下划线，后者使用小写字母加短杠。所以需要将原文中的锚点批量转换成小字字母加短杠的形式。想来想去，还是用Vim宏实现比较方便。今天就分享给大家作为学习宏的实践素材😄", "tags": [], "refs": ["/vim/vim-macro-quick-start.html"] }
- { "date": "2022-12-13", "path": "/unix/markdown-link.html", "title": "Markdown博客站内引用问题", "desc": "使用 Markdown 写博客绕不过引用链接的问题。源文件扩展名是.md，但转换后会变成.html。 为了正常跳转，写站内引用链接时只能使用 HTML 文件对应的路径。但这样一来，在编辑 的时候就没法跳转到对应的.md文件了。最近抽空给 pandoc 写了个 lua-filter，算是 比较完美地解决了这个问题。今天分享给大家。", "tags": [], "refs": ["/unix/blog-internal.html"] }
- { "date": "2022-12-09", "path": "/web/animated-background-stripes.html", "title": "纯CSS实现动态条纹背景效果", "desc": "CSS-TRICKS 最近发表了一篇文章用纯CSS实现了动态条纹背景效果。内容很 基础，却也是非常好的入门学习材料。今天整理出来分享给大家。虽然不是原创，但我也 没有简单地翻译原文内容。而是跟小鸟按钮一样，自己重新实现了一把。 读者可以直接在当前页面体验实际效果。另外原文中的组件尺寸比较大，直接嵌入博客正文 不太美观，我把中间的绝对尺寸提取出来改为变量。这也算一点微小的创新吧😂", "tags": [], "refs": ["/web/bird-button.html"] }
- { "date": "2022-11-27", "path": "/unix/openwrt-ddns-cloudflare.html", "title": "OpenWrt Cloudflare DDNS", "desc": "最近更新路由器系统，把 DDNS 搞坏了。路由系统是 OpenWrt，DDNS 服务使用 Cloudflare。上次配置就折腾了好久，忘记记录了。这次又折腾了一遍。趁此机会整理成文， 分享给大家。", "tags": [], "refs": [] }
- { "date": "2022-11-20", "path": "/unix/resize-qemu-ext4.html", "title": "扩展 QEMU 虚拟磁盘容量", "desc": "最近使用 QEMU 测试 ARM 平台，使用的虚拟磁盘是从网上下载的，总空间不到两吉字节。 我安装 clang 工具链，装到一半就把磁盘占满了。所以从网上查到了扩展虚拟磁盘容量的 办法，分享给大家。", "tags": [], "refs": [] }
- { "date": "2022-11-20", "path": "/unix/loop-virt-disk.html", "title": "使用 loop 设备访问虚拟磁盘", "desc": "loop 设备是 Linux 下一种虚拟设备。它广泛应用于跟虚拟磁盘、ISO镜像等文件的处理领域。最近在折腾 ARM 虚拟系统，需要使用 loop 设备制作系统镜像。多方调研才对它有了相对全面的认识。为了把文章讲清楚，本文还会介绍 Linux 磁盘管理的基础知识，也欢迎新手阅读。", "tags": [], "refs": ["/unix/resize-qemu-ext4.html"] }
- { "date": "2022-11-19", "path": "/web/bird-button.html", "title": "CSS的快乐：画一个可爱的三只小鸟按钮", "desc": "做为前端工程师，最大的快乐之一就是可以用 CSS 画出各种有趣的效果。", "tags": [], "refs": [] }
- { "date": "2022-11-19", "path": "/unix/modify-udp-packet-using-ebpf.html", "title": "Modify UDP packet using eBPF", "desc": "eBPF is one of the most important infrastructures for cloud computing. It’s widely used for networking observation and filtration. Under certain circumstances, we need to modify packets directly. In this article, I will show you how to finish this task.", "tags": [], "refs": [] }
- { "date": "2022-11-14", "path": "/go/go-sfile.html", "title": "Go语言实现HTTP文件下载服务", "desc": "本文是Go语言快速入门的实战部分。我会带领大家综合利用前面几节所学的基础知识， 开发一个简单的HTTP文件下载服务器程序。", "tags": [], "refs": ["/go/go-quick-start.html", "/net/http.html", "/go/go-http-server-timeout.html", "/go/text-image.html"] }
- { "date": "2022-11-13", "path": "/vim/migrate-nerdtree-to-nvim-tree.html", "title": "Migrate from NerdTree to NvimTree", "desc": "I have used NerdTree as file explorer for years. It is simple, but works. Even after I migrated to NeoVim, I still choose NerdTree to explore and manage file. However, after I upgraded my NeoVim to version 0.8, the NerdTree seems has some bugs under NeoVim. I think it is time to find some modern alternative.", "tags": [], "refs": [] }
- { "date": "2022-11-12", "path": "/unix/blog-rune-count.html", "title": "统计博客文章字数", "desc": "信息时代注意力极度稀缺，人们很难容忍长篇大论。但为了把事情讲清楚，文章内容又不可能太短。这就产生了矛盾。解决矛盾的方法是给读者一个明确的预期，也就可以让读者快速判断文章的内容有没有用，大约要花多长时间才能读完。前一个问题我用两个办法解决，一个是把第一段当成摘要来写，力求言简意赅；另一个是在文章开头生成子标题列表。后一个问题则需要统计文章字数，根据字数估算阅读时长。", "tags": [], "refs": ["/unix/blog-toc.html", "/unix/blog-internal.html", "/ipv6-world.html"] }
- { "date": "2022-11-10", "path": "/unix/blog-toc.html", "title": "为博客文章生成标题列表", "desc": "我写过一些较为系统性介绍某些主题的文章，主要面向初学者。因为要讲求内容的完整性和前后的逻辑性，文章内容不免写得很长。但是内容越长，对初学者的劝退作用也就越强。最近需要给组内同学编写Go语言入门材料，需要趁此机会把这个问题解决掉，方便大家阅读。", "tags": [], "refs": ["/go/go-quick-start.html", "/unix/blog-internal.html", "/about.html"] }
- { "date": "2022-11-09", "path": "/go/go-engineering.html", "title": "Go语言的工程设计", "desc": "Go语言是一门很实用的语言。它并不完美，甚至也难说优雅，但它很实用。设计者在总结前人多年工程实践的基础上设计了Go语言。这些设计导致了不少的争论，却也实实在在的解决了不少问题。工程设计的范围很广，今天只能面向初学者选几个方面加以简要介绍。", "tags": [], "refs": ["/go/bench.html", "/go/mock.html"] }
- { "date": "2022-11-09", "path": "/go/go-concurrency.html", "title": "Go语言的并发编程", "desc": "本文介绍Go语言并发编程的概念、用法和需要注意的问题。", "tags": [], "refs": ["/go/go-map-concurrent-misue.html"] }
- { "date": "2022-11-05", "path": "/firefox/death-to-blank.html", "title": "禁止网页在新窗口打开链接", "desc": "很多网页（尤其是简中互联网）强行在新窗口（或者新标签页,下面统称新窗口）打开链接。有人觉得这种做法很方便，可以保留原始网页。但我不喜欢这种方式。因为我用 Firefox，如果是在新窗口或者标签页打开网页就无法使用触摸板滑动返回到上一个页面。今天就整理一下强制在当前窗口或者网页打开链接的方法，分享给大家。", "tags": [], "refs": [] }
- { "date": "2022-10-29", "path": "/net/http.html", "title": "HTTP 协议简介", "desc": "HTTP 全称 Hypertext Transfer Protocol，中文是超文本传输协议。网上讲 HTTP 协议的 资料可以说是五花八门，但大多数都在罗列 HTTP 协议具体的规定，很少有讲 HTTP 协议 这样设计的原因。今天我就尝试从解决问题的角度分析 HTTP 协议主要特性，希望能帮助 大家快速理解 HTTP 协议。", "tags": [], "refs": ["/pb-encoding.html", "/net/http-103.html", "/dns/dns-svcb-https.html", "/http3-port.html"] }
- { "date": "2022-10-22", "path": "/firefox/vertical-tabs.html", "title": "配置 Firefox 垂直标签栏", "desc": "Firefox 原生不支持垂直标签栏，但可以通过扩展实现，比如 Sidebery 或者 Tree Style Tab。装上插件之后确实会展示类似 Edge 的垂直标签栏。但是，原来的水平标签并没有隐藏。也就是说同一个标签会显示在两个地方。这让强迫症晚期的我怎么忍？今天就分享一种在 Firefox 上完美实现垂直标签栏的方案。", "tags": [], "refs": [] }
- { "date": "2022-10-21", "path": "/go/go-type.html", "title": "Go语言的类型系统", "desc": "本文面试初学者，尝试用整体的观点统一介绍Go语言的类型系统。为方便读者理解，我将其分成简单类型、容器类型和自定义类型三大类。在此基础上又引申出指针、接口和泛型三个高级主题。本文并非要取代传统的学习材料和官方文档，其目标在于提供一份更加易用的入门材料。", "tags": [], "refs": ["/go/type-embedding-in-go.html", "/go/control-structures.html"] }
- { "date": "2022-10-20", "path": "/go/go-func.html", "title": "Go语言的函数", "desc": "本文是Go语言入门教程的一部分。我认为函数是Go语言的基础单位。传统教材都是先讲 Hello World，这个时候有意回避函数的概念。紧接着就转到介绍类型系统。我认为可以直接从函数入手，因为Go程序本身就是一个函数(main)，这样从逻辑上会更清晰一些。但因为没有类型系统的知识，所以只能强行跟读者假定int或string等基础类型。这是先讲函数的不足。现在开始正文内容。", "tags": [], "refs": ["/go/go-type.html"] }
- { "date": "2022-10-19", "path": "/go/go-prepare.html", "title": "学习Go语言的准备工作", "desc": "欢迎加入后端程序员行列😄本手册面假定读者已经完成大学理工科教育，对计算机有基础的理解，并且在校期间至少学习过 C/C++/Java 的一种编程语言。本手册以 Go 语言为例介绍后端入门所需的基础知识。在开始学习之前，读者还需要做一些准备工作。", "tags": [], "refs": ["/vim/go-vim.html", "/go/go-func.html"] }
- { "date": "2022-10-19", "path": "/apple/restore-firefox-logo-in-mac.html", "title": "在 macos 下修改 Firefox 图标", "desc": "Firefox 106 正式发布。在我看来最重要的更新是将 libwebrtc 从 86 升级到 103，改善了 WebRTC 的兼容性。其他的改动都比较花哨，不提也罢。另一个让我难受的更新是 106 修改了 macos 下的应用图标，从之前透明背景改成了蓝黑色图标。", "tags": [], "refs": ["/svg-to-png.html"] }
- { "date": "2022-10-18", "path": "/go/go-quick-start.html", "title": "Go语言快速入门", "desc": "组内有同学想学习Go语言。趁这个机会整理一个入门系列文章分享给大家。本文权当是一个索引。", "tags": [], "refs": ["/go/go-prepare.html", "/go/go-func.html", "/go/go-type.html", "/go/control-structures.html", "/go/errors.html", "/go/go-concurrency.html", "/go/goroutine-panic.html", "/go/bench.html", "/go/go-engineering.html", "/go/go-sfile.html"] }
- { "date": "2022-10-15", "path": "/unix/qemu-bridge-on-macos.html", "title": "在 macos 创建 QEMU 桥接网络", "desc": "今天记录一下如何在 macos 上为 QEMU 虚拟机创建桥接网络。", "tags": [], "refs": [] }
- { "date": "2022-09-29", "path": "/net/manual-ipsec-ip-xfrm.html", "title": "使用 ip xfrm 手工配置 IPsec VPN", "desc": "WireGuard 是在 Linux 内核实现的 VPN 模块。得益于内核实现，它跟基于 tun 设备的 OpenVPN 相比，可以省掉在内核态和用户态之间复制数据，性能自然要好很多。但我从 WireGuard 技术白皮书得知，WireGuard 的性能只比 IPsec 高一点。这让我不禁怀疑 IPsec 的核心功能也是在内核态实现的。经过一番研究Google，果不其然。Linux 内核通过 xfrm 模块实现了最基础的 IP 数据加解密功能，而用户认证和密钥协商等功能则交由用户态的 IKE 服务完成。如果没有 IKE 服务，我们也可以通过手工执行 ip xfrm 命令配置 IPsec VPN。", "tags": [], "refs": [] }
- { "date": "2022-09-28", "path": "/net/ipsec-strongswan-ikev2.html", "title": "基于 strongSwan 配置 IPsec IKEv2 VPN", "desc": "IPsec 是标准的 VPN 技术，主流系统都支持，不需要单独安装客户端。但是 IPsec 概念 非常多，配置起来很繁杂。经过一番折腾，终于摸索出了一套基于 strongSwan 的简便配置 方法，支持 iOS 和 macOS 拨入。", "tags": [], "refs": [] }
- { "date": "2022-08-31", "path": "/go/duff-zero.html", "title": "达夫设备与 Go 语言零值", "desc": "很久之前就听说过达夫设备(Duff’s device)，是串行复制的一种优化。最近发现 Go 语言在零值初始化的时候也利用了达夫设备的思想。于是便仔细研究了一下，今天分享给大家。", "tags": [], "refs": ["/go/monkey/generic.html"] }
- { "date": "2022-08-24", "path": "/git/ssh-sign.html", "title": "使用 SSH 签名 Git 提交记录", "desc": "Git 支持使用 GPG 来签名提交记录。但 GPG 用起来很复杂，一直赖得搞。Git 从 2.34 开始支持使用 SSH 签名。SSH 密钥大家肯定都有，所以开启签名也需要提上日程了。只不过这个功能发布后很长时间内 GitHub 都不支持显示 SSH 签名，我也就没有继续推进。昨天 GitHub 官宣正式支持 SSH 签名。今天就把相关的内容整理一下分享给大家。", "tags": [], "refs": ["/git/git-internal.html"] }
- { "date": "2022-08-09", "path": "/grpc-trailers.html", "title": "再谈 gRPC 的 Trailers 设计", "desc": "我多次「批判」过 gRPC。在它众多复杂的设计当中，最神秘的就是依赖 trailers 头。网上基本没有资料讲 gRPC 为什么要使用 trailers 传递状态码。最近读到 Carl 一篇文章 Why Does gRPC Insist on Trailers?。Carl 曾经是 gRPC 研发团队的成员，他在文章中详细说明设计 gRPC 的愿景以及后面逐渐走向失控的过程。今天结合自己的理解再谈一下 gRPC 的设计。", "tags": [], "refs": ["/twirp.html"] }
- { "date": "2022-08-04", "path": "/net/github-actions-vpn.html", "title": "使用 GitHub Actions 科学上网", "desc": "本文介绍一种科学上网的思路，基于 GitHub Actions 服务，可以流畅观看 YouTube 4k 视频，不限流量，完全免费！", "tags": [], "refs": ["/net/auto-upnp-port-forward.html"] }
- { "date": "2022-07-28", "path": "/web/push.html", "title": "WebPush 工作原理", "desc": "在今年的开发者大会上，苹果宣布 Safari 将会支持 WebPush 标准。首先支持的是 mac 平台，会在今年秋天发布。然后是 iOS 平台，会在明年上半年。届时主流的浏览器都会支持 WebPush 特性。这是 Web 领域的一个里程碑！现在的互联网市场都在拼命推广移动应用。一个主要的原因就是 Web 平台留存率很低，而留存率低的主要原因就是不支持推送。希望 WebPush 技术的普及能够促进 Web 生态的繁荣。为了这个愿望，我今天把 WebPush 技术的工作原理整理成文，分享给大家。也算尽自己一份力。", "tags": [], "refs": [] }
- { "date": "2022-07-26", "path": "/life/secrets-of-longevity.html", "title": "长寿的秘诀", "desc": "地球上有五个区域，当地人基本都能活过一百岁。这五个区域从东到西分别是：日本的冲绳、希腊的伊卡利亚(Icaria)、意大利的撒丁岛(Sardinia)、哥斯达黎加的尼科亚(Nicoya)，以及美国加州的罗马琳达(Loma Linda)。人们称这五个区域为蓝区(Blue Zones)。通过研究蓝区居民的生活习惯，研究人员找到了长寿的九种秘诀(Power 9)。", "tags": [], "refs": [] }
- { "date": "2022-07-17", "path": "/webfeed/turn-browser-into-feed-reader.html", "title": "WebFeed: Turn browser into feed reader", "desc": "While the Internet is becoming more and more centralize, the open web feed technology like RSS and Atom is becoming less popular. People tends to share their article by the closed platform like Facebook or Twitter instead of independent blog with RSS or Atom. But why? Why the open technology are of no better than the closed one? I think one most signifiant advantage of the closed platform is their better use experience. It is not easy to subscribe a blog with web feed. This is why I develop the WebFeed browser extension, by which you can turn your browser into a simple feed subscriber and reader. And then the use experience of web feed like RSS or Atom will be more friendly.", "tags": [], "refs": [] }
- { "date": "2022-07-09", "path": "/go/monkey/generic.html", "title": "Go语言泛型函数 mock 原理", "desc": "Go语言实现猴子补丁系列又有更新了。这一次跟大家分享泛型函数的打桩原理。", "tags": [], "refs": [] }
- { "date": "2022-05-28", "path": "/web/save-multiple-files.html", "title": "在浏览器中批量保存文件", "desc": "最近接到一个业务需求，说是需要批量导出图片功能。Web 开发同学觉得应该把下载任务提交给服务端，由服务端完成打包，然后给出下载链接。我个人感觉这个功能可以直接在浏览器里实现。于是调研了一下，找到了一个可行方案。今天整理出来，分享给大家。", "tags": [], "refs": [] }
- { "date": "2022-05-10", "path": "/webfeed/webfeed-for-chrome.html", "title": "Chrome 版 WebFeed 简介", "desc": "为推动开放 Web 的发展，我开发了 Firefox 版的 WebFeed。今天 Chrome 版也终于上架了。大家可以到 Chrome Store 下载体验。Chrome 要求新提交的扩展必须使用 manifest v3 接口，所以从 Firefox 移植到 Chrome 不光花了一些时间，更重要的是一些交互需要重新设计，还有一些功能只能砍掉。今天就专门写一篇文章介绍 WebFeed 在 Chrome 下的食用方法和功能限制。关于 WebFeed 的设计思想请阅读我介绍 Firefox 版的文章。", "tags": [], "refs": ["/webfeed/webfeed.html", "/webfeed/lets-webfeed.html", "/webfeed/lets-webfeed.html"] }
- { "date": "2022-05-07", "path": "/svg-to-png.html", "title": "SVG 转 PNG 图片", "desc": "我最近着手把WebFeed插件移植到 Chrome 平台。但 Chrome 非常贱，居然不支持使用 svg 作图标。所以我需要将 svg 图片转换成 png 图片。我也查到了好多在线的转换工具，但效果都不理想。我寻思着浏览器既然可以展示 svg，那一定有办法用 js 转成 png 等格式的图片。经过一翻调研，终于找到了理想的技术方案。今天就把命令行和浏览器两种场景下 svg 转 png 图片的方案分享给大家。", "tags": [], "refs": ["/webfeed/webfeed.html"] }
- { "date": "2022-05-01", "path": "/unix/blog-internal.html", "title": "我的博客技术栈", "desc": "很久之前就说要把博客的技术栈开源并撰文介绍，拖到今天终于下决心兑现承诺了。本站 使用 markdown + syncthing + pandoc 搭建，跟主流博客系统相比最鲜明的特点就是帮助 作者专注于内容创作，而不需要把大把的精力浪费到工具上。本文会详细说明我设计 这套系统的来龙去脉及其使用效果。如果你有一定的 Linux 基础，可以轻松部署使用。 本博客大量使用 bash 脚本，如果你想学习 bash 编程，那本文也能提供一些入门材料。 好了，我们开始文章正文。", "tags": [], "refs": ["/utf-8.html", "/emoji.html", "/unix/blog-toc.html", "/tty.html", "/awk-in-20-minutes.html", "/hello-regexp.html", "/unix/makefile-based-blogging.html", "/unix/blog-letter.html", "/unix/blog-rune-count.html", "/unix/markdown-link.html", "/unix/markdown-soft-break.html", "/unix/markdown-drawing.html"] }
- { "date": "2022-04-20", "path": "/webfeed/lets-webfeed.html", "title": "WebFeed 倡议书", "desc": "为了推动开放 Web 生态的发展，我设计并开发了 WebFeed 扩展。使用 WebFeed，用户可以在浏览器中方便地订阅独立博客，从而获取类似微信公众号的体验。但目前基于 WebFeed (RSS/Atom) 的订阅方案还有不少问题。今天向广大作者发出倡议，希望能一起解决这些问题。", "tags": [], "refs": ["/webfeed/webfeed.html"] }
- { "date": "2022-04-16", "path": "/webfeed/webfeed.html", "title": "WebFeed 简介", "desc": "独立博主一般都听说过 RSS/Atom，可以用它们输出结构化的内容信息供读者订阅。由此也衍生出各类阅读器和平台。但是 RSS/Atom 经过一段时间的发展后，又仿佛被各类社交媒体和自媒体平台取代。这些工具或者平台都有这样那样的问题，不利于甚至可能扼杀开放的 Web 生态。我虽然是一介匹夫，却也一真在思考如何构建更开放的 Web 生态。本文接下来要介绍的 WebFeed 就是我的最新的尝试。", "tags": [], "refs": ["/webfeed/webfeed-for-chrome.html"] }
- { "date": "2022-04-15", "path": "/free-economist.html", "title": "如何免费阅读经济学人文章", "desc": "上海爆发疫情，隔离在家的我一直为生计发愁，没有心情更新博客。期间的所见所闻让我 再一次意识到要加强英文学习。为此，我尝试阅读更多英文版的新闻和评论。其中，比较 有名的就是经济学人(The Economist)。原则上我们要支持正版，但每年一百八十九美元的 订阅费确实不符合咱们的国情。那只好想办法薅资本主义的羊毛了。今天就把薅羊毛的 办法分享给大家。", "tags": [], "refs": [] }
- { "date": "2022-03-22", "path": "/go/grpc-unary-client.html", "title": "实现一个简单的 gRPC 客户端", "desc": "最近在跟朋友讨论的时候提到了 gRPC 协议，我之前也写过介绍 gRPC 的文章。大家一般都直接用官方的 grpc 库通信。但就我观察，绝大多数都是 Unary 请求调用，Stream 调用极少。而 gRPC 的 Unary 调用跟普通的 http/2 请求区别不大，我们完全可以自己手写一个简单的 gRPC 客户端。今天就以 go 语言为例，跟大家分享一下实现思路。坚持使用官方 SDK 的朋友也不要错过，这篇文章也能为理解 gRPC 协议提供一些帮助。", "tags": [], "refs": ["/grpc.html"] }
- { "date": "2022-03-01", "path": "/dns/edns-client-subnet.html", "title": "EDNS Client Subnet 协议简介", "desc": "DNS系统默认使用明文UDP协议通信，所以用户的查询内容很容易受到监控，而服务器返回的解析结果是可以被轻易篡改。为了解决这个问题，人们引入了 DNS over HTTPS/TLS/QUIC 之类的技术，希望通过加密的方式传输DNS查询。但使用公共的 DoH 递归解析服务器后，权威DNS服务器只能获取递归解析服务器的地址，但无法获取用户的地址。所以于地理位置的智能解析就无法工作了。为了解决这个问题，人们制定了 EDNS Client Subnet (ECS) 协议，也就是RFC7871。本文就简单介绍一下 ECS 的工作原理和使用效果。", "tags": [], "refs": [] }
- { "date": "2022-02-26", "path": "/cache/memcached-meta-commands.html", "title": "Memcached Meta Commands", "desc": "Memcached 是老牌的内存缓存服务器。因为不支持像 Redis 那样丰富的数据结构，Memcached 用的越来越少。但我认为 Memcached 是 Unix 哲学的典范，一次只做一件事，并把它做好。而 Memcached 就是专注于内存缓存这一件事。其中，Memcached 在 1.6 版本引入的 Meta Commands 就是最好的佐证。今天就给大家介绍一下 Meta Commands(元指令)。", "tags": [], "refs": [] }
- { "date": "2022-02-18", "path": "/adsense-payment-error.html", "title": "Google AdSense添加银行卡报错问题", "desc": "我之前写过一篇香港 Google AdSense 开户，分享了自己 开通 AdSense 的过程。发布之后陆续收到好多朋友的咨询，其中网友 Peter 按照文章操作 还是遇到了添加银行卡报错的问题。沟通之后我也无计可施，只能建议 Peter 联系谷歌客服。 十天之后，Peter 发来喜讯说问题解决了。我鼓励他说可以把这个经验分享出来。他说可以 整理一下，发表在我的博客上。以下就是 Peter 整理的内容，略有删改。", "tags": [], "refs": ["/hello-google-adsense.html"] }
- { "date": "2022-02-13", "path": "/go/generics/when.html", "title": "Go语言泛型使用场景", "desc": "我已经写过多篇介绍泛型的文章。但跟所有技术一样，泛型也有其适用的范围。今天就讨论一下什么时候应该使用泛型，什么时候又不该使用它。", "tags": [], "refs": ["/go/generics/examples.html", "/go/generics/prequel.html"] }
- { "date": "2022-02-13", "path": "/go/generics/prequel.html", "title": "Go语言泛型前传", "desc": "Go语言泛型马上就要随1.18版本发布了，大快人心。Go语言最早于2009年11月11日发布。还没过24小时，就有人提出能否支持泛型(海外链接)。至今，已经过去了13年。为什么Go语言到现在才加入泛型特性呢？那是因为Go语言有很多特性可以在定程序上实现泛型的效果。因为这些特性都是在泛型之前引入的，所以文章取名为「前传」。", "tags": [], "refs": [] }
- { "date": "2022-02-12", "path": "/dns/dns-privacy.html", "title": "DNS隐私安全问题总结", "desc": "我在DNS简介中提到DNS系统有严重的安全和个人隐私问题。今天就具体 总结一下有哪些问题。如果你对DNS系统还不了解，请先阅读我前面的介绍文章。", "tags": [], "refs": ["/dns/dns-intro.html"] }
- { "date": "2022-02-12", "path": "/dns/dns-intro.html", "title": "DNS简介", "desc": "我们平时上网使用域名，比如小站的域名是taoshu.in。但计算机在通信的时候需要用到IP地址。但是IP地址不方便记忆和使用，于是人们发名了域名系统，英文是 Domain Name System，简写为DNS。DNS最核心的功能就是保存域名跟IP地址的映射关系。今天就介绍一下DNS的工作原理。", "tags": [], "refs": ["/dns/13-roots.html"] }
- { "date": "2022-01-22", "path": "/career/mentor-engineers.html", "title": "怎样辅导工程师", "desc": "最近读到 David Golden 写的文章，讲如何辅导工程师。说起了我也当了好几年的资深开发，自己也带十多人的技术团队，我自认为尽可能地对团队成员给予指导，来帮助他们成长。但是，怎样去指导新手呢？我好像从来没有系统地的思考过这个问题。读完 David 的文章，我受益匪浅。后面我也试着实践一下 David 总结的方法。好内容岂能独转。我通过邮件联系到 David 取得了翻译授权，现译成中文分享给大家。", "tags": [], "refs": [] }
- { "date": "2022-01-16", "path": "/dmit-25.html", "title": "记录开通 25 号端口的经历", "desc": "我的博客使用虚拟主机搭建。既然有了公网服务器，为什么不自己搭建电子邮箱服务呢？ 测试了一下发现，我的服务商居然封锁了 25 号端口。于是给商家提工单。经过漫长的 沟通，最终商家开通了 25 号端口的入站访问。在沟通的过程中，商家披露了一些电子邮件 相关的细节，也挺有意思。整个沟通过程也给予我不少启发。今天就把过程记录下来， 分享给大家。", "tags": [], "refs": ["/email-ports.html", "/open-source-contributing.html"] }
- { "date": "2022-01-12", "path": "/open-source-contributing.html", "title": "如何参与开源项目", "desc": "今天是个值得纪念的日子。经过长达半年的努力，我给pion/dtls项目贡献的会话恢复功能终于被社区合并了。这是我参与的功能最复杂、涉及面最广、沟通最深入、时间跨度最长的一次开源合作。社区最终接受了我的提案和代码，也进而承担了后续的维护工作。今天就说一说这个事情的来龙去脉，以及我对如何参与开源项目的经验和反思。", "tags": [], "refs": ["/vpn-over-tcp.html"] }
- { "date": "2022-01-08", "path": "/email-ports.html", "title": "细说电子邮件的端口号", "desc": "电子邮件（E-mail）是互联网上最古老的协议之一，但至今也是使用为广泛的通信协议。因为历史原因，电子邮件使用了多种不同的协议，每一种协议又使用不同的 TCP 端口，这极大地增加了邮件设置的难度，对不懂计算机专业知识的用户非常不友好。今天就系统地介绍一下相关的协议和端口，争取为新手扫清使用障碍。", "tags": [], "refs": [] }
- { "date": "2021-12-12", "path": "/go/generics/examples.html", "title": "Go语言泛型示例", "desc": "我之前已经介绍过Go语言泛型设计，今天为大家带来一些常用的的泛型代码示例。", "tags": [], "refs": ["/go/generics/design.html", "/go/no-change-lib-in-go-1.18.html"] }
- { "date": "2021-12-11", "path": "/hello-google-adsense.html", "title": "香港 Google AdSense 开户", "desc": "今天是个好日子。在香港朋友 Alex 的大力协助下，今天终于完成了 Google AdSense 帐户 的地址验证工作。现在把相关经验分享给大，希望大家少走弯路。", "tags": [], "refs": [] }
- { "date": "2021-12-05", "path": "/go/generics/design.html", "title": "Go语言泛型设计", "desc": "从18年转向Go语言之后，一直关注其发展。在众多缺失的特性当中，泛型无疑是呼声最高的那个。经过几年的努力，泛型特性终于要跟随 1.18 版本发布了。这是一个里程碑。考虑到Go泛型的原始设计文档比较艰涩，而且结构比较杂乱，今天就把自己的理解整理成文，分享给大家。因为内容很多，我自己对英文设计文档的理解也有限（尤其是类型推导部分），错误再所难免。欢迎各位读者通过留言批评指正。本文是Go泛型系列的第一篇，后续还会发表更多相关内容，也欢迎关注。", "tags": [], "refs": ["/go/generics/syntax.html", "/go/generics/examples.html"] }
- { "date": "2021-11-17", "path": "/vim/vim-go-struct-tag.html", "title": "在 Vim 下快速编辑 Go struct 标签", "desc": "我之前写过一篇文章如何配置 Vim 的 Golang 开发环境。Go 语言跳转补全 用了 NeoVim 内置的 lsp 功能，也就不需要集成额外的插件。但是 lsp 好像不支持给 struct 添加或者修改 tag 的功能。组里的同事就开始折腾，他们找到了一个 lua 实现的 小插件，还介绍给我。我想了一下，这个功能并不复杂，于是在该同学的电脑上现场调试， 最终写了一个仅有十三行的 VimL 函数，完美解决这个问题。今天整理出来分享给大家。", "tags": [], "refs": ["/vim/go-vim.html"] }
- { "date": "2021-11-10", "path": "/vim/go-vim.html", "title": "如何配置 Vim 的 Golang 开发环境", "desc": "今天给大家介绍一下如何配置 Vim 下的 Go 开发环境，功能上不输 GoLand。有兴趣的同学不妨折腾一把。", "tags": [], "refs": ["/vim/why-neovim.html", "/vim/fzf.html", "/vim/rethink-about-fzf.html", "/vim/vim-search-plugin.html", "/vim/mru.html"] }
- { "date": "2021-11-07", "path": "/vim/vim-search-plugin.html", "title": "开发一个简单的 Vim 搜索插件", "desc": "最近准备写一篇面向 Go 开发新手的 vim 入门文章，于是开始整理自己的 vim 配置和插件体系。在整理文件相关的插件时，我发现自己用了 ack 这个插件。ack 本身没有文件搜索能力，它是使用 ack/ag 这一类的工具来搜索文章。插件本身只起到调用命令和显示结果的作用。既然比较简单，为什么不自己实现一个呢？于是便用了今天文章的主角 ag.vim。这是一个仅有 15 行的插件，却实现了 ack 插件的基本功能。我是怎么做到的呢？一起来看看吧。", "tags": [], "refs": ["/vim/todo.html"] }
- { "date": "2021-11-05", "path": "/unix/blog-letter.html", "title": "基于邮箱实现留言功能", "desc": "网站上线已经有几个月了，一直都没有留言功能。有朋友建议说添加留言或者讨论支持。 我最早的想法是留言频很低，我也在个人页面留了邮箱地址。如果真有朋友需要讨论问题， 可以发邮件。但实际的情况是国人很少使用邮件，也不方便。于是我考察了 Disqus 和一众基于 Github 的评论系统，它们共同的缺点是需要注册，而且有一定的隐私风险。 它们的 CDN 在国内也不太稳定，影响用户体验。我希望找一个支持匿名评论的系统。最终 找到了 isso。它使用 sqlite 保存评论数据，支持匿名评论，各方面都很契合我的需求。 但是它用 python 开发，部署起来非常麻烦。最后，我想了一个比较取巧的方案：基于电子 邮箱来实现留言板功能。今天就把思路和方案整理出来，分享给大家。", "tags": [], "refs": [] }
- { "date": "2021-11-01", "path": "/unix/systemd-socket-activation.html", "title": "使用 systemd 监听服务端口", "desc": "这是博客系统开源计划的第一篇文章。网站的 http 服务使用 Go 语言开发。因为要监听标准的 80 和 443 端口，所以 http 服务一直是以 root 账号运行的。但使用 root 运行风险很大。一旦代码有漏洞被坏人攻破，就会被拿到根用户权限，后果不堪设想。所以像 nginx 这样的 http 服务都支持设置工作进程的运行账号。也就是说，主进程使用 root 账号启动，完成端口监听绑定动作，然后再使用非特权账号启动工作进程对外提供服务。我的博客是用 Go 语言开发，实现类似的机制不太容易。于是就选用 systemd 提供的 socket activation 功能来实现。本来 http 服务也需要使用 systemd 管理，所以就不需要再自己实现端口监听功能了。今天把相关的知识整理出来，分享给大家。", "tags": [], "refs": [] }
- { "date": "2021-10-27", "path": "/http-get-body.html", "title": "HTTP GET 请求能不能带 body", "desc": "最近在知乎上聊了一下对 REST 规范的看法，其中涉及到 http 的 get 请求到底能不能带 body 的问题。我的看法招来了刘正网友的批判。知乎的回复最长只能一千字，所以单独写一篇文章来回应。", "tags": [], "refs": [] }
- { "date": "2021-10-15", "path": "/vim/vim-macro-quick-start.html", "title": "Vim宏快速入门", "desc": "Vim提供宏录制功能，可以记录一系列操作然后重复执行。这个功能比较冷门，却也非常强大。只是因为操作复杂，很少有人学，也很少用。但我认为宏的思想很简单，之所以觉得复杂是没有特别好的实战案例来体现它的价值。我今天在修改Sniper框架的时候就遇到了一个比较适合使用宏的场景。现在整理出来分享给学习和使用 Vim 的朋友。除了宏之外，本文还向大家展示了复制粘贴、行内查找、临时插入模式、寄存器等功能综合运用，特别适合初级和中级 Vim 爱好者。", "tags": [], "refs": ["/go/sniper.html", "/vim/macro-find-replace.html"] }
- { "date": "2021-10-13", "path": "/go/no-change-lib-in-go-1.18.html", "title": "Rob 反对修改 Go 1.18 泛型标准库", "desc": "Go 语言之父 Rob Pike 昨天创建 issue，反对在 Go 1.18 的标准库中引入泛型支持。Rob 此举，既在意料之外，又在情理之中。Rob 的依据有两点：一个是改动范围过大，再一个是缺乏实战经验。所以 Rob 建议先不要在 Go 1.18 修改标准库，而是先在 golang.org/x 或者 golang.org/exp 提供相关库的泛型版本。经过一到两个版本的实际使用，才能积累足够的经验，届时再考虑更新标准库。那社区原本要对标准库做哪些有争议改动呢？请容我细细道来。", "tags": [], "refs": ["/go/generics/design.html"] }
- { "date": "2021-10-07", "path": "/go/go-1.18-netip.html", "title": "Go 1.18 将引入新的网络地址包", "desc": "很久之前，我在网上看到一篇 Brad Fitzpatrick 写的文章，名为 netaddr.IP: a new IP address type for Go。Brad 是 Go 语言的核心开发者，同时也是 tailscale 的创始人。Brad 在文章中分析了 Go 语言 net.IP 类型的问题和他们的应对方案以及方案的演变过程。最终 Brad 他们开源了 inet.af/netaddr 这个包。当时我就随便瞄了几眼，留下一点印象。今天收到邮件订阅说是 Go 1.18 接受了 Brad 的提案，准备引入一个新包 net/netip。打开一看，net/netip 就是前面说的 inet.af/netaddr。于是赶紧找出 Brad 的文章仔细读了一遍。读罢不仅明白了原有 net.IP 类型的缺点和新方案的巧妙，还对 Go 语言的内存分配、垃圾回收和 unsafe 包的使用都有更进一步的理解。今天整理成文分享给大家。", "tags": [], "refs": [] }
- { "date": "2021-10-05", "path": "/vim/vim-copy-over-ssh.html", "title": "使用 vim 跨 ssh 复制文本", "desc": "最近有朋友问如何在不用鼠标的前提下把服务器上 vim 的内容复制到本机剪贴板。说实话，我都是直接用鼠标选中再复制的。但因为朋友问起，只好研究一下。还真就找到了不用鼠标的方案。这种方法不但可以把 vim 的内容复制到本机剪切板，还可以复制到本机 tmux 的剪切板，非常方便。今天整理成文，分享给大家。", "tags": [], "refs": ["/colorful-term.html"] }
- { "date": "2021-09-18", "path": "/go/go-http-server-timeout.html", "title": "Go HTTP 服务超时控制", "desc": "系统对外提供 HTTP 服务的时候一定要控制好各个环节的超时时间，不然很容易受到 DDos 攻击。我们部门使用的业务框架是基于 Go 语言的 net/http 标准库二次开发的。在当年写 框架的时候，我对 Go 语言 HTTP 服务器的超时控制理解还不深刻。觉着只要在最外层加上 http.TimeoutHandler 就足够了。系统上线后也一直没有出这方面的问题，还自我感觉良好。 其实是因为我们运维在最外层的 Nginx 设置了各项超时控制，没有把系统的问题暴露出来。 等我们在 AWS 上运行另一套业务系统的时候，因为 AWS 的 ALB 配置跟原来的 Nginx 不同， 发现只用 http.TimeoutHandler 在特殊场景中会产生「死锁」！我当场就阵亡了，赶紧排查。 大致看了一遍 Go 语言 HTTP 服务的源码，找到了死锁的原因。今天就把相关经验分享给大家。 我在看代码的时候还发现 Go HTTP 服务器在读到完整的 HTTP 请求后会再起一个协程，该 协程会试着再读一个字节的内容。非常奇怪🤔，也一并研究了一下，最终找到了相关的 issue 和提交记录，还发现了 Go 语言的一个缺陷，今天也一起分享给大家。", "tags": [], "refs": [] }
- { "date": "2021-09-12", "path": "/go/monkey/monkey-3.html", "title": "Go语言实现猴子补丁【三】", "desc": "这是Go语言实现猴子补丁的最后一篇。在上一篇中，我分享了一种协程隔离的打桩思路。但在文章结尾部分也出了该方案的几个问题。其中最核心的问题是不支持闭包引用，而这个功能在写单元测试又极为常用，所以不得不花时间研究解决方案。期间我还给 Bouke 发邮件看能否接手他的项目继续开发，他也没回复，估计是不愿意。所以，我把自己的工作整理了一下，最终形成go-kiss这个项目。好了，今天就给大家分享一下修复闭包引用问题的过程。", "tags": [], "refs": ["/go/monkey/monkey.html", "/go/monkey/monkey-2.html", "/go/monkey/monkey-2.html"] }
- { "date": "2021-09-08", "path": "/go/sniper.html", "title": "Sniper框架专题", "desc": "Sniper框架上线已经三年有余，今年的主要任务之一是完善项目文档。我会陆续以文章的形式介绍各组件的设计思想和实现原理，最终集结成项目文档。如果读者有兴趣，也欢迎投稿或加入创作。", "tags": [], "refs": ["/go/go-quick-start.html", "/go/go-sniper.html", "/go/go-sniper-two-years.html", "/go/go-sniper-v2.html", "/twirp.html", "/grpc.html", "/pb-encoding.html", "/envoy-mesh-quick-start.html", "/go/control-structures.html", "/go/errors.html", "/go/monkey/monkey.html", "/go/monkey/monkey-2.html", "/go/monkey/monkey-3.html", "/go/create-protoc-plugin.html"] }
- { "date": "2021-09-07", "path": "/go/monkey/monkey-2.html", "title": "Go语言实现猴子补丁【二】", "desc": "我在前文Go语言实现猴子补丁中分析了 monkey patch 的实现原理，并指出这种方案的一个重大缺陷——只能做全局打桩。也就说，如果 patch 某个函数（比如 time.Now），那所有协程的都会受到影响。具体到单元测试领域，go test 的并发测试也是通过协程实现的。如果我们在A测试用例中 patch 了某个函数，B测试用例也会用到而且B跟A并发执行，那B测试用例就会被A测试用例干扰。所以到现在为止，我们内部的测试用例都没法开启并发执行。上文发布后我就一直在研究如何实现分协程打桩。折腾了两个星期，终于跑通了。现在把思路分享给大家。后面的文章假定读者已经熟悉我在前文中介绍的内容。", "tags": [], "refs": ["/go/monkey/monkey.html", "/go/monkey/monkey-3.html"] }
- { "date": "2021-08-28", "path": "/go/monkey/monkey.html", "title": "Go语言实现猴子补丁", "desc": "猴子补丁（Monkey Patch）可以在程序运行期间动态修改函数行为。也有人把这种技术叫「打桩」。这种技术在Python或者Ruby这样的动态语言中比较常见。但这并不意味着静态语言不能实现类似的效果。本文就跟大家分享一种上在Go语言中实现猴子补丁效果的黑科技。核心思想来自Bouke，我原本是想直接翻译的。但是原文一方面有部分内容需要对二进制文件做反汇编处理，而这部分内容其实不是必须的；另一方面，Bouke 采用了间接寻址跳转，也让整个方案变得很费解。如果按照信、达、雅的方式翻译，会带来很多不必要的理解成本。所以我结合自己的理解和实验，优化（简化）了 Bouke 的方案，最终形成本文的方案。下面开始分析实现思路。", "tags": [], "refs": ["/go/monkey/monkey-2.html"] }
- { "date": "2021-08-22", "path": "/go/errors.html", "title": "Go语言的错误处理", "desc": "本文是Go语言错误处理系列文章的第二篇。我在上文指出错误处理的本质是流程控制，并系统地介绍了Go语言的流程控制语句。今天就来介绍一下Go语言内置的错误处理机制。其实Go语言压根就没有内容什么错误处理机制，而是把错误处理的包袱甩给了程序员。请容我细细道来。", "tags": [], "refs": ["/go/control-structures.html", "/utf-8.html"] }
- { "date": "2021-08-21", "path": "/go/control-structures.html", "title": "Go语言流程控制语句新解", "desc": "最近准备总结一下如何在 Go 语言中处理错误。错误处理的本质还是流程控制。所以今天先介绍一下 Go 语言的流程控制语句。有人可能会说流程控制不就是判断、循环，都很简单，有什么可说的。我写这篇文章的主要观点是主讲 if 和 goto，其他像 switch 和 for 不过是前两者的变种或者结合，是语法糖，而panic/recover 则可以看成是跨越函数的 goto。通过这种方式，我们可以系统地理解 Go 语言的流程控制机制，为后面的错误处理打下基础。", "tags": [], "refs": ["/go/go-map-concurrent-misue.html", "/go/errors.html", "/go/go-concurrency.html"] }
- { "date": "2021-08-15", "path": "/hello-wechat-offical-account.html", "title": "微信公众号的正确使用方式", "desc": "个人博客最不好解决的问题就是用户触达。某一篇文章可能写得不错，也可能在社交平台大范围传播，但用户看完就走了，没法订阅作者后续的内容更新。但另一方面，开放互联网苦微信久矣。微信是流量黑洞，许进不许出。作为个人内容创作者，多少有点互联网情怀，不愿意为其贡献内容。我很早就注册了公众号，但一直没有使用。如果把它当成一个内容创作平台，当然会有很多限制。比如不能在文章内容随意插外链，文章必须指定封面图等。但如果把它当成一个消息推送通道，也就是 RSS 的替代品，那公众号确实是不错的工具。今天就来说一下如何用 RSS 的方式运营公众号。", "tags": [], "refs": ["/go/text-image.html", "/jq-quick-start.html", "/go/text-image.html"] }
- { "date": "2021-08-15", "path": "/go/text-image.html", "title": "Go语言实现文本转图片", "desc": "最近需要将大段的文本转换成图片。查了一下网上的资料，找到了hqbobo/text2pic这个项目，发现它在渲染仿宋字体的时候会给每个字加一个方框。于是简单研究了一下如何在 Go 语言中渲染文字。最终开发了txtimg小工具。今天就把相关经验总结一下分享给大家。", "tags": [], "refs": [] }
- { "date": "2021-08-12", "path": "/vim/phpcd-six-years.html", "title": "PHPCD六周年随想", "desc": "PHPCD是我维护的一个 vim 插件，自从在 2018 年转 Go 语言开发后，就不怎么开发了。如果有严重的 BUG 可能会修一下。这两天有人提新的 issue。我看了一下问题列表，好多 issue 都是 18/19 年提的。大多数问题也不复杂，完全可以在当时就说明白，可我什么也没说。作为项目的发起人，我有一种淡淡的伤感和羞愧，就仿佛是自己遗弃了自己的孩子。怀着这样的一种歉意，我仔细看了这些遗留的问题并逐一调试和反馈。其实，PHPCD这个插件是在 2015 年构思并实现的，到现在已经六年了。今年我就跟大家讲一下它的故事。", "tags": [], "refs": [] }
- { "date": "2021-08-07", "path": "/about.html", "title": "关于涛叔", "desc": "大家好，我是涛叔🧔🏻。欢迎来到我的个人网站。我会不定期发布自己的工作经验和学习 心得。本站是我的学习笔记，也用作自己的知识库，内容涵盖 Linux/Unix、网络、编程、 Web、Vim、职业生涯等各个方面，偶尔也会写点人生感悟。欢迎阅读和订阅。", "tags": [], "refs": ["/choose-domain.html", "/unix/blog-internal.html", "/hello-google-adsense.html"] }
- { "date": "2021-08-05", "path": "/go/create-protoc-plugin.html", "title": "如何开发 protoc 插件", "desc": "Protocol Buffers 是谷歌推出的编码标准，它在传输效率和编解码性能上都要优于 JSON。但其代价则是需要依赖中间描述语言（IDL）来定义数据（和服务）的结构（通过 *.proto 文件），并且需要一整套的工具链（protoc 及其插件）来生成对应的序列化和反序列化代码。除了谷歌官方提供的工具和插件（比如生成 go 代码的 protoc-gen-go）外，我们还可以开发或定制自己的插件，根据业务需要按照 proto 文件的定义生成代码或者文档。由 IDL 生成代码或者文档是元编程的一种形式，可以极大的解放程序员的生产力，建议每一位开发者都能掌握这一技能。今天就生成 markdown 文档为例讲解如何开发 protoc 插件。", "tags": [], "refs": ["/pb-encoding.html", "/grpc.html", "/go/go-sniper-v2.html"] }
- { "date": "2021-08-01", "path": "/go/go-sniper-v2.html", "title": "下一代 Sniper 框架", "desc": "两年前我写了Sniper 轻量级 go 业务框架的思考，一年前写了Sniper 框架两周年回顾。又一年过去了，我对 Sniper 框架有了新的认识。今年考虑发布一个新的版本，现在介绍一下未来可能引入的改动。", "tags": [], "refs": ["/go/go-sniper.html", "/go/go-sniper-two-years.html"] }
- { "date": "2021-07-26", "path": "/free-tls.html", "title": "付费HTTPS证书是智商税吗", "desc": "早期互联网协议都是明文，网络随便一个中间节点都可以监听甚至篡改链路上所有的通信内容。坏人越来越多，通信加密也就刻不容缓。后来人们设计了TLS（之前叫SSL）加密通信，而加密过的HTTP协议又叫HTTPS。TLS系统使用非对称算法交换加密用的密钥。非对称算法有公钥和私钥之分。如果我们从网上得到张三的公钥，怎么才能确认这个公钥一定是张三的呢？这就需要公钥基础设施（Public Key Infrastructure），简称PKI。简单说就是找人做公证。给张三做公证的人或者地方叫CA，它们出具的证明就是证书。公证自然是要付费的。现在出现了一些不收费的CA。大家都在纠结免费的证书到底能不能用。今天就谈谈我的思考。", "tags": [], "refs": ["/ssl-ev-is-dead.html"] }
- { "date": "2021-07-20", "path": "/http3-port.html", "title": "解决家庭宽带443端口封禁问题", "desc": "现在的运营商会主动封禁 80 和 443 端口，防止用户使用家庭宽带对外提供 WEB 服务。我们理论上可以使用非标准端口（比如 8080 或者 4430）。但不管你用哪个，只要哪天运营商注意到你，加一条规则就可以让你所有的链接都失效（URL 中有端口号）。这才是端口封禁最大威力。所以大家没法用家庭宽带建站，哪怕是一个简单的博客也不行。就我观察，这个事情有了转机。机会就在 HTTP/3 协议。今天就跟大家介绍一下如何利用 HTTP/3 协议最新的标准来突破端口封禁问题。", "tags": [], "refs": ["/dns/dns-svcb-https.html"] }
- { "date": "2021-07-12", "path": "/career/always-be-quitting.html", "title": "时刻准备离职", "desc": "最近读了 Julio Merino 的文章 Always be quitting。文章说时刻准备离职有利于自己的职业发展。咋一看有些匪夷所思，但细细读来却颇有一些道理。今天联系了作者拿到了翻译授权，现在译成中文分享给大家。后面我也准备做一些相关的实践。", "tags": [], "refs": [] }
- { "date": "2021-07-10", "path": "/go/go-map-concurrent-misue.html", "title": "Go 语言 map 的并发安全问题", "desc": "众所周知，Go 语言的 map 不是并发安全的数据结构。如果有多个协程并改读写同一个 map 会报错。说来惭愧，我之前一直以为这个错误可以用 recover 捕获。直到昨天有同学提出这个疑问，还给我发了一段演示代码。今天抽空研究了一下相关的知识，整理出来，分享给大家。", "tags": [], "refs": ["/go/go-sniper.html"] }
- { "date": "2021-07-09", "path": "/svg-favicons.html", "title": "使用 SVG 制作 favicon 网站图标", "desc": "我的个人网站一直使用 SVG 图标🧔🏻。今天偶然发现 Austin Gil 写的一篇文章 OMG, SVG Favicons FTW!，比较系统地整理了使用 SVG 制作 favicon 的玩法。现在 翻译出来分享给大家。以下是原文。", "tags": [], "refs": [] }
- { "date": "2021-07-03", "path": "/vim/vim-auto-im.html", "title": "自动切换 vim 中文输入法", "desc": "在 vim 输入中文需要先切换到 insert 模式，再切换到中文输入法，然后才能开始输入内容。如果需要返回 normal 模式，则又需要将输入法切回英文，不然无法使用 normal 下的快捷键。这是中文 vim 用户最主要的问题。今天就给大家分享一下我个人的解决思路。", "tags": [], "refs": [] }
- { "date": "2021-07-02", "path": "/transfer-big-file.html", "title": "Linux 局域网传输大文件", "desc": "今天需要把一个大文件（约20G）拷贝到路由器的硬盘上。一般大家会想到用 scp 命令。scp 是 ssh copy 的缩写。我试了一下，速度只有 5M 多。要花将近一个小时才能拷完，实在不能忍。于是我就研究了一下怎么才能加快传输速度。", "tags": [], "refs": [] }
- { "date": "2021-06-25", "path": "/git/good-git-log.html", "title": "如何写好 Git 提交记录", "desc": "今天总结一下如何写好 git 提交记录。主要供组内同学参考，也分享给大家。", "tags": [], "refs": [] }
- { "date": "2021-05-30", "path": "/tty.html", "title": "解密 TTY 设备", "desc": "终于找到一篇把 TTY 系统讲透的文章了。联系作者拿到了翻译授权。现在分享给大家。以下是原文。", "tags": [], "refs": [] }
- { "date": "2021-05-29", "path": "/regex-readability.html", "title": "提升正则可读性的六种方法", "desc": "最近读到一篇文章 Writing better Regular Expressions in PHP，讲如何提高正则表达式的可读性。感觉非常好。赶紧联系作者申请授权，现在译成中文分享给大家。如果大家是初学正则，也可以阅读我之前写的正则表达式入门一文。以下是翻译内容。", "tags": [], "refs": ["/hello-regexp.html"] }
- { "date": "2021-05-24", "path": "/go/how-to-use-go-embed.html", "title": "//go:embed 入门", "desc": "Go1.16 引入了//go:embed功能，可以将资源文件内容直接打包到二进制文件，方便部署。最近读到 Carl M. Johnson 写的 How to Use //go:embed 一文，写得相当不错。于是联系作者拿到了授权，现译成中文，分享给大家。", "tags": [], "refs": [] }
- { "date": "2021-05-23", "path": "/iptables-rule-order.html", "title": "关于 iptables 放行端口无效的问题", "desc": "最近想在路由器上运行 VPN 服务，需要对外开放端口。加了规则之后总是不生效。在网上搜了一下，原来是 iptbles 规则优先级导致的问题。今天给大家分析一下。", "tags": [], "refs": [] }
- { "date": "2021-05-21", "path": "/vpn-over-tcp.html", "title": "基于TCP的VPN的效率问题分析", "desc": "最近在研究 AnyConnect 协议和服务实现。AnyConnect 是思科开发的企业虚拟专用网（VPN）系统，其核心卖点之一是基于UDP的DTLS通道。本文就来分析一下为什么要用UDP传输VPN数据。", "tags": [], "refs": [] }
- { "date": "2021-05-04", "path": "/dns/dns-svcb-https.html", "title": "DNS SVCB/HTTPS 记录介绍", "desc": "IETF 正在为 DNS 引入一种新型记录，名为 SVCB/HTTPS。这个 SVCB/HTTPS 记录可以解决一些很重要的问题，我一直关注该标准的推进工作。今天就给大家介绍一下。", "tags": [], "refs": [] }
- { "date": "2021-04-23", "path": "/choose-domain.html", "title": "个人域名选择", "desc": "大家好，我是涛叔。我在知乎的ID也是涛叔，一直在知乎上写作。 翻译Emoji的奥秘的时候，我发现知乎居然连 Emoji 表情都支持不全。 于是我决心要建立自己的网站。本文就说说域名的选择问题。", "tags": [], "refs": ["/emoji.html"] }
- { "date": "2021-04-21", "path": "/go/go-is-better-php.html", "title": "Go 是更好的 PHP", "desc": "很多 PHP 程序员转 Go 是囿于当今大环境。今天我给大家带来一个新的观点——「Go 是更好的 PHP」。", "tags": [], "refs": [] }
- { "date": "2021-04-12", "path": "/emoji.html", "title": "Emoji的奥秘", "desc": "最近读到一篇写 Emoji 的文章，叫 Emoji under the hood，写得非常通透。 我联系了作者 Nikita，拿到了翻译许可。周末翻译了一把，分享给大家。", "tags": [], "refs": [] }
- { "date": "2021-02-24", "path": "/rss-wechat.html", "title": "谈谈微信公众号与RSS的关系", "desc": "RSS不用说，早已不是主流。微信公众号也基本上走过了最颠峰的时期，有点要走下坡路的意思。但无论如何，公众号产生的影响要远远超过RSS。", "tags": [], "refs": ["/choose-domain.html", "/hello-wechat-offical-account.html"] }
- { "date": "2021-02-23", "path": "/rss-soul.html", "title": "RSS 精神", "desc": "最近看到一个叫「蚁阅」的RSS阅读工具。感觉很有特色。试用了一下。今天谈谈自己的感受。", "tags": [], "refs": [] }
- { "date": "2021-01-30", "path": "/go/go-sniper-dao.html", "title": "谈谈 Sniper 框架的数据访问层", "desc": "我们在2018年开发了 go-kiss/sniper 框架，并成功驱动了部门的业务系统，为部门的发展奠定了稳固的技术基础。", "tags": [], "refs": ["/go/go-sniper.html", "/go/go-sniper-two-years.html"] }
- { "date": "2021-01-26", "path": "/docker-network.html", "title": "Docker容器网络解密", "desc": "Docker相信大家都不陌生。但Docker基于的容器技术估计好多人都不怎么了解。最近看了一篇关于容器网络的文章Container networking is simple，感觉写的非常好，通俗易懂。文中的内容我基本都知道，但没能像原作者那样总结整理出来。我照着原文操作了一下，也有了自己的体会。今天就结合原文和自己的实践跟大家讨论一下容器的网络原理。", "tags": [], "refs": [] }
- { "date": "2021-01-25", "path": "/ipv6-world.html", "title": "IPv6乌托邦", "desc": "本文原名叫 The world in which IPv6 was a good design。我一开始以为是说IPv6的。读完了才发现这是一篇讲网络的神文 （甚至都没怎么讲IPv6）。学过网络的人都会知道OSI分层。每一层干什么应该也清楚。但为什么要分这么多层大家有考虑过吗？我一直想研究一下分层的原理，分层到底解决了什么问题。网络技术是怎么一步一步演变成今天这个样子的。没想到 Avery Pennarun 在一篇文章里就写了个明明白白。读罢让人拍案叫绝。", "tags": [], "refs": [] }
- { "date": "2021-01-20", "path": "/vim/vim-edit-fast.html", "title": "Vim 快速编辑", "desc": "我在Vim 快速移动一文中相对系统地总结了 Vim 的移动操作。本文 补上编辑部分。目的只有一个，那就是帮助初学者尽快熟悉 Vim，形成战斗力。", "tags": [], "refs": ["/vim/vim-move-fast.html"] }
- { "date": "2021-01-09", "path": "/vim/vim-move-fast.html", "title": "Vim 快速移动", "desc": "很多人听说 Vim 就想试试。可一看到罗列出来的快捷键，就打退堂鼓🥁。这是人之常情。 错不在于退缩的人，而在于写 Vim 教程的人。", "tags": [], "refs": ["/vim/vim-edit-fast.html"] }
- { "date": "2021-01-06", "path": "/hello-regexp.html", "title": "正则表达式入门", "desc": "正则表达式说白了就是一堆约定俗成的匹配规则。如果从微观入手，你会发现有背不完的规则；如果从宏观入手，你会发现万变不离其宗。所以我们将从宏观到微观依次说起，依次为大家总结三大宏观规则、两大微观规则，再附带一部分高级内容。基本可以通过一篇文章让大家理解正则表达式的主要用法。", "tags": [], "refs": [] }
- { "date": "2020-12-27", "path": "/auto-login-easyconnect-in-docker.html", "title": "一种自动登录EasyConnect的思路", "desc": "我司使用深信服的 VPN 产品 EasyConnect。这个 EasyConnect 在 mac 平台上有诸多问题， 有兴趣的同学可以参考我之前的文章使用 docker 封印 EasyConnect （以下简称前文）。可是用了一段时间我发现，VPN 连接以后，每隔一两分钟就会断一次。 我司的 VPN 又开启了二次验证，即便 EasyConnect 记住了我的帐号和密码，重连的时候 还得输一遍动态口令，极不方便。这个断线重连太影响效率了，那能不能自动输入动态口令呢？ 今天休假回家，没连上两分钟，又断线了。我忍无可忍，下定决心解决这个问题。", "tags": [], "refs": ["/easyconnect-in-docker.html"] }
- { "date": "2020-12-06", "path": "/grpc.html", "title": "理解 gRPC 协议", "desc": "gRPC 是谷歌开源的一套 RPC 协议框架。因为自带大厂光环，gRPC 的使用也是愈加广泛。 那为我们在做技术选型的时候要不要用 gRPC 呢？这个需要考虑正反两个方面。如果 gRPC 好， 它好在哪里？为了这些好，它又牺牲了哪些方面？我们的业务面临什么问题？gRPC 的优点 能否为我所用？gRPC 的缺点会不会给我们带来不便？这都是我们需要静下心来好好思考的。", "tags": [], "refs": ["/pb-encoding.html", "/net/http.html", "/go/go-sniper.html"] }
- { "date": "2020-11-19", "path": "/go/type-embedding-in-go.html", "title": "理解 Go 语言的 Embedding 特性", "desc": "Go 语言没有类，也就没有继承，只能通过 embedding 特性（为行文方便，下面统一称之为嵌入）组合各种功能。", "tags": [], "refs": [] }
- { "date": "2020-11-03", "path": "/go/go-case-study-of-goroutine-leak.html", "title": "记一次 Go 协程泄漏的排查过程", "desc": "昨天例行查看服务监控，发现协程数量异常增加(超过10倍)。经过一翻努力查到了源头，并顺利上线修复(效果见题图)。对于协程，一千个程序员有一千种用法。了解这次泄漏的细节意义并不大。但从发现到修复的整个过程还是有一些参考价值的，所以记录一下，分享给大家。", "tags": [], "refs": [] }
- { "date": "2020-09-26", "path": "/easyconnect-in-docker.html", "title": "使用 Docker 封印 EasyConnect", "desc": "深信服的 EasyConnect 客户端非常垃圾，不但会在开机时以 root 自动启动，还会往系统 安装自己的 CA 根证书。这给使用者带来极大的安全隐患。今天分享一种方案，将其封印 到 Docker 容器里，消除隐患。", "tags": [], "refs": ["/auto-login-easyconnect-in-docker.html"] }
- { "date": "2020-08-23", "path": "/go/generics/syntax.html", "title": "Go语言泛型的进化", "desc": "最近看到 Ian Lance Taylor 在 golang-nuts 论坛发布了 Go 语言泛型的最新改动， 去掉了 type 关键字。为了弄明白他讲的内容，我周末又研究了一下Go语言泛型设计。 今天介绍一下泛型语法的设计演进过程。", "tags": [], "refs": [] }
- { "date": "2020-07-25", "path": "/go/go-sniper-two-years.html", "title": "Sniper 框架两周年回顾", "desc": "一年前我向大家介绍了 sniper 框架。年复一年，sniper不觉已平稳运行两年有余，是时候再次总结一拨该框架的实战经验了。", "tags": [], "refs": ["/go/go-sniper.html", "/twirp.html"] }
- { "date": "2020-07-21", "path": "/net/auto-upnp-port-forward.html", "title": "使用UPnP配置SDN端口转发", "desc": "上海电信的光猫只能通过一款叫网络管家的APP设置。如果想通过公网访问内网的服务， 需要配置虚拟服务器或者DMZ主机。它们在本质上都是端口转发。这些网络管家都支持。 但是，端口转发需要指定局域网机器的IP和端口。局域网设备的IP由光猫自动分配，网络管家上居然不支持给设备绑定IP。如果设备重启，就可能使用新的IP，这样之前配置 的端口转发就失效了。最简单的办法是直接给内网主机设置固定IP。如果你不想折腾， 读到这里就可以结束了。但这种方法可能产生IP冲突。虽然概率很小，但总觉得不优雅。 更好的方案是利用 UPnP 实现自动端口映射。", "tags": [], "refs": [] }
- { "date": "2020-06-07", "path": "/go/goroutine-panic.html", "title": "Go语言的跨协程异常处理", "desc": "Go语言内置协程，这极大降低了并发编程的门槛。但这并不意味着并发编程的难度降低了。多协程的报错协同处理就是难点之一。为此官方提供了 errgroup 包。但这个包无法处理协程 panic 的问题。在 Go 语言中，我们无法在父协程里捕获子协程的异常(panic)。如果服务有异常没有处理，整个进程都会退出。这会产生很多不可预知的问题。今天就给大家分享一种跨协程的的异常处理方案。", "tags": [], "refs": ["/go/go-sniper.html"] }
- { "date": "2020-05-02", "path": "/awk-in-20-minutes.html", "title": "20分钟降服awk", "desc": "awk是unix下的文本处理工具，功能复杂灵活，可以实现诸如cat, grep, cut, head, sed, wc 等所有文本处理命令的功能。相应的，awk的学习成本也是比较高的。我一直是边用边查边学。网是的内容比较零散，总有一种见树不见林的感觉。今天读到了Awk in 20 Minutes一文，让人拍案教绝。此文从宏观角度分析了awk程序的概念、结构和工作方式，用20%篇幅讲清楚了awk的80%的功能，让人不禁感概想见恨晚。现择其要点分享给大家。", "tags": [], "refs": [] }
- { "date": "2019-12-24", "path": "/jq-quick-start.html", "title": "在命令行中优雅地处理JSON", "desc": "当年刚出道的时候，整天使用 grep/sed/awk 处理各种文本日志。那时候的日志都是 一行一条，内容使用特定分割符。使用各种 unix 命令配合管道真可谓得心应手。然而， 到了 9102 年，好多数据都以 json 形式传输和存储。awk 这类工具在 json 面前就有点 力不从心了，毕竟是几十年前开发的工具。那有没有针对 json 专门开发的工具呢？ 答案是肯定的，这就是本文要介绍的 jq 命令。", "tags": [], "refs": ["/colorful-term.html"] }
- { "date": "2019-10-19", "path": "/envoy-mesh-quick-start.html", "title": "使用 Envoy 实现服务网格", "desc": "现在的 service-mesh 系统都比较完(复)备(杂)。如果是新项目，则可以随意选用。但大家很少有机会从零开始构建大型系统，更多的是在已有的系统上迭代。这就要求大家必需在现有系统的基础上设计 service-mesh 接入方案。", "tags": [], "refs": [] }
- { "date": "2019-09-05", "path": "/nginx/src/conf_ctx.html", "title": "Nginx 源码中的四级指针", "desc": "都说C语言的指针难，多级指针更是难上加难受。在 Nginx 源码有一个四级指针，就是少有 的多级指针实例。今天就来分享这个四级指针功能和设计。", "tags": [], "refs": [] }
- { "date": "2019-08-17", "path": "/vim/rethink-about-fzf.html", "title": "关于 fzf.vim 的几点思考", "desc": "最近读到一篇文章叫It’s dangerous to Vim alone! Take Fzf，其主要观点是 fzf.vim 不仅是更好的模糊文件搜索工具。还列举了 fzf.vim 的一系列功能。我觉得这些功能是没有必要的，下面细细分解。", "tags": [], "refs": ["/vim/mru.html", "/vim/fzf.html"] }
- { "date": "2019-08-16", "path": "/git/git-diff3.html", "title": "快速处理 Git 冲突", "desc": "我一直是在终端下使用 git 的。如果合并分支或者执行 rebase 遇到冲突，处理起来比较 麻烦。今天 rebase 开发分支的时候又遇到了一堆冲突，于是就花了点时间研究如何快速 处理 git 冲突。方法很简单，就是将 conflictstyle 配置改成 diff3。下面为大家 细细分解。", "tags": ["git"], "refs": [] }
- { "date": "2019-08-14", "path": "/ssl-ev-is-dead.html", "title": "SSL EV 证书之死", "desc": "今天看到了一则新闻，说是 Chrome 和 Firefox 将隐藏 SSL EV 证书的机构信息。这是一则对SSL证书行业非常利空的消息。", "tags": [], "refs": [] }
- { "date": "2019-08-10", "path": "/vim/why-neovim.html", "title": "我为什么选择 NeoVim", "desc": "前几日读到 Geoff Greer 的一篇讨伐 vim 的文章，名为 Why Neovim is Better than Vim，火药味实足，不过说得也没什么毛病。我应该算是 neovim 比较早的用户。今天就说说自己的使用感受。", "tags": [], "refs": [] }
- { "date": "2019-07-31", "path": "/git/gitolite.html", "title": "轻量级 Git 托管平台 Gitolite 介绍", "desc": "今天给大家介绍一下 gitolite。", "tags": [], "refs": [] }
- { "date": "2019-07-20", "path": "/git/git-tutorial.html", "title": "Git 原理实战", "desc": "我之前写了一篇深入理解 Git，有朋友反馈说太拘泥于细节，缺少具体的例子。今天就从实际操作角度写一下 git 的原理。", "tags": [], "refs": ["/git/git-internal.html"] }
- { "date": "2019-07-14", "path": "/pb-encoding.html", "title": "Protocol Buffers 编码", "desc": "当今云时代 gRPC 大行其道，gRPC 默认的序列化编码 Protocol Buffers 也跟着流行开来。都说 Protocol Buffers 效率很高，那到底高在哪里呢？今天就跟大家讨论一下 Protocol Buffers 的编码规则。", "tags": [], "refs": [] }
- { "date": "2019-07-09", "path": "/git/simple-git-flow.html", "title": "一种实用的 Git 工作流", "desc": "昨天回答了一个关于 Git 分支合并的问题。突然发现我们部门现行的 Git 工作流还是非常简单实用的。现整理出来分享给大家。", "tags": [], "refs": [] }
- { "date": "2019-07-08", "path": "/https-tunnel.html", "title": "HTTP 加密代理", "desc": "HTTP 代理在企业中有着应泛的应用。但 HTTP 代理信令是明文传输的，不但会泄漏 个人隐私，而且会导致网络审查和阻断。本文介绍一种使用 TLS 技术对 HTTP 实行加密 保护的方法。", "tags": [], "refs": [] }
- { "date": "2019-07-04", "path": "/c-utf-8.html", "title": "UTF-8 编解码实现", "desc": "我们在前文UTF-8往事中提到，Ken 和 Rob 用一个晚上就实现了 UTF-8 编解码的算法。代码非常精炼，很值得一读，分享给大家。", "tags": [], "refs": ["/utf-8.html"] }
- { "date": "2019-06-29", "path": "/git/git-internal.html", "title": "深入理解 Git", "desc": "讲 Git 的书有不少，我看过最好的一本书叫Git Internals。里面第二章 Understand Git 讲了一部分 Git 的基础概念，但很快就陷入流俗，开始罗列命令。强记命令不但让学习毫无趣味，还可能在实际使用中误用命令导致各种麻烦。我一直在思考如何才能高效的掌握 Git。现在把这些思考整理成文供大家参考。", "tags": ["git"], "refs": [] }
- { "date": "2019-06-23", "path": "/why-touch-typing.html", "title": "我为什么要求团队成员盲打", "desc": "工作中接触过好多工作多年程序员，都不怎么会盲打。我浏览了社区对盲打的讨论，大致有如下观点：", "tags": [], "refs": [] }
- { "date": "2019-06-22", "path": "/vim/viml-jump-plugin.html", "title": "开发一个 VimL 跳转插件", "desc": "vim 的插件大都使用 viml 开发。vim 插件一般会有一个 autoload 目录，公有的函数需要在这里定义。viml 强制要求函数名要跟所在源文件的路径对应起来。", "tags": [], "refs": [] }
- { "date": "2019-06-22", "path": "/utf-8.html", "title": "UTF-8 往事", "desc": "很久以前，我偶然发现了一个链接，内容是 Rob Pike 在 2003 发的 邮件，讨论的是 UTF-8 编码诞生之初的故事。Rob 和 Ken 是 UTF-8 的共同发明人。 读罢不仅深化了对 UTF-8 编码的理解，更为大师们的智慧所折服。 现在整理成文分享给大家。", "tags": [], "refs": [] }
- { "date": "2019-06-20", "path": "/twirp.html", "title": "Twirp 框架简介", "desc": "Twitch 开源了一套叫 Twrip 的 RPC 框架，还发表了 Twirp: a sweet new RPC framework for Go 加以介绍。我在部门技术选型的时候力排众异，选用了 Twirp。现在新业务已经平稳运行了一年，可以说 Twirp 经受住了实践的检验。但酒香也怕巷子深，Twirp 在 2018 年 1 月开源之后竟没引起多大反响。我觉得有必要为它写点科普性的内容了。", "tags": [], "refs": ["/grpc.html"] }
- { "date": "2019-06-19", "path": "/colorful-term.html", "title": "多彩的终端", "desc": "终端，现在也叫命令行。但在历史上，确实有一种设备叫终端。其中最为著名的，可能就是 vt100 系列了。我们现在能看到的 terminal 软件都是终端设备的模拟器。虽说终端设备已经作古，但终端的通信控制协议依然有效。我们可以在命令行下显示粗体、斜体、下划线字符，也可以显示不同的颜色，甚至还能显示简单的动画，这些功能依然使用几十年前终端设备通信协议。今天就给大家说说这种协议。", "tags": [], "refs": [] }
- { "date": "2019-06-18", "path": "/vim/vimrc.html", "title": "Vim 基础配置快速入门", "desc": "本文从零开始为初学整理一份看得懂、改得动的 vimrc 配置。初学者可以在这个基础上逐步加入需要的配置，最终形成自己的风格。另外，本文已经改为基于 NeoVim 进入配置。关于为什么使用 NeoVim 可以参考我的文章我为什么选择 NeoVim。", "tags": [], "refs": ["/vim/why-neovim.html", "/vim/plug-git.html", "/vim/fzf.html", "/vim/mru.html", "/vim/vim-search-plugin.html", "/hello-regexp.html"] }
- { "date": "2019-06-13", "path": "/go/go-sniper.html", "title": "Sniper 轻量级 Go 业务框架的思考", "desc": "Sniper 是我为部门设计的轻量级 go 业务框架。经过两年的平稳运行，至少在一定程度上印证了我当初的一些见解。现整理出来供大家参考。项目源码可以从 https://github.com/go-kiss/sniper 获取。", "tags": [], "refs": ["/go/go-is-better-php.html", "/grpc.html", "/twirp.html"] }
- { "date": "2019-01-15", "path": "/vim/todo.html", "title": "开发一个 Vim Todo 列表插件", "desc": "今天收到了 lemon0910 同学的评论，甚为喜悦。lemon0910 建议写几篇分析插件实现思路的文章，这跟我个人的想法不谋而合。正所谓授人以鱼不如授人以渔，何乐而不为呢？本篇就通过开发一个 todo 插件来分析任务类插件的实现思路。如果你对开发 vim 插件还不太了解，请先阅读这篇和这篇。", "tags": [], "refs": ["/vim/mru.html", "/vim/fzf.html"] }
- { "date": "2018-10-24", "path": "/vim/fzf.html", "title": "开发一个 Vim 文件路径搜索插件", "desc": "如果要我只能推荐一款 vim 插件，那我选 CtrlP。大约在 2013 年，我最早接触到这个插件（使用别人的 vimrc 配置）。当时还不知道它叫 CtrlP，只知道按一下 Ctrl + P 就可以按照文件路径搜索，而且是模糊匹配的，非常方便。后来我逐行研究当时用的 vimrc 文件，终于知道了 CtrlP 的大名。从此，再也没有离开过它。", "tags": [], "refs": ["/vim/mru.html"] }
- { "date": "2018-10-23", "path": "/vim/mru.html", "title": "开发一个 Vim 最近文件列表插件", "desc": "MRU 是 Most Recently Used 的缩写。使用 MRU 可以很方便地在几个文件之间切换。", "tags": [], "refs": [] }
- { "date": "2018-04-14", "path": "/vim/vim-tutorial.html", "title": "Vim 基础操作快速入门", "desc": "vim 号称辑器之神，与 Emacs, 其他编辑器三分天下。很多人都经历了从入门到放弃的痛苦过程。我在大二的时候首次接触 vim，先后放弃了三次，最终掌握了 vim 的使用技巧。算下来，我也是有超过 5 年经验的 vim 老人了。使用 vim 极大地提升了我的工作效率，我也在研究 vim 的过程中获得了很多的乐趣。我希望 vim 能够帮助到更多的朋友，我也希望有更多的朋友能够体会到我的乐趣。", "tags": [], "refs": [] }
- { "date": "2018-03-20", "path": "/vim/plug-git.html", "title": "使用 Git 管理 Vim 插件", "desc": "vim 有很多管理插件的插件，例如 vundle, vim-plug, dein 等等。我最早使用的是 vundle，后来迁移到了 vim-plug 上，中间也试用过 dein，但没有发现亮点。这些插件的核心功能就是从 GitHub 下载或者更新 插件。而实现这些核心功能并不复杂，git 就是很好的工具。而我自己的配置最终也 迁移到了 Git 上。", "tags": [], "refs": [] }
