把 117KB 配置塞进 64KB 限制:节点镜像元数据的压缩与裁剪实践

最近在给云原生节点镜像打包内置组件时,遇到了一个底层的硬限制:底层基础设施的镜像管理系统对单条元数据属性(Metadata Property)设置了严格的 64KB(65,536 字符)长度上限。 随着组件功能越来越丰富,里面内嵌的 CRD、OpenAPI Schema、网络拓扑模板越来越大,打包出来的配置字符串直接冲到了 117KB+,导致镜像在注册和导入阶段直接报错拦截。 由于下游控制器和模板引擎在节点初始化时必须依赖这些声明式配置,我们不能直接删减核心功能。本文记录我们如何在不破坏任何模板语法、类型校验和向后兼容的前提下,一步步把体积从 117KB 压进 64KB 安全线以内的过程。 问题与约束 1. 为什么配置会超标? 我们需要在节点镜像元数据中预置一整套 Kubernetes 声明式清单: Package 扩展包定义与 CRD:基础控制器与系统组件清单。 Data Values Schema:用于参数校验和默认值填充的 OpenAPI v3 Schema。 渲染模板:带有动态宏(如 #@ 指令)的 YAML/YTT 模板。 这套配置的流转链路如下: 原始 YAML 清单 → 内联处理与预打包 → 压缩并 Base64 编码 → 写入镜像元数据属性 → 节点启动时由控制面解压并渲染 2. 核心限制 硬限制:单个属性字符串长度必须 ≤ 65,536 字符。 零破坏:所有的优化必须是无损的,下游模板引擎的动态渲染、参数校验和默认值行为必须与优化前 100% 一致。 第一步:升级压缩算法(Gzip → Brotli L11) 面对最初 117KB+ 的体积,最直接的想法是换一个压缩率更高的算法。 此前流水线里默认用的是标准的 gzip (Deflate)。我们针对这组结构高度重复的 YAML/JSON 文本,拉了几种主流算法做基准测试: 压缩算法 压缩等级 构建期压缩耗时 运行时解压耗时 最终 Base64 字符数 Gzip 6 / 9 < 10ms < 1ms ~107,000 - 117,000 Zstd 19 ~15ms < 1ms ~72,000 Brotli 11 ~90ms < 2ms 66,109 为什么选择 Brotli L11? 匹配一次构建、多次分发的场景:节点镜像在构建阶段多花几十毫秒做高强度压缩是完全可以接受的,只要运行时解压足够快即可(Brotli 解压耗时小于 2ms)。 文本字典与大窗口优势:Brotli 拥有更大的滑动窗口(最高 16MB),且内置了专门针对 JSON/YAML/XML 等结构化文本的静态字典。在 Kubernetes 这种大量重复 key 和缩进的场景下,Brotli 相比 Gzip 带来了接近 40% 的体积下降。 结果:Brotli L11 把体积从 117KB 压到了 66,109 字符。虽然效果显著,但离 65,536 的上限依然超出了 573 个字符。这说明光靠通用压缩算法已经不够了,必须对文本内容本身动手术。 ...

2026-08-21 · 2 分钟 · 411 字 · 涯余

TC 的一些有趣功能

最近在看 TC 和 eBPF 相关的一些介绍。TC 本身的概念比较复杂,想要理清并不容易。加上 eBPF 的引入就更复杂了。本文用来记录与此相关的一些发现。 clsact clsact 即是用来给 TC + eBPF 是使用的一个 qdisc. qdisc 可以简单理解为使用某种算法(FIFO)的 Queue. clsact 的特点是: 不用排队 主要就是为了 attach ebpf 用的 在 ingress/egress 都提供了 hook lock free ingress 与 egress 的不同 不太确定直接的原因是啥,目前的 qdisc 只在 egress 方向上有。一般的 rate-limiting 都是在 egress 上实现, ingress上的比较困难。一个原因是难以对源头进行限速。 multiple qdisc 多个 qdisc 在 egress 上是很常见的。加了 clasact 之后就更常见了。那么 clsact 和他们什么关系? 这是我从另一个 Topic 摘过来的图。可以看出来, clsact 是在普通的 qdisc 之前的。 direct-action mode 请参考: Understanding tc “direct action” mode for BPF ...

2021-12-18 · 1 分钟 · 155 字 · 涯余

Cilium 的 Host Firewall 功能

Cilium CNI 之前一直看文档,功能大概了解,eBPF也大略看过。但一直没有深入从代码角度去研究。它的 bpf 相关代码做的比较全,而且比较老,跟现在网上的 BCC, CO-RW 还不太一样,而且代码量又大,所以读起来并不容易。这次借着 Host Firewall 功能的探究,深入看下。不可能面面俱到,但尽量多涉及一些。这篇文章在后面可能会继续更新,如果有新的发现的话。 一些基本概念 Endpoint: 可以理解为跟 device 挂钩的概念(host, pod 等等),还有特殊的 health endpoint等等。Endpoint有ID,也有对应的 Identity 和自己的 Policy. Endpoint 是 Node Scope 的概念。 Identity: 跟 label 挂钩。同一个deploy的两个Pod, 有不同的endpoint,但有同样的 identity. K8s Node共用同样的 Identity. Cilium 的 NetworkPolicy 即是针对于 Identity的。这个很好理解,二者主要都是使用 label 来做 filter. 跟 Endpoint 不同, Identity 是 Cluster Scope 的概念(label当然跟node无关)。 KV Store: 用来存储一些映射关系。Labels, Identity, Address, Node等这些元素之间的各种映射关系。Cilium 要实现 NetworkPolicy, 那么就需要这些映射关系。比如 Address -> Identity 等等。 不同的 CNI 在架构方面的差别还是挺大的。比如 KV Store 的需求,有的 CNI 就完全不需要。对 Cilium 来说,当有新的 NetworkPolicy时, agent 来计算 NetworkPolicy,如果发现自己所在的 Node 上有 Endpoint 受影响,那么就需要对此 Endpoint 做一些 Update (Regenerate BPF Programs等等),其他的 Node 则不需要。不同的 agent 则依赖于 KV Store 来互相交换一些数据。 引用一篇架构图所示: ...

2021-12-10 · 5 分钟 · 1047 字 · 涯余

eBPF Loop

eBPF Loop 是个老大难问题,只要实际开发大概率都会遇到。Kernel 版本越高,loop 的支持也越多。最近在 LWN 上又看到一篇关于 bpf loop 的新实现,如果能合并进去,那么就有三种 loop 得方式了: bounded loop: 跟传统编程模式很像,但loop的count必须是已知的 map iterator: 针对 bpf map 的迭代器,可以用来遍历 map bpf_loop 函数: 最新的提议,能让 verifier 处理的更快 目前的 verifier 的策略是比较激进的,因为它没法验证所有的场景,所以有可能会对一些实际正确的程序验证不通过。bpf_loop 主要就是从这个角度来考虑问题,相当于对最开始的 bounded loop 做的一个优化。下面将详细介绍。 Bounded Loop Kernel 5.3 之前, eBPF 不支持 loop. 要想实现类似功能,而且前提是明确 loop 的次数,那么基本上只能靠 unroll 来实现。示例如下: #pragma clang loop unroll(full) for (i = 0; i < 4; i++) { /* Do stuff ... */ } 相当于把 loop 展开。5.3 及之后,就不用这样了。 MAP Iterator Bounded Loop 只能解决一部分的问题。总会有 unbounded loop 的场景。5.13 引入了 map iterator, 支持对 bpf map做遍历。考虑到 bpf map 在 eBPF 程序中的普遍性,算是一个比较好的解决方案了. ...

2021-12-03 · 1 分钟 · 134 字 · 涯余

eBPF Timeline

bcc 的文档已经做了很多详细的介绍,关于 kernel 中 eBPF 的变化: https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md. 本文记录的是我在测试过程中发现的几个比较重要的节点。 5.2 这块没找到详细描述,是根据程序实测出来的。在运行包含 map 的 eBPF 程序时,有如下错误: failed to load objects: field Ingress: program _ingress: map .rodata: map create: read- and write-only maps not supported (requires >= v5.2) 错误信息提示的比较清楚,5.2内核及以上的就可以。不太确定为啥用上了 read/write only的 maps, 猜测是 libbpf 内部的实现机制导致的。因为这个问题,我目前将我测试的 eBPF 程序支撑的最低内核设定为 5.2 了。这点挺可惜的,因为 4.x 的内核对 eBPF 的很多支持已经很完善了, ubuntu 18.04 就是 4.18 的内核。 5.8 kernel 默认带了 BTF 文件。这个 5.8 是不准确的,但确实没查到具体在哪个 kernel 版本默认带的。ubuntu 20.04 没有, 20.10 有了,暂且将其作为开始版本。kernel 对 BTF 的支持从 4.x 就有了,只不过最开始不是默认带的,如果需要,需要自己 打开开关并重新编译内核。如果考虑生产环境,这个其实挺麻烦的。如果设定为只支持自带 BTF 文件的版本,那就省事多了。 ...

2021-09-21 · 1 分钟 · 120 字 · 涯余

eBPF, iptables, nftables

把它们三个列在一起,是因为确实有错综复杂的关系。我也想理一理他们在网络方面的前景,比较侧重于 K8s 和 CNI 方面. iptables: kube-proxy最开始基于 iptables 来实现了 service 的负载均衡,只支持随机算法。因为 iptables 本身的局限性,目前提供了 ipvs 模式. nftables: 可以说是 iptables 的替代品,更好的API及性能,但是因为 iptables 的遗留使用率太高,暂时难以看到全面替代的可能。 eBPF: 影响越来越多,有可能会在很多场景下替代 iptables 这是简要的说明。下面将根据不同侧面来详细分析一下。 iptables 两个主要问题: 规则数量线性增长 更新规则需要整个替换掉,无法保证原子性。更新规则的效率也很低。 ipset ipset 是 iptables 的一个扩展,主要解决的问题是 iptables 的线性增长问题。考虑到 iptables 的现实中的普及性以及大家求稳的心态, ipset 在很多场景下仍是一个不错的选择。其基本思想是用 hash 等数据结构来替代原来的线性匹配,这种设计我们在很多地方都可以见到,比如 ovs, eBPF等等。 nftables 作为 iptables 的继任者,很多设计已经了 ipset 里的设计。二者有一定的相似性。 ipvs 没找到太好的深入介绍的资料,但根据网上找的测试数据,比 iptables 好很多. Service 数量少时不明显,数量很大时差距就出来了。 kube-proxy eBPF, iptables, nftables,ipvs, ipset这些东西,最终在 k8s 上的落脚点都是 kube-proxy. kube-proxy实现了各种 Service 的功能,底层用什么,是可能会变的,从最开始的 iptables 到现在的 ipvs, 以后真用其他的东西也说不准. 除了 iptables 和 ipvs, 我们也能找到其他方面的尝试: ...

2021-09-20 · 1 分钟 · 175 字 · 涯余

CNI Notes: IPAM

CNI相关笔记,本文主要记录下 IPAM 功能相关。 CNI的职责包括给 Container 分配网络设备,并且分配IP. IPAM(IP Address Management)就是指IP分配相关的功能和规范。 CNI本身只是一个规范,不同的实现在 IPAM 方面的功能集也不尽相同。下面将首先尝试说明下不同 CNI在这块的实现。 CNI实现 Calico calico开发的IPAM组件叫 calico-ipam, 它使用 calico 的 IP pool resource来管理 IP. 它的主要优势就是可以将 pool 分割成小的 block, 并且可以动态对分配给不同的 node. node 上的 ip 利用率会更高,并且可扩展性也很好。calico 目前也支持给不同的 namespace分配不同的 ip pool. 从大的方面来看,其实最终 各个CNI 实现要解决的问题都是类似的,一般区别只是 dataplane 以及具体的 CRD的设计不太一样。所以这些功能很可能(已经有了或者以后会有)出现在其他的 CNI 上面。 下面看一个实际的例子: apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: external-pool spec: cidr: 172.16.0.0/26 blockSize: 29 ipipMode: Always natOutgoing: true --- apiVersion: projectcalico.org/v3 kind: IPPool metadata: name: internal-pool spec: cidr: 192.169.0.0/24 blockSize: 29 ipipMode: Always natOutgoing: true 这里面可以创建两个 IPPool, 并且可以通过给 NS 打 label 的方式,分配给不同的 NS. ...

2021-09-17 · 1 分钟 · 187 字 · 涯余

Rebuild Kernel 生成 BTF 文件

在测试 golang + libbpf + ebpf 的时候发现一个问题,load ebpf object时,报错说找不到 BTF 文件。这个很奇怪,我已经 include 了 vmlinux.h, 按说不应该再依赖于 BTF 文件。看了下 libbpf 的代码,大概出错的地方在: /* * Probe few well-known locations for vmlinux kernel image and try to load BTF * data out of it to use for target BTF. */ struct btf *btf__load_vmlinux_btf(void) { struct { const char *path_fmt; bool raw_btf; } locations[] = { /* try canonical vmlinux BTF through sysfs first */ { "/sys/kernel/btf/vmlinux", true /* raw BTF */ }, /* fall back to trying to find vmlinux ELF on disk otherwise */ { "/boot/vmlinux-%1$s" }, { "/lib/modules/%1$s/vmlinux-%1$s" }, { "/lib/modules/%1$s/build/vmlinux" }, { "/usr/lib/modules/%1$s/kernel/vmlinux" }, { "/usr/lib/debug/boot/vmlinux-%1$s" }, { "/usr/lib/debug/boot/vmlinux-%1$s.debug" }, { "/usr/lib/debug/lib/modules/%1$s/vmlinux" }, }; char path[PATH_MAX + 1]; struct utsname buf; struct btf *btf; int i, err; uname(&buf); for (i = 0; i < ARRAY_SIZE(locations); i++) { snprintf(path, PATH_MAX, locations[i].path_fmt, buf.release); if (access(path, R_OK)) continue; if (locations[i].raw_btf) btf = btf__parse_raw(path); else btf = btf__parse_elf(path, NULL); err = libbpf_get_error(btf); pr_debug("loading kernel BTF '%s': %d\n", path, err); if (err) continue; return btf; } pr_warn("failed to find valid kernel BTF\n"); return libbpf_err_ptr(-ESRCH); } 猜测是用到了 CO-RE 相关的功能,走到了这块逻辑。奇怪的是我已经通过 ddeb repo安装了相关的 vmlinux 文件。但还是不行。这个后续还是要查查,比较快的解决方案就是 rebuild kernel 带上 BTF 先跑通。 ...

2021-09-01 · 2 分钟 · 289 字 · 涯余

如何生成 vmlinux.h

eBPF 的一些开发方案里涉及到 vmlinux.h, 它包含了kernel里所有用到的data structure等等。有了它可以方便地替代其他一大堆杂七杂八的头文件。网上基本上都提到了如何用 vmlinux 来生成 vmlinux.h, 但最开始的 vmlinux 怎么来的并没有找到比较明确的文档。本来将提供一个比较全面的如何生成 vmlinux.h 的文档. 环境说明 OS: ubuntu 20.04.1 Kernel: 5.4.0-70 安装 vmlinux 需要添加 ddeb 的源 echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list echo -e "deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse\ndeb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ddebs.list repo key相关的 sudo apt install ubuntu-dbgsym-keyring 安装 apt-get update sudo apt-get install linux-image-$(uname -r)-dbgsym 最后安装好的文件在 /usr/lib/debug/boot/ 目录下。 ...

2021-08-25 · 1 分钟 · 126 字 · 涯余

卸载UOS

用了大半年,最近卸载了。整体来说,不如最开始的期待。优点仍在,但缺点对于重度使用者来说,难以忍受。 将常见应用做到开箱即用,这是个很大的贡献,也是目前所有 Linux 发行版里做的最好的一个,这点不可抹杀。但正如其他的一些本地化分支一样,稳定性都相比于 Debian/Ubuntu 大幅度降低了。这点在长期使用中会逐渐暴露出来,并且抵消掉前者带来的优势。如果UOS想继续好好做下去,后面的着力点也应该是这个。 就我的体验来说,问题主要是以下几个: 电脑发热明显,最开始是非夏天用的感觉还不明显,到了夏天非常严重。而换了ubuntu之后好转。 移除了添加repo的一些命令,让开发者很难受 界面稳定性差,基本上难以做到经常不关机 这里面部分是 Deepin 的问题,部分是 UOS 的问题了。其他的国内的发行版总体来说都是更差。 从产品角度来说,用户想要什么以及该怎么做其实是很明显的。既然有这个人力物力了,就应该定好优先级,把这些核心的问题解决好,这样才能普及开来。 另外从美观程度上来讲,不算差,但也不算太好。很多方面也不如 Ubuntu 的 Unity. 如何用统一美观的设计语言去做出一个美观好用的界面,应该就是下一阶段的重点了。

2021-05-25 · 1 分钟 · 21 字 · 涯余

Kubernetes 的 Server-Side Apply

从 Kubernetes 1.18 开始,可以看到一个明显的变化就是资源的 YAML 在 metadata 部分多了很多信息: apiVersion: v1 kind: Namespace metadata: creationTimestamp: "2021-02-18T03:03:40Z" managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:status: f:phase: {} manager: kube-apiserver operation: Update time: "2021-02-18T03:03:40Z" name: default resourceVersion: "199" uid: 2f4536b5-7302-4dc1-9620-052a567c917c spec: finalizers: - kubernetes status: phase: Active 比如这个 NS 的例子, 其中大部分都是 mangedFields部分.简单来讲,managedFields 字段是用来声明一个资源的各个字段的具体的管理者是谁.我们可以参考一个有点类似的案例, Node 的 conditions 字段: conditions: - lastHeartbeatTime: "2021-02-18T03:05:25Z" lastTransitionTime: "2021-02-18T03:05:25Z" message: Flannel is running on this node reason: FlannelIsUp status: "False" type: NetworkUnavailable - lastHeartbeatTime: "2021-02-18T07:34:05Z" lastTransitionTime: "2021-02-18T03:03:36Z" message: kubelet has sufficient memory available reason: KubeletHasSufficientMemory status: "False" type: MemoryPressure Contaions 是一个列表,不同的 Component 负责上报自己所观察到的信息,最终汇总到 Node 的 Status 下面.然后再合并汇总出一个整体的 Ready 状态. mangedFieds 也是有点类似的思路, 一个资源的不同部分是由不同组件/人负责维护的,显式的声明这种维护关系有助于各方协同地维护一个资源完整的状态.更直观的,我们可以看一个 Deployment 的例子. 首先,我们使用如下的 deployment 文件来创建 ...

2021-02-18 · 5 分钟 · 895 字 · 涯余

LB系列1 - Bandaid

很多大厂的LB都是自研的,而且设计考量都不相同。本文介绍 Dropbox自研的LB,名字叫 Bandaid.参考链接: Meet Bandaid, the Dropbox service proxy. Queue设计 Bandaid 处理 Queue 中的 Request 的思路是后近先出(Last in, First out)。当系统负载不高的时候,Queue中的数据其实不多,那么先进后出或者后进先出其实区别不大。如果负载过高,考虑到先进的排队太久,可能很快就timeout了,还不如优先处理后进来的 request. User Space的 Read Queue Read Requests 会被放到一个用户态的 Queue 中,而不是 kernel 里的 queue中。这样做的原因是, 可以在用户态提前关闭已经被 client close的 request, 而不是等到真正的request经由 kernel, 然后 application 在处理的时候才发现 request 已经 close 了。尤其是 client 一般还带有重试,很容易把 Kernel queue打满。User Space 的 Queue 能够通过处理只把正常的 request 传给后面。 权重以及 Route划分 精细程度可以做到按照 Route 将一个 Service 的重要的 Route 以及不重要的 Route 按权重定向到不同的 Backend上。不同的 Queue 可以有不同的权重,rate limit, 并发度。

2021-01-18 · 1 分钟 · 71 字 · 涯余

算法笔记(1): 链表及其应用

最近准备系统的过一遍算法。一是因为看的很多项目最终多多少少都会涉及到算法,如果提前了解好会理解的更深入。二是之前也没有系统的看过,最近想定一个比较长久的学习计划。看文章的话网上质量参差不齐,有些东西只能这样弄,自己花时间慢慢找,总结,记录。算法这类前两天想看 LevelDB, 然后又得先看 LSM Tree, 然后发现还是得看红黑树。然后在网上找,发现文章一大堆,但写的都不行。我觉得想要了解红黑树的基本上肯定都会有一个疑惑“为啥要两种颜色”,可惜基本上没几篇文章能把这个东西讲清楚,知其然而不知其所以然。所以最终决定第一次在网上买了一个电子课,因为发现能讲的清楚的一篇文章是从这门课里摘录的。 所以后面会尝试更新一个持续的算法系列,笔记性质的。记录一些感兴趣的点。然后才是其他的一些感兴趣的项目和技术。本身作为第一篇,跳过了一些更基础的,直接从链表开始了。 链表本身也比较简单,基本原理都清楚。我觉得从应用层来看更有意思一点。主要就是两个 约瑟夫问题 LRU等算法 约瑟夫问题 阿橋问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。 人们站在一个等待被处决的圈子里。 计数从圆圈中的指定点开始,并沿指定方向围绕圆圈进行。 在跳过指定数量的人之后,处刑下一个人。 对剩下的人重复该过程,从下一个人开始,朝同一方向跳过相同数量的人,直到只剩下一个人,并被释放。 问题即,给定人数、起点、方向和要跳过的数字,选择初始圆圈中的位置以避免被处决。 或者更加数学的问法: 已知 n 个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为 k 的人开始报数,数到 m 的那个人出圈;他的下一个人又从 1 开始报数,数到 m 的那个人又出圈;依此规律重复下去,直到剩余最后一个胜利者。 这个问题可能是与单项循环链表最贴近的一个问题模型了。每个人都是一个节点,然后出局的人可以用操作指针的方法将其从链表中移除即可,最后剩下一个人的时候就是答案。下面是C代码 #include <stdio.h> #include <stdlib.h> /*声明一个链表节点*/ typedef struct node { int number;//数据域,存储编号数值 struct node *next;//指针域,指向下一个节点 }Node; /*创建链表节点的函数*/ Node* CreatNode(int x) { Node *p; p = (Node*)malloc(sizeof(Node)); p->number = x;//将链表节点的数据域赋值为编号 p->next = NULL; return p; } /*创建环形链表,存放整数1到n*/ Node* CreatJoseph(int n) { Node *head,*p,*q; int i; for(i = 1; i <= n; i++) { p = CreatNode(i);//创建链表节点,并完成赋值 if(i == 1)//如果是头结点 head = p; else//不是头结点,则指向下一个节点 q->next = p; q = p; } q->next = head;//末尾节点指向头结点,构成循环链表 return head; } /*模拟运行约瑟夫环,每数到一个数,将它从环形链表中删除,并打印出来*/ void RunJoseph(int n,int m) { Node *p,*q; p = CreatJoseph(n);//创建循环链表形式的约瑟夫环 int i; while(p->next != p)//循环条件,当前链表数目大于1 { for(i = 1; i < m-1; i++)//开始计数 { p = p->next; } //第m个人出圈 q = p->next; p->next = q->next; p = p->next; printf("%d--",q->number);//输出出圈的序号 free(q); } printf("n最后剩下的数为:%dn",p->number); } int main() { int n,m; scanf("%d %d",&n,&m); RunJoseph(n,m); return 0; } LRU算法 不考虑各种优化算法的话,LRU场景本身也是很适合用来链表实现。需求描述如下: ...

2020-12-03 · 2 分钟 · 348 字 · 涯余

io_uring 介绍

这几年内核带来了很多革命性的新特性。一个是 ebpf, 现在主要被广泛应用于网络处理,性能分析等领域。另一个是 io_uring, 带来了真正的全异步IO。本文将对 io_uring 做简要介绍。 总览 在 io_uring 之前,只有 aio 这个异步框架。为什么要重新弄一套,是因为 aio 自身限制比较多,比如: 只支持 direct_io.而O_DIRECT要求bypass缓存和size对齐等,直接影响了很多场景的使用。而对buffered IO,其表现为同步。 即使满足了所有异步IO的约束,有时候还是可能会被阻塞,例如,等待元数据IO,或者存储设备的请求槽位都正在使用等等。 存在额外的开销,每个IO提交需要拷贝64+8字节,每个IO完成需要拷贝32字节,这在某些场景下影响很可观。在使用完成event的时候需要非常小心,否则容易丢事件。IO总是需要至少2个系统调用(submit + wait-for-completion),在spectre/meltdown开启下性能下降非常严重。 aio本身扩展性也很差,很多基于aio的开发也经常需要用dirty hack的方式来满足自己的需求。Linux 自己对它的评价也不好: So I think this is ridiculously ugly. AIO is a horrible ad-hoc design, with the main excuse being “other, less gifted people, made that design, and we are implementing it for compatibility because database people — who seldom have any shred of taste — actually use it”. ...

2020-11-27 · 4 分钟 · 693 字 · 涯余

KV系列1 - LMDB

注: 本文仅为笔记,不怎么通顺和严谨。 KV系列属于一个大规模场景的必备品,而且通常很多公司会选择自研,一方面是各种不同产品均有局限,而大家的需求都有差别。另一方面社区已经有一些比较好的 building blocks,可以方便地进行组装和修改。最常见的情境是,分布式KV的开发 = 选一个底层kv存储 + 一个分布式协议。比如TiDB,ETCD等等。最近看 cloudflare 的 blog, 也是类似的场景。他们选的是 LMDB + 自研的分布式策略(称不上算法,比较简单),也很好的满足了自己的需求。 LMDB 全称是 Lightning Memory-Mapped Database, 使用内存映射文件,读写性能比较高。 LMDB的一些特性: 支持APPEND模式,提高写操作的性能 支持多进程/线程同时访问。这种场景下读性能可以随着实例数增加而线性提升。 单独写不block读, 读也不 block 写。 不需要 transcation log, 提高了写性能。 实现上来讲,利用内存映射是一大特色。通常的文件读取操作,通过read系统调用,要先把数据从硬盘 copy 到内核,然后再拷贝到用户空间。而 mmap, 不直接进行数据拷贝,而是在缺页中断时进行处理。而且是直接拷贝到用户态,所以会比 read 效率高些。另外,这种内存映射是只读的,也避免了程序错误破坏存储结构。写操作则是通过 write 系统调用完成,由系统来保证数据一致性。其他细节: 使用 B+ tree. LMDB只允许单个写,性能有所降低,但是不再需要WAL日志,以及其他种种并发控制的冲突及代价。 LMDB中,数据的基本操作单元是页,COW也是以页为单位。 如果写操作比较多,那么数据版本也会很多,旧数据会占用大量空间。LMDB会将旧的页插入到一棵B+tree当中,然后等没有事物再用到它之后就可以重复利用。这样省去了定期清理操作,但是无法保证数据可以恢复到任意时刻了。 数据访问可以直接返回内存指针,避免内存拷贝。 COW保证存储结构一直是合法的。系统崩溃不会导致数据库处于一个不一致的状态。最坏的情况只是丢失了一些未提交的数据。根据一些学界的研究,尚没有发现因为使用 LMDB 导致数据损坏的案例。 LMDB中事物的实现思路如下: Atom(A): LMDB中通过txn数据结构和cursor数据结构的控制,通过将脏页列表放入 dirtylist中,当txn进行提交时再一次性统一刷新到磁盘中或者abort时都不提交保证事务要不全成功、要不全失败。对于长事务,若页面spill到磁盘,因为COW技术,这些页面未与整棵B-Tree的rootpage产生关联,因此后续的事务还是不能访问到这些页面,同样保证了事务的原子性。 Consistency(C): 有如上的操作,保证其数据就是一致的,不存在因为多线程同时写数据导致数据产生错误的情况。 Isolation(I):事务隔离通过锁控制(MUTEX),LMDB支持的锁互斥是进程级别/线程级别,支持的隔离方式为锁表支持,读读之间不锁,写等待读完成之后开始,读等待写完成后开始. Duration(D):LMDB中,没有使用WAL、undo/redo log等技术来保证系统崩溃时数据库的可用性,其保证数据持续可用的技术是COW技术和只有一线程写技术。假如LMDB或者系统崩溃时,只有读操作,那么数据本来就没有发生变化,因此数据将不可能遭到破坏。假如崩溃时,有一个线程在进行写操作,则只需要判断最后的页面号与成功提交到数据库中的页面号是否一致,若不一致则说明写操作没有完成,则最后一个事务写失败,数据在最后一个成功的页面前的是正确的,后续的属于崩溃事务的,不能用,这样就保证了数据只要序列化到磁盘则一定可用,要不其就是还没有遵循ACI原则序列化到磁盘 总结来看,LMDB是一个极为优秀的产品。即使作者声称它主要是为了读场景而不是写场景,但实测的结果都不错。BUG少,稳定性强。 Cloudflare 的实践 Cloudflare 需要一个分布式的 KV Storage 来存储用户配置信息,当用户做了改动之后,能很快地分发到所有的数据中心。最开始用的是Kyoto Tycoon datastore, 在使用过程中发现了不少问题,最终切换到了 LMDB. ...

2020-11-26 · 1 分钟 · 197 字 · 涯余

C语言中的 strtok 函数

在看一个项目的代码时,发现了它用到了strtok 这个API, 它的作用是 split 一个字符串。这个功能在各个语言都很常见,而且接口也大同小异,比如 Golang: s := strings.Split("a,b,c", ",") fmt.Println(s) // Output: [a b c] Python: txt = "hello, my name is Peter, I am 26 years old" x = txt.split(", ") print(x) 而 strtok 的接口就比较奇怪了,看一个例子: Live Demo #include <string.h> #include <stdio.h> int main () { char str[80] = "This is - www.tutorialspoint.com - website"; const char s[2] = "-"; char *token; /* get the first token */ token = strtok(str, s); /* walk through other tokens */ while( token != NULL ) { printf( " %s\n", token ); token = strtok(NULL, s); } return(0); } 输出为: ...

2020-11-24 · 2 分钟 · 350 字 · 涯余

OpenKruise 简介

Kubernetes 作为一个 building blocks 来说,已经逐渐趋于稳定。大家对于其功能的增强,已经逐渐开始以 CRD + Controller Pattern 为主。另一方面,仍然有不少尝试是针对像 Deployment/StatefulSet/DaemonSet 这样的原始功能的。OpenKruise项目就是专注于此。原生的三种 Workload 能满足于日常的需求,但并不完善。在 OpenKruise 项目中,我们可以看到,多种 Workload 之间提供的功能是有很多共性的,我们可以提炼出他们的一些共同的功能模块, 比如: 分批升级 灰度更新 原地升级 SideCar管理 PVC管理 Pod 固定名字 … 这些原子功能有些原生的 workload 已经具有,有些没有。OpenKruise项目通过将这些原子功能排列组合,提供了一些功能增强型的 workload.下面将一一介绍。 SidecarSet SidecarSet 定义了一些 sidecar 模板,然后通过 webhook 的方式注入到通过标签匹配的 Pod 中,这和 istio 等的方式比较像。它比较有用的地方在于,假设你希望给很多 Pod 注入一些通用的附加功能,比如 log, metrics或者init等功能,那么通过 SidecarSet 是一种比较好的方式。下面是一个 yaml 示例: # sidecarset.yaml apiVersion: apps.kruise.io/v1alpha1 kind: SidecarSet metadata: name: test-sidecarset spec: selector: matchLabels: app: nginx strategy: rollingUpdate: maxUnavailable: 2 containers: - name: sidecar1 image: centos:6.7 command: ["sleep", "999d"] # do nothing at all volumeMounts: - name: log-volume mountPath: /var/log volumes: # this field will be merged into pod.spec.volumes - name: log-volume emptyDir: {} apiVersion: v1 kind: Pod metadata: labels: app: nginx # matches the SidecarSet's selector name: test-pod spec: containers: - name: app image: nginx:1.15.1 这个示例给 test-pod 注入了一个 sidecar1, 同时也加入了一个 log-volume. 另外,依据对 SidecarSet 不同字段的更新,相应的 Pod 有两种更新方式 ...

2020-11-21 · 4 分钟 · 649 字 · 涯余

eBPF系列2 - XDP

XDP是指 eXpress data path, 基于 ebpf 技术上的 高性能 data path. 其主要的意图是在网络 packet 处理的早期(网卡驱动处, 在数据包到达RX queue之后, 用hook的方式),让用户可以编写 ebpf 程序来进行一些决策。 这个hook的位置早于所有的内存分配时期(sk_buffer),也没有上下文切换,系统调用等开销, 所以对于性能的提升是很明显的。测试数据表明,在普通的硬件上, XDP 可以 dop 大概 2600w packets per cpu. 一个网络包的处理流程大致如下: 网络包到达网卡 从 NIC queue 拷贝到内存(DMA-backed ring buffer)中 网卡驱动使用 NAPI look 触发 soft IRQs per cpu 特定线程处理网络包 分配 socket_buffer(sk_buffer), 作为网络包的基本数据结构 kernel 填充 metadata, clone sk_buffer 并且交给上层网络处理层 IP layer 做校验, netfilter hook 处理 如果 netfilter 未 drop 此包,交给更上层网络层处理 …. 最终数据被 copy 到 userspace (recv,read,poll等网络调用获取) 其中, XDP hook 在数据到达 NIC RX queue 之后即触发(上面第二步之后).而 iptables 等的处理(上面第八步之后)非常靠后, 需要分配大量资源来处理网络包. ...

2020-11-17 · 3 分钟 · 475 字 · 涯余

Linkerd系列1 - Linkerd2-proxy

ServiceMesh这块属于 k8s 的一个大热门,但据我所知,落地的并不是太多。看文章大概理解为 overhead 以及部署都比较重,对一般的企业来讲,要考虑的东西比较多,所以落地难度比较大。有的建议是说,先上 envoy,其他的先搁置,这也是个思路。envoy 轻量级一些,可以先作为一个 proxy 去体验一下 ServiceMesh 架构下的 proxy 设计。然后再逐步看看怎么引入 ServiceMesh. 另一个方面,Istio vs Linkerd 应该还没完全分出胜负,对于企业的选型来说,仍然是个难点。好处是,我们可以去对比二者在各个功能点上的设计思路差异,然后加深理解。在 ServiceMesh 的组件中,毫无疑问 proxy 是核心组件之一。linkderd 的 linkderd2-proxy 也是一个经过深思熟虑并且设计良好的组件,可以作为切入点研究下 linkderd 的整体设计。 语言选型 envoy 是 C++ 写的,而 linkderd2-proxy 是 Rust. 语言的选型不仅对于实现者来说很重要,对于用户来说也是。想一下 marathon 用的 scala 以及它的失败就知道了。为什么选 Rust, 这跟它最开始的目标有关 资源占用低。因为 ServiceMesh 场景下的 proxy, 经常是以 sidecar 方式挂载在业务组件的 Pod 里,所以一定不能有太多的资源占用。 延迟低。因为要转发所有的请求,不能有太多的 overhead. 安全。同上,因为要转发所有的请求,如果 proxy 本身有安全漏洞,那就影响太大了。 因为 linkderd2-proxy 主要的设计思路是简单,只定义为 ServiceMesh 场景,那么上面几个条件几乎都是硬性的,符合上面几个条件的语言基本上就只有 Rust了。因为上面几个条件意味着: 不能有GC. GC会带来 worst case tail latency。Go/Java不符合 不能太重了。JVM再经过调优也不符合。linkderd2-proxy第一版就是用 Scala 写的,被抛弃了。 有内存安全保障。C/C++不符合。历史经验表明,很多组件一大半的安全漏洞都是跟内存安全有关的。 自动协议检测及 mTLS 这点跟 envoy 的差别也比较大。envoy 的配置文件上来一看还是挺吓人的,虽然可维护性更好。而linkderd2-proxy的设计思路是尽可能的少配置,基本上能做到插入即用,不需要额外配置什么。 ...

2020-11-17 · 1 分钟 · 199 字 · 涯余

etcd 的 clock diff 问题

最近碰到一个客户问题,最初始的现象是一个resource的处理逻辑一直不生效,看 k8s apiserver 的日志如下: 这里面没有 call webhook 相关的日志。主要的错误有两类,一是超时,暂时不清楚为啥。另一类是 object conflict, 这个比较奇怪。因为一般 obejct conflict 不会这么普遍,而且一般 controller 都会对此种情况做特殊处理,会自动恢复。从这个日志中能看出来的问题不多,继续排查一下业务相关的 contrller看看线索: 也是大量的 object conflict,非常异常的场景。而且还有一个 invalid object。到这只能怀疑是不是 etcd 出问题了,因为正常的 controller/apiserver 逻辑不太会触发这么多的此类错误。看 etcd 的日志: 大量的 clock diff 日志,日志意思应该是有一台节点的时间不同步。盲猜可能会导致数据存储的异常,修复时间问题后重启了 apiserver以及相应的 contorller 之后问题修复。看官方提到的 issue 里: Is there any other side effects when the system clock differs on each node, 只说了会影响设置有 TTL 的 object,目前看起来影响远远大于这个。 这个问题的警示是,ETCD的作用太核心了,基于 Kubernetes 的容器平台在监控层面应该尽可能收集多的 etcd 的 metrics 信息,以供管理员决策和提供告警。如果日志不好收集,apiserver 的错误码应该也能提供不少信息。 另一方面,NTP等服务应该作为默认部署的组件,以确保 master 节点在进行调整,维护,扩容或者新增 master 节点的时候,保证时间同步。而不是一次性地校准一次就不管了。

2020-11-17 · 1 分钟 · 68 字 · 涯余

笔记: Postgresql里的事务实现

虽然日常工作需要涉及到数据库的底层部分并不多,但Postgresql作为一个数据库实现的范本是很值得研究的。可以通过它的实现来探索很多通用的数据库设计以及系统设计的理念。本文主要关注于事物设计方面。 MVCC pg底层使用MVCC,修改数据时会直接创建新版本,而不是直接修改旧数据。这部分有一点需要注意的是在pg中,所有的语句都是在事物中执行的,不管是不是明确地用了BEGIN/COMMIT。 Transactions, tuples, and snapshots 先看一下 Transaction 的主要数据结构: typedef struct PGXACT { TransactionId xid; /* id of top-level transaction currently being * executed by this proc, if running and XID * is assigned; else InvalidTransactionId */ TransactionId xmin; /* minimal running XID as it was when we were * starting our xact, excluding LAZY VACUUM: * vacuum must not remove tuples deleted by * xid >= xmin ! */ ... } PGXACT; 事物以xid作为标识.pg针对它做了很多优化,仅在真正开始写数据的时候才分配xid,如果是只读的事物就完全不分配。 xmin表示当这个事物开始的时候仍然处在运行中的事物列表中最小的那个xid ...

2020-10-27 · 4 分钟 · 825 字 · 涯余

关于 OAM 与云原生应用

OAM 是指阿里开源的一个应用模型,主要是云原生场景。年初的时候出来到现在,断断续续地了解过一些,之前一直没有特别想清楚,其实现在也是,但多多少少有一点点感悟,在这里记录一下. 云原生应用的现状 这里主要就是说围绕 kubernetes 生态的应用现状,基本上就是一团糟。没有标准,没有好的开源产品,没有社区来推动这个事情。先看看目前有啥: Helm: 适合作为rpm/apt类似的东西,不适合直接给 end user 用 Application CRD: 社区不活跃,结构定义也不好 Operator: 过于复杂,目前仅有的统一规范是 ocp 弄的 operator framework. 尚未推广开来 其他林林总总的各种 ad-hoc 方案 这就导致云厂商要么是完全自己做一套新的应用模型,要么是用上面这一类现成的,但功能受限,扩展不易。且用户需要有一定的前置知识才能用好。 OAM 的主要优点 目前看来其最主要的优点是角色分离。Kubernetes YAML 的一个主要问题就是,它是一个大而全的 cofig 方案,不宜手写,只适合运维方向。如果想要做成用户友好的模型,OAM提供了一种可能性: 将大的 config 按角色分拆开,开发人员写一部分,运维人员写一部分,各自只关心各自的部分,最终再合并成一个整体的 config. 细节问题比如具体分成几个角色,谁来合并,可以再迭代改进。但这个分离的设计可以认为是走在一个正确的方向上。 其他的几个重要的方面有: 跨平台性。从设计上来讲,一开始设计成跨平台的思路当然是对的。但实际上可能还是90%以上的人主要是用在 k8s 上,所以 spec 设计时比较贴近于 k8s 的设计。其他平台的实现估计会花不少时间,这也是导致目前没有其他平台实现的主要原因 插件系统。OAM 提供的 traits 能力,可以理解为对应用的一个插件系统。之前国内有厂商做过类似的东西,是一个非常好的尝试。以插件形式给应用附加能力,是一种非常自然和方便的用户体验,traits可以用来提供类似的能力。 用户体验 即使做了职责分离,OAM 看起来仍然是非常难以直接 edit 的东西。这也是之前我一直比较纠结的一个点。因为它更像一个面向开发者的 spec, 而不是一个对 end user 的产品。我们如何让用户方便地使用这个功能呢,有下面几种可能: 纯 UI 封装。这里需要 UI 设计的比较好,能够隐藏比较多的细节,不要暴露太多的底层名词给用户,不然用户就要去理解 OAM 是个什么东西了。而我们理想的形式就是让用户不去学习新概念。一个可能的点就是我上面说的,把 traits 叫做 插件等等。这里需要考虑的一个点是API怎么设计,因为一般UI/API都是必须的产品。如果直接把 OAM 用 API 暴露出来也不怎么友好,这就涉及到下面的第二点 我们能否再设计一层 spec, 简单一点的,能提供便捷的API,UI也方便? 这个诱惑力比较大,但其实又回到了远点,因为又回到了老的大的 config 上? 假设我们想做的更简单一些,那势必又无法全面覆盖 OAM 的能力。似乎唯一的可能性就是,我们换了一种 spec 格式,让用户更容易理解,但可以和 OAM 一一对应。 因为之前有厂商放出了一些 OAM UI 的截图,比如: ...

2020-10-22 · 1 分钟 · 132 字 · 涯余

ARM指针的一些安全功能

伴随着 ARM 平台的越来越普及,Linux 内核对其的支持也逐渐丰富。而且 ARM 的设计也有很多独特的优点,本文将介绍在 ARM 平台关于指针操作的一些安全方面的设计。 Pointer Authentication 很多攻击都是通过骇客设计的不安全指针进行的。比如 buffer-overflow 和 return-oriented programming, 都是通过在返回地址放置一个指针来进行的。内核已经针对这个问题做了很多改善,ARM 提供了 pointer authentication 来检测和拒绝这种插入的非法指针。 简单来说,就是在指针上附加一个签名,要想使用一个指针,先得签名验证通过。如果是外部的攻击者,它没法伪造这种签名,那么自己就不能制造指针来进行攻击了。 目前一般指针都是64位,但一般都不是所有bit都是用了。ARM64的系统,如果是三级页表,那么只有低位的40bit用来存放地址,那么其他的bit就可以用来干其他事情。 签名一般是通过以下几个参数确定: pointer 本身 process context 里的一个 secret key, kernel生成 这个肯定是攻击者无法获取到的 current stack pointer。防止签名过的pointer泄露被复用 使用的话,需要先重新计算一下签名,确保二者对的上,然后把签名部署的数据清理掉,这时候就是一个正常的指针了。不然的话,就认为其非法。 Memory Tagging Memory Tagging的做法与Pointer Authentication类似,都是利用指针里未利用的 bits 去存储一些信息。Memory Tagging 是利用 4bit 的信息存储了一个 key, 同时在 pointer 指向的内存地址里也存储了同样的 key. 当对指针进行解引用操作时,需要对比 pointer 里的 key 与实际内存地址里的 key 是否一致。如果不一致将会报错。这个功能有以下用处: 被释放的内存可以修改其 key, 防止被二次引用 每个 stack frame 分配一个 key, 越界访问也会报错 野指针也能检测到 …. 这些 keys 可以用应用层来管理,也可以用 CPU 来随机生成。当用应用层来管理的时候,可以提前检测出内存方便的 bug. ...

2020-10-19 · 1 分钟 · 103 字 · 涯余

Semantic Web

最近开始关注Semantic Web的原因是Abstract Wikipedia项目的发展.作为一个很老的互联网课题,大家的愿景都是很清楚的:想要一个语义更加清晰,更方便机器处理的互联网,然后构建一个真正的互联网.但是市场的行为总是与技术理想有很大偏差,想要用后期的技术来补救,其成功的概率是很低的.Abstract Wikipedia 的让人期待的地方在于,它成功的可能性更高,带来的改善也可能不止局限于Wikipedia. 先说什么是 Abstract Wikipedia, 首先目前维基百科的内容结构大家基本上也都了解:不同语言分别维护,体量差异巨大.从一个程序设计的结构来讲,这种模式是很糟糕的,因为它抽象的程度太低,大量的内容无法复用,造成的 interface 对很多用户不友好: 不同语言内容差别大,同步不及时,维护工作繁重等等.之前的低地苏格兰语被一个美国小哥勤奋地填充了大量的错误内容就是一例. 如何解决? 按程序设计的思路来看,需要分清数据和算法两部分,数据是什么,是语言无关的抽象知识,用抽象符号来表述知识.算法是什么?怎么来组织这些数据,让他们构成合理的语句.最后可能再需要一个Render层,将结果最终呈现给用户.这基本上就是一个MVC结构了. Abstract Wikipedia就是想做这个事情.数据部分,由wikidata维护,大致如下所示的模式: 可以打开这个Hawaii State Public Library System的链接看下实际的数据是什么样子的.这部分的内容跟RDF的结构很像,可以预见的是,以后wikidata的内容越来越多,也能促进Semantic Web本身的发展. 算法部分,由wikilambda维护,这部分暂时还没有现成的实现,但是已经有初步的概要设计.一个完整的示例如下: 假设原始语句为: San Francisco is the cultural, commercial, and financial center of Northern California. It is the fourth-most populous city in California, after Los Angeles, San Diego and San Jose 我们想把这个语句专为通用的结构化内容,那么数据部分如下: (这部分数据完全可以用 RDF 表示出来) 注意上面的 Q62等,就代表着它们目前在wikidata中的链接.有了这些数据,那么我们可以通过一个简单的函数把它们拼接成一句英语: 这是个比较简单的示例,真实的情况肯定会比目前复杂一些.但是理论上都可以通过更丰富的数据和算法来组合出所有目前常用的句式. 对维基百科来说,这是一条看起来很有希望的路.期待的场景是人类的大部分的知识,都能通过这样的方式,以不同的语言展示给读者.当然,也不会局限于此.稍微展望一下,我们就可以发现它带来的变革几乎是无限的.我们能用它来做各种各样的事: 知识图谱.有了维基百科的数据,我们可以更方便地构建各种知识图谱 wikidata和wikilambda的内容可以以各种方式直接给其他系统集成,提供各种编程语言的 binding. 机器学习系统以及学术界可以利用维基百科的内容做各种数据研究 …. 期望这个美好的愿景能实现吧.

2020-10-17 · 1 分钟 · 66 字 · 涯余

Kubernetes Webhook Cert 方案

Kubernetes 里的 Cert 数据是挺零散的一块,而 Webhook 中又强制要求必须是 HTTPS, 在实际使用的过程中尝试了几种方案,发现各有优劣,也都有不少问题.下面将分别详述一下: Cert Manager Cert Manager 可能是最成熟的方案了,使用 Let’s Encrypt 来签发证书.只需要配置好响应的 CR (Issuer, Cert…等), Cert Manager会自动生成证书,比较方便. 缺点也很多,首先是引入了一个比较重的依赖,Cert Manager本身的稳定性也是一个问题.实际用的时候经常发现它的 apiservice Not Ready等问题.假设本来环境中已经有 Cert Manager,那么直接用它是一个比较好的选项,否则并不建议使用. 脚本 + CSR Resource 这种方式是利用 Kubernetes 内置的 CSR 资源来管理证书,最终 caBundle 就可以直接用 Kubernetes 集群自带的 cert 数据,但 Server CRT 和 Server KEY 需要自己用脚本生成.Github 上有现成的项目演示了这种用法: k8s-webhook-cert-manager. 其基本使用方式如下: ./generate_certificate.sh --service ${WEBHOOK_SERVICE_NAME} --webhook ${WEBHOOK_NAME} --secret ${SECRET_NAME} --namespace ${WEBHOOK_NAMESPACE} 我们需要提供SVC的名字和NS以生成cert数据.脚本也可以根據自己需求進行適當裁剪.当数据生成之后,使用的 CSR 资源我们还可以通过集群看到(一段时间后会被GC): [core@bastion ~]$ kubectl get csr NAME AGE REQUESTOR CONDITION csr-w6ldh 5m56s system:node:master3 Approved,Issued 需要注意的细节如下: ...

2020-10-16 · 1 分钟 · 138 字 · 涯余

VSCode 和 Emacs

除了 Emacs 和 Vim 之外的 Editor 战争,现在看起来 VSCode 已经赢了。 先不说现存的一些 Editor 的现状和对比,我们直接猜想一下一个比较完美的 editor 应该是什么样子的 modern look. 就是好看,让人想用. 易上手: 不需要学太多才能才能用好 易扩展: 就是 plugin 多到满足常用需求 跨平台,方便配置 这几点就差不多了,能做到这几点基本上就能做到广受欢迎了。下面一个一个细拆开讲讲. 决定特性 Modern Look 这点最成功的案例就是 JetBrains 的一系列产品,单是其 Darcula theme 就让多少人愿意试一试。Atom 和 VSCode, LightTable, Sublime 做的也都不错, Vim 和 Emacs 做的不行,太古老了。虽然可以配置好的好看,但默认配置太劝退了。 易上手 这点 vim 和 Emacs 还是做的最差,虽然最终效率最高,但学习曲线太漫长了。VSCode 和 Atom 比较像,做的都不错。 易扩展 但从扩展性上来讲,Atom/VSCode/Vim/Emacs都差不多,该有的插件都有,安装也不难。不过前两个明显使用起来更方便些,是真正的易扩展,直接在 UI上就可以点点安装 plugin。也有方便的介绍和使用说明。LightTable 插件太少,半死不活的。Sublime 插件也不算多。 跨平台 跨平台的能力不难做,前面提到的几个都能做到。难的是尽量保证一致性的体验和性能。这里面 VSCode 做的最好, Atom 是在哪个平台性能都不咋样.Emacs在Linux下性能最好,Mac下 UI 效果一般, Windows 下配置着更麻烦。Vim 主要是 Terminal 领域了。 ...

2020-09-27 · 1 分钟 · 108 字 · 涯余

eBPF系列1 - 介绍

一直想写(整理)一些关于 eBPF 的文章。在我眼中,它是 未来技术 之一。 最近零零碎碎看了一些,感觉信息太多,怕是永远也看不完,索性开始一遍看一边记笔记吧。这一篇是介绍。 eBPF 是什么 先介绍下 eBPF 的前身: BPF -> Berkeley Packet Filter. 看名字就知道,主要是用于网络 packet 的过滤用的。 过滤程序是运行于基于寄存器的虚拟机之上. BPF 展示了在内核中运行用户程序的良好开端.但它的缺点也很明显,虚拟机的设计以及指令集都比较落后,跟不上现代处理器的发展(尤其是多核方面),也无法利用现在的 64bit 寄存器. eBPF extend 了 BPF, 将其机制大大地泛化,不止用于网络数据包的处理,而是可以 hook 在任意的系统调用上, 这样它就变成了一个几乎具有无限功能的系统。 在硬件上,它跟现代的处理器的指令集更为贴近,并且能充分利用现在数量众多的 64bit 寄存器.这样就使 JIT 编译器的产生变为了可能,大大提升了性能. 在没有 eBPF 只之前,如果我们想对内核行为进行修改,基本上就是往内核里添加代码或者通过 kernel modules 进行.这二者的门槛都不低,所以影响有限.eBPF 提供了一种新的可能性,通过普通的编程方式,去控制内核的行为,同时保证了高性能.这就是它之所以独特和具有极大潜力的主要原因. 程序校验 当 eBPF 程序被加载进内核之后,要先做一些程序校验工作, 比如: 权限是否具备。 加载 eBPF 的程序是否有权限加载.一般来讲,程序必须得是 root 用户运行的或者有 CAP_BPF 权限才能加载。 如果开启了 unprivileged eBPF, 那么普通程序也可以加载一些功能受限的 eBPF 程序(比如不允许指针操作)。 eBPF 程序是否有可能 crash 在每次指令执行前后都需要校验虚拟机的状态,保证寄存器和栈的状态都是有效的. 校验器不会检查程序的每条路径,它能够知道程序的当前状态是否是已经检查过的程序的子集。由于前面的所有路径都必须是有效的(否则程序会加载失败),当前的路径也必须是有效的,因此允许验证器“修剪”当前分支并跳过其模拟阶段。 不能访问未初始化的变量或者越界访问 程序大小有 limit. 复杂程度有 limit. 检查程序会估算所有的执行路径以评估其复杂程度。 eBPF 是否会无法终止运行(比如 loop forever) 不同类型的 eBPF 程序只能访问特定类型的系统调用 之所以有这么多检查,就是因为它能做的事情太多了,也太有可能造成破坏了,所以在校验阶段就尽量多做一些。 ...

2020-09-22 · 3 分钟 · 534 字 · 涯余

Hugo 的优点

经历了 N 次在各个不同的环境里初始化 Hexo 的环境失败之后,我终于换了 Hugo. 从 Jekyll -> Hexo -> Hugo 我算是发现了,依赖不好处理的平台根本不适合做 static blog generator.想在一个新的环境里部署好一个这样的软件,而且你又没有这个语言的比较多的经验的时候,大量的时间会被浪费在配置环境上。这时候,像 hugo 这样的 static binary 就是一个 killer feature, 我根本不用看它有什么优缺点,就直接切换过来了。 事实上也是,从互联网的内容检索上来看,大部分的迁移路线基本上就是 Jekyll,Hexo -> Hugo, 基本上没有反着来的。 先不说 Golang 语言设计的怎么样,但说能如此方便地 build 出来一个 cross-platform 的 static binary, 简直是造福程序员。 曾经,我以为 Hexo 的 theme 与 content 分离已经很优秀了。没想到它还是个垃圾。

2020-09-12 · 1 分钟 · 46 字 · 涯余

统信 UOS 究竟咋样

统信 UOS 是今年国产化浪潮中比较出风头的一个做国产操作系统的公司。这几天换掉了我的 Manjaro 装上了 UOS 体验了一下。总体来说,可以打七八十分吧。 单说这个分数可能不是很好度量它究竟是个什么水平。可以横向对比一下,分两个方面。一个是底座上,linux 发行版里, Deepin 团队做的很出色,现在基本上是排名第十左右的发行版(按流行程度)。UOS 是基于 Deepin 改的,底座上有了不错的保证。 再跟国内这一众国产操作系统比的话,那简直是碾压。红旗之类的不用说,之前中国软件的麒麟,基于 Ubuntu 来改的( Deepin 的底座也是Ubuntu), 做了这么多年,了解 Linux 的基本上没啥好感,还不如 Ubuntu。因为国产操作系统向来上先往政企方面推,像麒麟这种大概率是拿到之后用户基本上就闲置不用了,继续用 Windows了。UOS 终于让人看到一点希望,至少,看起来让人有想尝试一直用用的念头了。 国产操作系统怎么做的问题,其实就是 Linux 桌面版怎么做的问题,虽然我们也自有国情在此。从操作系统内核来讲, Linux 比 Windows/MAC 并不差,问题全出在软件生态上。主要原因有以下几个: 非商业模式。只要商业公司才能做出像 Adobe / Oracle 这样的软件。有赚钱的目标,合理的分工及 KPI, 再卖出去软件形成一个正向循环。Linux 生态里有点像现在各家的 Andriod, 还更差,基本上连个统一付钱买软件的地方都没有,再说 Linux 用户普遍还没有养成这样的习惯 开发者太容易分散人力开发类似的项目了。大家对项目有了争议,可以立马基于当前版本重新立一个新项目,分开开发。这就导致现在 Linux 已经有了几百个发行版,N 个桌面系统,每一类软件都有 N 个不同的产品。除了个别的项目之外,大部分都不好用。 没有领导,群龙无首。跟上面说的两个问题相关。内核发展的好,是因为有强有力的领导。但桌面系统,基本上就是百家争鸣,谁也不服谁 这些问题杂糅起来,就形成了一个恶性循环。 Linux 桌面不好用 -> 用的人少 -> Linux 桌面发展缓慢,程序员各自魔改 -> 用的人更少。所以导致现在的局面就是,你想要一个方便使用的 Linux 桌面,如果没有一点编程背景,总会有一天栽在一个无法解决的最基本的硬件问题上。蓝牙坏了,网络连不上,显示器连不上,显卡配置不好等等在 MAC/WIN 上很少碰到的问题上。 假设硬件的问题解决了,那么更难的来了: 软件。国内因为更加封闭的互联网生态这个问题更加严重。让用户苹果/Wechat 二选一,大部分人会放弃苹果,Linux 也类似,没有微信这些软件,没有正常用户会喜欢用 Linux。问题摆在这,那么国产操作系统怎么做的答案也就呼之欲出了: ...

2020-09-10 · 1 分钟 · 99 字 · 涯余

Docker 私有 Registry 的镜像 GC 问题

需求 一个轻量级的 docker registry 方案讨论 Harbor 的功能丰富,但是过于重量级了,与其他平台集成并不方便。Portus 类似。这一类产品都比较侧重 UI 和认证,一般都带有数据库, 与K8S 的集成也比较麻烦。最终看起来还是 registry 最合适,不过就是功能太简陋了。需要考虑的东西很多: HTTPS 暴露方式 -> Ingress/NodePort. 最好的方式当然是 Ingress, 但虽然 registry 支持 SubPath, 但docker client 不支持。所以只能退而求其次用 NodePort. 对外地址的暴露只能设计 CR 来做了 GC -> 这块看起来都受限于 registry 本身的能力。它提供了命令行工具能手工清除 blob, 但其他的 metadata 没删完。导致做完 GC 还得重启下 registry. 同时还得保持 GC 时 readonly. 这里面最繁琐的就是 GC, 目前提供的工具非常的原始。harbor 集成了 gc 的功能,应该是在 admin 页面提供了按钮来手动触发。那么在 K8S + registry 的限定范围内应该怎么做,目前想好的流程是: 在 registry 的 deployment 里加一个 container 来处理 GC, 这样的好处是可以共享配置,存储和 PID gc container 和 registry container 共用同样的镜像,但 CMD 不一样 gc container 的 CMD 是一个 while 循环,用来周期性地跑 GC 命令 跑完 GC 之后,如果确实清理了一些 blob, 还需要重启 registry. 不然因为其他一些 metadata 没有清理, 直接 push 和 pull 会出错。 这样就组成了一个低成本的可用的 GC 方案。缺点就是没法方便地将 registry 置为 readonly. 理论上来讲, gc 的周期设置长一点,问题不大. ...

2020-09-07 · 2 分钟 · 252 字 · 涯余

BlueTrace与健康码

疫情之下,各国都在不断地调整自己的防疫政策。中国的模式已经证明了其可能是最有效的方式之一,但因为种种原因大部分国家难以效仿和接受。从技术层面来讲,基于支付宝/微信等国民APP之上的健康码模式也是如此:有效,但是别国难以效仿。新加坡最开始使用BlueTrace协议并开发出了相应App让国民下载使用,部分其他国家也开始效仿,甚至有望能成为一种国际通用的防疫标准。 隐私保护是 BlueTrace 所考虑的最主要特点之一。其手段包括如下方面: 尽可能少的收集用户信息,卫生部门唯一收集的信息是手机号 本地存储用户接触记录,只有当被感染者愿意分享此数据时卫生部分才能获取 第三方无法通过此协议一直追踪用户。因为相关标识ID会一直更换 … 所以在对用户的标识方面,主要是通过算法生成的 UserID 和 TempID 来进行。当用户用手机号进行注册时,后台服务会生成一个 UserID, 而用户在互相接触需要通过蓝牙交换信息时,则通过 TempID 进行。TempID的 大概结构如下: 可以看到其有如下特点: 加密。只有卫生部门才有密钥能解密数据 过期时间。TempID有过期时间,一般在15m左右.这样可以尽量减少恶意攻击或者伪造假数据 TempID的生成需要联网(加密啥的),所以为了考虑到用户不联网的情况,所以TempID可以一次性尽量生成多个,在联网的时候一次性发给用户以备用。 当装有相应APP的用户相遇时,设备会通过 Bluetooth Low Energy (BLE)协议进行通信并交换信息,其数据流程如图所示: 这里面两个 Device 分别有一个角色定位,一个叫 Central ,一个叫 Peripheral。Central 主动扫描 Peripheral 并读取其信息,然后返回一些额外信息。一个手机一般是按一定时间比率轮换在两个角色之间切换。其交换的数据大概如下所示: { "id": "FmFISm9nq3PgpLdxxYpTx5tF3ML3Va1wqqgY9DGDz1utPbw+Iz8tqAdpbxR1 nSvr+ILXPG==", // TempID "md": "iPhone X", // Device model "rc": -60, // Signal strength "o": "IJ_HAI", // Health authority identifier "v": 2 // Protocol version } 这里面需要注意的是: ...

2020-07-23 · 1 分钟 · 80 字 · 涯余

给 Kubernetes Lens AppImage 添加 Desktop Entry

本文记录如何为 Kuberntes Lens 的 Linux 版本的 AppImage 手动创建一个 Desktop Entry 以及生成 Icon 手动创建 Desktop Entry 创建一个 desktop 文件,任意位置均可。假设名字为 lens.desktop: [Desktop Entry] Name=Lens Comment=A full-featured Kubernetes IDE Exec="/home/yayu/Soft/AppImage/Lens-3.3.1.AppImage" %U Terminal=false Type=Application Icon=kube-lens StartupWMClass=Lens X-AppImage-Version=1.4.1.271 Categories=Network; X-AppImage-BuildId=1N3OgzauYTeCvM55WzKgL7MIQe0 X-Desktop-File-Install-Version=0.24 X-AppImage-Comment=Create By HangYan TryExec=/home/yayu/Soft/AppImage/Lens-3.3.1.AppImage 其中 Exec 以及 TryExec 是 AppImage 的位置,应相应更改 生成 ICON 这里我下载了一个黑色的 Kubernetes ICON, 背景透明,webp 格式 这里我已经转为 png 格式,转换命令为: dwebp <origin>.webp -o kube.png 然后,生成相应的 Icon, 并安装到系统中 xdg-icon-resource install --theme Moka --size 48 kube.png kube-lens 安装的 icon 可以在 ~/.icons 目录中看到。 ...

2020-04-29 · 1 分钟 · 125 字 · 涯余

Linux 桌面的失败

之所以说它失败,是指从功利的角度来看。不管是市场占有率,还是软件的可用性,用户友好性,以及生态上,都是失败的。直到2020的今天,以 Ubuntu 为代表的厂商在不断地向竞争对手学习,但仍然不足以和后者相提并论。当然,从自由以及民主的角度来看,它是有成功之处的,开源软件的流行,对于用户隐私以及自由的尊重,这一切的努力都值得我们敬重。 从表面上看,如果我们细数其失败的现象,很容易列出以下几点: 生态太差,没有多少厂商愿意开发 Linux 平台的软件。而进一步的原因则是用户基数少,以及开源社区天然对商业软件的不支持态度 用户不友好。偏技术化,普通用户难以方便地使用。 造成这种现状的原因很多,不一而足。其中人和制度自然是重点因素。 从 Linux 桌面的相关开发人员来看,有相当一部分人是满足于现状,并且不愿意 Linux 桌面朝着 Mac OSX 和 Windows 的方面演进,因为它是 Linux, 是自由软件的核心,似乎不应该走那样的路。这一部分人满足于目前 Linux 的生态,并沉迷于从技术角度去使用和解读 Linux,同时和普通用户区分开来。另一部分人,在努力地丰富 Linux 应用的生态,但却走入了一个怪圈。到今天,我们有几百个发行版,几十个桌面系统,几十个文件管理器,Terminal应用,音乐播放器,数种不同的 GUI SDK 框架。他们共享了一个 Kernel, 使用起来功能也大同小异,一样的不如商业软件好用。但所有人都似乎相信自己所选择的框架/方向是对的,至少相对于其他选择是有一定优势的。假设这些精力能集中起来,会不会更好? 没有假设,因为没有这个可能。与当世的民主制度一样,开源软件的开发流程大体上遵循了同样的模式,也遵循了同样的制度性缺陷。它防止了最大的恶(隐私,对自由的侵犯),但却受制于其极低的效率。作为一个开发人员,你发现了一个应用的 BUG,或者有了一个新的 Idea 或者非常好的 Feature 想要合并进去,可能目前这个项目的 Owner 太慢了没时间处理,或者暂时不认可你的想法,那么你要么搁置你的 idea 或者 bugfix, 要么狠心下来自己 fork 一个新的版本。或者你直接掠过沟通这一步,直接开始自己的新项目。这就是 Linux 应用生态的开源项目的常态。 这个怪圈有没有逃出的方法,有,而且很成功。只是,他的价值观显的不那么普世罢了。这样的例子很多。GNU/Linux 二者的创始人便是。可惜的是,后面的一票人都走偏了。这个方式现在有一个专业的词汇,叫终身仁慈独裁者(Benevolent Dictator For Life). 我们很熟悉日常新闻上的 Linux 本人,经常骂这个骂那个,言辞粗暴,但其地位和声誉却未受影响。因为: 他本身水平之高有目共睹,是 Linux Kernel 和 Git 的创始人. 他领导下的 Linux Kernel 项目发展顺利,效率高,质量好 就这,就足够了。就是这个仁慈独裁者,能保证一个产品拥有稳定的内部结构,统一的 interface, 和高效率的产出节奏。而 Linux 应用层恰恰缺少这么一个人,来决定大方向和开发流程,造成了现在的局面。 ...

2020-04-04 · 1 分钟 · 89 字 · 涯余

Kubernetes in 2020: Let's Forget About Kubernetes

犹记得 Docker 横空出世的时候,像明星一般,吸引了无数的人去研究它,使用它,推广他。技术的狂热让我们隐隐约约觉得它似乎解决了无数的业内问题,即使它实际上只是适用于单机环境的一个容器实现。没关系,我们有 Mesos / Marathon, 或者其他类似的东西来弥补它的不足, Docker 仍然是我们关注和研究的核心,其他的都是陪衬。 Kubernetes 的出现改变了这一切,与 Docker 一样,从一开始它就拥有了极好的架构基础。在它的逐步成长中,Mesos/Marathon的先天不足逐渐被显露出来,并且逐渐被推至边缘地带,Docker 也渐渐地回到了它应有的位置上,成了软件行业中,无数层级抽象中的一环,在 Linux Kernel 之上,在 Kubernetes 之下,默默工作,只有在出问题时才会被人们想起。Kubernetes 成了新的核心。 而过去一年,我们看着 Kubernets 也在逐渐成熟,性能更好,功能更完善,一切都在向着更稳定的方向迈进,也在紧随着 Docker 走过的路。我们永远需要一个 Platform, 这个 Platform 是谁则是在不断变化的。 因为 Platform 的稳定,所以它应该被遗忘,因为我们在思考更高层级的东西。在这整个链条中,我们知道了起点是二进制,终点是人的需求,中间的路,我们走到了 Kubernetes 这里。那么 Kubernetes 的上面是什么?这可能是我们在今后的一年或者很多年里探索的答案。 过年的一年已经给了我们很多零碎的拼图: ServiceMesh, DevOps, GitOps, Application, Helm, Servrveless, Cloud Native…. 这些名词如雨后春笋般冒了出来,让人眼花撩乱。它们并不能总是解决所有的问题,但却给我们指明了一定的方向。 从代码自动到产品,这一个长久而又清晰的诉求,在 DevOps 提了这么多年的情况下,依然在被呼吁,说明我们仍未达成目标,因为它本身需要的前置条件太多, 我们需要良好的 CI/CD, 能够适配各种 VCS;我们需要简明清晰的语法,来描述出了流水线的 Steps;我们需要统一的 Artifact Storage, 用来存储诸如 Test Coverage Output, Docker Image, Binary…等各种各样的文件;我们需要完善的 Unit Test, Integration Test, 来保证能尽量自动化地检测产品的质量,我们需要蓝绿发布,滚动更新,自动的故障恢复…等等。即使这其中的每一步都有了优质的候选技术,将他们真正地串联起来成为真正的 GitOps, 才是更难的地方。因为成功的 GitOps 也需要达到像 Docker / Kubernetes 那样既是一个稳定的核心,也是一个扩展性良好的平台。而这正是开源社区的弱项,人们更容易被技术的偏好分心而不断 fork 分裂,而不像公司那样能有一个稳定的目标和节奏。Docker 和 Kubernetes 的成功历史都证明,公司 + 开源的模式才是最有效的。期待在新的一年里在此方面能有大的突破。 ...

2020-01-03 · 1 分钟 · 182 字 · 涯余

Git 与 GitOps

伴随着 GitOps 的盛行, Git 本身的影响力也在逐渐增大。从最开始只是为了管理 linux kernel, 到支撑起了 Github 这个最大的软件社区,Git 本身的应用范畴早已超出了 vcs 本身,我们还用它来存储配置文件,文档,制作电子书,维护 blog (由 Github 等平台展示)等等。在 GitOps 的范畴内,它更是成为了 the single source of truth。 但 Git 最初的设计目标只是针对于 linux source code, 大量的小文件,而伴随着 git 应用范围的增多,明个明显的问题便显现出来 大文件的管理:大文件通常都是难以 diff 的二进制文件,跟 git 本身的设计理念完全背道而驰 大仓库的管理: 像 Google 的 Single Repo 以及微软的 windows 等巨型仓库,在使用 git 时都都会遇到非常多的性能问题。 对于前者来讲,目前的主流的解决方案都是 LFS, 主流的托管平台都支持。但在 GitOps 的大前提下,这个问题仍然算是未解决,因为在企业场景下又多了一个 Git 大文件管理的 dashboard, 而 GitOps 得了理念是为了减少 dashboard, 将所有的一切都统一起来。 一个可能的选项是用Cloud Native Artifact Registries的理念,用distribution 作为 LFS Server。 大仓库的管理微软的贡献比较显著些。像 Windows 这样的巨型项目,他们必须提前做很多改善才能把 Windows的代码放到一个仓库里。最终的实现原理跟 Google 类似: 与普通的 Git 仓库不同,他们使用一个 virtual filesystem 来作为底层存储,当用户使用 git 时,只有那些被访问的文件才会被实时从 Server pull 下来。这种方式带来的一个附带好处是也解决了大文件的管理问题。 ...

2019-09-12 · 1 分钟 · 114 字 · 涯余

GitOps

Kubernetes 确定了统治地位之后,Service Mesh 和 GitOps 也渐渐火热起来。Service Mesh 领域内 , Istio 已经逐渐占了上风,但 GitOps 扔未有统治性的工具出现,尚处于早期阶段。 相较而言, Service Mesh 本身技术性更强一些,而 GitOps 涉及的面比较广。对企业来说,会有很多额外因素需要考虑,一方面要考虑旧有系统的兼容性,另一方面要考虑所采用工具的可占有性(是否可以自己部署,维护,开发?)。所以即使现在社区有功能比较完善的 GitOps 工具,也很难被企业所采用。 但就理念而言, GitOps 本身的前景非常广阔,最直观的改善就是对于研发人员效率的提升以及产品 Deploy 效率的提升。这些优点是以如下特性/流程为基础的: 让工具去处理杂事 研发人员使用熟悉的 Git ,专注于产品研发 使用声明式语法描述环境目标状态,类似于 kubernetes 使用 Pull Request 进行 Review 流水线集成 lint /test, 进行较为基本的自动化 check PR 通过即可直接上线 通过线上故障来总结反馈并修正自动化 check 这个流程的效率非常高,事实上很多企业会在改善自身研发流程的过程中多多少少都实行了类似的模式,但问题就在于很多使用了 ad-hoc 或者 无法产品化的手段。如何解决社区标准化的产品与企业应用场景之间的鸿沟,是 GitOps 亟待解决的问题之一。

2019-09-11 · 1 分钟 · 52 字 · 涯余

Raft 协议笔记

关注于 Raft 可能更多是因为 kubernetes/etcd 的原因,二者似乎变成了 raft 最大的客户,时不时地重新理解一下这个协议还是很有必要的。 之所以这样讲,是因为分布式协议本身就很难理解。即使 raft 论文中一再声称它的主要目的之一便是容易理解,比 paxos 更好懂,但对于一般从业者来说,要想完整地理解它还是有困难的。所以经常需要温故而知新。并且,在网上一搜,关于 raft 协议的 blog 也是汗牛充栋,每个人都在试着理解,记录。可能后续还需要参考下别人的笔记,互相印证。 论文地址: In Search of an Understandable Consensus Algorithm 基本知识 leader 的概念是核心之一。raft 作为一个分布式系统,各个 server 通过固定的算法选举出 leader , 然后 leader 负责状态的维护,处理 client 的请求,维护整个集群的状态等等。跟民主制度里面多党选举有点像, 一旦大多数投票通过,总统就拥有至高权力。 除了 leader 之外,还有另外两个角色是 follower以及candidate。选举状态下,大家都是candidate,正常工作情况下,就分成了leader和follower。 Raft 并没有使用系统时钟来作为同步机制,因为太不可靠了,而且维护起来很麻烦。raft 使用了一个 term 的概念来代表时序。 Leader election 一般的流程如下所示: server 启动时,成为 follower 如果 follower 在 election timeout 的时间内,没有收到 leader 发来的 heartbeat, 那么它就假定没有 leader, 准备开始选举 follower 将自己变为 candidate, 然后给自己投一票, 然后发信息给其他 server 问其他 server 是否可以给自己投票。结果要么是大多数投了它,它成为 leader, 或者另外一个 candidate 成为了 leader, 或者没有 leader (splite vote,没有 server 获取到大多数 vote),那么就进行一下次投票。 在 election 的过程中,有可能其他的 candidate 已经成为了 leader, 并给当前的 candidate 发来了同步数据的请求。这时候就可以通过 term 的比较来判定,如果发过来的 term 数不小于自己的,那么就承认发送者的 leader 地位,并且把自己切换回 follower 状态。否则的话拒绝请求并且继续选举。 这里面有一个问题,所有人都在给所有人发请求要求 vote 自己,怎么能达到有一个 candidate 获取到 大多数 vote 呢? raft 采用了 randomized election timeout 来解决这个问题。不同 server 的 lection timout 不一样,避免了大部分情况下的 splite vote 情况。如果发生 splite vote, 重来一次。这种机制跟 TCP 的拥塞控制有些类似。 ...

2019-09-10 · 2 分钟 · 293 字 · 涯余

Oberon操作系统

文章链接: Oberon – The Overlooked Jewel 很多行业以及技术在早期都呈现出了一种百花争鸣的态势,然后到了成熟期一般都只是几家公司或者技术在互相竞争。操作系统也是如此,目前的主流 OS 里基本上就是 Mac/Linux/Windows 三家。但是在互联网早期,出现了很多设计里面差别很大的操作系统,从 GNU 的 Hurd,到 Lisp Machine,还有 Oberon 等。他们之所以消失,并不是说本身技术方面不占优势,而是从商业以及用户习惯上来讲,最终使用的人数太少而逐渐销声匿迹而已。而且从设计里面上来说,相当一部分的 OS 设计是因为理念太过超前而违背了当时用户的使用习惯而被舍弃。所以即使从今日来看,探究一下这些老旧的操作系统的设计理念仍然是大有益处的。 Oberon 的作者也是 Pascal 的作者。他自己对 Pascal 是很矛盾的感情。Pascal 本身只是他的一个作品之一,他本身的能力是非常强的,先后设计了很多完整的操作系统,编译器等等。但是外界对他的期待都在 Pascal 上。他也一直拖着不去更新,而是专注于自己感兴趣的领域。Pascal 渐渐式微,Oberon 也淡出大众视野。他沉浸于自己的创造乐趣之中,也给后世留下了无数珍宝。 文章提到了几个 Oberon 操作系统的特点,让人都非常印象深刻。 系统级 GC GC 在我们一贯看来都是属于高级语言的概念,操作系统很少有人会用 GC 来做资源管理。Oberon 实现了系统级别的 GC: 进程使用文件,不需要显式地自己去关闭文件句柄等。现在操作系统的应用程序因为系统没有 GC,不管是操作系统本身还是应用程序都需要许多额外的处理此类细节的代码逻辑。而且需要设置各种文件句柄的上限等等。Oberon 所采取的策略对未来的操作系统来说很是很值得借鉴的。 更少的对话框 对话框这个东西,自操作系统有了图形界面不久就一直存在。软件用它来给用户提供编辑,确认等操作。但是对话框并不是一个好的设计,因为它需要会打断用户的思路,强行让用户分散注意力去关注于一个全新的界面。现代的操作系统仍然有很多的这样的或无奈或多余的设计, 比如: Mac OSX 在关机的时候,因为平时开的软件比较多。经常就是需要一个一个地点各个软件弹出的确认退出的对话框。系统并没有一个默认的忽略选项,即使用户完全不在意各个软件是否需要保存当前状态。Mac OSX 为此做了一个 Force Quit的功能,用户需要一个一个地强制退出可以忽略的软件 文件的更名操作既可以弹出对话框,也可以直接按 Enter 去修改。后者就比前者简单好用的多 Mac 安装软件的一步拖动方式就比 Windwos 的在对话框中一直点下一步好很多 Oberon 在设计之处就考虑到了这个问题,尽量在用户界面少使用对话框。比如它的文件另存为操作,就可以直接点击文件名,输入一个新的,然后点击保存命令即可。这比现在常用的弹出一个对话框让用户输入再确认好的多。 校园合作 这是最让我震惊的一点。 Oberon 操作系统在作者所在的学校里,所有的人都在用这种操作系统。它发源于本校,主要的开发者都在本校工作或者退休了,使用同一种编程语言: Oberon,有足够用的应用程序。这样一种操作系统对学生来说, 在教学上拥有无与伦比的优势:详尽的文档,近在咫尺的开发者,你可以从头到尾了解一个操作系统的实现细节,学习它,研究它,改造它, 提升它的性能,给它开发应用程序。所有的这一切,都会被记录下来,作为一个学校的令人自豪的存在,被后来的追随者延续它的生命。 ...

2019-09-04 · 1 分钟 · 75 字 · 涯余

失败博物馆 - Hadoop

Hadoop 严格来说并不算完全失败了,不像 Mesos 那样,而是人们期待它成为一个大象, 而它却变成了一只兔子。 我刚毕业就开始接触Hadoop, 当时它基本上就是大数据的代名词。在这个光辉的表象之下,研发人原面临的是一个非常不透明,UI丑陋,运行缓慢的系统。当然,技术的狂热周期会让人放弃自己的疑虑,专心地去这座 shit mountain 扒拉东西。另外,也没得选。 同样的情况也发生在 Jenkins, Mesos 身上,当没有对比的时候,人们很难意识到自己面对的东西的优缺点是什么。时至今日,在吃尽了很多苦头之外,我们可以回过头来想想,为什么有的软件成功了?为什么有的失败了? 成功的特性几乎是不言自明的,我们可以列举如下: 模块化 / 可扩展性好 接口用户友好 (页面,编程语言,API) 文档全面 这些就够了。而失败软件在每一方面,几乎都没做好。Hadoop 也是如此。 同时因为这些因素存在,人们从最初的狂热清醒下来之后,便开始想尽一切办法找寻可能的替代品。在数据量不大的时候尽量选择其他工具(pandas, unix tools等等), Spark, Hive, Pig…. 甚至连 Hadoop 第二代的 YARN 都被 Kubernetes 无意打残了。当然,这个现状有一个比较好的词叫生态圈,但它和 Kuberentes 的生态圈还是很不同的。前者是因为太难用,被肢解,被不断替代。后者是根系稳固,枝叶繁茂。(没写完,待补。) Links What happened to Hadoop Don’t use Hadoop - your data isn’t that big Command-line Tools can be 235x Faster than your Hadoop Cluster Let’s build a modern Hadoop 与 Hadoop 对比,如何看待 Spark 技术? Hadoop再凉凉,前大数据独角兽公司MapR被惠普企业(HPE)收购 驳「Hadoop 快不行了」 Hadoop 不再权威,开源大数据的未来何去何从? The dark side of Hadoop

2019-08-31 · 1 分钟 · 82 字 · 涯余

科大讯飞

讯飞并不是一个很坏的企业,它只是太懒了。 国内的互联网巨头基本上没有干净的,无不是带着一身罪恶爬到了现在的位置。讯飞是一个巨头,因为他在语音行业是老大,规模也不小。但它也不是,因为它身处合肥,似乎互联网的新潮跟它都没什么关系,也很难在新闻上见到它的身影。可以概括地来讲,它是成也合肥,败也合肥。 之所以说是成也合肥,一是中科大保证了它的研发能力不弱,而是与政府互相依赖,保证了他政府方面的合作优势。败也合肥,则是因为在这么一个三线城市了,所有人都想过着朝九晚五的生活,并不想像去参与北上广的发展竞赛。再加上公司的营收严重依赖于国企的订单,它自身也变得越来越像一个国企。 机器学习大火之后,语音识别技术变得更加简单和常用。所有的大厂都可以投入人力物力去研发自己的语音识别技术,并且与讯飞的差距逐渐缩小,进一步去蚕食其本身的市场。在可以预见的未来里,它的日子并不会好过。 还有一个更明显的问题是, 它太不会营销了。虽然说酒香不怕巷子深,但是当别人都在拼命地宣传自己的产品,而消费者又不是很在意这之间的细微差距,那么累积起来的用户习惯是很怕的。纵观互联网的历史,有太多产品好却死去的案例了。 还是希望它能好好正视下自己现在的困境,好好发展下去。

2019-08-31 · 1 分钟 · 7 字 · 涯余

Rick and Morty

我给很多人推荐过这部动画片。可能也是唯一一个我一直在不遗余力地给很多人推荐的艺术作品。 曾经我也跟很多人一样,在追《马男》,看似颓废却又奋进,真是一部优秀的剧集。后来偶然接触到了《Rick and Morty》,再去回看《马男》,不是它不好了,只是相比来说,它太平庸了。 很多喜好的东西都太过个人化了,即使你再爱,也知道你没法总给别人讲。但《Rick and Morty》不同,它是一个流行于全世界的东西,是文化本身,也是文化的反面本身。 很多美国的青少年如此痴迷,以至于在现实中做出种种奇怪的举动。川香酱的疯狂正是一个绝妙的对于这个剧的呼应。而对于中国的青少年来说,这个美国文化里衍生出来的动画片,包含了太多反美国文化的东西,以至于每一个大洋彼岸的年轻人都被它深刻地吸引,感同身受。它成了一个跨域东方与西方,跨越资本主义与社会主义,跨域文化的东西。 从内容上来说,抛开表面的光怪陆离,看似蹩脚的宇宙设定,下面隐藏的是每一个人一生都要遇到的问题:父母,子女,家庭,婚姻,种族,文化,梦想,自我的哲学思考等等。同样是表面的虚无主义,底层的复杂深刻。不同的人看会有不同的感受,但每个人都会沉浸其中。 《马男》的失败在于,同样是在探讨这些问题,但是太根植于美国文化,让其他地方的人总觉得有隔阂,且太过肤浅,没有悲剧式的深刻。《Rick and Morty》里最为痛苦的设定在于,Rick 知晓自己是一个动画角色,这种痛苦一直伴随着他,自杀而不能。 同样无法摆脱这种可能的我们,可能为了规避这样的痛苦而不敢去想这个问题。

2019-08-29 · 1 分钟 · 14 字 · 涯余

KARL, ASLR, KASLR

前段时间在零星地看一些关于 unikerel 的文章。里面提到的一些安全相关的技术非常有趣,就又开始转向 kernel 安全这部分。本文主要探讨的是 KARL, KASLR 以及 ASLR 相关的技术。 安全相关的实现技术很多,但想了想,大体的分类可以归为以下几类: RWX 权限检查 随机化: 尽量不用确定性的内存地址 暴露最少的信息: 日志,输出里尽量不暴露相关信息 KARL, KASLR 以及 ASLR 都属于第二类。尽量让应用程序以及内核在内存中的地址随机化,这样攻击者就难以猜出相关的地址。 简单来讲, ASLR(Address-space layout randomization) 主要是指内存中用户层的地址随机化,KASLR 是指内存中内核的地址随机化, 而 KARL(kernel adress randomized link)是指内核本身的地址随机化。ASLR出现的时间更早一些,更成熟一些。 注意有一个区别, 三者中都有一个 L,但意义不一样。 ASLR/KASLR 中的 L 是指 layout,是说内存中的布局。你不可能要求大量的应用程序自己去考虑随机化,而是在应用程序无感知的情况下,由内核来选择一个随机的位置来运行应用程序。而 KARL 中的 L 是指 link ,是在链接阶段就做好了地址的随机化处理,这样就不需要再内存中在做了。 ASLR ASLR 理解起来相对更为简单,本身不需要再重复介绍。 RNG 不管是 KARL / ASLR / KASLR,其核心都是一个随机的概念。明显它依赖于随机数的生成算法。当然随机程度越高越安全,但是又要考内核的性能。这个平衡很难掌握,选择哪个 RNG(random number generation) 也经过了很多讨论。 一开始用的是 get_random_int(),一个性能比较好但安全性不够好的方法,后来有人提出用 get_random_bytes(),安全性大大提高,但是性能下降也比较多。再后来 Linux 自己对get_random_int()做了一些改进, 增加了一些噪音进去,在不影响性能的情况下改进了随机性。这也是目前在用的方法。 ...

2019-08-27 · 1 分钟 · 171 字 · 涯余

Cloud Native Artifact Registries

Helm3 的 design proposal 里面介绍了对于Chart Repo 融合到 Distribution 的提议。目前已经有了实验的 demo,理论上并没有什么问题。因为 Distribution的设计里并不是只能存镜像,而是一个开放的协议。我们可以想下目前企业在 DevOps 场景下,除了代码之外,有哪些文件需要存储 构建产物 二进制程序 Helm Charts 镜像仓库 测试报告 代码分析报告 Kustomize 文件 等等。为数众多,而且都是存储于不同系统的。构建的产物一般都暂存于构建系统内部,有失效时间。镜像在 Docker Hub 或者 Harbor 中,Helm Charts 在官方仓库或者 chartmusuem 这样的仓库中。基本上每一项都需要单独维护存储设施,分别要使用不同的客户端。Helm3 的这个迁移的决定,不止对 Helm 本身有很大的意义,对于其他的产品也具有很大的启发意义。Docker公司本身也在做这方面的 整合, Docker app 既可以用来分发 docker-compose 文件还有 CNAB 格式的应用。在可以遇见的将来,所有的 Artifacts 共用一个存储和客户端是可能的。 这有一另外的一个好处是,这是一个独立于云提供商的平台。不管是在 AWS/GCE/AZURE 还是其他云平台,不管是用 S3 还是其他存储,Distribution 这样的平台可以提供统一的接口。 另一方面,Kubernetes 平台上应用的分发格式也处于激烈的竞争阶段,目前常用的就有以下几个: Docker App Docker Compose CNAB Helm Kustomize Distribution 这样的平台能帮助用户减少很多使用负担和迁移成本。预测来看,Helm/Kustomize 最终的赢面大些,docker 可能只能继续专注于更底层的业务。 链接 Helm 3 Preview: Charting Our Future – Part 3: Chart Repositories Docker App and CNAB Cloud Native Artifact Registries evolve from Docker Container Registries

2019-08-26 · 1 分钟 · 89 字 · 涯余

失败博物馆 - Mesos/Marathon

Mesos/Marathon 的失败,怎么说,太过典型了。如教科书一般,清晰的不能再清晰。 首先,它流行的时候,是因为大家没得选。一个还在蹒跚学步阶段的 Kubernetes 就开始将它打的节节败退。到了现在,连Mesosphere 已经转向了 Kubernetes。 从一开始,这就是一个学术界的产品。由一堆并不擅长编写工业化软件的研究室的学生和老师写出。这导致了 Mesos / Marathon 的致命缺陷 首先,用 C++ 写 Mesos, C++可以说是学术界和工业界态度差别最大的编程语言了。对学术界来说,这是一个可以用来探究编程语言极限的编程语言,数不清的高级设计,数不清的语言学设计,你再任何其他语言里都见不到这么博大精深的体系。但对工业界来说,这是一种灾难级的编程语言。Linux 都在天天骂它,难学难用难调试,市场占有率也一直不断下滑。Mesos 选择了 C++之后,极大地减少了开源社区去贡献代码的可能性,彻底沦为了创世的开发团队自己的作品。 更灾难的是,Marathon 用了 Scala,一个除了 C++之外第二复杂的语言。其同样程度的撕裂性,对研发人员的折磨,让人都开始怀疑为什么当初要选择编程这份工作。语言设计者一旦觉得自己过于聪明,会趋向出设计出来大众难以理解的东西。这并不怪大众,而是怪语言设计者。两个世界上最复杂的语言,组合起来,成了一个大部分研发人员都不愿意碰的黑盒子。然而,它又不是稳定到不出什么问题,总会有出故障需要排查的时候。这便是让所有人都痛苦的时刻。所以即使今天大家发现 Kubernetes 已经非常复杂难以理解了,但没有人会抱怨太多,因为只要你肯花点时间,分析分析代码,总能慢慢理解。而对于 Mesos/Marathon,如果你发现了一个问题,想去阅读代码,大部分时间你都在纠结:这个语法什么意思?这个语法又是什么意思? 另外一种研究室出来的代码的问题在于,很少有人会考虑到架构的可扩展性。有了一个 idea,实现出来,发个 paper,大部分就完了。等到真正开始用户多的时候已经来不及了。而工业界的产品不一样,从一开始可扩展性就是所有系统在设计之初必须考虑的一个问题。所以我们看到,Mesos/Marathon 自发布之后,后续的功能扩充是极其微弱的,难以有任何实质性的功能增强。而 Kubernetes 在发布之后,新功能如井喷一般。当然这里面也有编程语言本身的功能。Golang 问题再多,简单易用本身就足够让很多人开始使用了。

2019-08-23 · 1 分钟 · 34 字 · 涯余

失败博物馆 - Jenkins

我想记录一下工作中使用过的软件里的一些在设计上比较失败的。因为它们,工作中多了很多痛苦。 Jenkins 便是其中之一。跟很多软件一样,Jenkins 在商业上是成功的,成功的原因就是用户并没有什么选择。对 Jenkins 非常了解的人来说,能很容易列举出非常多的详细的jenkins 的问题。从一个普通使用者的角度来看,最直观的两个感觉就是: Jenkinsfile 难写,流水线很慢。 对我来讲,Jenkinsfile 就是最大的设计败笔。从工作中的反馈来看,即使看了文档,也没人知道怎么去写一个正确的 Jenkinsfile。用户想描述好一些流水线步骤,最直观的想法应该就是贴近于 Makefile, 每个步骤几条命令,清清楚楚。再加上其他一些并发控制,产出物管理即可。用 YAML 或者 TOML 这样的配置文件即可。 几乎除了 Jenkins 之外的所有 CI/CD 工具,都选择了 用YAML。而 Jenkins 使用了 Jenkinsfile, Groovy 语言,意味着用户需要首先了解 Groovy,在学习 jenkins的语法,才能写好 Jenkinsfile。一些自作聪明的开发人员在类似的设计问题时,会有一种炫技的想法,总想用 DSL 来解决问题。结果通常是不好的,既给用户带来了沉重的负担,也让系统变得难以理解。基于 JVM 的很多语言都是如此命运。 即使你费劲千辛万苦,终于写出了一个能跑通的 Jenkinsfile,这时候你会发现,在 Jenkins 的流水线里面,有很多复杂的难以理解的东西。为什么多出来很多莫名其妙的步骤?系统日志和构建日志怎么这么杂乱无章地堆在一块?为什么速度总是这么慢?界面为什么这么丑?不一而足。 想深刻地理解这些问题在哪,当然可以去好好探究一下它的架构,然后了解这些问题的根源。但这是在没有必要了,对于一个将死的系统来说。作为用户来讲,在已经有了替代品之后,赶快逃离就是了。

2019-08-22 · 1 分钟 · 41 字 · 涯余

数据库与操作系统

数据库一般认为是一种系统软件。而操作系统处于更底层的位置。这是一种通常的认知。 Unix 的一切皆文件的设计思想,从一定程度上来讲,表明了操作系统内部不同组件之间有一定的结构上的一致性。比如网络接口和文件接口,都有如下的操作 Rread Write Close/Open permission 等等。进程和内存管理也是类似的逻辑。所以我们现在可以看到 Linux 系统中有很多类似的尝试,用文件的形式来来作为很多内部组件对外的接口。 这是 Linux 一直在被夸赞的地方。一个听起来优美的设计哲学,吸引了很多人从奇怪的,程序员不友好的 Windows 逃离过来,并花费大量的精力来学习和理解这个设计之下隐藏的诸多肮脏的细节。 工作越久,越来越多的人发现。相比较而言,平时还是 Mac 和 Linux 用起来更方便。即使是爱折腾的程序员,也大多不愿再去浪费时间去折腾 Linux,去折腾 VIM/Emacs。不是因为年纪大了,而是因为这些东西确实用户不友好,而且有设计缺陷。 几十年前, 已经很清楚地点评了 Linux/Unix 上的诸多问题。然而它并没有推动 Linux/Unix 去改变和解决这些问题。开源是一面美好的大旗,但它也蒙蔽了跟着的人。 Linux 桌面的失败简直惨不忍睹。自由的 fork, Client-Server 的架构,无尽的口水仗。最终活下来了两个无法合作的 KDE/Gnome。Windows 自然也有很多设计的问题,但注册表现在看来相对于 Linux 的配置文件来说简直是太优秀了。多年人,有些人会想到: 不同 cmd 的 input 和 ouput 的格式都不一样,增加了很多研发的负担 能否用一种统一的数据格式来表达配置以及输入输出?比如 json 也有人尝试过,但从来不会真正影响到社区。即使成功了,systemd 的经历也历历在目。 虽然能将操作系统的诸多概念简化成文件,但文件仍然是一个相对复杂的抽象概念。除了文件系统,没有哪个其他模块能够与文件如此对齐。就数据本身而言,打开文件之后,读写数据,本质上简化为两个结构: list 和 dict,组合起来就是一个 table。 读写文件,基本上就是不断地对这个 table 做修改,增加,删除,修改行。CSV 格式的文件更是可以直接直接对应于一张表。所有其他的内部模块,其操作也是类似的; 创建进程: 往进程列表里添加一个元素 销毁进程: 从进程列表里删除一个元素 添加设备: 往设备列表里增加一个元素 配置设备: 修改设备的属性 不只是 Linux,所有操作系统面临的都是同样的问题,使用的都是同样的的机制: 不听地对Table 做各种操作。现状是,所有的操作系统在不同模块的管理上都是有差别的,因为没有统一数据模型的支持,每个模块都在不断地用不同的形式做类似的操作,其提供给用户的功能也因此而受限。 ...

2019-08-22 · 1 分钟 · 96 字 · 涯余

What is Helm Doing Wrong and How a Helm3 Controller Can Fix It

Helm is big success for sure, it’s nearly the standard application package format in kubernetes.You only need to provide some metadata about your application’s name, version, description….Helm can help you package up and upload to a central or custom chart repo.Just like npm,rpm,docker image, or whatever other package management system. About a year ago, Helm3 was drafted. Since it’s still on the proposal stage, most users are still using helm2, include us. The journey we spent with helm was not a very pleasant one, we struggled very hard to make it work. In the end, we decided to create a kubernetes controller based on Helm3 proposal, it works well and we open source it on github: captain. ...

2019-08-03 · 7 分钟 · 1381 字 · 涯余

Sinfonia: a new paradigm for building scalable distributed systems

论文链接: Sinfonia: a new paradigm for building scalable distributed systems Sinfornia 像是一个构建分布式系统的 SDK 。目前的市面上分布式系统已经非常多了,比如 etcd, zookeeper, ceph 等等。但他们都是分别实现的,彼此之间的代码并无太多借鉴。Sinfonia 的实践是很难得的,可以说比大部分的分布式软件都更有指导价值一些。 这个图中展示了 Sinfonia 中关键的几部分 user library: SDK 部分,隐藏了分布式系统处理内部细节的 SDK。用户基于 user library 来开发分布式应用 minitranscations: Sinfonia 的核心概念。一个分布式的事物机制 application node: 应用节点 memory node: 维持分布式系统内部状态的节点。 图中没有画出的有 管理节点: 用于执行一些定期的 recover 任务 directory node: Application Node 访问 Memory Node 使用的是逻辑 id, directory node 记录了逻辑 id 与 真实地址的映射。 Memory Node Memory Node 存储了应用的状态数据,根据不同情况,可能是存在 RAM 中,也可能是在磁盘上。 user library 封装了操作 Memory Node 中数据的方式。Application Node 可以与 Memory Node 是同台机器也可以是不同机器(有些 Application 出于性能考虑会需要 Applicaiton Node 与 Memory Node 为同台机器,Sinfonia 会告知 Application 此种情况以便让其尽量把数据写在本机)。 ...

2019-07-24 · 3 分钟 · 507 字 · 涯余

Golang FAQ

文档的可得性 语言设计细节 可读性 Generic types Error 与 Exception assertions static link Tips unused imports Golang FAQ 是一篇比较好的关于 Golang 设计的文档。比较琐碎,但是解释了很多 Golang 设计的权衡之处。没有语言是完美的,从工业界来看,Golang 是一种流行的,成功的编程语言。从语言设计来看,当然仍有欠缺与不足之处。许式伟是前者的角度,王垠是后者的角度。当然除了宏观层面,FAQ 文档仍然提供了很多有趣的细节。 文档的可得性 所有的语言都知道支持文档。但可得性是一个更重要的维度。 Python 有 PEP 系列详细解释了语言设计的细节, 有 read the docs 系统为各种 library 提供用户文档。 Golang有 godoc 工具以及 Golang blog, 还有像 FAQ 这样的设计 detail 解释。二者在此方面都是做的比较优秀的,也在一定程度上促进了语言的流行。 语言设计细节 可读性 可读性一方面是要考虑尽量贴近大部分程序员的背景以及知识结构,另一个方面语言的设定要尽量 common, 不会让人需要额外思考来确定一个语法的语义。Golang 对于大部分具有 c / python 等背景的程序员来说非常易懂,也尽量控制新的语法的数量,所以能够迅速的大范围的流行起来。 另一方面,为了语言的可维护性,去掉一些便捷的语法糖也是一个重要的考量。Golang 去掉了 ?: , 隐式类型转换,指针操作,Goroutine 的内部结构不对外暴露等等。虽然代码不会像 python 那样简短,但在可维护性以及稳定性上都有了改善。 ...

2019-07-19 · 1 分钟 · 172 字 · 涯余

Common Index

这几天在重温 《疑犯追踪》,作为一个近科幻的代表作,其描绘的社会是如此地贴近现实,让人想到时都会有一种无力感。在技术社区的种种讨论中,总会有零星的关于此话题的讨论以及解决之道。去中心化网络作为其中一个核心概念,已经有很多落地的实验性产品,虽然仍然无法撼动如今的网络结构,但总是一些希望之火。另外一个最近引起热门讨论的技术便是 Common Index。搜索引擎发展了这么多年,在给人们带来了很多便利的同时,也带来了无数的负面影响。Common Index 是一种解决途径,虽然如去中心化网络一样,看起来都很遥不可及,但仍然是值得期待的。 搜索引擎是一种商业产品,其背后的公司依靠大部分依靠广告牟利。这是一种非常容易理解的商业模式,但是却造成了以下后果: Too Big To Fail: 搜索引擎技术随着互联网规模的发展,会形成越来越高的技术壁垒。没有任何新公司能进入到这个市场。Google 目前占据着绝对主流,其他几家只占有微量的比例: 搜索引擎公司可以通过一些看似 公平的策略来调整不同网页的权重,从而来影响互联网的发展。比如 Google 一直在提高支持移动互联网网页的比重。这样的行为不能说是很坏,但是否应该由一家公司来做这些决定呢? 搜索引擎公司可以通过人为调整搜索结果顺序来牟利。比如毫无底线的百度。 信息时代人们对于信息检索的需求是非常庞大的,而且或许正如某些人所说,它不再是一个普通的商品,而是成为类似于水电一样的基础设施,每个人的生存都依赖于这些东西。在这种情况下,搜索技术再由某些垄断公司提供是不合适的,尤其是公司容易受到地缘政治的影响。Common Index 对此的提议是将搜索技术分为两层: 底层是通用的 index 技术,上层是各个公司的定制部分。 这里OWI 指 Open Web Index, 即本文一直说的 Common Index。Common Index 作为基础设施,提供基本的互联网索引,不同的商业机构可以基于此添加自己的特定的索引,并提供不同的界面以及功能。比如我可以实现一个专门搜书的搜索引擎,或者一个专门用来搜索特定某个人的公开信息的搜索引擎等等。Common Index 让新公司加入搜索引擎这个市场变得可能,并且可以通过差异化的服务来获取用户。有了这个基础,搜索引擎市场便有可能成为一个良性竞争的市场。 要想这个设想变成可能, 操作起来是很麻烦的,所以很多类似的设想都只停留在设想阶段。我能想到两个问题便是: 谁来承担 Common Index 的成本? 理论上可以应该让参与这个市场的公司来共同承担,想扶持本国在此方面有所发展的国家可以资助本国公司去参与这个市场。 如何打破现有市场的格局? 最理想的情况是,在未来的某一个天,通过反垄断法将 Google 拆分, 分离出其 部分index 技术作为 Common Index 的基础。 当然,希望在未来讨论这些问题的时候,百度已经不在了。 相关链接: Can an Open Web Index break Google’s stranglehold over the search engine market? The Web is missing an essential part of infrastructure: an Open Web Index

2019-07-16 · 1 分钟 · 81 字 · 涯余