分类目录归档:阅读笔记

【翻译】如何快速给 MacBook 预热

前言

文章来源:z3ugma.github.io

原文标题:Warm up your MacBook

原文发布时间:2019-11-18

原文链接:https://z3ugma.github.io/2019/11/18/warm-up-your-macbook/

本文根据原文内容翻译整理,并补充了适用范围与使用注意事项,译文由 AI 辅助完成。


这是一篇带点玩笑意味的实用短文:如果你刚从冬天室外、冷车厢,或者空调很足的环境里拿出 MacBook,金属机身摸起来冰凉,想让它快一点“热起来”,最直接的方法其实不是等,而是让 CPU 开始工作

如何快速给 MacBook 预热

如果你只是想粗暴地让机器升温,可以打开终端,执行下面这条命令:

yes > /dev/null

这个命令会持续输出 y,再把输出丢进黑洞设备 /dev/null,从而让一个 CPU 核心持续忙碌起来。机器开始算东西,温度自然就会上去,机身也会慢慢暖和。

不过,如果你想要一个更可控的方式,原文更推荐使用 stress 这个小工具。先用 Homebrew 安装:

brew install stress

然后运行:

stress -c 6 -m 2 -t 300

这条命令的意思是:

  • -c 6:启动 6 个 CPU 压力任务;
  • -m 2:再启动 2 个不断做内存分配/释放的任务;
  • -t 300:持续 300 秒,也就是 5 分钟后自动停止。

这样做的好处是,你不用自己记得去停掉进程,时间到了它会自动结束。

如果你觉得这个操作以后还会经常用,原文建议顺手在 shell 配置里加一个别名,例如:

alias warm='stress -c 6 -m 2 -t 300'

以后只要输入:

warm

MacBook 就会开始“自我发热”。

补充说明

原文写于 2019 年,更贴近当时的 Intel MacBook 使用场景。放到今天,有几点值得补充:

1. yes > /dev/null 只会打满一个核心

在多核机器上,这条命令并不会让整台机器瞬间满载,它只是让一个核心一直忙。想要更明显地升温,还是 stress 这类可以并发压测的工具更直接。

2. 不要把“预热”变成“过热”

这类做法本质上是在主动制造负载。短时间内问题不大,但如果你把任务开得太猛、时间拉得太长,风扇噪音、电池消耗、表面温度都会明显上升。放在腿上、被子上或者不通风的地方跑,就更不推荐了。

3. Apple Silicon 时代通常没那么需要

现在的 M 系列 MacBook 在能耗和发热控制上比当年的 Intel 机型好很多。多数情况下,你可能根本不需要专门“预热”它;如果只是嫌机身太凉,降低空调、换个环境,往往比专门压 CPU 更自然。

4. 想停下来就直接中断

如果你是手动运行 yesstress,发现已经够热了,直接按 Ctrl+C 即可结束。不要让它在后台一直跑着忘了关。

小结

这篇原文其实就讲了一件事:想让冷冰冰的 MacBook 快点变暖,最简单的方法就是让 CPU 忙起来。

2019 年看,这是一个略带黑色幽默的 MacBook 小技巧;今天再看,更像是一篇属于 Intel 时代的轻松小品。拿来了解一下无妨,真要使用时,记得适可而止。

【翻译】这篇博客在 Ubuntu 16.04 上跑了 10 年,我把它迁移到了 FreeBSD

前言

文章来源:CrociDB

原文作者:CrociDB(Antonio Vivace)

原文标题:This blog ran on Ubuntu 16.04 for 10 years. I migrated it to FreeBSD

原文发布时间:2026-05-21

原文链接:https://crocidb.com/post/this-blog-ran-on-ubuntu-16-04-for-10-years-i-migrated-it-to-freebsd/

本文为全文翻译,译文由 AI 辅助整理,若有理解偏差欢迎指正。


这篇博客已经在一台 DigitalOcean VPS 上运行了十多年。那是一台托管在纽约、运行着 Ubuntu 16.04 LTS 的机器。而这个 LTS 版本至少已经有 5 年不再受支持了。是时候做些改变了。经过一番权衡,我把它迁移到了一台 Hetzner 的虚拟机上:配置比我旧的 Ubuntu 机器强得多,价格还不到以前的一半,而且机房就在我所在国家的另一端。不仅如此,我还顺便挑战了一下自己,把整套栈迁到了 FreeBSD。这篇文章会有点长,但如果你愿意读下去,会看到一个关于 FreeBSD JailsBastille,以及一些有意思的网站负载测试结果的故事。

动机

如果你了解 Ubuntu 的发布和支持周期(我自己其实也不算熟),就会知道一旦某个版本结束支持,apt 对应的软件仓库也就没了,你再也拿不到更新。运行这样一套过时系统会带来不少问题,其中最显而易见的一点就是:你的服务器不再那么安全了。互联网上肯定有各种机器人,专门扫描存在漏洞的节点,然后想办法塞进去恶意内容。幸运的是(我想大概算幸运),我这里一直没出过事。反正机器里也没什么特别重要的东西可偷。但我记得很久以前,我还有一个 WordPress 博客,也是跑在一台老旧 VPS 上,结果某天文章正文里突然被插满了可疑的赌场和赌博链接。

我原本就有一台 Hetzner VPS,当作远程开发机来用,可以随时 SSH 上去,考虑到价格,它一直都很可靠。所以我决定先从对比配置开始。下面这台就是原来跑我的博客和其它网站的 droplet:

(原文此处有旧 DigitalOcean 服务器截图)

它有 2GB 内存、1 个 vCPU、50GB 磁盘、每月 2TB 流量,运行的是 Ubuntu 16.04 x64。机房位于纽约,这大概也是它比较贵的原因:我每个月要为它支付 13 美元。

Hetzner 是一家老牌欧洲 IT 公司,在德国有大型数据中心,而我也住在德国。它最便宜的一款服务器,只要 3.56 欧元,配置就已经比我原来那台更好了:

(原文此处有 Hetzner 入门套餐截图)

内存和 CPU 都翻倍,存储稍微少一点,但月流量多了十倍。不过我最后还是选了一台更“豪华”的配置,每月不到 6 欧元:

(原文此处有更高配套餐截图)

对我的这些站点来说,这配置甚至有点过头了,不过为什么不呢?

旧的部署方式

我那套旧环境托管的不只是这个博客,还有几个别的网站。它们都不算热门;在所有站点里,这个博客流量算最高的了,但每个月也不过几千次页面浏览。除了偶尔有几篇文章在 Hacker News 上爆一下,平时根本没多少流量。说到底,这台机器基本就是在提供静态网站服务,没有复杂 CGI,也没有什么自定义后端代码在跑。整套栈很简单:所有内容都通过 nginx/1.10.3 以静态方式提供。我基本上就是在 /etc/nginx/sites-available 里给每个站点放一份配置文件。像静态站点生成器、LaTeX 套件之类的附加程序(比如这个博客就是用 Hugo 生成的)则通过 aptsnap 安装。事实上,我更新博客的流程一直是这样:

  • 在本地写文章
  • 提交并推送到仓库
  • SSH 登录服务器
  • 拉取仓库更新
  • 运行 hugo

这台 VPS 刚开始用的那些年里,我还会拿它做一些测试和编程实验。所以机器里积累了很多已经不再使用的老旧软件,显得很臃肿。但它就是能跑,而且跑得还挺好。

(原文此处有 Linux 4.4 截图)

它甚至好到什么程度呢?我关机前,它的 uptime 达到了 1491 天,也就是差不多连续 4 年没中断过。

为什么选 FreeBSD

说实话,我的主要动机之一就是想亲手折腾点不一样的东西。我最近读了很多、也看了很多关于 BSD 家族的内容,而我之前也曾短暂用过 FreeBSD,所以我觉得这是一个把它放到真实生产场景里试一试的好机会。FreeBSD 常常因为它的一体化设计、安全性以及 Jails 而被人称赞。后面我会详细展开。

我不想装得好像自己一开始就完全知道该怎么做,但当我读到 Jails 时,我立刻就知道自己想怎么搭了。Jails 是一种虚拟化/容器化机制,已经存在于 FreeBSD 里超过 25 年了,比 Docker 诞生还早得多。乍一看,它实现的效果和你对 Docker 容器的预期很像:在系统里面隔离出一个“小系统”,你可以在里面运行一些拿不到宿主机权限的东西。但在我看来,Docker 以及其它容器方案更适合“打包程序”——它们强调短生命周期和不可变;容器内处理的数据来自外部,但容器内部本身最好始终保持一致。而 Jails 更像真正的子系统,几乎可以把它看作迷你虚拟机,只不过底层仍然共享同一个内核。

除此之外,FreeBSD 的文件系统 ZFS 对服务器场景也确实非常好用。如果你来自 Linux 世界,大概听说过 Btrfs,它是近几年越来越多发行版开始采用的新文件系统。它和 ZFS 有一些相似点,比如数据完整性和快照。不过 ZFS 大概比 Btrfs 要成熟得多。如果我能频繁地给系统做快照,就不需要依赖 VPS 提供商的快照或备份服务,而那些通常都要额外付费。

我的想法是:每个网站一个 Jail,里面装上构建这个网站所需的工具(比如博客需要 Hugo),再在 Jail 里运行一个 nginx 实例来提供服务;另外再单独放一个主 Web 服务器 Jail,通过反向代理把外部世界和这些站点连起来。这样一来,哪怕某个 Jail 被攻破了,我也可以把它直接销毁,再建一个新的。后面我会更具体地介绍配置过程。

Hetzner VPS

先给所有喜欢看系统信息的人一点细节:

(原文此处有 fastfetch 截图)

搭建过程

我会快速讲一下自己是如何把这台服务器搭起来的,以及其中最关键的几个点。平时我会记开发日志,所以这次也是边做边记。不过不要把这篇文章当作教程,因为我可能漏掉了一些步骤。更准确地说,这是一篇“在 Hetzner 上用 FreeBSD 搭 Web 服务器是什么感觉”的记录。

安装 FreeBSD

Hetzner 在创建虚拟机时会提供一些镜像,但选择其实非常有限:

(原文此处有 “No BSD” 截图)

不过我在 FreeBSD 官方 YouTube 频道 上看到了一份讲如何在 Hetzner 上安装 FreeBSD 的指南。顺带一提,这个频道非常推荐。

关键在于:Hetzner 其实提供了 FreeBSD 镜像,只是藏得比较深,需要多点几步。它是以 ISO 镜像的形式提供的。所以在创建虚拟机、选择操作系统镜像的时候,你先随便选一个,反正稍后都要把它抹掉。创建完虚拟机后,只需要去控制台里的 ISO Images 标签页,挂载你想要的镜像即可:

(原文此处有 FreeBSD ISO 镜像截图)

我选的是 14.3。然后重启,基本上就按安装器一路走下去。印象里我没有改什么默认选项,当时是跟着官方 FreeBSD 频道里的安装视频一步步做的。很快,系统就装好并跑起来了。

Bastille

前面我已经提了好几次 Jails。但我用的不只是 Jails,还用了 Bastille,这是一个用来管理 Jails 的系统。原因很简单:手动创建 Jail 这件事比我想象中复杂,而且每新建一个 Jail,都有一堆独立步骤需要自己处理。Bastille 把这些都简化了,很多事情只差一条 bastille 命令。比如 bastille list 可以列出系统里的所有 Jail,bastille create 用来创建新 Jail,bastille console 则可以直接打开某个 Jail 的 shell,等等。

安装并启用它(几乎)就像 官方文档 里说的这么简单:

pkg install bastille
sysrc bastille_enable="YES"

当然也有别的 Jail 管理器,我只是选了一个名字最酷的。

整体架构

整套思路是:先有一个运行 Caddy 的 Jail,负责对外提供所有站点,并处理域名和 SSL 证书;然后每个站点再单独有自己的 Jail,里面安装各自构建和服务所需要的软件。这个“服务器 Jail”会把所有流量反向代理到对应的站点 Jail。所以第一步就是配置一个内部虚拟网卡。你可以把这套结构理解成:一组跑在同一个宿主机里的小型虚拟机网络。

配置虚拟网络接口的方式如下:

sudo sysrc cloned_interfaces+="lo1"
sudo sysrc ifconfig_lo1_name="bastille0"
sudo service netif cloneup
sudo sysrc ifconfig_bastille0="inet 10.0.0.1 netmask 255.255.255.0"

这几条命令本质上就是克隆一个回环接口,把它命名为 bastille0,再给它分配网络参数。所有 Jail 只会跑在这张网络接口上。至于 Caddy 所在的 Jail,因为它需要监听外部请求,就必须具备访问外网的能力。为此,我们要用 PF(Packet Filter,也就是 FreeBSD 的 防火墙)配置一条出网规则。

我们需要编辑 /etc/pf.conf

# the main interface `vtnet0` and the virtual one we created `bastille0`
ext_if = "vtnet0"
int_if = "bastille0"
vpn_if = "tailscale1"

# skip internal traffic
set skip on $int_if
set skip on $vpn_if

# allow outbound access to the internet from the jails
nat on $ext_if from 10.0.0.0/24 to any -> ($ext_if)

# redirect HTTP/HTTPS traffic from the web to the server jail (10.0.0.5)
rdr pass on $ext_if proto tcp from any to any port {80, 443} -> 10.0.0.5

# block everything else
block all

# allows outbound traffic from host
pass out quick on $ext_if keep state

正如配置里的注释所写,这段规则主要做了几件事:把 bastille0tailscale1 上的内部流量跳过处理;允许 Jail 和宿主机出网;再把所有 80443(HTTP/HTTPS)流量重定向到内部 IP 10.0.0.5,也就是我们的 Caddy 服务器。

然后启用 PF:

sysrc pf_enable="YES"
service pf start
sysrc gateway_enable="YES"

至此,网络层的基础就完成了。接下来开始创建 Caddy 服务器 Jail。

创建第一个 Jail:Caddy Server

我的旧服务器一直在跑老朋友 nginx。除了早年短暂用过一点 Apache,我人生的大多数时间都在用 nginx。不过 nginx 有个烦人的点,而 Caddy 正好能漂亮地解决:SSL 证书。以前在旧服务器上,我得隔三差五跑一遍 certbot 去续域名证书,而且续签忘掉的次数比我愿意承认的要多。Caddy 会自动处理这些事情。

要创建一个新 Jail,先得为当前要使用的 FreeBSD 版本做 bootstrap,在我的场景里是 14.3-RELEASE

bastille bootstrap 14.3-RELEASE

然后创建真正的 Jail:

bastille create caddy 14.3-RELEASE 10.0.0.5 bastille0
bastille start caddy

这里我们创建了一个名为 caddy 的 Jail,使用 14.3-RELEASE,IP 为 10.0.0.5,挂在 bastille0 接口上。

检查 Jail 状态:

# bastille list
 JID  Name      Boot  Prio  State  Type   IP Address  Published Ports  Release       Tags
 3    caddy     on    99    Up     thin   10.0.0.5    -                14.3-RELEASE  -

这样它就已经跑起来了!要记住,Jails 并不是像 Docker 容器那样追求 ephemeral 的东西。你不会先把容器完全定义好,然后指望它永远都保持一模一样。这里的感觉更接近虚拟机。你随时都可以通过下面的命令进到它的 shell:

# bastille console caddy

[caddy]:
root@caddy:~ #

配置 Caddy Server

既然我们已经在 caddy 这个 Jail 里了,那只需要把 Caddy 装上:

pkg install caddy
sysrc caddy_enable="YES"
service caddy start

Caddy 的所有配置都放在 Jail 里的 /usr/local/etc/caddy/Caddyfile。如果我们想在宿主机上也能直接访问这份配置文件,就需要把宿主机上的一个目录挂载到 Jail 里。甚至还可以用只读模式挂载,这样 Jail 自己就无法修改配置。大概像这样:

bastille mount caddy /usr/local/etc/my-caddy-config /usr/local/etc/caddy nullfs ro 0 0

好了,服务器差不多配完了,但我们还没有一个真正的站点。所以先建一个站点 Jail,然后再回来配置 Caddy。

第一个站点:es.cro.to

疫情期间,巴西总统选择把 Covid-19 当作感冒来对待,没有采取任何紧急封锁政策。他不断强调国家不能停摆,承担不起代价,哪怕有人因此死掉也无所谓。这完全无视科学,甚至无视常识。历史已经证明他有多错。对此我很不爽,于是我想做一点“抗议”。

所以我做了一个小页面 es.cro.to。它在葡萄牙语里字面上是“阴囊”的意思,但更常被用来形容一个人是个 混蛋烂人,或者别的类似意思。我买下了 cro.to 这个域名(现在回头看,这域名其实挺酷的,英文里听起来也不错),然后加了一个 es 子域名。它刚好把这个词按字典式的音节拆开,于是我就在页面里放上了这个词的词义和对应生物的图片:

(原文此处有 escroto 页面截图)

它当然也是跑在我的旧服务器上的,而且在封锁那段时间还获得了不小的流量。我决定先从它开始迁,因为它本质上只是一个带着一张压得很惨照片的单页站点,而且现在也没那么重要了。

这个站点和我其它站一样,也是一个 Git 仓库。所以我准备把所有网站都放在宿主机的 /usr/local/www 下,然后把各个站点对应的目录以只读方式挂载到对应 Jail 里。于是,当我把这个站点放到 /usr/local/www/escroto 后,就开始用 Bastille 创建 Jail。这次我直接用了一个用于 nginx 的 bastille 模板:www/nginx。模板其实就是预装一些软件的小脚本。其实不用也行,但既然有,为什么不用呢?

bastille bootstrap https://github.com/bastillebsd/templates
bastille create escroto 14.3-RELEASE 10.0.0.11 bastille0
bastille template escroto www/nginx

我给它分配的 IP 是 10.0.0.11,等会会用到。此时,这个 Jail 里的 nginx 已经在提供默认页面了。它的站点目录位于 /usr/local/www/nginx(注意,FreeBSD 几乎什么都放在 /usr/local 下)。

接着,我把宿主机上的站点目录挂到 Jail 里:

bastille mount escroto /usr/local/www/escroto /usr/local/www/escroto nullfs ro 0 0

这样一来,我在 Jail 内就能访问 /usr/local/www/escroto 这个站点目录了。因为它是个 Git 仓库,里面带有 .git 之类不该被直接提供出去的文件,所以我写了一个 deploy.sh 脚本,把 /usr/local/www/escroto/* 复制到 /usr/local/www/nginx/*,再删掉 .git 目录:

rm -fr /usr/local/www/nginx/*
cp -R /usr/local/www/escroto/* /usr/local/www/nginx/
rm -fr /usr/local/www/nginx/.git

这样以后每次要部署这个站点的新版本,只需要登录宿主机,更新仓库,然后执行这个脚本:

cd /usr/local/www/escroto
git pull
bastille cmd escroto /root/deploy.sh

后来我发现这个思路挺好,于是也给其它站点都放了一份 /root/deploy.sh

让 Caddy 把域名指向这个 Jail

回到 Caddy 配置:

cro.to {
        redir https://es.cro.to{uri} permanent
}

es.cro.to {
        reverse_proxy 10.0.0.11
}

配置非常简单:主域名重定向到 es.cro.to,再由后者反向代理到 10.0.0.11,也就是 escroto Jail。

配置好 DNS 记录后,这个站点就上线了。

部署这个博客

这个博客本身是用 Hugo 写的,仓库放在 GitHub 上。所以我把它克隆到了 /usr/local/www/blog,然后像 escroto 一样创建了一个新的 Jail:

bastille create blog 14.3-RELEASE 10.0.0.12 bastille0
bastille template blog www/nginx
bastille mount blog /usr/local/www/blog /usr/local/www/blog nullfs ro 0 0

然后在这个 Jail 里安装 Hugo:

bastille pkg blog update
bastille pkg blog install gohugo

部署脚本也很类似:

rm -fr /usr/local/www/nginx/*
cd /usr/local/www/blog
hugo -d /usr/local/www/nginx

在正式把站点迁离旧服务器前,我想先做一些基准测试,所以先把博客绑定到了一个旧域名上:

crocidb.cro.to {
        reverse_proxy 10.0.0.12
}

就这么简单,博客已经在新服务器上跑起来了。

给我的服务器做基准测试

在正式更新 DNS 记录前,我想确认这台新服务器到底能不能扛住流量。理论上看它当然行,但万一我哪里配错了呢?于是我开始琢磨,怎么比较旧服务器上的 crocidb.com 和新服务器上的 crocidb.cro.to

一开始我先想清楚,到底该测什么。测延迟没什么意义,因为我知道,对大多数访问我博客的人来说,新服务器的延迟本来就会稍微更长一些。这个站点的大部分流量通常来自北美,其次是欧洲,再然后是南美。不过那点差距其实不至于影响体验。所以更值得测的是:服务能力和高负载表现。

市面上有一些免费的在线工具,可以做比较通用的测试,比如 GTMetrixPingdomWebPageTest。但老实说,这两台服务器在这些工具里的差异几乎只体现在延迟上,其它方面基本差不多。所以我决定往更深一点挖。

我发现了 wrkhey 这两个 HTTP 压测工具。它们本质上就是对网站发起成千上万的并发请求,然后收集诸如请求延迟、错误响应、每秒传输量等信息。比如,我在另一台 Hetzner VPS 上跑了下面这个 wrk 命令:

wrk -t4 -c100 -d30s --latency https://crocidb.com/

这表示让 wrk4 个线程、维持 100 个并发连接、持续压测 30 秒。这个数字其实也不算特别离谱。结果如下:

Running 30s test @ https://crocidb.com/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    89.63ms   18.31ms 189.09ms   95.29%
    Req/Sec   211.74     38.44   313.00     87.69%
Requests/sec:    833.41
Transfer/sec:      8.29MB

而对 crocidb.cro.to 测出来是:

Running 30s test @ https://crocidb.cro.to/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     6.75ms    3.59ms  51.98ms   74.41%
    Req/Sec     3.08k   260.11     4.34k    81.83%
Requests/sec:  12260.10
Transfer/sec:    130.80MB

旧服务器每秒能处理 833 个请求,而新服务器则达到 12,260。平均延迟则是 89ms6ms。但这当然不公平:压测机器和新服务器在同一个数据中心。所以我想,要不试试挂 VPN,从不同地区去测?

我用的是 Proton VPN,于是写了一个小脚本,从一组地点里选一个,连上 VPN,再跑 wrk。结果非常平淡:旧服务器平均大概 300 req/s,新服务器大概 800 req/s。测试地点如下:

LOCATIONS=(
    "US//New York|US - New York"
    "US//Los Angeles|US - Los Angeles"
    "BR//São Paulo|BR - São Paulo"
    "GB//London|GB - London"
    "DE//Frankfurt|DE - Frankfurt"
    "FR//Paris|FR - Paris"
    "SE//Stockholm|SE - Stockholm"
    "CH//Zurich|CH - Zurich"
    "IN//Mumbai|IN - Mumbai"
    "JP//Tokyo|JP - Tokyo"
    "KR//Seoul|KR - Seoul"
    "SG//Singapore|SG - Singapore"
    "AU//Sydney|AU - Sydney"
    "ZA//Johannesburg|ZA - Johannesburg"
    "AR//Buenos Aires|AR - Buenos Aires"
)

于是我放弃了“用终端用户 VPN 视角来测”的思路,转而采用一种更硬核的办法:直接在世界各地的数据中心创建真正的 VPS 节点来跑测试。

Vultr VPS 基准测试

我需要一家不同于 DigitalOcean 和 Hetzner 的 VPS 提供商,以免底层基础设施带来额外优势。除了这两家,我唯一用过的 VPS 服务商就是 Vultr。于是我决定在不同地区各开一台机器跑测试。因为整个过程是手工完成的,所以最后只选了四个地区:伦敦圣保罗硅谷东京。我在这些地区各创建了一台最便宜的 Fedora 虚拟机。

经过几轮测试后,我发现 hey 更符合我的需求。于是我的流程就变成:SSH 登录测试机,执行脚本,然后把结果拷出来。第一个测试点是圣保罗:

dnf install -y tmux vim htop
wget https://storage.googleapis.com/hey-releases/hey_linux_amd64
chmod +x hey_linux_amd64
./hey_linux_amd64 -n 1000000 -c 10000 -t 10 -z 5m -h2 https://crocidb.com/ > crocidb.com.log
./hey_linux_amd64 -n 1000000 -c 10000 -t 10 -z 5m -h2 https://crocidb.cro.to/ > crocidb.cro.to.log

这会下载 hey,然后以一种非常夸张的配置运行:总请求数 1M,并发 10k,超时 10s(也就是任意单个请求超过 10 秒就算失败),总时长 5m

第一次跑的时候,我很快就发现新 FreeBSD 服务器确实有个问题:它很早就开始失败,因为它根本扛不住 1 万并发连接。查了很多资料后,我发现可以用 netstat -Lan 查看 socket 队列大小,而我的队列值全都是 128。继续追下来才发现,原来 kern.ipc.somaxconn 的默认值就是这个数字。于是我把它调高了:

sysctl kern.ipc.somaxconn=16384

差不多十分钟后,日志跑完了,我开始看结果:

(原文此处有圣保罗 VPS 上 hey 输出对比图)

左边是旧服务器,右边是新服务器,差异简直夸张。

虽然两台机器都返回了不少错误,但 FreeBSD 这边至少完成了预期的 1M 请求,而 Ubuntu 那边连 20k 都没撑住!这个差距实在是太大了

基准结果分析

(原文此处有成功率图表)

旧服务器 crocidb.com 只能完成极少一部分请求,这一点真的很惊人。实际上,它只完成了大约 7% 的请求,而 FreeBSD 服务器完成了 94%。东京地区里新服务器的成功率略微低一点,但我觉得还不至于值得担心。

从每秒请求数来看,新服务器最少也有 3 倍提升,最高甚至能到 11 倍。

(原文此处有 Requests per second 图表)

这里的差距看起来更夸张,而我猜测东京那边的延迟可能确实更高一些。

(原文此处有延迟百分位图表)

如果看延迟百分位(例如 p50 表示 50% 的请求都比这个值更快返回),会发现两台服务器呈现出很不一样的曲线形态。新服务器直到大约 90% 之前都保持比较 线性 的增长,所以表现更可预测;而旧服务器的增长曲线则明显更不稳定。

这说明,即使在高负载下,全球范围内 90% 的访问者在尝试加载我博客首页时,仍然能在 3.5 秒 内拿到内容。我觉得这已经相当不错了。

我没有再继续深挖东京地区的问题,不过目前我也没那么担心。看 hey 的请求阶段分解(也就是它会拆分 DNS、连接、等待响应、读取响应等耗时),确实能看出日本方向的流量更慢一些:

(原文此处有请求阶段耗时分解图)

不过这些数据在我看来又有点怪。首先,第二个域名的 DNS 拨号和查询时间低得离谱,也许是因为它是个 CNAME 记录?其次,resp wait(基本可以理解成首字节时间)和 resp read(传输时间)在这里高得有点异常。但这也可能是因为它统计的只是成功请求:也就是说,旧服务器可能一开始响应很快,但很快就把新请求都拒掉了。

结论

我并不认为这个差距是因为我选的这套栈本身就有多神奇。更有可能的原因是:旧 Ubuntu 系统本身就有一些配置问题,再加上我选的 Hetzner VPS 有 4 个 CPU 核,而旧 DigitalOcean 机器只有 1 个,自然能同时处理更多请求。我也不觉得这在实际意义上真的有多重要,因为这些服务器几乎不可能真的遇到这么大的并发量。也许跑一遍 WebPageTest 这样的 Web 基准工具就已经够了。

真正切换

虽然这些基准测试还留下了很多未解答的问题,但我对结果已经很满意了。所以我直接更新了 DNS 记录。现在,这个博客已经正式跑在那台新机器上了。

说到底,经过很多个小时的实验、折腾、构建、推倒重来之后,我意识到:搭一台用 FreeBSD 托管网站的机器,其实并没有我想象中那么复杂。满足我需求的 Web 托管服务其实也有不少,我本来也完全可以用别的方案。或者装一个 Proxmox,用图形化仪表盘来管理容器和系统。甚至也可以试试它在 FreeBSD 世界里的对应物 Sylve。但我还是很喜欢自己这次走过的这条路,因为整个过程让我学到了很多。

其中几个最主要的收获是:

  • 那台 Ubuntu 服务器真的非常皮实。十年来,它一直把我这些站点的负载扛得很好。最后四年甚至一次都没重启过。而这一切并不需要多复杂的配置。
  • 配置 FreeBSD 比我原本想象的容易。我很喜欢把系统配置集中在一个地方管理的感觉,而且它的在线文档也真的非常好。
  • 想自己搭一台托管博客的机器,往往需要大量网络知识,而这些远远超出了一个游戏开发者通常具备的范围。
  • 学习另一套系统真的很有趣。也许下次我会试试 OpenBSD 或 NetBSD。

不过说到底,这一切也没什么意义,因为我的大部分流量现在反正都来自各种 AI 抓取器……

Refs

【翻译】Programming Still Sucks:编程依然很糟糕

前言

文章来源:stvn.sh / Writing

原文作者:Jerome Choo

原文标题:Programming Still Sucks

原文链接:https://www.stvn.sh/writing/programming-still-sucks-fqffhyp

本文为全文翻译,译文由 AI 辅助整理,若有理解偏差欢迎指正。


2026 年 4 月 19 日 · 阅读约 8 分钟

编程依然很糟糕

编程 · 领导力 · AI

抱歉,Peter。

——

我在一个生日派对上。来的人大多数也都在科技行业工作,但这种场合里总会有那么一个“有正经工作的人”。你知道的,那种做实体工作的,真的在造某些人们需要的东西的人。而这个人总会问出某种变体的同一个问题:你不担心 AI 会抢走你的工作吗?我朝四周瞥了一眼,看见几张脸微微转过来,轻轻翻了个白眼,然后又回到他们原本的聊天里。对,又是这个问题。

他们有个外甥在帮人搭 Shopify 商店。他们听不懂那孩子嘴里一半的词,但他说自己麻烦大了,而且科技行业里所有人都一样。他的外甥是不是得去学一门“手艺”?我们是不是都得这样?

如果酒喝得够多,我就会认真回答,因为那时候我已经不在乎别人觉得我说的话有没有意思、是不是真的了。但通常我只会叹口气,说一句:“当然,会有一点。我们大多数人都会。不担心才奇怪吧,对吧?”他们点点头,然后就转去聊个更轻松的话题,比如我们会不会去核平伊朗。

但事实是,在科技行业工作一直都很糟糕,而且从来都不是他们以为的那个样子。

有些人以为,我的工作就是坐在一张干净的桌子前,待在一间角落办公室里,外面是开放式办公区,摆满长桌,上头放着一排排 MacBook 或 ThinkPad。在我的角落办公室里,我制定完美的计划,我那群完美的员工会为我鼓掌喝彩。没有任何东西能逃过我的目光,每一个决定都由我完美地做出,每一分钱、每一分钟都被精确记录。

当掌声散去,我的员工——或者叫下属,或者在我心情好的时候叫“我的团队”——就开始疯狂敲键盘。敲啊敲,敲啊敲。不久之后,完美的软件就诞生了。它从集体流水线上滚下来,像第一个孩子一样,绝不会出错。

但现实根本不是这么回事。是的,我确实也很不爽自己从来没混到一间角落办公室,但我更忙着陷入恐慌,因为我根本不知道自己在干什么,没人知道,而现在整辆车的轮子都已经飞出去了。CEO 说 AI 让他那位朋友 Jared 的团队生产力高得惊人,高到他能裁掉一半人;但他说这话时像是在炫耀,不是在威胁?我不知道,反正我是感到威胁了,不过这大概只是我的焦虑症又犯了。没事,我总能从那几个在厕所里哭的员工那里借一片 Xanax 吧。

想象一下,你接了一份船长的工作。第一天,你骑车来到港口,兴冲冲地准备见你的船员。你注意到船并不在那儿,但那个你之前聊过、情绪特别亢奋的招聘人员 Greg 朝你挥手,跟你保证这不是问题。接着你被绑到一台投石机上,竟然奇迹般地被发射到了船上。上一任船长在 Captainpalooza 2025 上听另一个船长给他解释了内燃机的概念,然后就想“朝着这个方向开始迭代”。于是他把船点着了。他后来被人从船上推了下去,但顺手把使用手册也一起带走了。这本来也不该是问题,偏偏整艘船都是专门为他定制打造的。船上倒是还有帆,可它们没连在桅杆上;而那台半固定在船尾的内燃机,零件还散落在甲板各处。

你走进甲板下方,想搞明白这艘船到底怎么运作,以及你们究竟要去哪儿。但当你沿着楼梯往下层走时,你却莫名其妙地走进了桅杆里?你问一个水手这是怎么回事。他卡顿了一下,然后说:“你说得完全对!我之前的方法有问题,不过这里有一个更好的楼梯实现方案。”接着桅杆啪地一下倒转过来,你又回到了甲板上,回到一开始站着的地方。帆现在也上下颠倒了,而你的“水手”还兴奋地等着你夸他干得多好。

你又去问另一个人:“等等,我们到底是要去哪儿?”太好了,是个人类!没有卡顿,没有热情却没帮助的回答,是真正的人。她已经一周没睡了。她几乎没看你,只是说:“去问领航员。”
“领航员?”你问。
她抬手一指。那个领航员,是一个按下背后按钮就会说“勇往直前,步步高升”的娃娃。

那娃娃着火了。

这就是现在的工作。你站在一艘着火的船上,手里拿着一张地图,试图搞明白我们他妈到底要去哪里,以及我们到底要怎么去。

你知道这艘船。有些人曾经就在一艘一模一样的船上当工程师。有些人曾经就是那个离开的船长。我不是写给生日派对上的那个“有正经工作的人”看的。我是写给你的。

你曾经也是工程师。你记得代码评审原本是用来做什么的。你记得自己也曾是那个新人,第一份 PR 被某个资深工程师撕得粉碎,但对方愿意花时间解释“为什么”。你不是在 2024 年某天早上醒来后,突然决定要废掉这一切的。

真正发生的是:跑道被砍掉了。董事会会议里压根没有出现“价值观”这个词。CFO 手里拿着一张电子表格。CEO 则刚从一次线下管理层 retreat 回来,有人在那里给他看了一个 agent 在十四分钟里写完一整个功能的演示,而他竟然信了——就像人们在想相信某件事的时候,总是特别容易相信那样——然后他告诉董事会,到第二季度之前,他可以把工程团队裁掉 30%。现在,你的工作就是想办法把这件事做成。

你告诉自己,新人不会有事的。他们会适应,会重新学习技能,会找到别的出路。你告诉自己,资深工程师可以吸收掉少掉的人手,agent 会把缺口补上。你告诉自己,下个季度再回头看看。你在那张名单上签了字。你回了家。你比平时多喝了一点。然后你上床睡觉。

你知道的。

你知道,因为你也曾经是那个工程师——那个不得不去收拾上一位被“简单答案”忽悠瘸了的领导留下烂摊子的工程师。你看着 Goodhart 定律一口一口吞掉速度指标、故事点、测试覆盖率;几乎所有非工程背景的人手里拿过、并当成“工作一切顺利”证据的数字,最后都被它吃掉。你知道,当你引入工具的速度快于培养判断力的速度时,DORA 指标早就在告诉你部署稳定性会发生什么。你知道,当那些本来会发现错误的人被挤出去,或者学会了不再发现错误时,代码库会变成什么样。

你知道。可你还是签字了。因为不这么做的代价,可能就是丢掉工作;而工作背后是房贷、学费、签证,以及那个“等局势稳定下来、以后再把事情修好”的你自己。

可“以后”从来不会来。我们都知道。我也签过一张名单。直到现在,我们还在互相指责到底是谁那张名单更糟。

已经没有新人了。2024 年,我们为他们的消失办过一场葬礼。没人来。机器现在在做他们做的事,而且更便宜。当然,新人的价值从来都不在于他们眼下能产出什么,而在于他们将来会成为什么:那个知道“尸体埋在哪儿”的资深工程师。我们为产出做了优化,于是废除了学徒制。再过几年,我们会一脸困惑地问:那些资深工程师都去哪儿了?是我们亲手把他们打死了。没人会记得。

可即便如此……

在你的某处基础设施里,一定有一个 cron job。它在凌晨 3 点运行。从 2016 年起,它就一直在跑。它做着某件关键的事情。你没法准确告诉我它到底做什么,但你知道有一个人肯定知道,而那个人在 2019 年就离职了。文件开头的注释写着:# DO NOT CHANGE!!! Ask Ben。Ben 联系不上了。过去四年里的每一次路线图规划会议上,“modernize legacy cron(现代化改造遗留 cron)”都会作为候选项目出现。它从来没排上优先级。你甚至亲手把它从列表里删掉过两次。

但总有人让它继续跑着。她叫 Sara。你并不知道这件事。

她五十多岁。她没去过 Captainpalooza。她以前在离总部三条街外的一间小办公室上班。去年,为了省钱,有人把那间办公室关掉了。离她最近、同时有桌子和网络连接的地方就是这艘船,所以她现在会自己带午餐,然后顺着舷梯走到甲板下的一间小舱室。船上没人知道她在那里。还记得 Ben 吗?Ben 从 1998 年开始带她,她后来从 Ben 那里接过了这个 cron job。

她知道 Ben 几年前已经去世了。她还参加了他的葬礼。你不知道这件事。

那个任务卡住的时候——而且它经常会卡住——她就轻轻推它一下,它就会再试一次。电话响了。她确认告警。她推一下。这个任务依赖一个早已失落于时间洪流中的模块。嗯,也不算完全失落,因为 Ben 去世后,她在他的办公桌里找到了一根 U 盘,里面正好有一份备份。没有 agent 碰过它。以后也不会有。

她不是这个行业里“最安全”的人。她代表着一种你无法触碰的东西。她就是你那场转型刚刚删除掉的所有组织记忆,装在一个五十五岁的身体里四处走动。她正是从你亲手废掉的那条学徒制管道里成长出来的:Ben、1998 年、那根 U 盘。她就是那条管道。当她死去时,那个能生产出像她这样的人才的系统,也早就已经不存在了。三年前,是你亲手杀掉它的。你根本不可能再招到一个能替代她的人,因为制造她的机器已经被你砸烂了。

她就像那个拿着勺子在魔多地下挖隧道的人。勺子是她的,隧道也是她的。没有别人想要这把勺子,也没有别人想要这条隧道。而等她死了,这个 cron job 也会死,工资发放会停摆,一家有三万名员工的公司将不得不想办法重新给所有人发薪水,而到那时,只会剩下一个答案:去找一个手里有勺子的人。你找不到的。是你自己确保了这一点。

那个 cron job 负责发工资。你并不知道这件事。

生日派对上的那个家伙还在等我回答。现在我已经喝得太多,没法再撒谎了。我告诉他:不是 AI 抢走了我们的工作。是贪婪。是同一种贪婪——那种把工厂迁去孟加拉、让刚果钴矿里继续存在奴工的贪婪——只不过现在换上了一张新面具。告诉你那个外甥,去做点别的吧。做什么都行。那也未必救得了他,但至少他不用假装,摧毁他生活的东西是一台机器人。

除了 Sara。她在甲板下,带着她的 U 盘。他们动不了她,因为他们甚至不知道她在那里。

而我们其余的人,都在甲板上,盯着那些上下颠倒的桅杆,琢磨那边那个娃娃到底是干什么的。

那娃娃着火了。

Refs

【翻译】如果你以为写代码速度是你的问题,那你还有更大的问题

前言

文章来源:Andrew Murphy

原文标题:If You Thought the Speed of Writing Code Was Your Problem You Have Bigger Problems

原文链接:https://andrewmurphy.io/blog/if-you-thought-the-speed-of-writing-code-was-your-problem-you-have-bigger-problems

本文为博主翻译,若有理解偏差欢迎指正。


周二早上,你们 VP of Engineering 站在投影前,兴奋得像刚在 2017 年买到第一枚加密货币。TA 刚从某个大会(或者厂商晚宴)回来,三杯黑皮诺下肚,看完一场 Demo,然后带回了“好消息”:

“我们要给所有团队上线 AI 编码助手。早期数据显示,代码产出提升 40%。这会彻底改变我们的研发速度。”

会议室里就会出现那种经典场面:一半人在点头,另一半人突然对自己的笔记本屏幕产生了浓厚兴趣。资深工程师脸上写着“要不要现在说真话,还是回去更新 LinkedIn”。

但没有人问最关键的问题:

你说的速度,是朝着什么目标在加速?

因为你们刚刚做了一件事:在整个交付系统里,挑中了本来就不慢的一环,然后把它继续加速。你们给“非瓶颈”砸了钱。

而系统论告诉我们,这不仅不会帮到你,甚至会让情况更糟。

Goldratt 会想跟你聊聊

1984 年,Eli Goldratt 写了《The Goal》。这是一本讲制造业的小说,却对软件交付异常适用。

核心思想是约束理论(Theory of Constraints)

  • 每个系统只有一个真正约束(瓶颈);
  • 整体吞吐量由这个瓶颈决定;
  • 在瓶颈解决前,优化别的环节意义不大。

很多人理解到这里就停了。真正可怕的是下一句:

当你优化的不是瓶颈时,你得到的不是“更快系统”,而是“更坏系统”。

很直观:A 工位更快了,但瓶颈 B 速度不变,于是 A 和 B 之间堆起半成品;库存上升,交付周期变长,B 工位被淹没,优先级更混乱,质量也会下降。

你并没有提速,你只是制造了一场交通堵塞,并把它叫做“生产力”。

恐怖现场:当你“3 倍代码产出”后会发生什么

开发者 PR 提交更快了,听起来很好。但评审人数没变,没人去扩容 reviewer。

于是 PR 堆在队列里:一天、两天、一周。作者已经上下文切换去写下一个 AI 加持功能,回头再看第一个 PR 时,连自己都快不认识了。为了赶队列,评审开始“橡皮图章式”通过;CI 跑 45 分钟,偶发失败,重跑通过;发布还需要人工审批,而审批人正在开“关于会议的会议”;功能在 staging 再躺三天,因为没人真正对“尽快上线”负责。

同时,开发者已经又提了两个 PR。队列越来越长,在制品(WIP)爆炸,人人手里都有 6 件“进行中”,但“真正完成”的反而更少。真正衡量价值交付速度的 cycle time 不降反升。

你会得到一个很荒诞的局面:

  • 代码更多;
  • 软件交付更少;
  • 仪表盘显示“生产力 +40%”。

你们建成了一个世界级工厂:特别擅长生产会堆在地上腐烂的库存。

更糟的是,很多 AI 生成代码没有被任何人真正理解。提示词的人不一定真正“写过”它;值班排障的人也不一定懂它。于是系统可出故障面积变大,而能推理系统的人变少。

更多代码,更少理解。这不是生产力提升,这是定时炸弹。

那真正的瓶颈在哪?

沿着价值流走一遍:从“有人提出想法”到“用户真正获得价值”。瓶颈会自己跳出来。

1. 你根本不清楚该做什么

PM 两个月没访谈真实用户;需求是三句 Jira + 一个 Figma;工程师每天要替产品做几十个没人定义的细节决策。大家在猜。

结果是:你可能用 6 周做了一个功能,最后只有 11 个人用,其中 9 个还是内部 QA。

这不是“交付慢”,而是“我们到底在干嘛”。

在这种环境里加速写代码,只会更快把错误功能做完。

瓶颈是“理解问题”,不是“敲键盘速度”。

2. 代码“写完”之后的所有环节

在多数组织里,写代码可能只占 20%,其余 80% 都在排队。

评审、CI、staging、QA、安全审查、产品验收、发布窗口、灰度……代码在各个环节之间静止等待。很多功能代码半天写完,却两个月后才到生产。

你看见过“紧急修复”9 天才上线,就知道瓶颈根本不在编码。

想提速,先看“等待时间”,而不是“编码时间”。

3. 发布信任的恶性循环

测试不稳定、可观测性混乱、灰度流程没人信,团队越来越怕发布。越怕越攒大包,包越大风险越高,风险越高就更怕。

这时再提高代码产出,只会把“恐惧文化”喂得更肥:更多代码、同样恐惧、更大批次、更低发布频率。

4. 上线了,但到底有没有效果?没人知道

功能发布后,没有像样分析、没有用户回访、没人复盘“问题是否被解决”。于是下一个需求继续猜。

你只是更快地重复“做了—发了—耸肩”的循环。

5. 你的日历才是承重墙

有时瓶颈不是技术,而是协作:

  • 等一个决策会议;
  • 三个团队一个月没对齐 API;
  • 某架构师成了所有设计的单点审批;
  • 季度规划流程太重,紧急事项也要排队。

这都是组织问题、人问题、协调问题。

写代码更快,对这些问题的作用是 0

应该做什么(不性感但有效)

  • 画出价值流:把一个功能从想法到上线的每一步写下来,也写下步骤之间“等了多久”。
  • 衡量 cycle time,不是产出量:别再盯代码行数、PR 数、故事点;看从提交到用户拿到价值要多久。
  • 消灭等待态:评审慢就改评审机制;发布卡人工审批就自动化或降低摩擦;决策依赖会议就拆小决策。
  • 少开工,多完工:限制 WIP,3 个真正完成比 10 个进行中更有价值。
  • 听一线团队的:开发者早就知道瓶颈在哪,只是通常没人认真听。

结语

如果真的想加速交付,正确的管理台词不该是“代码产出提升 40%”,而应是:

“我们做了价值流分析,发现功能平均在流程间等待 9 天。接下来我们要把这个时间砍半。”

写代码速度从来不是大多数团队的核心问题。真正的优势不属于“写得最快”的团队,而属于能持续做到这三件事的团队:

  1. 搞清楚该做什么;
  2. 把它做出来;
  3. 快速、稳定地送到用户手里。

修瓶颈。瓶颈不在键盘。

《我看见的世界:李飞飞自传》

阅读感悟

暂略

阅读摘录

《我看见的世界:李飞飞自传》

[美]李飞飞 82个笔记

03 鸿沟渐窄

◆ 1956年,他们将好奇心编撰成文,提出了现在广为人知的《达特茅斯人工智能夏季研究项目提案》,“人工智能”一词就是在这份提案中诞生的

◆ 最著名的例子是麻省理工学院博士生特里·威诺格拉德(Terry Winograd)开发的一个程序,用户可以用简单的英语命令来操控一组虚拟的彩色方块。有段对话格外令人难忘,以下是部分摘录:用户:找一块比你手中的方块更高的方块,把它放进盒子里。计算机:我想,你说的“它”是指比我手中的方块更高的方块。计算机:好的。用户:盒子里装了什么?计算机:蓝色三角锥和蓝色方块。用户:三角锥是由什么支撑的?计算机:盒子。用户:有多少个方块不在盒子里?计算机:四个。

◆ 费根鲍姆的创新开启了后来被称为“知识工程”(knowledge engineering)的全新子领域。在这个子领域中,有关特定领域(医学、制药、金融或几乎其他任何领域)的事实被整理成数据库。机器可以读取相关数据,像威诺格拉德的几何形状一样进行分析,并以自然流畅的书面问答形式提供答案,实现了媲美咨询人类专家的自动化体验。这些程序被称为“专家系统”(expert systems)。

◆ 美国孩子似乎普遍比较吵闹粗暴,对老师似乎也缺乏尊重

◆ 师生之间的互动常常是对抗性的,但也充满了俏皮和温暖。在这原本令我生畏的第一天,我立刻确定了一件事:我会喜欢美国的老师

◆ 此前的几代人试图用规则详尽描述智能,算法相对僵化,这种人工智能通常被称为“符号人工智能”(symbolic AI);20世纪80年代末到90年代初,潮流开始转向更自然的方法

◆ 杨立昆的成果就预示着一个大胆的未来。随着时间的推移,行业研究重点从“通过明确编程来解决问题”转变为“从示例中发现模式”。

◆ 换言之,算法不是被告知该做什么,而是去学习该做什么。研究人员给它起了一个贴切的名字:“机器学习”(machine learning)。

◆ 1950年,图灵发表了一篇题为《计算机器与智能》的论文,简要对比了“基于规则的人工智能”(rule-based AI)和机器学习

◆ 基于规则的人工智能是指从零开始构建具有智能行为能力的完整体,而机器学习指的是允许智能体自主发展。图灵问道:“与其努力打造程序来模拟成人的思维,为何不尝试用程序模拟儿童的思维呢?”

◆ 大脑可以被看作由简单元素组成的大型网络,元素之间的联系可以随着时间的推移而改变;通过将复杂的行为分布于网络中,我们几乎可以完成无限的任务,并且可以不断学习新的任务,即使到了晚年也可以。

◆ 人类大脑的复杂性远远超越已知宇宙中的任何其他事物,但其构造又极其优雅,几乎把复杂性全部掩藏

◆ 汽车或手机都是由清晰区分的零件组装而成,这是人类设计师认为直观的形式。但大脑的构造与此不同,它是由近1000亿个神经元构成的巨大网络,其中的神经元就是一个个互相连接的微小单元,可以在电化学传输中精细聚焦

◆ 大脑在最初在子宫内形成后的很长时间里,才通过学习形成了(或者至少是逐渐完善了)这些网络结构。这就是为什么尽管我们的灰质在解剖学上看起来并无二致,但每个人的个性、技能和记忆都是独一无二的。

◆ 休伯尔和威塞尔的研究发现,感知不是发生在单个神经元层次上,而是通过由多层神经元组成的层次结构进行的

◆ 由于大脑的网络结构允许无数步骤同时进行,我们的感知体验是连续不断、充满活力的

◆ 福岛邦彦将这一成果称为“新认知机”(neocognitron)。新认知机对输入数据的异常具有很高的复原力和容忍度,因此在准确辨认笔迹方面取得了突破性的进展

◆ 随着网络接触到越来越多的实例(如照片或音频波形集),神经元之间的连接就会因所见所闻而被重塑,留下越来越详细的印记。就像流淌几百年的河水雕刻出的峡谷壁一样,在经过一定的训练后,神经网络会逐渐呈现出特定的特征。经过多年的努力,神经网络突然开始以前所未有的规模进行学习,并达到了前所未有的精确度,这预示着真正的转折点即将到来

◆ 这是父亲真正的天赋所在——不是工程学,不是相机修理,甚至不是文字游戏,而是在任何情况下,哪怕再平淡无奇,都可以发现幸福和快乐。

04 心智探索

◆ 母亲出生在一个国民党家庭,她的出身属于敌对的阵营,因而一直戴着精神枷锁做人。而现在,她在这新泽西州的干洗店里变得春风扑面、笑口常开

◆ 物理学为我学习计算机打下了坚实的基础。我开始学习一门新的语言——一种简称为C的编程语言。与英语不同,C语言以一种前所未有的方式赋予我力量。它的清晰度和精确度都堪称完美,让我能够以复杂、抽象的方式进行计算,而且计算规模之大是我以前无法想象的

◆ 学习一门新语言,就像打开了一扇通往新世界的大门

◆ 当神经元以千亿计的数量级复制,当它们之间的连接达到10的11次方时,质变就发生了

◆ 物质变成了思维,产生了爱、喜悦、悲伤、愤怒、恐惧和欢笑,也造就了我们在科学、艺术、音乐和数学等方面的能力

05 第一道光

◆ 我们的远古祖先形态简单,考虑到当时的环境,这也是很自然的事。它们居住的水下空间生物稀少,无须为了食物相互竞争。在三叶虫出现之前,生物捕获猎物主要靠运气,而猎物也采取了同样漫无目的的方式来躲避捕食者,双方均靠运气生存。只有当食物近在咫尺、无须付出任何主动努力时,生物才会进食。

◆ 他认为,引发寒武纪生命大爆发的导火线是一种能力的出现:光敏感性,这也是现代眼睛形成的基础

◆ 对光的感知迅速发展,其核心在于一类被称为“视蛋白”的蛋白质。这种蛋白质具有独特的性质,比如在吸收光子时会改变形状(本质上是对光的物理反应),并连接成一种叫作“离子通道”的链条,将这种反应转化为生物电信号,传输到身体的其他部位

◆ 在进化过程中,神经网络虽然原始,却是与竞争日益激烈的外部世界保持同步的权宜之计,即使今天也依然存在,尤其是在水生生物中,例如某些种类的水母

◆ 大脑并不是内部某种神秘的智力火花的产物,而是对外部世界的反应。

◆ 当时的我还不知道,视觉研究是人工智能本身的产物

06 北极星

◆ 通过进一步研究,索普精确地指出,大脑中的识别时刻是在图像出现后仅仅150毫秒(大概相当于眨眼的一瞬间)

◆ 我们的视觉基础在于识别定义明确的类别,也就是对事物的识别

◆ 视知觉依赖于分类

◆ 我们的视觉系统就像是某个神秘巨人以极大的耐心精雕细琢出的发条装置,而我们的研究工作像是其逆向工程。虽然发条装置的小齿轮在我们面前嘀嗒作响,但其神秘面纱仍然未被揭开,距离完全理解视觉原理还有很长一段路要走,但我们已经窥得一些非凡的东西

◆ 生物进化是宇宙中唯一能够从零开始创造真正智能的力量,我觉得我们正在复原其线路图,或者至少是其中的一些片段

◆ 作为人类,我们天生就有一种神奇的本领,那就是可以仅凭对陌生事物的一瞥,再次遇到时就能认出来,不管是一样新的乐器、一种我们从未见过的动物,还是一位新当选的政治家

◆ 即使面对全新的事物,无论多么新奇,我们也会借助一生的经验来加以理解。我们所看到的几乎一切都深深地融入了过往的经验——轮廓、光影、纹理和图案等熟悉的细节,以至我们很难想象能真正孤立地看到任何东西。

◆ 我们选择的机器学习算法的数学核心是“贝叶斯网络”(Bayesian network),这是一种概率技术

◆ 数据被公然视为一种惰性商品,只在算法需要时才重要,虽然这种观点并不稀奇,但我开始意识到,有一些重要的东西一直都被低估了。

◆ 我欣赏他勇于冒险的精神,但也不得不考虑现实情况。我知道收集、标记和组织图像的实际工作将会落在我身上,所以我总是尽力平衡我们的研究需求和日常生活的实际问题。

◆ 我比以往任何时候都更加确信,分类是连接一切研究的核心思想

◆ 我们的算法出现了数据科学中所说的过拟合现象(overfitting)。

◆ 也就是说,无论算法设计得多么巧妙(我们探索了所有能找到的算法),即使是那些在测试中表现最好的算法,在遇到新的刺激时,也会很快出现问题

◆ 那些看似经过有效训练的算法,却无法将它们所学到的知识,或者说它们本应学到的知识,应用于现实世界

◆ 从本质上讲,这与人类的感知能力恰恰相反

◆ 人类的感知能力是由泛化能力决定的,泛化能力增强了我们的灵活性和适应性,甚至让我们富有创造力,让我们能够随时利用新想法的力量锐意进取,而不是停留在过去的经验中止步不前

◆ 任何缺乏泛化能力的生物都会很快被自然界的不可预测性击垮,因此这种能力是生物进化思维的关键特征。然而,对机器来说,泛化在很大程度上仍然是遥不可及的

◆ 单词在帮助我们对所见事物进行分类方面发挥着基础性的作用,因此他推断,对所有离散且可量化的事物的单词(即英文中的可数名词)进行计数,将是一个很好的起点

07 一个假设

◆ ImageNet不仅是一个数据集,它是一个假设、一个赌注,即实现真正机器智能的第一步,是沉浸在完整的视觉世界中

◆ 视觉不仅仅是一种“感觉”,至少不是那种可以用温度计或盖革计数器测量的“感觉”,而是一种体验的催化剂

◆ WordNet是心理学和认知科学领域的传奇人物乔治·阿米蒂奇·米勒(George Armitage Miller)的杰作

◆ 他想通过WordNet以极其庞大的规模绘制出语言结构图

◆ 例如,我们不因为拼写接近而把“apple”(苹果)这个词与“appliance”(器具)进行关联,而是将它与“food”(食物)、“fruit”(水果)、“tree”(树)等一系列相关的词汇进行集群配对。这样形成的词汇数据库就像一张地图,将人类所珍视的一切(也就是我们用词汇描述的一切)排列在一个相连的空间里。简而言之,这就是WordNet

◆ 1985年启动以来,WordNet已经发展到极其庞大的规模,收录了超过14万个英文单词,并迅速扩展到新的语言

◆ 首先是WordNet,一个目标无比宏大的词汇数据库,几乎捕捉了世界上所有的概念,并以人类意义的自然层次组织起来。然后是ImageNet,它致力于为每个概念配上一张图片

◆ 深深地陷进椅子里,缓缓地呼出一口气。我简直不敢相信自己即将说出口的话。“你对干洗了解多少?”

◆ 在线平台可以将任务分配和结果收集过程自动化,有效组织远程的临时工作团队,规模小到个人,大到数百万人的团队。“如果你感兴趣的话,亚马逊就在提供这种服务,叫作‘土耳其机器人’。”

◆ 这个名字很妙,源于18世纪的一种会下国际象棋的自动机器“土耳其机器人”。当时,这个机器人在世界各地巡回展出,被视为一个工程奇迹。它棋艺高超,就连国际象棋高手也甘拜下风。但实际上这个装置纯属骗局:在机器人底座里就藏着一个人类国际象棋大师,正是这个人在操控机器,让观众既兴奋又困惑

◆ 几个世纪后,新兴的众包实践基于同样的理念:真正的智能自动化仍然最适合由人类来完成。亚马逊土耳其机器人(Amazon Mechanical Turk, AMT)围绕这个概念建立了一个市场,“请求者”可以发布“人类智能任务”,由贡献者完成,这些贡献者被称为“土耳其人”(Turker),他们可能来自世界上的任何地方。从理论上讲,这个模式很合理,似乎可以提供我们想要的一切:既有人工标注图片带来的智慧成分,又有与自动化相当的速度与规模。有趣的是,亚马逊称之为“人工人工智能”,这个名字相当贴切。

◆ 我还开始不定期地前往旧金山湾区,拜访斯坦福大学的机器学习和计算机视觉先驱,其中包括吴恩达(Andrew Ng)、达夫妮·科勒(Daphne Koller)和塞巴斯蒂安·特龙(Sebastian Thrun)

08 实验验证

◆ 我不由得笑了起来。一个21世纪的学生用“老古董”这个词来形容几十年前的工作,足以证明我们的领域是多么年轻(可能也证明我正在变老——我选择无视这种可能性)

◆ 神经网络是由生物学启发、层次分明的相互连接的决策单元阵列。由于计算机视觉领域的迅速发展,到了21世纪初,我们中的大多数人已经把神经网络看成是尘封已久的艺术品,包裹在玻璃罩中,四周用天鹅绒绳索保护,闲人勿近。

◆ 冠军算法名为AlexNet,是向这项技术和项目的主要作者、多伦多大学研究员亚历克斯·克里热夫斯基(Alex Krizhevsky)致敬。

◆ 这就像是听说一辆本田思域以每小时160千米的速度差打破了陆地速度的纪录。根本不可思议。进步不应该是这样的。

◆ AlexNet是卷积神经网络(Convolutional Neural Network, CNN)的一个实例

◆ 卷积神经网络的叫法源于图形卷积过程。在这个过程中,一系列滤波器在图像上扫过,寻找与网络所识别事物相对应的特征

◆ 这是一种独特的有机设计,灵感来自休伯尔和威塞尔对哺乳动物视觉系统的观察,即视觉处理在多个层次上进行。就像在自然界中一样,卷积神经网络的每一层都会逐渐整合更多的细节信息,从而形成越来越高层次的感知,最终将真实世界的物体完整地呈现在我们的视野中

◆ 最终,经过各层过滤后,仅剩下少数几个信号被融合成识别对象的详细图像,进入网络的最后阶段:识别阶段

◆ 当然,这些并不是什么新的创意。自从贝尔实验室成功将卷积神经网络应用于手写邮编,杨立昆多年来一直对卷积神经网络保持着惊人的忠诚。在AlexNet诞生时,他已经花了20年时间坚持不懈地完善算法、发表研究成果,但一直没有必要的资源来充分实现这些成果。

◆ 事实上,在ImageNet的帮助下,AlexNet焕发生机,它贪婪地吸收着ImageNet的内容,在ImageNet规模和多样性的土壤中生根发芽,茁壮成长

◆ 现实世界中幽灵般的碎片,以恰到好处的方式组织起来,供算法来查看

09 万物以外是什么

◆ 几十年来,曾经大胆自称“人工智能”的领域已经分裂成许多细分的学科,其中许多学科的命名抛却了其认知根源,转而使用更机械化的术语,比如模式识别(pattern recognition)和自然语言处理(natural language processing)。在这个过程中,对中心实验室的需求逐渐消失

◆ 另一个则是长期兼顾教育和硅谷领导职务的吴恩达,他卸任了斯坦福大学人工智能实验室的主任一职。在许多资深同事的支持下,我接任了实验室的第七任主任,也是首位担任这一职务的女性

10 似易实难

◆ 然后,她又想了一会儿,找到了背后的原因。“我一点儿尊严都没有了。彻底丧失了。在那样的时刻……”她似乎有些语无伦次。我正想鼓励她继续说下去,她就接着说完了:“甚至健康都不重要了。”

◆ 个体的尊严是至高无上的——这是任何数据集都无法解释、任何算法都无法优化的变量

11 无人可控

◆ AGI指的是“通用人工智能”(artificial general intelligence),是一种极其复杂、灵活的人工智能,

◆ 请大家不要每天只从arXiv下载最新的预印本作品了。去读一读拉塞尔和诺维格的著作,去读明斯基、麦卡锡和威诺格拉德的书,读哈特利和西塞曼的作品,读一读帕尔默写的东西。不要因为这些材料距离现在时间久就忽略它们。我们就是要多读一些以前的东西,他们的理念经得起时间的考验,依然非常重要。”

12 下一颗北极星

◆ 那是2019年的春天,是“CS231n:卷积神经网络视觉识别”课程开设的第三年

◆ 原来我们都是彻头彻尾的普通人。我们也许略有成就,但依然有弱点,依然会犯错,而犯错的方式是学生时代的我无法想象的。

◆ 人工智能如何才能尊重人的尊严呢?这个问题是一切研究工作的立足点

◆ 伦理、社会与编写代码之类的工作有什么关系呢?”

译后记

◆ 《我看见的世界》是李飞飞博士的自传。她是美国三院院士,是计算机科学家,是人本主义者,是母亲、女儿、妻子,是曾短暂涉足商界的学术人士。

— 来自微信读书

《奔跑吧,程序员:从零开始打造产品、技术和团队》

阅读感悟

暂略

阅读摘录

《奔跑吧,程序员:从零开始打造产品、技术和团队》

叶夫根尼·布里克曼 194个笔记

1.2 什么是科技创业公司

◆ 创业公司就是在极度不确定的条件下创造新产品或服务的人类组织。——Eric Ries, 《精益创业》

◆ 创业公司的目标在于快速增长。一家公司成立的时间短并不能让其本身成为创业公司,创业公司也未必要从事科技领域的工作,未必要接受风险投资基金或有某种“退出”的机制。创业公司唯一必不可少的东西就是增长,其他和创业相关的所有东西都是伴随着增长而来的。——Paul Graham, Y Combinator联合创始人,硅谷创业教父,《黑客与画家》作者

◆ 成熟企业拥有的产品已经被市场证明是为大家所接受的,所以它们关注的是扩大规模、优化产品和提升执行效率。而创业公司并不知道什么样的产品能在市场中立足,所以公司的主要注意力将放在试验、尝试和纠错上,重点是寻找一种可重复、可扩展的商业模型

◆ 可以这么说,创业公司的最后一个要素就是它们是按探索模式运作的

◆ 科技创业公司”是具有下述特征的组织。·产品:技术。·环境:极度不确定。·目标:大幅增长。·运作模式:探索。

◆ 这本书的内容既可以用在全新的只有3个人的公司上,也可以用在有着3000人规模的成熟公司新成立的创新机构上。只要你进行的是技术研发,环境总处于变化之中,主要目标是为了增长,机构是以探索的模式在运行,那么书中的内容就适合你

1.3 为什么应该在创业公司中工作

◆ 要在科技创业公司中工作,甚至自己创立这样一家公司,我们应该考虑三个主要因素:更多的机会、更多的所有权以及更多的乐趣。

◆ 一直以来,我们的大脑和身体都通过各种人造部件和技术得到增强。这种情况潜移默化地发生,让人无法觉察

◆ 换句话说,就像Marc Andreessen在2011年预测的那样——“软件正在蚕食世界”。因为科技愈加无所不在,软件公司将会占据越来越多的产业

◆ 也可以使用New Relic、KISSMetrics或者MixPanel,而不用去研发自己的监控软件;可以使用Amazon SES、MailChimp或者SendGrid,而不用去搭建自己的email服务;如果需要logo,可以使用DesignCrowd;如果需要法律服务,可以使用RocketLawyer;如果需要接受付款,可以使用Stripe;如果需要管理客户数据,可以使用Salesforce;如果需要提供客户支持,可以使用Zendesk。

◆ 我们的父母或祖父母很可能会在同一家公司工作50年,顺着职业的阶梯不断攀爬,戴着金表退休[插图]。现在,我们再也享受不到这种待遇了,因为那种工作已经消失了。据统计,美国60年代初期出生的大多数人会在18~46岁从事11.3份工作,这个数字可能在不断地上升,20世纪80年代初期出生的大多数人到26岁时已平均从事了6.2份工作。从第一个数字可以算出,平均一份工作的持续时间还不到3年。大公司的工作并不比小公司的工作更稳定。例如,仅仅在2014年,思科就解雇了6000名员工,IBM解雇了13000名员工,微软解雇了18000名员工,HP则解雇了27000名员工。所以,所谓的工作稳定性已经不复存在。

◆ 我在大学毕业正决定去哪儿的时候得到了一条建议:你应该把硅谷当作一家大公司,其中有Facebook部门、Google部门和一大堆小型创业部门

◆ 真正的风险并不是因加入了小型创业公司而失业——毕竟我们在大公司工作也没办法保证不失业——而是失去机会的风险

◆ 可能某一天要编写数据库查询,第二天又要设计用户界面,之后还得回复客户的服务邮件,中间又要腾出时间准备投资者的融资演讲稿,期间培养出的这些技能将对你今后的职业生涯大有裨益。你也会学到如何应对紧张、压力和风险,会被推出自己的舒适区之外,这才是你真正能学到东西的地方。这也就是为什么很多人在创业公司三个月要比在大公司工作三年学到的还多。

◆ 师综上所述,自主权、掌控力和使命感是激励人的三个最强有力的因素

◆ 如果你找到了一份可以同时提供这三者的工作,那么就是找到了一份你会热爱的工作,也是一份你可以为之自豪的工作

1.5 小结

◆ 在你成长的过程中,人们总是会告诉你:这个世界就是……尽量不要撞了墙也不回头,要努力拥有美好的家庭,要学会享乐,要存下一点钱。那是一种非常有限的生活。生活可以变得更加多彩,只要你发现这样一个简单的事实:你周围的一切,即你所谓的生活,都是由不如你聪明的人组成的,你可以去改变它,可以去影响它,也可以做出自己的东西供他人使用。一旦意识到这一点,你将从此不同。——史蒂夫·乔布斯

2.1 点子从何而来

◆ 点子也不会凭空发展进化出来,物理学的能量守恒定律阐述了能量从来不会凭空产生或湮灭,而是以不同的形式被重新利用,点子也同样遵守这样的守恒定律,所有新的想法只不过是现有想法组合而成的结果

◆ 。但真实的情况是“我们都在用相同的材料做东西”(弗格森语),混搭和重新合成是产生新点子的常见方法[插图]。那是因为创造力的产生可以归结为三个阶段,这三个阶段都不过是不同形式的重新合成:(1)模仿;(2)转换;(3)合并。

◆ 如果我们想要找到创业的好点子,就需要一整堆的“原料”,从而可以在此基础上去研究、注明出处、重新合成、聚合和转换,也就是说,我们需要掌握大量的知识

◆ 其目标,正如史蒂夫·乔布斯所说的,就是努力“让自己感受人类最美好的东西”。

◆ 纵观人类历史,出现了不少多重发现(multiple discovery)的例子,即有两个或多个科学家或发明家在差不多相同的时间内提出相同的想法,比如牛顿和莱布尼茨都在17世纪发表了关于微积分最早的论文,达尔文和华莱士都在19世纪提出了进化论,而格雷和贝尔在同一天提交了电话的发明专利申请。这一切都不是偶然,它表明环境对新点子的涌现有巨大影响

◆ 如果你深深沉浸在某一个主题中,日复一日致力于此,你的潜意识除了解决问题就不干别的了。也许在某天清晨或者午后醒来,你就找到了答案。对于那些并没有想方设法、全心投入解决现有问题的人,潜意识就会在其他事情上“游手好闲”,不可能有什么大作为。所以自我管理的方法就是一旦你有什么真正重要的问题,就不要把其他事情置于自己注意力的中心,你要一直把心思放在这个问题上。让你的潜意识保持在“饥饿”状态,不得不解决你的问题。这样你就可以平静地入睡,等待清晨醒来时得到答案,得来全不费工夫。——Richard Hamming, You and Your Research演讲

2.2 验证

◆ 精益和敏捷方法的核心原则就是尽可能快地把可用的产品放在用户面前,即便产品离最终完成还有很远的距离。相反,瀑布方法则是希望先做出完整的解决方案,再呈现给用户

◆ 在《四步创业法》一书中,Steve Blank描述了一种叫客户开发的过程,该过程应该和产品开发过程同时进行

◆ 我们应该在第一天就把客户纳入开发过程当中,而不是等到产品完成了,才考虑它有没有客户

◆ 客户验证是指,承认我们的产品点子只不过是需要测试的、未经证明的假设,这样的测试必须以尽可能快、尽可能低的成本对真实客户实施。越早向同事之外的人验证你的点子,成功的概率就越大

◆ 可以分解为以下三个连续的阶段[插图]。第一步:验证问题确保找出客户实际面临并且痛苦到愿意掏腰包去解决的问题。第二步:验证MVP实现潜在解决方案的最简可行产品(minimum viable product, MVP),让少量客户购买该产品进行验证。第三步:验证产品把MVP完善为完整的产品,让更多客户去购买,对可扩大化的商业模式进行验证。

◆ 医生想要更多的病人,而不是更有效率的诊所

◆ 高效的诊所就会有更多的病人,但是关注错误的问题会导致整个公司误入歧途

◆ 关注了错误的问题就意味着所有的产品、市场营销策略和促销材料全都是错误的

◆ 在思考问题的大小时,有三个方面需要考虑:频率、密度和痛苦程度。

◆ ·频率:你所解决的问题经常发生吗?·密度:有很多人都会面临这个问题吗?·痛苦程度:该问题只是让人讨厌,还是绝对必须解决?

◆ 考虑市场规模有一个好方法,就是考虑建立一家赚得10亿美元收入的公司的几种方法。·以1美元的价格销售10亿件产品:可口可乐(罐装汽水);·以10美元的价格销售1亿件产品:强生(家用产品);·以100美元的价格销售1000万件产品:暴雪(《魔兽争霸》);·以1000美元的价格销售100万件产品:联想(笔记本电脑);·以1万美元的价格销售10万件产品:丰田(汽车);·以10万美元的价格销售1万件产品:Oracle(企业级软件);·以100万美元的价格销售1000件产品:Countrywide(高端金融抵押公司)。——Balaji S. Srinivasan,斯坦福创业项目工程课程

◆ 由丰田的创始人丰田喜一郎所提倡的,就是五个为什么

2.3 小结

◆ 许多数学家更喜欢因为问题固有的美而去研究问题,而非这些问题有什么实际的好处

3.1 设计

◆ 在顾客看来,界面就是产品。——Jeff Raskin, 《人本界面》

◆ Joel Spolsky把这种情况叫作冰山的秘密。我们所看到的冰山在水面上的那部分只占它总体积的10%;同样,我们可以看到和触碰到的产品的那部分——用户界面,只占全部工作的10%。所以,这个秘密就是大多数人并不清楚这一点。

◆ 以用户为中心的设计应该纳入我们的产品开发过程中,下面是它的五个基本原则:·用户故事;·人物角色;·情感设计;·简单;·可用性测试。

◆ 所谓用户故事,就是从用户的角度简短地描述你所做的东西。它应该回答下面三个问题。·用户是谁?·他们要实现什么?·他们为什么需要?

◆ 如果你是一名程序员,想理解你的用户会更加困难。每个人都会对“一种产品如何工作”形成自己的概念模型。但程序员的模型通常是非常细节化的,一般都处于界面、事件、消息、API、网络协议和数据存储这样的层次,而典型的用户模型通常没有那么多的细节,既不精确也不完整(例如,许多用户是不能区分软件与硬件、显示器与电脑有什么差别的)。这种概念模型上的不匹配会导致程序员很难和用户沟通。

◆ 许多程序员并没有意识到,因为对自己的软件非常了解,所以考虑软件的方式和用户是完全不同的,我们全然记不起初学者面对我们的软件是什么感觉。这种情况称为知识之祸,

◆ 作为程序员,当你在设计软件的时候,你的大脑其实一直都在“听着歌曲”。然而,你的用户却什么都没有听到,他们必须通过你所设计的用户界面(user interface, UI)去使用软件

◆ 你不能期待用户知道你所知道的,你也不能指望用户通过文档或教程来填补这一鸿沟

◆ 正如Steve Krug所说的:“关于说明书你必须知道的最主要的一件事就是,没有人想读说明书。”

◆ 想要做出成功产品的唯一选择就是做出出色的设计

◆ 大多数设计给程序员使用的软件也是设计给电脑用的,而电脑可不在乎可用性的问题

◆ 要成为一名成功的程序员,就得对糟糕的设计有很强的容忍力,几乎要到熟视无睹的地步

◆ 但如果要开发普通人可以使用的软件,就必须和他们一样去感同身受,压制自己作为程序员的许多本能感受

◆ 编程的过程和制作易用产品的过程是格格不入的,简单来说就是程序员的目标和用户的目标是有显著差别的。程序员希望构筑产品的过程顺利简单,用户则希望与程序的交互顺利简单。而这两个目标通常都不会共生于相同的程序。——Alan Cooper, The Inmates Are Running The Asylum

◆ 即便你克服了理解用户的障碍,还会面临第二个问题:他们希望实现什么

◆ 最常见的设计错误就是把用户的目标(他们要实现的是什么)和任务(他们可以如何实现)混淆了

◆ 经典的例子来自于冷战时期的太空竞赛,NASA的科学家意识到无法在太空的微重力环境下使用钢笔,所以花了数百万美元研发出一种带有加压墨盒的钢笔,它可以在零重力、上下颠倒、水下以及高温严寒等各种环境下书写。与此同时,苏联人使用的却是铅笔

◆ 这个故事虽然只是个传闻,但它却精彩地说明了当我们无视根本目标,而对做事情的某个特定方法过于关注时,会有什么荒唐的事情发生

◆ 正如亚伯拉罕·马斯洛所说:“如果你唯一的工具是一把锤子,那么你看到的任何东西都像钉子,我想这对人们是很有诱惑力的。

◆ 把任务从目标中分离出来的一种最佳方法就是使用上一章介绍过的“五个为什么”技巧(阅读2.2.3节了解更多信息)。

◆ 要区分任务和目标之间的差异,有一种很简单的方法。任务会随着技术的变化而变化,但目标有一种讨人喜欢的属性——它会非常稳定。例如要从圣路易斯到旧金山旅行,我的目标始终是速度、舒适性和安全。但在1850年去加州金矿的话,我会在全新的高科技科内斯托加马车中度过这段旅程。为了安全,我还会带上温切斯特来复枪。而在1999年从圣路易斯到硅谷,我会乘坐最新的高科技波音777飞机。——Alan Cooper, The Inmates Are Running The Asylum

◆ 第三个问题“为什么人们需要它”实际上就是强迫你证明为什么你要做你所做的东西,这就是上一章介绍的客户开发过程可以发挥作用的地方(阅读2.2.2节了解更多信息)

◆ 如果该产品或功能并不能为真正的用户解决重要的问题,你就不应该浪费时间去实现它。

◆ 无论什么时候,我们都应该花些时间用书面方式回答这三个用户故事问题,把你的产品点子从脑海中短暂、模糊的想法转变成纸面上具体的文字和图示,这样可以暴露出你在问题理解上存在的一些缺陷

◆ 在Readme文件、维基系统或便利贴上记录几行文本、画出几幅草图,就可以促使自己从用户的角度去感受这种端到端的体验,确保自己知道自己在做什么,为谁做,以及为什么值得做

◆ 这里有另一种可以显著提升设计技能的快捷方法:不要再为“平均的人”设计产品。人平均下来就是不洋不土、不男不女,如果你为平均的每个人做设计,那么谁都不会喜欢你设计出的东西

◆ 真正的平均用户被保存在日内瓦国际标准局的密不透气的地下室中。——Steve Krug, 《点石成金》

◆ 你关注的目标越广泛,错失靶心的必然性就越大

◆ 想让大量人口中50%的人满意你的产品,从而实现50%产品满意度的目标,这种做法是行不通的。我们只能挑选出50%的人,想方设法让他们100%满意,才能实现我们的目标。我们甚至可以瞄准市场中10%的人,让他们100%地心醉神迷,从而取得更大的成功。这听起来可能有点违背我们的直观感觉,但为单个用户进行设计是满足广大人群需求最有效的方式。——Alan Cooper, The Inmates Are Running The Asylum

◆ 人物角色之所以是如此强有力的设计手段,是因为它可以促使你去考虑真正的人,估计他们的真实需求、局限性和个性,最为重要的是,考虑他们的情感

◆ 研究表明,人与计算机及软件之间的交互在很大程度上与人类之间的交互类似

◆ 我们大部分的情感反应是自动产生的,控制这些反应的大脑区域尚未进化到能够对人和举止像人的无生命物体进行区分的程度

◆ Mailchimp把它的吉祥物(一只穿着像邮递员的猴子)放在了几乎所有的页面上,Tumblr的停机页面显示的是一只叫Tumblebeast的神奇小野兽在机房里发泄破坏,而Twitter的停机页面则是一只“失败鲸”(见图3-2)。

◆ 设计的情感因素对用户而言就像功能因素一样重要

◆ 你的产品是能说话的——每天24小时都在和你的客户交谈。——Jason Fried、David Heinemeier Hansson、Matthew Linderman, Getting Real

◆ 只要有可能,我们就应该把软件设计得像把你记在心上、考虑周到的人一样。要记住用户的参数设置,记住他们上次使用你的软件做了什么事情,记住他们过去搜索了什么东西,要尝试使用这些信息预测用户在以后会做什么事情

◆ 响应能力也没必要做得太过花哨,经常被忽视的一种最简单的设计元素,就是要提供基本的反馈

◆ 如果UI没有给出反馈,用户就不知道点击操作是否执行了,要么会连按10多次按钮,要么就失去信心完全放弃了

◆ 人都会犯错,而且还会不断犯错。在设计软件时,要假设用户也会输入错误、点击错误的按钮或者忘了一些重要的信息

◆ 这里不谈人为的错误,而是探讨沟通与交互:我们通常把糟糕的沟通或交互称为错误。当一个人与另一个人合作时,永远不要用错误这个词来形容另一个人的表达方式。因为每个人都希望能够理解他人的话语并做出回应,如果出现了无法理解或看似不恰当的地方,可以质疑,可以澄清,可以继续合作下去。那为什么人与机器之间的交互不能看作是合作呢?——Don Norman, 《设计心理学》

◆ 以下是一些经验法则,可以避免这种错误的发生。·提供帮助和指引,而不是错误消息。例如避免使用“错误”“失败”“问题”“无效”和“异常”这样的词,而是向用户解释程序希望获得的输入与用户的输入之间有什么差异。·在用户输入的同时进行检查(而不是在页面提交之后再进行),并分别给出肯定和否定两种反馈,给出的反馈应该在用户视线附近(而不是页面的顶部)。·永远不要把用户做好的东西弄丢。

◆ 除了显示有帮助的信息,我们也应该试着做出能在一开始防止错误发生的设计。在精益制造中,这种方法称为poka-yoke,这是一个日本术语,意思是“防止错误发生”

◆ 除了要对错误的状态进行处理,还要确保设计能够处理空白状态:应用程序就像用户第一次和它交互、什么数据都没有输入的样子

◆ 如果动态消息是完全空白的,新用户一定不觉得是很好的体验,他们可能不会继续使用你的服务

◆ 几乎所有的创新训练都有一个相同的目标:简单

◆ 《代码大全》的作者、程序员Steve McConnell在书中写道:“攻克复杂性是软件开发中最为重要的技术主题。

◆ Apple的首席设计师Jonathan Ive说:“简单之中蕴含着一种深刻而经久不衰的美。”每个人都在为简单而努力,问题是让事情简单并不是一件简单的事

◆ 我本来想把信写得简短一点,但我没有时间。——Blaise Pascal

◆ 正如Antoine de Saint Exupéry所言:“达到完美并不是没有东西可以添加,而是没有东西可以去除。”

◆ [插图]图3-8:简单(图片由Eric Burke提供)

◆ 在设计上做得比较成功的公司,均认识到加入软件中功能的数量并不受“瑞士军刀空间有限”那样的物理限制,而是受使用软件的人的心理限制

◆ 人们认为专注就是要对你关注的东西点头称是,但是事实并非如此。专注其实是要对其他一百个出现的好主意说不——所以你只能小心挑选。实际上,我对我们不做的事与我们做了的事一样感到自豪。创新就是对1000种东西说不。——史蒂夫·乔布斯

◆ 在这份教程中,我将主要关注两个例子:一是解决一份简历存在的设计问题,这是几乎所有人都非常熟悉的设计任务;二是从头开始设计一个网站(具体而言就是本书的配套网站hello-startup.net),其过程有点像典型的创业产品所需要经历的过程

◆ 实际上所有软件设计的真正核心几乎都是文案

◆ 伟大的界面是写出来的。如果你认为每一个像素、每一个图标和每一种字体都很重要,那么你也要相信,每一个字母都很重要。——Jason Fried、David Heinemeier Hansson、Matthew Linderman, Getting Real

◆ 好的艺术家模仿,伟大的艺术家偷窃。——史蒂夫·乔布斯

◆ 如果你不想立马跳到代码中,可以先使用线框图或原型工具,比如Balsamiq、UXPin或者Justinmind。利用这样的工具,可以从UI元素库拖拽出一些元素进行摆放,组合成一份设计

◆ 布局有一个要点就是亲密性,元素之间的亲近程度表明它们在逻辑上是否相关联。逻辑上联系在一起的元素应该更加接近,没有关联的元素则应该远离一些

◆ 作为经验法则,我们要尽量挑选一条明显的线,让所有东西都向它对齐。换句话说,不要把一些内容左对齐,而另一些内容右对齐,有些内容又居中对齐。另外,要谨慎使用居中对齐,因为这种方式在我们的大脑中并没有建立起一条明显的线,会让设计看起来更加业余。这也仅仅是一条经验法则,你当然可以偶尔打破它,但也必须是有意识而为之

◆ 排版就是安排文本的艺术与科学,目的是让文本易读和美观。这一节将关注排版最重要的几个因素:行宽、行距、字体和样式

◆ 行宽是指每一行的长度。如果文本行太短,读者就会太过频繁地被打断而跳到下一行。如果文本行太长,读者就没耐心把它读完

◆ 行距是行与行之间的垂直间距。和行宽一样,如果行距设置得太小或太大,文本的阅读都会比较困难。行距的最佳尺寸一般都是字体尺寸的120%~145%

◆ 总的来看,所有字型都可以分成五类:衬线字型、无衬线字型、装饰性字型、手写型和等宽字型

◆ 。衬线字型作为最古老的字型,不仅可以追溯到使用印刷术的年代,甚至可以一路追溯到古罗马人刻在石头上的字母。所以,如果想要有“传统的”感觉,就可以在标题中使用衬线字型

3.2 MVP

◆ 。执行是昂贵的,所以你需要尽可能低成本、快速地向客户验证你遇到的每一个新问题和想法。最好的方法就是实现所谓的最简可行产品(minimum viable product),或者叫MVP。

◆ [插图]图3-35:如何实现可行的MVP,图片由Henrik Kniberg提供

◆ 我问(我的客户)“如果产品是免费的,有多少人会真的购买或使用”的目的就是把价格因素抛开,看看产品本身是否能够让客户心动。如果做到了,我会接着问几个问题:“好了,这个产品不是免费的。事实上,假设我要收取你们100万美元,你们还会购买吗?”虽然这种对话听起来有点儿像开玩笑,但我一直在用这种方法。为什么呢?因为超过一半的时候,客户会像这样说:“Steve,你怕是疯了吧。这个产品不会值25万美元以上。”其实,我只不过想让客户告诉我,你们愿意支付多少钱而已。——Steve Blank, 《四步创业法》

◆ 在《创新的扩散》一书中,Everett Rogers把客户分成了5种类型。(1)创新者愿意承担新技术的风险,因为技术本身就是他们生活中的主要兴趣,不管功能如何,他们总是留心寻找最新的发明。(2)早期采用者也愿意承担新技术的风险,不仅仅因为他们对技术感兴趣,还因为他们很容易联想到该技术将会给生活带来什么样的好处。(3)早期的大多数客户是迫切需要解决具体问题的。他们能够想象到新技术是怎样成为解决方案的,但又知道许多新的技术革新最终都会失败,所以在自己购买新技术之前,他们更愿意等待,看看该技术是否能解决他人的问题。(4)后期的大多数客户也有需要解决的具体问题,但他们不喜欢使用新技术去解决问题。他们更愿意等到一项技术成熟,自身已经成为标准,并且已经具备了很好的支持体系才会购买。(5)滞后者会尽可能避免使用新技术。他们是最后采纳新发明的人,而且通常都是在别无选择的情况下才会采纳。

◆ Pinterest的创始人会去咖啡店里亲自让陌生人使用他们的产品,也会去Apple的体验商店把所有的浏览器首页都设置为Pinterest

◆ 蒂姆·库克不会在你买了笔记本电脑之后给你寄一张手写的卡片——他做不到,但你可以。这就是小公司的好处:你可以提供大公司实现不了的服务。一旦意识到现有的一些习惯做法并没有超出用户的预期体验,不妨好好想想,我们可以用多少手段去取悦用户,这是很有意思的。——Paul Graham, Y Combinator联合创始人,硅谷创业教父,《黑客与画家》作者

◆ 当你还是一家小型创业公司,仍然还在验证自己的点子时,做一些无法规模化的事情去获得早期的客户是你可以承担的方式。如果点子可行,后续可以通过自动化的方式,让这一过程变得更具扩展性;但如果点子不可行(大多数点子都是这样的结果),那么你也节省了大量的时间,因为你不需要为了错误的事情而做一大堆自动化工作

◆ 另外,这种方式可以让你直接接触业务的烦琐细节,你会成为领域的专家,而这一点在前面已经说过,它对于想出伟大的点子是至关重要的。

3.3 小结

◆ 用户界面就像讲笑话,如果非得解释清楚,就不那么好玩了。——Martin Leblanc, iconfinder创始人

4.1 数据

◆ 产品经理的工作就是要把两件简单的事情说清楚:·我们正在进行什么比赛?·我们怎么得分?把这两件事情做对,就可以不经意间聚集一批在技术、运维、质量、设计和市场推广上具备天赋的杰出人才,在同一个方向上聚力前行。没有这两点,无论做多少优化和执行管理,都拯救不了你。——Adam Nash, Wealthfont主席、CEO

4.2 营销

◆ 以下是创业公司最常见的4种营销渠道:·口口相传;·市场推广;·销售;·品牌化。

◆ 传播产品信息最强大的方法就是不自己去传播,而是让你的客户去传播

◆ 近来许多人都在谈论如何使用“病毒营销策略”,但现实中却没有这样的东西。在所有社交网络上出现的病毒式传播的博客文章或视频并不是一种市场推广策略,而是纯属好运

◆ 病毒式传播不仅仅是另一种形式的口口相传,如果你想突破我们前面讨论的实现途径(做出更好的产品、提供出色的客户服务),更进一步激发人们对你的产品进行口头宣传,需要的不是一种“病毒式的市场推广策略”,而是让产品进入一种病毒式循环。

◆ 同样,Apple最成功的广告之一 ——“不同凡想”(Think Different)并不是在介绍电脑或CPU速度,也不是在介绍Apple为什么比微软更出色,而是在回答“Apple是谁”以及“它代表什么”的问题。

第二部分 技术

◆ 好的技术栈的扩展要快于需要进行的维护。

◆ 谈到技术的作用,WhatsApp团队就是一个很好的例子。该团队基于Erlang搭建了一个技术栈,可以支持每秒7000万条Erlang消息、4500万用户,每天500亿条消息,每年7.2万亿条消息[插图],而完成这一切的团队只有32个工程师

◆ 当然,这里讲WhatsApp的故事并不是说所有人都应该用Erlang。各种成功的创业公司都会基于他们所有可能想得到的技术,去实现他们的产品。这一章会帮助你解决“创业中可以使用什么样的技术栈”这一问题。首先,我会描绘如何选择最初的技术栈,如何随着时间推移使它逐步进化;接着,将把实现内部技术方案、购买商业软件和使用开源软件之间的权衡利弊呈现给大家;最后,我会深入谈谈创业公司最常遇到的3个技术决定的一些细节,即编程语言、服务器端框架和数据库

5.2 技术栈的进化

◆ 当我(离开Google)到了AdMob后,我心中的第一个冲动就是“噢,垃圾垃圾垃圾”。当然我没有说出口,因为作为领导者,学会的更重要的一课就是在开始说之前先闭上嘴巴去听。但是我脑子里想的是:“在Google我们是不会这样做的,在Google我们是不会这样做的,在Google我们是不会这样做的。”这句话在脑海里闪过了好多次之后,我才意识到这并不是在Google,我要解决的是不同的问题,面对的是不同的技术文化——这是很好的事情,真的很好

◆ 我们先从需要在很早就做出的一个决定开始:如何为创业公司选择初始的技术栈?我可以只用一句话来回答:熟悉什么就用什么

◆ 这句话源于我为这本书所做的访谈,我可以告诉你,每一家创业公司选择的只不过是创始团队最精通的技术。LinkedIn是用Java实现的,因为创始团队了解Java; GitHub的创始人全部都是Ruby开发者,所以他们也是用Ruby去实现网站的;Twitter主要用的是Rails,因为他们的早期员工中有许多人熟悉Rails。Foursquare开始时使用PHP,因为这是联合创始人Dennis Growley所了解的;Pinterest使用的是Python,因为创始团队对Python比较熟悉

◆ 学习一门新的技术、享受它承诺的所有理论上的好处可能很好玩,但在创业早期,我们的目标是认识到用户需要什么,其他任何花时间的事情都是浪费。在早期,产品的用户和代码都不多,所以可扩展性并不是太大的挑战,我们要做的就是尽可能快地进行迭代

◆ 如果你的创业公司足以成功地生存到“下一周”,也许你可以让技术栈有所进化,使之满足新的需求。例如,Twitter开始时用的都是Ruby on Rails,但发展起来之后,就不得不迁移到Scala和JVM上;HubSpot从.NET和SQLServer迁移到JVM和MySQL、Hadoop及HBase; Coursera现在从PHP迁移到了Scala; LinkedIn在它的发展历史中已经尝试了十多种技术,包括Java Servlets、Groovy on Rails、JRuby on Sinatra、Java和SpringMVC、JavaScript和Node.js,以及Scala和Play Framework

◆ 最初选择的技术并不是关键,最初的决定在最后看来一定是错的,唯一的问题是它会错多久。关键在于,你要在遇到拐点时有壮士断腕的勇气,而不是为了存活而一层一层地给它贴上创可贴。遵循这样的准则极其重要。从目前来看,这比紧跟潮流、为了做出初始的最佳技术决定而进行无穷无尽的设计分析要更为重要。我们应该做的是,确保把自己和环境锻造成能够适应各种变化,能够知道什么时候是重建的合适时机。——Kevin Scott, LinkedIn高级副总裁、Admob副总裁、Google主管

5.3 内部实现、购买商业产品,还是使用开源产品

◆ 一个残忍的事实是:当你的关键业务过程运行在内部情况不清楚(更别提修改)的不透明代码上时,你就失去了对业务的控制。你对供应商的需要超过了供应商对你的需要——因为这一巨大的不平衡,你只能付钱、付钱、再付钱。——Eric S. Raymond, 《大教堂与集市》

◆ 简而言之,每当使用商业产品时,就是将公司的一部分投注在无法控制的第三方身上

◆ 如果某些技术自己实现起来非常复杂、很容易出错、要花大量时间,但开源和商业领域已经有很好的方案,那么作为创业公司,就永远不要自己去实现这些技术。下面列出了部分这样的技术

◆ ·安全:加密、密码存储、信用卡存储。·Web技术:HTTP服务器、服务器端和客户端框架。·数据系统:数据库、NoSQL存储、缓存、消息队列。·软件分发:版本控制、构建系统、自动化部署。·计算机科学:基本数据结果(映射、列表、集)、排序算法。·处理通用数据格式的库:XML、HTML、CSV、JSON、URLs。·实用库:日期/时间操作、字符串操作、日志记录。·操作系统。·编程语言。

◆ 如果你要从头开始实现上述系统中的任何一个,只能有两个原因:一是以学习为目的的个人项目,二是你的创业公司对其中的某项技术有极其独特的需求

◆ Google就是一个很明显的“非自主发明不可”的栈,它所有的一切都是内部编写出来的。在Google的时候,我想可能除了gcc之外,我没用过任何一种开源工具或库。部分原因是Google领先行业内其他所有公司5年或5年以上。Google所做的东西,就是像MapReduce(使用无数低成本的商业硬件去运行分布式系统)这样的产品,他们基本上都是在发明和普及很多这样的产品。这些产品现在全都成了行业标准,但是大部分在Google之前并不存在。我觉得Goolge就是因为比其他公司超前了许多,所以不得不去实现,这样的境况也许又成为了一种自我增强,因为我们已经形成也适应了非自主发明不可的文化。——Brain Larson, Google和Twitter的软件工程师

◆ [插图]图5-1:内部实现、购买商业产品或使用开源产品

5.4 选择编程语言

◆ 每一种编程语言对于如何解决问题都有不同的哲学。我们可以把编程语言的范式当作是该语言的词汇和语法,决定了你如何说和可以怎么说。目前很少有确切的证据表明某种范式优于其他范式[插图],但某些范式相比其他范式可以更方便地表达一些想法

◆ Ruby支持线程,但它有全局解释器锁(Global Interpreter Lock, GIL),这意味着一次只能执行一个线程。此外,大多数主流的Ruby库执行的是同步I/O,在等待磁盘读取或网络调用返回的时候会阻塞线程。这导致了Ruby并不是处理大量并发的高效语言

◆ 当你选择一门语言时,面对的不仅仅是技术上的权衡取舍,而是一个社区。就像选择一间酒吧一样,没错,你去酒吧是为了品尝美酒,但那还不是最重要的——酒吧更是人们休闲和聊天的地方。这和选择计算机语言的道理是一样的,一门语言随着时间的推移会建立起社区,不仅仅是人,还包括软件方面的产物:工具、库等。这就是为什么有一些语言理论上比其他语言要出色,实际却不如其他语言的原因之一——它们还没有建立起健全的社区。——Joschua Bloch, Sun公司分布式工程师、Google首席Java架构师

◆ 我们可以应用三个过滤条件快速缩短这一列表:适用问题、编程范式和性能需求

◆ 例如,一家做计算机视觉和机器学习系统的创业公司,应该根据适用问题把这份列表限制为三门语言:C++、Java和Python。如果该创业公司更偏爱静态类型,就可以把Python从列表中剔除。最后,如果他们实现的是高性能的实时系统,就不能使用垃圾回收,这样就只剩下了C++

◆ 一家做Web应用程序的创业公司,最有可能觉得Java、JavaScript、PHP、Python、Ruby和Scala最适合解决他们的问题。如果该团队更喜欢动态类型,可能会从清单中去掉Java和Scala。如果他们中有一小部分人已经熟悉Python,并发现几个Django插件可以节省许多时间,Python将成为他们的最佳选择

5.5 选择服务器端框架

◆ 库和框架之间的区别是什么呢?通常的答案就是控制反转:我们把库插入到代码中并调用它们,反之我们把代码插入到框架中并让它们去调用你

◆ 这也称为好莱坞原则:不要给我们打电话,我们会打电话给你

◆ 如果你在开发Web服务,除非你调用socket.accept并编写自己的HTTP解析代码,否则总是应该把代码插入到某种调用你的框架中

◆ 这种框架也许是像Ruby on Rails这样的全栈框架,可以把控制器(controller)插入到处理请求中;或者是更加精简的框架,比如原始的HTTP服务器,可以插入函数去处理HTTP消息。无论哪种情况,你都离不开框架。所以,选择库还是框架通常都不是问题,问题是选择最精简的框架还是选择全栈框架

◆ 全栈框架,比如Ruby on Rails,就是一种为大多数常见任务内置提供了默认解决方案的框架,像路由、数据建模、视图渲染、国际化、配置和测试。最精简的框架,比如Sinatra,只为你提供简单的基本功能——也许只有HTTP路由,再内置一点其他功能,你自己想办法处理各种常见任务

◆ 任何库复杂到一定的程度之后,都会包含一个临时的、不合规范的、充满程序错误的、运行速度很慢的、只有一半功能的全栈Web框架,这是向格林斯潘的编程第十定律致敬。该定律告诉我们,任何C或Fortran程序复杂到一定程度之后,都会包含一个临时开发的、不合规范的、充满程序错误的、运行速度很慢的、只有一半功能的Common Lisp实现

◆ 全栈框架包含所有内置功能只有一个原因:现实中的大部分应用程序都需要它们。即便你现在并不太用得上这些功能,框架中内置这些功能增加的成本也不多,所以因为某种程度上“感觉繁重”就舍弃不用,未免有点目光短浅。当然,并不是每一个内置的解决方案都能满足你的需求,所以要寻找80%~90%的默认方案都能满足要求的框架。一旦它不能满足需要,你可以立马用定制的库去代替

◆ 大多数Web框架都提供了渲染HTML的模板库。当我们评估模板库的时候,主要需考虑内置的视图辅助方法、服务器端与客户端的对比、有逻辑和无逻辑模板的对比。

◆ 许多Web框架,比如Ruby on Rails、Servlets和Django,会为每一个请求分配一个线程(或进程),在等待I/O调用的时候(比如等待数据库或远程Web服务的响应)阻塞该线程。

◆ 如果线程太少,很容易出现所有线程都被占用而处于等待I/O的状态,阻止线程对任何新请求的处理,即便大部分线程仅仅是在空等待;如果线程太多,就会引发额外的内存使用和上下文切换导致的巨大开销。所以,确定合适的线程数量也是很困难的,因为它取决于服务器的负载和下游依赖项的延迟情况,而这一切都是经常处于变化中的

◆ 处理I/O更高效的方式是使用基于非阻塞I/O实现的Web服务端,比如Node.js或者Netty。

◆ 利用这种方式,在等待I/O完成的时候,我们并不是将线程阻塞,而是注册一个回调(或者承诺),该线程就可以继续处理其他请求。当回调被触发时,线程可以将请求唤起,完成对它的处理。因为I/O处理的时间会比其他进程内的处理时间长好几个量级,所以异步处理I/O的方式使我们对服务器资源的利用更加高效。通常来说,非阻塞服务器的每个CPU核心只需要一个线程(或进程),并且对下游的延迟情况也不那么敏感

◆ 和所有性能问题一样,找到答案的唯一方式就是进行性能测试和测量。读者可以查看TechEmpower Web框架基准测试作为入门,或者阅读第7章了解有关性能测量的内容。

◆ 跨站点请求伪造(Cross-Site Request Forgery, CSRF)攻击是指恶意网站让用户在受信任的网站上执行有害的动作。

◆ 例如,假设你访问win-an-ipad.com网站,该网站让你在表单中输入一些数据并提交,这样你就有机会赢得一部iPad。但你不知道的是这个表单实际上被提交到了Amazon,这是一个你信任并且已经登录的网站。如果Amazon没有CSRF保护,攻击者就可以精心制作出符合要求的表单,Amazon会将提交的表单解释为你在购买东西,因为浏览器会将你的Amazon cookies和提交的内容一起发送出去

◆ 为了防范CSRF攻击,Web框架应该具有为每位用户生成短暂的随机令牌的机制,将其存放在cookie中,如果body中的令牌和cookie中的令牌不匹配,则拒绝提交表单。这样在渲染自己网站上的合法表单时,我们就可以轻松地将令牌作为隐藏的表单字段,把它包含在表单中。当攻击者尝试渲染网站上的恶意表单时,他们无法读取你的cookies,也就无法猜测到正确的令牌值

◆ 下面列出了一些截至2015年最流行和成熟的框架,按照编程语言划分并根据首字母进行了排序,内容主要来源于HotFrameworks和我自己的经验。·C#:.NET。·Clojure:Ring、Compojure、Hoplon。·Go:Revel、Gorilla。·Groovy:Grails。·Haskell:Snap、Happstack、Scotty。·Java:Spring、Play Famework、DropWizard、JSF、Struts。·JavaScript:express.js、sails.js、derby.js、geddy.js、koa、kraken.js、meteor。·Perl:Mojolicious、Catalyst、Dancer。·PHP:Laravel、Phalcon、Symfony、CakePHP、Yii、Zend。·Pyhton:Django、Flask。·Ruby:Ruby on Rails、Sinatra。·Scala:Play Framework、Spray。

◆ 应用三个过滤条件,可以快速删减这个列表,即编程语言、适用问题和可扩展性

第6章 整洁的代码

◆ 编程是一种让他人了解你想让电脑做什么的艺术。——Donald Knuth

◆ 作为一名程序员,只有不到50%的工作时间是花在编程任务上的。编程的时间里面,阅读代码和编写代码的时间比大大超过了10∶1,而实际花在编写代码的极少部分的时间中,80%以上的时间又是在维护代码,即修改或修复已有的代码。如果一天工作8小时,能有5分钟花在编写新代码上就已经不错了。结果就是,程序员的工作并不是在编写代码,而是在理解代码。

8.3 构建

◆ 理想状态就是每一次提交都是完全实现了某一个单一目的、大小合理的单元

◆ 开发人员同时完全隔离工作数周或数月,到了最后一分钟又尝试将所有的代码合并到一起。这个过程就是所谓的后期集成,就像我们在本章开头看到的LinkedIn的故事一样,通常都会导致灾难

◆ 更好的方法就是第二种选择所描述的,是持续集成,所有开发人员定期(每天或者每天多次)将代码合并到一起,这一过程可以尽早暴露设计中的问题,避免在错误的方向上走得太远,我们也可以增量式地改进设

9.3 组织设计

◆ [插图]图9-2:不同公司的组织结

9.6 办公室

◆ 分心是专注工作的敌人。对于基本的脑力劳动(比如算法)来说,只要一小点办公室噪声都会产生影响。编程甚至需要更加集中的注意力,你必须把问题加载到大脑中,就像用纸牌搭房子,需要花时间并且耗费大量的脑力,而且一丁点儿干扰都可能把整个屋子弄倒,让你不得不从头开始

◆ [插图]图9-7:这就是不要打断程序员工作的

◆ 研究表明,一名程序员平均要花10~15分钟才能从干扰中恢复过来,重新开始编写代码。一个小时被干扰4次,就足以让你的生产效率下降到零

◆ 这就是为什么搞开发的人为了回答你的问题,眼睛从屏幕离开时会给你一个如此不友善的眼神。因为他们脑子里的纸牌屋已经摇摇欲坠了。仅仅因为存在会被打断的可能性,就会让这些开发人员不敢开始困难的项目。这也是为什么他们喜欢在深夜工作的原因,也是他们几乎不可能在隔间里写出出色软件的原因(除非在深夜)。——Paul Graham, Y Combinator联合创始人,硅谷创业教父,《黑客与画家》作者

◆ 也许你就是这样的人(我知道我曾经就是)。你甚至会对自己长时间的工作感到自豪,认为自己是个英雄。但是更长的时间——每周超过50个小时,并不会提高工作效率。这一切只会增加压力、损害健康、让你犯下粗心的错误,最终情绪沮丧、逃离工作。换句话说,需要英雄事迹就是一种失败的信号,意味着一些事情严重缺乏规划和管理

◆ 不需要英雄的环境好过一个团队都是英雄的环境。——Ernie Miller, Nvisium技术主管

9.8 沟通

◆ 健康的公司文化鼓励员工去分享坏的消息。可以自由、公开讨论其问题的公司也可以快速地解决这些问题,而掩盖问题的公司则会打击员工参与的积极性。所以CEO的做法应当是:营造一种文化,奖励(而非惩罚)人们将问题公开,使得问题得以解决。——Ben Horowitz, 《创业维艰

9.10 小结

◆ 《基业长青》一书对一项6年研究项目的结果进行了概括,该项目致力于研究成功打造一家“有远见的公司”的必要条件——被认为是所在行业中最出色的公司之一。这样的公司历经多年,产品和领导也经受住了考验,对世界产生了持续的影响,比如迪士尼、IBM、波音和通用电气

◆ 《基业长青》中有一个关键发现,就是有远见的公司的领导会关注创立伟大的组织而不是伟大的产品——那样的公司只是“制作钟表的”,而不是“时代的讲述者”。他们最伟大的产物并不是特定的想法或产品,而是公司本身及其代表的东西

10.1 寻找创业公司的工作

◆ 我曾经遇到过一个人,他是一家银行的软件工程师(不是JP摩根或者其他顶级的银行,只是中等的、相对不那么知名的区域银行),在两三年前给我发了电子邮件,说到“我想加入一家创业公司,应该做些什么呢”。我的回答是,出去参加两三次黑客马拉松,花几个星期去做项目。你可以把你的简历水平从B或C变成至少B+或A-,这一点在几个月内就可以做到,然后再去硅谷,申请公司职位就可以了,因为湾区或任何科技中心的机会要比其他城市的机会多得多。他在一年后发邮件给我,告诉我他已经成了Uber早期的工程师,赚到了许多钱,现在的情况好了很多。——Gayle Laakmann McDowell, CareerCup创始人、CEO

◆ 读者不妨访问http://www.hello-startup.net/resources/jobs/网站找找你周边的编码竞赛

◆ 如果想让社区关注你,最好的方法就是为社区做贡献:做演讲、写博客、将代码开源、处理邮件列表、在Stack Overflow上回答问题、在IRC上聊天。只要我参见会议或聚会小组,离开时便可以获得5~10个新的联系人;只要我在会议或聚会小组上做展示,离开时便可以获得几十个新的联系人。读

◆ 如果不能通过人脉找到工作,那么退而求其次,看看能不能让工作找到你。在过去的5年,我已经收到大约1300封电子邮件,这些邮件来自创业公司的创始人、招聘经理和招聘人员。我的收件箱在几乎每个工作日都会收到至少一个工作机会,我不再需要申请工作——而是工作向我申请。

◆ 几乎每一名程序员都可以在恰当的地方创建一个网络身份,从而实现类似的事情。如果想让公司找到你,你的名字应该出现在它们会去的地方

11.2 招聘什么人

◆ 创业的低谷非常之深,几乎没有人可以独自承受这一切。如果有多名创始人,团队精神会使他们以看似违背能量守恒定律的方式凝聚在一起。每个人都认为“我不能让我的朋友失望”,这是人性中最强大的力量之一,在仅有一名创始人的情况下,他将失去这一力量。——Paul Graham, Y Combinator联合创始人,硅谷创业教父,《黑客与画家》作者

◆ 两个创始人通常是通往成功的最好途径。你可以平均分配工作和股权,也没有政治操作的空间。三个创始人也没有问题:只是在职责划分上会更困难一些,但总可以通过投票决定。一旦超过了三人,事情会变得越来越不稳定,做出决定也变得更加困难,职责和股权的分配也更难处理,更有可能出现政治问题和内耗

◆ 开始的时候,我只会聘请全栈的人,因为不想有人说:“这不是我的工作。”在公司最开始的阶段,每个人头上都要顶着好多帽子。只有到了稍后期,我才会去招聘专才。——Julia Grace, Weddinglovely联合创始人、Tindie CTO

◆ 这里有一个重要的提醒:专才的反面并非是通才。不是任何方面的专家并不意味着你就擅长各个方面。真正的通才是那些多面手的人,你把一些新东西放在他们面前,他们就会把它带走并弄清楚它是如何工作的。他们拥有永不满足的好奇心,愿意对所有东西都稍做尝试。但你要是看看他们的简历,就会看到大量行业的经历(例如旅游、医药、社交网络、消费者、部署自动化、编程语言设计)以及职业角色(例如开发人员、技术主管、产品经理、设计师)

◆ 如果你听到一个开发人员说“噢,不,我不是搞前端的”,或者因下一个项目不得不学习新的编程语言时有所畏缩,这样的人可能就不是通才

◆ 真正的通才喜欢学习新东西,不管问题的领域是什么,也不管他们是否已有相关经验

◆ 当你需要调优数据库查询、建立复制或共享表格,也许就是时候去招聘一个专职DBA了;当你需要管理数以千计的服务器、把代码部署到多个数据中心、建立24×7的网站监控,也许就是时候去招募一个专职发布工程师了;当你的网站规模大到经常遭受黑客和垃圾邮件的攻击,也许就是时候招募一支专职安全团队了。但要提防招聘那些只擅长一种技能而别无他长的人。相反,我们需要T型的人,这一点已经在2.1.1节讨论过

◆ 我们并不是要编写更多的代码,而是要编写正确的代码。10倍能力的程序员实际上是10倍能力的决策制定者

◆ 你是想要10个平均水平的科学家还是1个牛顿呢?10个普通的科学家想不出运动定律、引力理论或者微积分原理,但1个牛顿却可以做到。你是想让埃隆·马斯克去运营公司,还是想把钥匙交给10个普通的执行官?10个普通的执行官想不出Paypal、电动汽车、可回收火箭和超级高铁,但1个埃隆·马斯克却可以做到

◆ 超级明星程序员,就像超级明星运动员,是格外稀少的。如果你尝试根据只招聘“摇滚明星”的原则去制订自己的招聘策略,或者更糟的情况是,如果你的公司招摇过市,好像已经是一群“摇滚明星”,你就永远无法发展你的团队

◆ 开发人员在能力上也许会有巨大的差异,但是这一点不能掩盖下面这三个关键的事实:(1)开发人员的表现会根据不同的任务而有所不同;(2)开发人员可以逐步变得更好;(3)大多数软件都是由团队而不是由个人开发的。

◆ 第一个事实意味一个公司中10倍能力的开发人员在另一家公司中可能只是1倍能力甚至0.1倍能力的开发人员。文化、激情和使命才是至关重要的(阅读第9章了解更多信息)。

◆ 只有聪明才智是不够的,也许你和这样的聪明程序员共事过,他可以滔滔不绝地说上几个小时,介绍一种技术与另一种技术相比,在理论上有什么好处,他会花许多时间去撰写设计文档、架构图和UML图,但是从来没有实际给出过任何代码。我们有时候称他们为设计师,有时候又称他们为教授。不管怎么样,创业公司需要的不仅仅是聪明的人,还需要能够把事情做好的人。我们需要的是可以权衡取舍、可以利用不完美的信息做决定、能够理解完成比完美更好的人

◆ 注意,我们要寻找的这种“聪明”和IQ或者漂亮的学历并没有多大关系,更多的是要有清晰的思维和解决问题的能力

◆ 你还要花大量时间和你所招聘的人在一起,在小型创业公司中更是如此。设想你要每天都和这个人一起吃午饭,要他们去评审你的代码,要花时间去了解他们或者从他们身上学习东西,当网站出了问题他们会在凌晨3点打电话给你。虽然你没必要希望招聘到能够变成朋友的人,但你肯定要避免招聘到你无法相处的人

◆ 在许多公司中,这种品格有一个好听的名字:无混蛋规则。这是一条很好的规则,但不是“混蛋”还不够,你真正需要的是找到很好地与你的公司文化相契合的人(阅读第9章,了解有关创业文化的深入讨论)

11.3 寻找出色的人选

◆ 假设你是一个有两名合伙人的团队,想要招聘12名技术人员。你认为完成这一任务要花多少时间?答案是:大约990小时,或者在一整年里每周花大概20小时去招聘。

12.2 学习的技巧

◆ 我已经发现了三种让我的研究时间变得更有效率的方法。第一,设立具体的、可测量的目标。例如,我为2015年设定的目标是阅读30本书(我用Goodreads来记录我的进度)。

◆ 我还有一个目标,就是每年至少学一种主要的新技术,比如一种新的编程语言或数据库,我发现“七周七种”系列图书,比如《七周七语言》[插图]《七周七数据库》[插图]和《七周七并发模型》[插图],都是实现这一目标很好的方法,因为这些书会向你介绍各种各样的技术,还会重点强调不同的编程范式,

— 来自微信读书

跟着 《Hell Yes! CSS!》和 AI 学 CSS

最近发现 ★ wizard zines 中的这本 《Hell Yes! CSS!》 对于理解 CSS 的核心概念特别有用。结合 DeepSeek 的解释,对于理解 CSS 似乎非常有帮助,因此结合原文核心内容和 DeepSeek 的解释汇集为本文。

Hell Yes! CSS!》是一本杂志,短短几十页将 CSS 的核心概念用n漫画的形式讲述清楚,是我在看 《Modern CSS with Tailwind Flexible Styling Without the Fuss – Second Edition (beta B3.0) (Noel Rappin)》这本书时推荐的。本文不会带有全部的原文内容,而只会有一些关键的页面,如需查看原文请自行寻找或购买。

Hell Yes! CSS!

Hell Yes CSS (Julia Evans) (Z-Library)-01

P3-TOC

Hell Yes CSS (Julia Evans) (Z-Library)-03

这是《Hell Yes CSS》小册子的目录部分,列出了书中涵盖的主要主题和章节。以下是对每个主题的简要解释:

ATTITUDE(态度)

  1. CSS isn’t easy: CSS 并不容易掌握,尤其是对于初学者来说,理解和应用 CSS 可能需要时间和实践。
  2. CSS isn’t design: CSS 是一种实现设计的工具,但它本身并不等同于设计。设计是关于视觉和用户体验的决策,而 CSS 是实现这些决策的技术手段。
  3. CSS specifications: CSS 规范是由 W3C(万维网联盟)制定的标准文档,定义了 CSS 的各种特性和行为。
  4. backwards compatibility: 向后兼容性指的是新版本的 CSS 特性在旧浏览器中仍然能够正常工作或优雅降级。

BASICS(基础)

  1. selectors: 选择器用于选择 HTML 元素并应用样式。常见的选择器包括元素选择器、类选择器、ID 选择器等。
  2. specificity: 特异性是决定哪个 CSS 规则将应用于元素的规则。特异性较高的规则会覆盖特异性较低的规则。
  3. default stylesheets: 默认样式表是浏览器为 HTML 元素提供的默认样式。不同的浏览器可能有不同的默认样式。
  4. units: CSS 单位用于定义长度、宽度、间距等。常见的单位包括像素(px)、百分比(%)、em、rem 等。

LAYOUT(布局)

  1. inline vs block: 行内元素(inline)和块级元素(block)是 HTML 元素的两种基本显示方式。行内元素不会独占一行,而块级元素会。
  2. the box model: 盒模型是 CSS 布局的基础,每个元素都被视为一个矩形盒子,包括内容、内边距(padding)、边框(border)和外边距(margin)。
  3. padding & margin: 内边距(padding)是内容与边框之间的空间,外边距(margin)是元素与其他元素之间的空间。
  4. borders: 边框是围绕元素内容和内边距的线条,可以设置宽度、样式和颜色。
  5. flexbox basics: Flexbox 是一种用于创建灵活布局的 CSS 模块,特别适合一维布局(行或列)。
  6. CSS grid: areas: CSS 网格布局(Grid)是一种用于创建复杂二维布局的 CSS 模块,网格区域(areas)是网格布局中的一个概念。
  7. centering: 居中是 CSS 布局中的常见需求,可以通过多种方式实现,如使用 Flexbox 或 Grid。
  8. position: absolute: 绝对定位(absolute positioning)允许元素相对于其最近的定位祖先元素进行定位。

GETTING FANCY(高级技巧)

  1. hiding elements: 隐藏元素可以通过多种方式实现,如设置 display: nonevisibility: hidden
  2. stacking contexts: 堆叠上下文(stacking context)是 CSS 中用于管理元素层叠顺序的概念。
  3. CSS variables: CSS 变量允许你在样式表中定义可重用的值,并在整个文档中使用。
  4. transitions: 过渡(transitions)允许你在 CSS 属性值变化时创建平滑的动画效果。

MAKING IT WORK(实际应用)

  1. media queries: 媒体查询(media queries)用于根据设备的特性(如屏幕宽度)应用不同的样式,常用于响应式设计。
  2. the CSS inspector: CSS 检查器是浏览器开发者工具的一部分,用于检查和调试网页的 CSS。
  3. testing checklist: 测试清单是一系列步骤或检查点,用于确保 CSS 在不同浏览器和设备上正常工作。

这个目录涵盖了从基础到高级的 CSS 主题,适合不同水平的读者学习和参考。如果你对某个具体主题有疑问或需要更详细的解释,请告诉我!

P6-CSS specifications

Hell Yes CSS (Julia Evans) (Z-Library)-06

这段内容介绍了 CSS 规范(CSS specifications)的基本概念和相关信息。以下是对内容的详细解释:

CSS 规范(CSS Specifications)

  1. CSS 规范的定义:

    • CSS 规范是由 W3C(万维网联盟)制定的标准文档,详细描述了 CSS 的各种特性和行为。
    • 每个 CSS 特性(如 max-widthflexboxtransforms 等)都有其独立的规范文档。
  2. CSS 2:

    • CSS 2 是早期的 CSS 规范版本,发布于 1998 年。它包含了 CSS 的基础特性,如盒模型、选择器、文本样式等。
    • 尽管现在有很多新的 CSS 特性,CSS 2 仍然是学习 CSS 基础的重要参考。
  3. 现代 CSS 规范:

    • 如今,每个 CSS 特性都有独立的规范文档。例如,flexboxgridtransforms 等都有各自的规范。
    • 你可以在 W3C 的 CSS 规范页面 找到所有这些规范。
  4. 浏览器对规范的实现:

    • 主流浏览器(如 Chrome、Firefox、Safari、Edge)通常会遵循 CSS 规范来实现 CSS 特性。
    • 但有时浏览器可能存在 bug 或未完全实现某些特性,导致行为与规范不一致。
  5. CSS 版本(Levels):

    • CSS 的版本被称为“级别”(Levels),例如 CSS Level 1、CSS Level 2、CSS Level 3 等。
    • 新的 CSS 级别只会添加新特性,而不会改变现有 CSS 代码的行为。这意味着旧代码在新版本中仍然有效。
  6. 新特性的实现:

    • 新的 CSS 特性需要时间才能被浏览器广泛支持。你可以使用 Can I use 网站来查看某个 CSS 特性在不同浏览器版本中的支持情况。

关键术语解释

  1. max-width: 一个 CSS 属性,用于设置元素的最大宽度。如果内容超过这个宽度,元素会自动调整大小以适应。
  2. flexbox: 一种 CSS 布局模块,用于创建灵活的、响应式的布局。
  3. transforms: 一种 CSS 特性,允许你对元素进行旋转、缩放、倾斜等变换。
  4. Can I use: 一个网站,用于检查不同浏览器对 CSS、HTML5 和 JavaScript 特性的支持情况。

这段内容强调了 CSS 规范的重要性,并解释了如何通过规范文档和工具(如 Can I use)来学习和使用 CSS。

P7-backwards compatibility

Hell Yes CSS (Julia Evans) (Z-Library)-07

这段内容讨论了 CSS 的向后兼容性(backwards compatibility),并解释了它对开发者的影响。以下是对内容的详细解释:

向后兼容性(Backwards Compatibility)

  1. 定义:

    • 向后兼容性指的是浏览器能够支持旧的 HTML 和 CSS 代码,即使这些代码是多年前编写的。
    • 例如,1988 年编写的 CSS 代码在现代浏览器中仍然可以正常运行。
  2. 优点:

    • 长期稳定性: 由于浏览器支持旧的代码,开发者可以放心地编写 CSS,而不必担心未来的浏览器更新会破坏现有的样式。
    • 投资价值: 花费时间调试和编写的 CSS 代码在未来仍然有效,这使得学习和使用 CSS 成为一项值得的投资。
  3. 挑战:

    • 奇怪的 CSS 单位: 由于 CSS 需要支持旧的行为,一些 CSS 单位(如 pxemrem 等)可能看起来很奇怪或难以理解。这些单位的设计考虑了历史兼容性。
    • 复杂性: 为了确保兼容性,CSS 的某些部分可能变得复杂,开发者需要花费更多时间调试和适配。
  4. 标准和实验性特性:

    • 如果开发者使用了非标准的或实验性的 CSS 特性,可能会失去向后兼容性。例如,某些浏览器可能会停止支持实验性特性,导致网站样式失效。
    • 示例:如果 Firefox 停止支持某个实验性特性,依赖该特性的网站可能会“崩溃”。

现代 CSS 开发建议

  1. 不需要支持所有旧浏览器:

    • 开发者不需要确保 CSS 在 1988 年的浏览器中正常工作,只需关注用户实际使用的浏览器即可。
    • 使用工具(如 Can I use)检查目标浏览器的支持情况。
  2. 新特性的优势:

    • 新的 CSS 特性通常更易于使用,并且能够更好地满足现代网页设计的需求。
    • 例如,现代 CSS 特性(如 flexboxgridmedia queries)使得响应式设计(responsive design)变得更加简单。
  3. 用户期望的变化:

    • 自 1988 年以来,用户对网站的期望发生了巨大变化。现代网站需要适应多种设备和屏幕尺寸,而新的 CSS 特性正是为此设计的。

关键术语解释

  1. CSS 单位:

    • CSS 单位用于定义长度、宽度、间距等。常见的单位包括:
      • px(像素):绝对单位。
      • em:相对于父元素的字体大小。
      • rem:相对于根元素(`

        `)的字体大小。

      • %:百分比单位,相对于父元素的尺寸。
  2. 响应式设计(Responsive Design):

    • 一种网页设计方法,使网站能够根据设备的屏幕尺寸自动调整布局和样式。
  3. 实验性特性:

    • 浏览器引入的新特性,可能尚未成为标准。这些特性通常带有浏览器前缀(如 -webkit--moz-),并且可能在未来被修改或移除。

这段内容强调了 CSS 向后兼容性的重要性,同时也提醒开发者不必过度关注旧浏览器的支持。现代 CSS 特性更易于使用,并且能够更好地满足现代网页设计的需求。

P8-a few CSS selectors

Hell Yes CSS (Julia Evans) (Z-Library)-08

这段内容介绍了一些常见的 CSS 选择器(CSS Selectors),这些选择器用于选择 HTML 元素并应用样式。以下是对每个选择器的详细解释:


1. .button

  • 作用: 选择所有具有 class="button" 的元素。
  • 示例:
    <a class="button">Click me</a>
    .button { color: red; }

2. div > .button

  • 作用: 选择所有作为 div 元素直接子元素.button 元素。
  • 示例:
 ```

3. :checked

  • 作用: 选择所有被选中的复选框(<input type="checkbox">)或单选按钮(<input type="radio">)。
  • 示例:
    <input type="checkbox" id="agree">
    <label for="agree">I agree</label>
    :checked { background-color: yellow; }

4. div

  • 作用: 选择所有 `
    ` 元素。
  • 示例:
This is a div
 ```
 ```css
 div { border: 1px solid black; }
 ```

5. div .button

  • 作用: 选择所有作为 div 元素后代.button 元素(不一定是直接子元素)。
  • 示例:
 ```

6. .button, #welcome

  • 作用: 选择所有 .button 元素和 id="welcome" 的元素(逗号表示“或”)。
  • 示例:
    <a class="button">Click me</a>
    <div id="welcome">Welcome!</div>
    .button, #welcome { color: blue; }

7. a:hover

  • 作用: 选择所有鼠标悬停在其上的 <a> 元素。
  • 示例:
    <a href="#">Hover over me</a>
    a:hover { color: green; }

8. #welcome

  • 作用: 选择具有 id="welcome" 的元素。
  • 示例:
    <div id="welcome">Welcome!</div>
    #welcome { font-size: 20px; }

9. div.button

  • 作用: 选择所有具有 class="button" 的 `
    ` 元素。
  • 示例:
    <div class="button">Click me</div>
    div.button { background-color: yellow; }

10. [href^="http"]

  • 作用: 选择所有 href 属性值以 http 开头的 <a> 元素。
  • 示例:
    <a href="http://example.com">External link</a>
    <a href="/about">Internal link</a> <!-- 不匹配 -->
    [href^="http"] { color: purple; }

11. tr:nth-child(odd)

  • 作用: 选择表格中所有奇数行(` ` 元素)。
  • 示例:
Row 1
Row 2
Row 3
 ```
 ```css
 tr:nth-child(odd) { background-color: lightgray; }
 ```

这些选择器是 CSS 中非常常用的工具,能够帮助你精确地选择并样式化 HTML 元素。

P9-specificity

Hell Yes CSS (Julia Evans) (Z-Library)-09

这段内容详细解释了 CSS 特异性(Specificity) 的概念,即当多个 CSS 规则应用于同一个元素时,浏览器如何决定使用哪个规则。以下是对内容的详细解释:


1. 什么是特异性(Specificity)?

  • 当多个 CSS 规则为同一个元素的同一个属性设置不同的值时,浏览器需要决定使用哪个规则。
  • 特异性 是浏览器用来确定哪个规则优先级更高的机制。特异性越高的规则,优先级越高。

2. 特异性的计算规则

  • 特异性是基于选择器的类型和组合来计算的。以下是选择器的优先级(从高到低):
    1. !important: 最高优先级,但应谨慎使用,因为它难以覆盖。
    2. 内联样式(Inline Styles): 直接在 HTML 元素中使用的 style 属性。
    3. ID 选择器(#id): 如 #start-link
    4. 类选择器(.class)、伪类选择器(:pseudo-class): 如 .button:hover
    5. 元素选择器(element): 如 diva

3. 示例解析

  • 示例 1:

    a:visited {
        color: purple;
        font-size: 1.2em;
    }
    #start-link {
        color: orange;
    }
    • 对于 <a id="start-link" href="#"> 元素:
      • color: orange 会被应用,因为 #start-link(ID 选择器)比 a:visited(伪类选择器)具有更高的特异性。
      • font-size: 1.2em 仍然会从 a:visited 规则中应用,因为没有其他规则覆盖它。
  • 示例 2:

    .sidebar .link {
        color: orange;
    }
    #header a {
        color: purple;
    }
    • 对于 <div id="header"><a class="link"> 元素:
      • color: purple 会被应用,因为 #header a(ID + 元素选择器)比 .sidebar .link(类 + 类选择器)具有更高的特异性。

4. !important 的作用

  • !important 是 CSS 中的一种强制优先级机制,它会覆盖其他所有规则。
  • 示例:
    a {
        color: blue !important;
    }
    #start-link {
        color: orange;
    }
    • 即使 #start-link 具有更高的特异性,color: blue 仍然会被应用,因为它使用了 !important
  • 注意: 过度使用 !important 会导致代码难以维护,应尽量避免。

5. 内联样式的优先级

  • 内联样式(直接在 HTML 元素中使用 style 属性)的优先级高于外部或内部样式表中的规则。
  • 示例:
    <a id="start-link" href="#" style="color: green;">Link</a>
    • 即使有 #start-link { color: orange; }color: green 仍然会被应用,因为内联样式的优先级更高。

6. 总结

  • 特异性 是 CSS 中非常重要的概念,它决定了当多个规则冲突时,哪个规则会被应用。
  • 优先级顺序:!important > 内联样式 > ID 选择器 > 类/伪类选择器 > 元素选择器。
  • 尽量避免使用 !important,以保持代码的可维护性。

P10-default stylesheets

Hell Yes CSS (Julia Evans) (Z-Library)-10

这段内容介绍了 默认样式表(Default Stylesheets) 和 CSS 属性的优先级。以下是对内容的详细解释:


1. 默认样式表(Default Stylesheet)

  • 定义: 每个浏览器都有一个默认样式表(也称为“用户代理样式表”),用于为 HTML 元素提供基本的样式。
  • 作用: 默认样式表确保即使没有自定义 CSS,HTML 元素也能以合理的方式显示。
  • 示例:
    h1 {
        font-size: 2em;
        font-weight: bold;
    }
    • 这是 Firefox 默认样式表中对 `

      ` 元素的定义。


2. 不同浏览器的默认样式

  • 不同浏览器(如 Chrome、Firefox、Safari、Edge)的默认样式表可能有所不同。
  • 常见差异: 按钮(<button>)和表单元素(如 <input><select>)的默认样式差异较大。

3. 查看默认样式表

  • 你可以查看浏览器的默认样式表。例如,Firefox 的默认样式表位于:
    resource://gre-resources/
  • 这些文件定义了浏览器如何在没有自定义 CSS 的情况下渲染 HTML 元素。

4. 初始值(Initial Value)

  • 每个 CSS 属性都有一个默认的 初始值,这是在 CSS 规范中定义的。
  • 如果没有样式表(包括默认样式表和自定义样式表)为某个属性设置值,浏览器会使用该属性的初始值。
  • 示例:
    • background-color 的初始值是 transparent(透明)。
    • font-size 的初始值通常是 medium

5. CSS 属性的优先级

CSS 属性可以通过以下 5 种方式设置,优先级从低到高依次为:

  1. 初始值(Initial Value):

    • 如果没有任何样式表设置属性值,浏览器会使用初始值。
  2. 浏览器的默认样式表(User Agent Stylesheet):

    • 浏览器为 HTML 元素提供的默认样式。
  3. 网站的样式表(Author Stylesheet):

    • 开发者编写的 CSS 文件或 ` ` 标签中的样式。
  4. 用户样式表(User Stylesheet):

    • 用户自定义的样式表(较少见)。
  5. 内联样式(Inline Styles):

    • 直接在 HTML 元素中使用 style 属性设置的样式,优先级最高。

6. 示例

  • HTML:
    <h1 style="color: red;">Hello World</h1>
  • CSS:
    h1 {
        color: blue;
    }
  • 结果: `

    ` 的文字颜色为红色,因为内联样式的优先级高于网站的样式表。


7. 总结

  • 默认样式表确保 HTML 元素在没有自定义 CSS 时也能正常显示。
  • 不同浏览器的默认样式可能有所不同,尤其是按钮和表单元素。
  • CSS 属性的优先级从低到高依次为:初始值 < 默认样式表 < 网站样式表 < 用户样式表 < 内联样式。
  • 了解这些优先级有助于更好地控制网页的样式。

P11-units

Hell Yes CSS (Julia Evans) (Z-Library)-11

这段内容介绍了 CSS 单位(Units),包括绝对单位和相对单位,并解释了它们的用途和区别。以下是对内容的详细解释:


1. CSS 单位的分类

CSS 单位分为两类:

  • 绝对单位(Absolute Units): 固定大小的单位,不随其他因素变化。
  • 相对单位(Relative Units): 相对于其他值(如字体大小或视口大小)的单位,具有灵活性。

2. 绝对单位

  • 常见绝对单位:
    • px(像素)
    • pt(点,1pt = 1/72 英寸)
    • pc(派卡,1pc = 12pt)
    • in(英寸)
    • cm(厘米)
    • mm(毫米)
  • 特点:
    • 绝对单位的大小是固定的,不会随页面缩放或用户设置变化。
    • 示例:font-size: 16px; 表示字体大小为 16 像素。

3. 相对单位

  • 常见相对单位:
    • em: 相对于父元素的字体大小。
    • rem: 相对于根元素(`

      `)的字体大小。

    • vw: 相对于视口宽度的 1%。
    • vh: 相对于视口高度的 1%。
    • %: 相对于父元素的尺寸。
  • 特点:
    • 相对单位具有灵活性,能够根据上下文动态调整大小。

4. rem 单位

  • 定义: rem 是相对于根元素(`

    `)的字体大小的单位。

  • 特点:
    • 1rem 等于根元素的字体大小(通常为 16px,除非显式修改)。
    • rem 是设置字体大小的好单位,因为它在整个文档中保持一致。
  • 示例:
    html {
        font-size: 16px;
    }
    .model {
        width: 20rem; /* 20 * 16px = 320px */
    }

5. em 单位

  • 定义: em 是相对于父元素的字体大小的单位。
  • 特点:
    • 如果父元素的字体大小为 16px1.5em 就是 24px
    • em 的值会随着父元素的字体大小变化。
  • 示例:
    .parent {
        font-size: 16px;
    }
    .child {
        font-size: 1.5em; /* 1.5 * 16px = 24px */
    }

6. 的特殊性

  • 在所有单位中相同:
    • 无论使用 pxemrem 还是其他单位, 的值都是相同的。
    • 示例:margin: 0; 表示外边距为 0。
  • none 的区别:
    • border: 0; 设置边框宽度为 0。
    • border: none; 设置边框样式为无。

7. 绝对单位的实际大小

  • 1 inch = 96px:
    • 在屏幕上,CSS 中的 1 inch 并不等于实际的 1 英寸,1px 也不一定等于屏幕的物理像素。
    • 这与 设备像素比(Device Pixel Ratio) 有关,高分辨率屏幕可能会将 1 个 CSS 像素映射为多个物理像素。

8. remem 的辅助功能

  • 优势:
    • 使用 remem 可以使页面更好地适应不同的用户设置。
    • 如果用户增加了浏览器的默认字体大小,使用 remem 的元素会自动缩放,提升可访问性。
  • 示例:
    .model {
        width: 20rem; /* 根据根元素字体大小动态调整 */
    }

9. 总结

  • 绝对单位(如 pxin)适合需要固定大小的场景。
  • 相对单位(如 remem%)适合需要灵活性和响应式设计的场景。
  • remem 有助于提升页面的可访问性,特别是在用户调整字体大小时。

P12-inline vs block

Hell Yes CSS (Julia Evans) (Z-Library)-12

这段内容详细解释了 内联元素(Inline Elements)块级元素(Block Elements) 的区别,以及如何使用 display 属性来控制元素的布局行为。以下是对内容的详细解释:


1. 内联元素(Inline Elements)

  • 特点:
    • 默认情况下,内联元素在页面上水平排列。
    • 它们不会独占一行,而是与其他内联元素共享同一行。
    • 内联元素忽略 widthheight 属性(除非使用 line-height 调整高度)。
  • 常见内联元素:
    • <a><span><strong><i><small><abbr><img><q><code> 等。
  • 示例:
    <a href="#">Link</a>
    <span>Text</span>
    <img src="image.jpg" alt="Image">

2. 块级元素(Block Elements)

  • 特点:
    • 默认情况下,块级元素在页面上垂直排列,每个元素独占一行。
    • 块级元素可以设置 widthheight 属性。
  • 常见块级元素:
    • `

      `、`

  • 示例:
Block 1

Block 2

 ```

3. display 属性

  • 作用: display 属性用于控制元素的布局行为,包括:
    1. 元素本身是内联、块级还是其他类型。
    2. 子元素的布局方式(如 flexgrid 等)。
  • 常见值:
    • inline: 元素表现为内联元素。
    • block: 元素表现为块级元素。
    • inline-block: 元素表现为内联元素,但可以设置 widthheight
    • flex: 元素表现为弹性盒子容器。
    • grid: 元素表现为网格容器。

4. inline-block 的特殊性

  • 特点:
    • inline-block 使块级元素像内联元素一样水平排列,同时保留块级元素的特性(如可以设置 widthheight)。
  • 示例:
    .box {
        display: inline-block;
        width: 100px;
        height: 100px;
        background-color: lightblue;
    }
    <div class="box">Box 1</div>
    <div class="box">Box 2</div>

5. 内联元素的例外情况

  • <img> 元素:
    • <img> 是内联元素,但它是一个 替换元素(Replaced Element),可以设置 widthheight
  • line-height:
    • 对于其他内联元素,可以通过 line-height 调整高度。

6. 布局的灵活性

  • 如果需要更复杂的布局,可以使用 display: flexdisplay: grid
  • 示例:
    .container {
        display: flex;
        justify-content: space-between;
    }
    
    <div class="container">
Item 1
Item 2
 </div>
 ```

7. 总结

  • 内联元素 水平排列,适合用于文本或小部件。
  • 块级元素 垂直排列,适合用于布局容器。
  • 使用 display 属性可以灵活控制元素的布局行为,如 inline-blockflexgrid

P13-the box model

Hell Yes CSS (Julia Evans) (Z-Library)-13

这段内容详细解释了 CSS 盒模型(Box Model),包括盒模型的组成部分及其行为。以下是对内容的详细解释:


1. 盒模型的基本概念

  • 定义: 每个 HTML 元素都被视为一个矩形盒子,这个盒子由以下部分组成:
    1. 内容(Content): 元素的实际内容(如文本、图片等)。
    2. 内边距(Padding): 内容与边框之间的空间。
    3. 边框(Border): 围绕内容和内边距的线条。
    4. 外边距(Margin): 盒子与其他元素之间的空间。

2. 盒模型的组成部分

  • 内容(Content):
    • widthheight 属性定义。
  • 内边距(Padding):
    • 使用 padding 属性设置。
    • 示例:padding: 10px;
  • 边框(Border):
    • 使用 border 属性设置。
    • 示例:border: 1px solid black;
  • 外边距(Margin):
    • 使用 margin 属性设置。
    • 示例:margin: 20px;

3. widthheight 的行为

  • 默认情况下,widthheight 只定义内容区域的大小,不包括内边距、边框和外边距。
  • 示例:
    .box {
        width: 100px;
        height: 100px;
        padding: 10px;
        border: 5px solid black;
        margin: 20px;
    }
    • 实际占用的总宽度 = width + padding + border + margin = 100px + 20px + 10px + 40px = 170px

4. box-sizing: border-box

  • 作用: 将 widthheight 定义为包括内容、内边距和边框的总大小。
  • 示例:
    .box {
        box-sizing: border-box;
        width: 100px;
        height: 100px;
        padding: 10px;
        border: 5px solid black;
    }
    • 实际内容区域的宽度 = widthpaddingborder = 100px - 20px - 10px = 70px
  • 优点: 使用 border-box 可以更直观地控制元素的总大小。

5. 外边距合并(Margin Collapse)

  • 定义: 当两个垂直相邻的元素都有外边距时,它们的外边距会合并为一个外边距(取较大的值)。
  • 示例:
    .box1 {
        margin-bottom: 20px;
    }
    .box2 {
        margin-top: 30px;
    }
    • box1box2 之间的实际外边距为 30px(而不是 50px)。
  • 注意: 外边距合并只发生在垂直方向上,水平方向不会合并。

6. 事件触发的范围

  • 内边距和边框: 点击内边距或边框会触发元素的 onclick 事件。
  • 外边距: 点击外边距不会触发元素的 onclick 事件。

7. 总结

  • 盒模型 是 CSS 布局的基础,理解其组成部分(内容、内边距、边框、外边距)非常重要。
  • 使用 box-sizing: border-box 可以更直观地控制元素的总大小。
  • 外边距合并 是 CSS 中的一个常见现象,特别是在垂直布局中。

P14-padding + margin syntax

Hell Yes CSS (Julia Evans) (Z-Library)-14 这段内容详细介绍了 内边距(Padding)外边距(Margin) 的语法及其区别。以下是对内容的详细解释:


1. 内边距(Padding)的语法

  • 定义: 内边距是内容与边框之间的空间,可以使用 padding 属性设置。

  • 四种设置方式:

    1. 所有边相同:
      padding: 1em;
      • 上下左右的内边距均为 1em
    2. 垂直和水平:
      padding: 1em 2em;
      • 上下内边距为 1em,左右内边距为 2em
    3. 上、水平、下:
      padding: 1em 2em 3em;
      • 上内边距为 1em,左右内边距为 2em,下内边距为 3em
    4. 上、右、下、左:
      padding: 1em 2em 3em 4em;
      • 上内边距为 1em,右内边距为 2em,下内边距为 3em,左内边距为 4em
  • 记忆技巧:

    • TRBL: 顺序为 Top(上)、Right(右)、Bottom(下)、Left(左)。
    • 顺时针方向: 从顶部开始,顺时针依次设置。

2. 单独设置某一边的内边距

  • 可以使用以下属性单独设置某一边的内边距:
    padding-top: 1em;
    padding-right: 10px;
    padding-bottom: 3em;
    padding-left: 4em;

3. 外边距(Margin)的语法

  • 定义: 外边距是元素与其他元素之间的空间,可以使用 margin 属性设置。
  • 语法与 padding 相同:
    margin: 1em; /* 所有边相同 */
    margin: 1em 2em; /* 垂直和水平 */
    margin: 1em 2em 3em; /* 上、水平、下 */
    margin: 1em 2em 3em 4em; /* 上、右、下、左 */
  • 单独设置某一边的外边距:
    margin-top: 1em;
    margin-right: 10px;
    margin-bottom: 3em;
    margin-left: 4em;

4. 内边距与外边距的区别

  1. 位置:
    • 内边距 在元素内部,背景颜色会覆盖内边距。
    • 外边距 在元素外部,背景颜色不会覆盖外边距。
  2. 点击事件:
    • 点击内边距会触发元素的点击事件,点击外边距不会。
  3. 居中:
    • 可以使用 margin: auto; 实现水平居中,但 padding 不能用于居中。
  4. 负值:
    • 外边距可以为负值(如 margin: -10px;),但内边距不能为负值。

5. 边框宽度(Border Width)的语法

  • 边框宽度的设置顺序与 paddingmargin 相同:
    border-width: 1em 2em 3em 4em; /* 上、右、下、左 */

6. 总结

  • 内边距外边距 的语法非常相似,都可以通过简写形式或单独属性设置。
  • 内边距在元素内部,外边距在元素外部。
  • 外边距可以用于居中,且支持负值;内边距不支持负值。

P15-borders

Hell Yes CSS (Julia Evans) (Z-Library)-15

这段内容详细介绍了 边框(Border) 及其相关属性,包括边框样式、圆角、阴影和轮廓。以下是对内容的详细解释:


1. 边框的组成部分

  • 定义: 边框由三个部分组成:
    1. 边框宽度(Border Width): 如 2px
    2. 边框样式(Border Style): 如 solid(实线)。
    3. 边框颜色(Border Color): 如 black(黑色)。
  • 简写语法:
    border: 2px solid black;

    等同于:

    border-width: 2px;
    border-style: solid;
    border-color: black;

2. 边框样式(Border Style)

  • 常见样式:
    • solid: 实线。
    • dotted: 点线。
    • dashed: 虚线。
    • double: 双线。
    • 其他样式:inset(内嵌)、groove(凹槽)等。
  • 示例:
    border-style: dotted;

3. 单独设置某一边的边框

  • 可以使用以下属性单独设置某一边的边框:
    border-bottom: 2px solid black;

    其他属性:

    • border-top
    • border-right
    • border-left

4. 圆角(Border Radius)

  • 定义: border-radius 用于设置元素的圆角。
  • 示例:
    border-radius: 10px; /* 圆角半径为 10px */
    border-radius: 50%; /* 将正方形变为圆形 */
  • 特点:
    • 可以使用百分比或固定值(如 pxem)。
    • 设置为 50% 时,正方形元素会变为圆形。

5. 阴影(Box Shadow)

  • 定义: box-shadow 用于为元素添加阴影。
  • 语法:
    box-shadow: 5px 5px 8px black;
    • 5px: 水平偏移(x 轴)。
    • 5px: 垂直偏移(y 轴)。
    • 8px: 模糊半径(blur radius)。
    • black: 阴影颜色。
  • 示例:
    box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); /* 带透明度的阴影 */

6. 轮廓(Outline)

  • 定义: outline 类似于边框,但它不会影响元素的尺寸。
  • 特点:
    • 轮廓不占用空间,不会改变布局。
    • 常用于 :hover:focus 状态,提升可访问性。
  • 示例:
    outline: 2px solid blue;
  • 可访问性:
    • 在键盘导航时,轮廓可以帮助用户识别当前聚焦的元素。

7. 总结

  • 边框 由宽度、样式和颜色组成,可以使用简写语法或单独属性设置。
  • 圆角 通过 border-radius 实现,支持百分比和固定值。
  • 阴影 通过 box-shadow 添加,可以设置偏移、模糊半径和颜色。
  • 轮廓 类似于边框,但不影响布局,常用于提升可访问性。

P16-flexbox basics

Hell Yes CSS (Julia Evans) (Z-Library)-16

这段内容介绍了 Flexbox 的基础知识,包括如何使用 display: flex 创建弹性布局,以及常见的 Flexbox 属性。以下是对内容的详细解释:


1. Flexbox 的基本概念

  • 定义: Flexbox 是一种用于创建灵活布局的 CSS 模块,特别适合一维布局(行或列)。
  • 使用方法: 在父元素上设置 display: flex;,其子元素会自动成为弹性项目(flex items)。

2. flex-direction 属性

  • 作用: 定义子元素的排列方向。
  • 默认值: flex-direction: row;(子元素水平排列)。
  • 其他选项:
    • row: 子元素从左到右排列。
    • row-reverse: 子元素从右到左排列。
    • column: 子元素从上到下排列。
    • column-reverse: 子元素从下到上排列。
  • 示例:
    .container {
        display: flex;
        flex-direction: row;
    }

3. flex-wrap 属性

  • 作用: 控制子元素是否换行。
  • 默认值: flex-wrap: nowrap;(子元素不换行,可能会溢出容器)。
  • 其他选项:
    • wrap: 子元素在容器宽度不足时换行。
    • wrap-reverse: 子元素换行,但顺序相反。
  • 示例:
    .container {
        display: flex;
        flex-wrap: wrap;
    }

4. justify-content 属性

  • 作用: 控制子元素在主轴(水平方向)上的对齐方式。
  • 常见值:
    • center: 子元素居中对齐。
    • flex-start: 子元素向主轴起点对齐(默认值)。
    • flex-end: 子元素向主轴终点对齐。
    • space-between: 子元素均匀分布,首尾元素贴边。
    • space-around: 子元素均匀分布,周围留有相等空间。
  • 示例:
    .container {
        display: flex;
        justify-content: center;
    }

5. align-items 属性

  • 作用: 控制子元素在交叉轴(垂直方向)上的对齐方式。
  • 常见值:
    • center: 子元素居中对齐。
    • flex-start: 子元素向交叉轴起点对齐。
    • flex-end: 子元素向交叉轴终点对齐。
    • stretch: 子元素拉伸以填满容器高度(默认值)。
  • 示例:
    .container {
        display: flex;
        align-items: center;
    }

6. 嵌套 Flexbox

  • 特点: 可以在一个 Flexbox 容器中嵌套另一个 Flexbox 容器,以实现更复杂的布局。
  • 示例:
    <div class="container">
        <div class="item">Item 1</div>
        <div class="item">Item 2</div>
        <div class="nested-container">
            <div class="nested-item">Nested Item 1</div>
            <div class="nested-item">Nested Item 2</div>
        </div>
    </div>
    .container {
        display: flex;
    }
    .nested-container {
        display: flex;
    }

7. 总结

  • Flexbox 是一种强大的布局工具,适合创建灵活的、响应式的一维布局。
  • 常用属性包括 flex-directionflex-wrapjustify-contentalign-items
  • 可以通过嵌套 Flexbox 容器实现更复杂的布局。

P17-CSS grid! areas!

Hell Yes CSS (Julia Evans) (Z-Library)-17

这段内容介绍了 CSS Grid 中的一个强大功能:网格区域(Grid Areas)。通过网格区域,你可以以直观的方式定义复杂的布局。以下是对内容的详细解释:


1. CSS Grid 的基本概念

  • 定义: CSS Grid 是一种用于创建二维布局的 CSS 模块,适合复杂的网页布局。
  • 网格区域(Grid Areas): 网格区域允许你为布局中的每个部分命名,并通过名称来放置元素。

2. 使用网格区域的步骤

  • 步骤 1: 编写 HTML 结构

    <div class="grid">
        <div class="top">Header</div>
        <div class="side">Sidebar</div>
        <div class="main">Content</div>
    </div>
  • 步骤 2: 定义网格区域

    .grid {
        display: grid;
        grid-template-columns: 200px 800px; /* 定义两列,宽度分别为 200px 和 800px */
        grid-template-areas:
            "header header"  /* 第一行:header 占据两列 */
            "sidebar content"; /* 第二行:sidebar 和 content 各占一列 */
    }
    • grid-template-areas 通过直观的方式定义布局,类似于绘制布局图。
  • 步骤 3: 为每个元素分配网格区域

    .top {
        grid-area: header; /* 将 .top 元素放置在 header 区域 */
    }
    .side {
        grid-area: sidebar; /* 将 .side 元素放置在 sidebar 区域 */
    }
    .main {
        grid-area: content; /* 将 .main 元素放置在 content 区域 */
    }

3. 布局效果

  • 最终布局:
    ---------------------
    |       Header       |
    ---------------------
    | Sidebar | Content  |
    ---------------------
  • 解释:
    • header 区域占据整个第一行。
    • sidebarcontent 区域分别占据第二行的两列。

4. 网格区域的优势

  • 直观性: 通过 grid-template-areas,你可以以近乎视觉化的方式定义布局,便于理解和维护。
  • 灵活性: 网格区域可以轻松调整布局结构,只需修改 grid-template-areas 的值即可。

5. 总结

  • CSS Grid 的网格区域功能非常适合创建复杂的二维布局。
  • 通过 grid-template-areasgrid-area,你可以直观地定义和分配布局区域。
  • 这种方法使布局代码更易读、更易维护。

P18-centering

Hell Yes CSS (Julia Evans) (Z-Library)-18

这段内容介绍了如何在 CSS 中实现 文本居中块级元素居中,并提供了多种方法。以下是对内容的详细解释:


1. 文本居中

  • 方法: 使用 text-align 属性。
  • 示例:
    h2 {
        text-align: center;
    }
  • 效果: 文本在容器中水平居中。

2. 块级元素水平居中

  • 方法: 使用 margin: auto;
  • 示例:
    <div class="parent">
        <div class="child"></div>
    </div>
    .child {
        width: 400px;
        margin: auto; /* 水平居中 */
    }
  • 注意: margin: auto; 只能实现水平居中,不能实现垂直居中。

3. 块级元素垂直居中

  • 方法 1: 使用 Grid 布局

    .parent {
        display: grid;
        place-items: center; /* 水平和垂直居中 */
    }
    • 解释: place-items: center;justify-itemsalign-items 的简写,用于在 Grid 容器中居中子元素。
  • 方法 2: 使用 Flexbox 布局

    .parent {
        display: flex;
        justify-content: center; /* 水平居中 */
        align-items: center; /* 垂直居中 */
    }
    • 解释: justify-content 控制主轴(水平方向)的对齐,align-items 控制交叉轴(垂直方向)的对齐。

4. 使用 Flexbox 或 Grid 仅居中一个元素

  • 示例:
    <div class="parent">
        <div class="child">Centered!</div>
    </div>
    .parent {
        display: flex; /* 或 display: grid; */
        justify-content: center;
        align-items: center;
    }
  • 效果: 即使只有一个子元素,也可以使用 Flexbox 或 Grid 轻松实现居中。

5. 总结

  • 文本居中: 使用 text-align: center;
  • 块级元素水平居中: 使用 margin: auto;
  • 块级元素垂直居中: 使用 Grid 的 place-items: center; 或 Flexbox 的 justify-content: center; align-items: center;
  • Flexbox 和 Grid 不仅适用于复杂布局,也适合简单的居中需求。

P19-position:absolute

Hell Yes CSS (Julia Evans) (Z-Library)-19

这段内容详细介绍了 position: absolute 的使用方法及其行为。以下是对内容的详细解释:


1. position: absolute 的基本概念

  • 定义: position: absolute; 用于将元素从文档流中移除,并相对于其 包含块(containing block) 进行定位。
  • 包含块: 包含块是距离该元素最近的、position 不为 static 的祖先元素。如果没有这样的祖先元素,则包含块为 &lt;body&gt;

2. position: absolute 的使用

  • 示例:
    <div id="parent">
        <div id="square"></div>
    </div>
    #parent {
        position: relative; /* 设置包含块 */
    }
    #square {
        position: absolute;
        top: 1em;
        left: 1em;
    }
  • 解释:
    • #parent 设置了 position: relative;,因此 #square 会相对于 #parent 进行定位。
    • #square 的左上角距离 #parent 的左上角分别为 1em

3. topbottomleftright 属性

  • 作用: 这些属性用于设置绝对定位元素相对于包含块的位置。
  • 示例:
    #square {
        position: absolute;
        top: 50%; /* 距离包含块顶部 50% */
        bottom: 2em; /* 距离包含块底部 2em */
        right: 30px; /* 距离包含块右侧 30px */
        left: -3em; /* 距离包含块左侧 -3em */
    }
  • 注意:
    • 可以使用负值(如 left: -3em;)。
    • 如果同时设置 leftright,元素的宽度会根据包含块的宽度自动调整。

4. 绝对定位元素的行为

  • 脱离文档流: 绝对定位元素会从文档流中移除,不会影响其他元素的布局。
  • 父元素是否扩展: 父元素不会扩展以适应绝对定位的子元素。绝对定位元素的位置和大小不会影响父元素的尺寸。

5. 常见问题

  • 宽度计算:
    • 默认情况下,width 不包括边框和内边距。如果设置了 width: 100%;,元素可能会超出包含块的范围。
    • 可以使用 box-sizing: border-box; 来使 width 包括边框和内边距。
  • 包含块的设置:
    • 如果未设置包含块(即所有祖先元素的 position 均为 static),绝对定位元素会相对于 &lt;body&gt; 进行定位。

6. 总结

  • position: absolute 用于将元素从文档流中移除,并相对于包含块进行定位。
  • 包含块是距离该元素最近的、position 不为 static 的祖先元素。
  • 使用 topbottomleftright 可以精确控制绝对定位元素的位置。
  • 绝对定位元素不会影响父元素的尺寸。

P20-hiding elememts

Hell Yes CSS (Julia Evans) (Z-Library)-20

这段内容介绍了在 CSS 中隐藏元素的几种方法,并解释了它们的区别。以下是对内容的详细解释:


1. display: none;

  • 作用: 完全隐藏元素,元素不会占据任何空间。
  • 特点:
    • 元素从文档流中移除,其他元素会重新排列以填补空白。
    • 元素不可见,也无法通过点击或屏幕阅读器访问。
  • 示例:
    .hidden {
        display: none;
    }

2. visibility: hidden;

  • 作用: 隐藏元素,但元素仍占据空间。
  • 特点:
    • 元素不可见,但仍会占据布局空间。
    • 元素无法通过点击访问,但对屏幕阅读器可见。
  • 示例:
    .hidden {
        visibility: hidden;
    }

3. opacity: 0;

  • 作用: 使元素完全透明,但仍占据空间。
  • 特点:
    • 元素不可见,但仍会占据布局空间。
    • 元素可以通过点击访问,对屏幕阅读器也可见。
    • 可以与 transition 结合使用,实现淡出效果。
  • 示例:
    .fade-out {
        opacity: 0;
        transition: opacity 1s ease;
    }
    .fade-out:hover {
        opacity: 1;
    }

4. z-index

  • 作用: 控制重叠元素的堆叠顺序。
  • 特点:
    • 仅对定位元素(position 不为 static)有效。
    • 可以与其他隐藏方法结合使用,控制元素的可见性和堆叠顺序。
  • 示例:
    .overlay {
        position: absolute;
        z-index: -1; /* 将元素置于其他元素下方 */
    }

5. 如何选择隐藏方法

  • display: none;:
    • 适用于完全移除元素,且不需要保留空间的情况。
  • visibility: hidden;:
    • 适用于需要保留元素空间的情况。
  • opacity: 0;:
    • 适用于需要实现淡出效果或保留交互性的情况。

6. 总结

  • display: none;: 完全隐藏元素,不占据空间。
  • visibility: hidden;: 隐藏元素,但保留空间。
  • opacity: 0;: 使元素透明,保留空间和交互性。
  • z-index: 控制重叠元素的堆叠顺序。

P21-stacking contexts

Hell Yes CSS (Julia Evans) (Z-Library)-21

这段内容详细介绍了 堆叠上下文(Stacking Contexts) 的概念及其对 z-index 的影响。以下是对内容的详细解释:


1. 堆叠上下文的基本概念

  • 定义: 堆叠上下文是 CSS 中用于管理元素层叠顺序的一种机制。每个堆叠上下文都是一个独立的层叠环境,元素在同一个堆叠上下文中的层叠顺序由 z-index 决定。
  • 类比: 堆叠上下文类似于 Photoshop 中的图层,每个图层(堆叠上下文)内部有自己的层叠顺序。

2. z-index 的作用

  • 作用: z-index 用于控制元素在堆叠上下文中的层叠顺序。
  • 示例:
    .first {
        z-index: 3;
    }
    .second {
        z-index: 0;
    }
    • .first 元素会显示在 .second 元素的上方,因为它的 z-index 值更大。

3. z-index 的限制

  • 问题: 有时即使一个元素的 z-index 值更大,它也可能不会显示在另一个元素的上方。
  • 原因: 元素可能位于不同的堆叠上下文中。z-index 只在同一个堆叠上下文中有效。
  • 示例:
    .parent1 {
        z-index: 0;
    }
    .parent2 {
        z-index: 10;
    }
    .child {
        z-index: 2;
    }
    • 如果 .child 位于 parent1 的堆叠上下文中,而 parent2 位于另一个堆叠上下文中,parent2z-index: 10 会使整个堆叠上下文位于 parent1 的上方,即使 .childz-index 更大。

4. 创建堆叠上下文

  • 方法: 以下属性会创建新的堆叠上下文:
    • positionabsoluterelativefixedsticky,并且 z-index 不为 auto
    • opacity 小于 1。
    • transformfilterwill-change 等属性。
  • 示例:
    #modal {
        z-index: 5;
        position: absolute; /* 创建新的堆叠上下文 */
    }

5. 堆叠上下文的嵌套

  • 默认行为: 元素的子元素会共享其堆叠上下文。
  • 示例:
    <div class="parent">
        <div class="child"></div>
    </div>
    .parent {
        z-index: 1;
        position: relative; /* 创建堆叠上下文 */
    }
    .child {
        z-index: 10;
    }
    • .childz-index 只在 .parent 的堆叠上下文中有效。

6. 总结

  • 堆叠上下文 是 CSS 中管理元素层叠顺序的重要机制。
  • z-index 只在同一个堆叠上下文中有效。
  • 通过 positionopacitytransform 等属性可以创建新的堆叠上下文。
  • 如果 z-index 没有按预期工作,可能是因为元素位于不同的堆叠上下文中。

P22-CSS cariables

Hell Yes CSS (Julia Evans) (Z-Library)-22

这段内容介绍了 CSS 变量(CSS Variables) 的基本用法,包括如何定义、使用和动态修改变量。以下是对内容的详细解释:


1. CSS 变量的定义

  • 语法: 使用 -- 前缀定义变量。
  • 作用域: 变量可以在任何选择器中定义,其作用域为定义它的选择器及其子元素。
  • 示例:
    body {
        --text-color: #f79; /* 定义变量 --text-color */
    }
    #header {
        --text-color: #c50; /* 在 #header 及其子元素中覆盖 --text-color */
    }

2. CSS 变量的使用

  • 语法: 使用 var() 函数引用变量。
  • 示例:
    body {
        color: var(--text-color); /* 使用变量 --text-color */
    }
  • 注意: 变量名必须以 -- 开头。

3. 在 CSS 中进行数学运算

  • 方法: 使用 calc() 函数对变量进行数学运算。
  • 示例:
    #sidebar {
        width: calc(var(--my-var) + 1em); /* 计算宽度 */
    }

4. 通过 JavaScript 动态修改变量

  • 方法: 使用 JavaScript 动态修改 CSS 变量的值。
  • 示例:
    let root = document.documentElement;
    root.style.setProperty('--text-color', 'black'); /* 将 --text-color 修改为黑色 */
  • 效果: 修改后的变量值会立即应用到所有使用该变量的元素。

5. CSS 变量的优势

  • 复用性: 可以在多个地方使用同一个变量,便于统一管理和修改。
  • 动态性: 通过 JavaScript 可以动态修改变量的值,实现实时样式更新。
  • 作用域: 变量的作用域可以限制在特定的选择器及其子元素中,避免全局污染。

6. 总结

  • CSS 变量 通过 -- 前缀定义,使用 var() 函数引用。
  • 可以在 CSS 中使用 calc() 对变量进行数学运算。
  • 通过 JavaScript 可以动态修改变量的值,实现实时样式更新。
  • CSS 变量提高了样式的复用性和灵活性。

P23-transitions

Hell Yes CSS (Julia Evans) (Z-Library)-23

这段内容详细介绍了 CSS 过渡(Transitions) 的基本概念和使用方法。以下是对内容的详细解释:


1. CSS 过渡的基本概念

  • 定义: CSS 过渡用于在元素的样式发生变化时,创建平滑的动画效果。
  • 触发条件: 样式变化可以通过伪类(如 :hover)或 JavaScript 代码触发。

2. 过渡的使用

  • 语法: 使用 transition 属性定义过渡效果。
  • 示例:
    a {
        color: blue;
        transition: all 2s; /* 所有属性变化在 2 秒内完成 */
    }
    a:hover {
        color: red; /* 鼠标悬停时颜色变为红色 */
    }
  • 效果: 当鼠标悬停在链接上时,颜色从蓝色平滑过渡到红色,持续 2 秒。

3. 过渡的三个部分

  • transition 属性 包含三个部分:
    1. 属性: 指定要过渡的 CSS 属性(如 colorwidth 等)。
    2. 持续时间: 指定过渡的持续时间(如 1s)。
    3. 时间函数: 指定过渡的速度曲线(如 ease)。
  • 示例:
    a {
        transition: color 1s ease; /* 颜色变化在 1 秒内以 ease 速度曲线完成 */
    }

4. 可过渡的属性

  • 可过渡的属性: 大多数数值和颜色属性都可以过渡,例如:
    • font-size
    • rotate
    • width
  • 不可过渡的属性: 一些属性(如 list-style-type)无法过渡。
  • 示例:
    .box {
        width: 100px;
        transition: width 1s ease;
    }
    .box:hover {
        width: 200px; /* 宽度从 100px 过渡到 200px */
    }

5. 过渡的优势

  • 平滑动画: 过渡可以使样式变化更加平滑和自然。
  • 提升用户体验: 通过过渡效果,可以增强用户交互的视觉反馈。

6. 总结

  • CSS 过渡 通过 transition 属性实现,用于在样式变化时创建平滑的动画效果。
  • 过渡包含三个部分:属性、持续时间和时间函数。
  • 大多数数值和颜色属性都可以过渡,但某些属性(如 list-style-type)无法过渡。

P24-media queries

Hell Yes CSS (Julia Evans) (Z-Library)-24

这段内容详细介绍了 媒体查询(Media Queries) 的基本概念和使用方法,以及如何通过媒体查询实现响应式设计。以下是对内容的详细解释:


1. 媒体查询的基本概念

  • 定义: 媒体查询允许你根据设备的特性(如屏幕宽度、设备类型等)应用不同的 CSS 样式。
  • 语法: 使用 @media 规则定义媒体查询。
  • 示例:
    @media print {
        #footer {
            display: none; /* 打印时隐藏页脚 */
        }
    }

2. 基于屏幕宽度的媒体查询

  • max-width: 应用于屏幕宽度小于或等于指定值的情况。
    @media (max-width: 500px) {
        /* 小屏幕样式 */
    }
  • min-width: 应用于屏幕宽度大于或等于指定值的情况。
    @media (min-width: 950px) {
        /* 大屏幕样式 */
    }

3. 设备类型

  • screen: 用于计算机和移动设备的屏幕。
  • print: 用于打印网页时的样式。
  • 其他类型: 还包括 tvttyspeechbraille 等。

4. 可访问性相关的媒体查询

  • prefers-reduced-motion: 检测用户是否偏好减少动画。
    @media (prefers-reduced-motion: reduce) {
        /* 减少动画的样式 */
    }
  • prefers-color-scheme: 检测用户是否偏好深色模式。
    @media (prefers-color-scheme: dark) {
        /* 深色模式样式 */
    }

5. 组合媒体查询

  • 语法: 可以使用 and 组合多个条件。
  • 示例:
    @media screen and (max-width: 1024px) {
        /* 屏幕宽度小于等于 1024px 时的样式 */
    }

6. Viewport Meta 标签

  • 作用: 确保网页在移动设备上正确显示。
  • 示例:
    <meta name="viewport" content="width=device-width, initial-scale=1">
  • 解释:
    • width=device-width: 使页面的宽度与设备的屏幕宽度一致。
    • initial-scale=1: 设置页面的初始缩放比例为 1。

7. 总结

  • 媒体查询 允许你根据设备的特性应用不同的 CSS 样式,是实现响应式设计的关键工具。
  • 常见的媒体查询包括基于屏幕宽度、设备类型和用户偏好的查询。
  • 使用 viewport meta 标签可以确保网页在移动设备上正确显示。

P25-the CSS inspector

Hell Yes CSS (Julia Evans) (Z-Library)-25

这段内容介绍了 CSS 检查器(CSS Inspector) 的功能及其在调试和开发中的重要性。以下是对内容的详细解释:


1. CSS 检查器的基本概念

  • 定义: CSS 检查器是浏览器开发者工具的一部分,用于检查和调试网页的 CSS。
  • 主要功能:
    • 查看和编辑元素的 CSS 属性。
    • 查看被覆盖的 CSS 属性。
    • 查看计算样式(Computed Styles)。
    • 调试布局(如盒模型、Flexbox、Grid 等)。

2. 如何使用 CSS 检查器

  • 打开方式:
    • 通常可以通过右键点击页面元素并选择“检查”(Inspect)来打开。
    • 不同浏览器可能有不同的快捷键或菜单选项。
  • 示例:
    <button>Click me</button>
    • 右键点击按钮并选择“检查”,可以查看和编辑按钮的 CSS 属性。

3. 查看和编辑 CSS 属性

  • 功能: 在 CSS 检查器中,可以直接编辑元素的 CSS 属性,并实时查看效果。
  • 示例:
    button {
        display: inline-block;
        color: var(--orange);
        border: 1px solid black;
    }
    • 在检查器中,可以修改 colorborder 属性,并立即看到按钮样式的变化。

4. 查看被覆盖的 CSS 属性

  • 功能: CSS 检查器会显示哪些样式被覆盖,并解释原因(如更高的特异性或 !important)。
  • 示例:
    button {
        color: red; /* 被覆盖 */
    }
    button.special {
        color: blue; /* 实际应用的样式 */
    }
    • 检查器会显示 color: red;color: blue; 覆盖。

5. 查看计算样式(Computed Styles)

  • 功能: 计算样式显示了元素最终应用的所有 CSS 属性值。
  • 示例:
    • 如果某个链接的 font-size 最终计算为 12px,检查器会显示这一结果。

6. 调试布局

  • 盒模型(Box Model):
    • 检查器可以显示元素的内边距(padding)、边框(border)和外边距(margin)。
  • Flexbox 和 Grid:
    • 一些浏览器(如 Firefox)提供了专门的工具来调试 Flexbox 和 Grid 布局。

7. 不同浏览器的工具

  • 浏览器差异: 不同浏览器的 CSS 检查器可能有不同的功能和界面。
    • Firefox: 提供强大的 Flexbox 和 Grid 调试工具。
    • Chrome: 提供详细的盒模型和计算样式查看功能。

8. 总结

  • CSS 检查器 是开发和调试网页样式的强大工具。
  • 主要功能包括查看和编辑 CSS 属性、查看被覆盖的样式、查看计算样式以及调试布局。
  • 不同浏览器的检查器可能有不同的功能和工具。

P27-thanks for reading

Hell Yes CSS (Julia Evans) (Z-Library)-27

这段内容总结了 CSS 学习资源,并感谢了参与制作这本小册子的人员。以下是对内容的详细解释:


1. CSS 学习资源推荐

  • CSS Tricks:
    • 网站: css-tricks.com
    • 特点: 提供数百篇有用的博客文章和指南,例如关于居中和 Flexbox 的指南。
  • Can I use…:
    • 网站: caniuse.com
    • 特点: 提供浏览器对 CSS 特性的支持情况,帮助你了解哪些浏览器版本支持特定的 CSS 功能。
  • Mozilla Developer Network (MDN):
    • 网站: developer.mozilla.org
    • 特点: 提供关于 CSS、JavaScript、HTML 和 HTTP 的详细参考文档。
  • W3C CSS 规范:
    • 网站: w3.org/TR/css
    • 特点: 提供 CSS 规范的官方文档,适合作为参考工具。

2. 感谢参与人员

  • 封面设计: Vladimir Kasikovic
  • 编辑: Dolly Lanuza, Kamal Marhubi
  • 技术审阅: Melody Starling
  • 感谢所有测试读者: 感谢他们的反馈和支持。

3. 总结

  • CSS 是一个庞大的主题,这本小册子只是冰山一角。
  • 推荐了一些高质量的学习资源,帮助你进一步深入学习 CSS。
  • 感谢所有为这本小册子做出贡献的人员。

总结

这本书内容涵盖了 CSS 基础知识布局技巧调试工具学习资源,对于了解 CSS 核心知识很有用,部分内容不易理解的,结合 DeepSeek 的解释能够帮助快速理解。

References

《赵玉平说职场智慧》

阅读感悟

职场道路必经之路,通透一些人间的事情。

阅读摘录

《赵玉平说职场智慧》

赵玉平 137个笔记

第一章 好领导要送公明——宋江的团队领导策略

◆ 宋江这个没形象、没背景、没水平的人,凭什么领导了英雄团队?宋江的团队领导策略其实就在他的名字中,宋公明——送、公、明。

送——及时雨不是白叫的

◆ 宋江,绝对是一个会送的领导。你要钱我给你钱,你要机会我给你机会,你要感情我给你感情……而且送得很贴心,比如对李逵,李逵有母亲没父亲,是个缺少父爱的人。所以,宋江对李逵张嘴就骂,举手就打,转过身掏出钱来,你要多少钱我都给你。再如对武松,武松不光没有父爱,也缺少母爱。宋江对武松的管理,就要柔和得多,融合了很多母爱的因素

◆ 送得多、送得贴心,就稳定了团队,凝聚了人心,所以领导会送是很重要的。而且,给钱给物不能瞎给、乱给,升米恩,斗米仇,你必须要给得有策略、有道理

◆ 宋江有儒家思想。你看《水浒》里边有儒、道、佛各家的代表人物,像鲁智深是佛家,公孙胜是道家,宋江则是儒家

◆ 儒家有一个说法:要是遇到可怜人,能帮人家就帮人家;要帮不了人家,就快步从人家眼前通过,不要在人家面前嘚瑟、招摇

◆ 武松的性格就是心高气傲,瞧不起别人,平时闹别扭,喝点酒就和别人起冲突。武松眼前的处境和他自己的行为有着直接关系

◆ 在现代团队管理中,我们认为,一个团队成员应该有两种行为:第一种,关系行为,就是处理人和人之间的关系,搭建人脉平台,构造满意度和和谐氛围;第二种,任务行为,就是完成工作指标。很多没经验的人可能就把精力都用到关系行为上面,或者都用到任务行为上面。精力用到一个方面是不对的,必须两条腿走路,平衡前进

◆ 武松就是片面的人,他觉得,只要我有本事,只要我把工作完成了,只要我能力上去了,我怕什么?我才不理你们呢。所以武松跟其他人的关系处得非常不好。再加上武松脾气大,比较骄傲,喝点酒,张嘴就骂,抡拳就打。整个公司除了董事长柴大官人之外,剩下的副总以下他都打过,报销个差旅费也能把财务科打一遍。所以说,武松就属于能力很强、人际关系特差的人

◆ 人际关系差,就产生了巨大问题,下面的人总到柴大官人那儿汇报说,武松这个人有问题。有本事还得有名声,名声没了,本事就被埋没了

◆ 在团队当中,特别是有本事的人,要很融洽地处理好与周围人的关系。企业的“企”字怎么写?上面一个“人”,下面一个“止”,这叫无人则止。把人脉关系搞垮了,事业就要停顿了。

◆ 武松就是这样有本事但是不会处理关系的人,结果呢?汇报的人多了,柴大官人就对武松有意见了。这一下,职位不给了,工资不发了,奖金扣除了,连医药费都不报了,所以武松发烧只能烤火,饿着肚子。这一方面体现了,武松这个人,没处理好关系行为;另一方面,也体现了柴大官人这个领导,对下面有本事的人关心不够啊。

◆ 宋江的做法值得称道,这叫“把名给在明处,把利给在暗处,当众给面子,背后给红包”。为什么给好处、给实惠的时候要悄悄地?因为宋江很深刻地理解一个道理:看重名的人想要过好日子,他的排场、他的花费,要比一般人大。但是和这些人能直接谈钱吗?不能,一提钱,脸面就没了。所以这些人的苦恼在于既离不开钱,又不能谈钱。宋江高明啊,把形象和面子给在明处,把实惠和利益给在暗处。当众发一张大奖状,给朵大红花,大家热烈鼓掌,宣扬他的事迹,给一个好待遇,安排一个好职务;散了会之后,私下里没人的地方,悄悄给一个大口袋,口袋里装的全是金子。给完之后,转身就走了。这是宋江的贴心之处。很多领导舍得给,但是不会贴着心给。而宋江的做法让武松心里特感动

◆ 在心理学层面有一句话叫,日常交往拉近距离,关键事件深化感情

◆ 现代心理学有个分支,叫人际关系心理学,其中有一个特别棒的理论:要跟一个人拉拢感情,拉近心理距离,最有效、最简单的方法是什么呢?就是请对方吃饭。吃饭能交流感情,吃饭能造就亲密的氛围。我们一般都跟家里人吃饭,所以一起吃饭,能造就认同感,形成家里人的感觉

◆ 什么叫感情?所谓有感情,就是有回忆呀。感情可以消失,只要你忍住不去回忆,感情就会消失;感情可以创造,只要你跟一个人一起,做很多值得回忆的事情,终究有一天,他会对你产生感情。所以你要对一个人积累感情,就一定得跟他做很多值得回忆的美好的事情。你要激发一个人的感情,怎么办呢?就跟他一起回忆美好的过去,回忆得越多,这人对你的感情就越深

◆ 在这个高明的策略中,有四点可以借鉴:第一,根据对方的需求,打造差异化的激励方案;第二,日常工作拉近距离,关键事件升华感情;第三,先制造回忆,后产生感情,有了感情以后再做大事;第四,精神的内容要有物质的载体

公——大公无私才能成就大公司

◆ 什么叫公?公的第一层含义就是在做公事的时候,能够把情绪放在一边,不管有多么不良的情绪,一旦面对工作,都能把情绪调整过来,能用工作态度、工作眼光对待人和事

◆ 人至少要有三个频道:有一个工作频道,正襟危坐、慷慨激昂;有一个娱乐频道,开开心心、又蹦又跳、活力四射;还有一个体育频道,认真锻炼、生龙活虎。不同的频道得用不同的情绪,人家宋江就是有不同频道的人。

◆ 很多团队领导没这个频道,家里的烦恼带到公司去,公司的烦恼带到家里来。公司里有事朝家里发火,家里有事朝公司发火。一天到晚,主要的工作状态就俩字——“串台”。这是不对的。领导的工作千头万绪,接触那么多的人,得学会转频道。一个人有若干个格子,这件事装在这个格子里,那件事装在那个格子里,绝不干扰别的格子

◆ 在宋江的团队当中,基本上都是黑社会分子,很多人都像张横一样,曾经威胁过他的性命,而且差一点就要了他的命。比如“矮脚虎”王英,那王英当初在清风山上,就差一点要了宋江的命,宋江却积极给他找媳妇。这些事情宋江居然都可以放下,把私人恩怨搁在一边

◆ 刘邦平定天下以后,下属都急着要待遇,不给就要谋反。但刘邦又没时间各个都给。事实上,很多领导面临这一情况,金山银山在这儿搁着,时间紧、任务重,大家都闹着要,你又不可能都给。当领导不能“天女散花”,得作考核算指标,这需要时间啊。但员工急,等不起。如何让又哭又闹的孩子等着排队,不抢眼前的饭,这是团队领导的高明之处

◆ 这个策略叫给待遇由远及近。你还就得先给那些跟你有过节的、闹过对立的、跟你拍过桌子瞪过眼睛的、平时不服气的人。你给这样的人一个公公正正的待遇,可以让整个团队稳定,这叫稳定的力量、凝聚的力量。刘邦跟张良的交往当中,张良有定汉四策。这四策当中,封雍齿排在第一位。古书上讲这叫“封一人而安天下”。

◆ 所以,“公”的含义也就是:给感情立个频道,学会用公心对待私人恩怨。给待遇设个顺序,能够由远及近,先给不顺眼的人公正待遇。你能做到这些,那就是“公”了。

明——看到大节,也看到小处

◆ “大树将军”因此成为典故,形容有才华、有水平、有贡献,但是不爱过度炫耀的人

◆ “老黄牛”要是挨饿了,就没人愿意再当“老黄牛”了;英雄要是死了,就没人愿意再当英雄了。所以,要让“老黄牛”披红戴花、有草有料,要让英雄不死,只有这样,才能有更多的人去做基础工作。

◆ 这就是为什么伯乐不常有:当伯乐不快乐,当伯乐没待遇。当完伯乐之后,千里马闪光了,我自己连饭都没得吃了,那我当伯乐干什么?

◆ 这叫不要让发现和培养人才的人被冷落。于是,齐国人就发现了,虽然我们自己没本事,但没关系,我们发现培养一个人才照样可以过好日子。于是,齐国伯乐辈出;于是,千里马辈出。

◆ 在团队管理中,建议大家:第一,别让默默无闻在一线的人受冷落;第二,别让发现和培养人才的人受冷落。做到这两点了,你的团队才能够有足够的后劲。

◆ 我们说,没有人干不成事,但人一扎堆,没干事,先出事,所以不能让人闲下来

◆ 人没事做,感觉被冷落,就要闹事;感觉有闲心,就要捣乱

◆ 老员工没事做,身体会垮掉;年轻人没事做,不长本事

◆ 人就像一辆自行车,天天骑没事,要是扔在楼下两个月,准垮

◆ 现代团队管理管这叫“人人有事做,处处忙起来”。没什么事做怎么办?先找点正事,学习培训、技能提升都行

◆ 企业可以有闲事,但不能有闲人,你得让大家都忙起来

◆ 这个策略叫“不断搅动锅里的水,让所有员工总在运动当中”。中国古人讲“流水不腐,户枢不蠹”。运动是进步的基础,停顿了就麻烦了

◆ 生命在于运动,管理在于折腾

第二章 给你一个干的理由——宋江的精神激励策略

◆ 干工作,没有物质是不行的,只有物质是不够的

◆ 带领团队谨记“要做一个会树大旗的领导”。

◆ 人为什么会工作?工作的驱动力在哪儿?结论很简单,人做工作、干事业,驱动力有两个:利益驱动和价值承诺。

屁股决定脑袋的理论根源

◆ 《聊斋志异》里的书生代表中国男人,特别是知识分子,在恋爱过程中的六大劣根——分别是“胆小、怕事、吃软饭、花心、好色、不负责”。

◆ 问:为什么狐狸精那么喜欢书生?答:因为写书的人是个书生

◆ 要是杀猪的写《聊斋志异》,狐狸精一定喜欢杀猪的;打铁的写《聊斋志异》,狐狸精一定喜欢打铁的;你要写《聊斋志异》,那狐狸精肯定喜欢你这个类型的;我要写《聊斋志异》,狐狸精肯定喜欢上了《百家讲坛》、身高1.8米以上,尤其是正在研究《水浒》的博士。这叫利益点决定观点,通俗化地讲,就是屁股决定脑袋。这人哪,屁股坐在哪个利益点上,脑袋中就会自然有哪种想法。这是不受主观意志控制的。

◆ 所以看人先看利益点,看完利益点,他即使不说话,也能知道他是怎么想的

◆ 要改变一个人,先改变他的利益点,只要利益点变了,观点自然就会变

◆ 要了解人,先要了解他的利益点,观点是会撒谎的,但利益点不会,一看就准

◆ 利益是一个人在团队中,做工作、表达观点主张时最主要的驱动力

◆ 我们带小团队要看个人利益,带大团队要看分利集团

◆ 《水浒》的基本团队模式就是共享财富,即“大秤分金,小秤分银;大碗喝酒,小碗吃肉”

◆ 所以一个公司在制定战略的过程中,一定要加上这一条:“关注一线。”假如不包含这一条,你的战略就是一纸空文

◆ 为什么有本事、够忠诚、有能力的人不出业绩?我认为主要原因是他对公司没有认同感

◆ 人的积极性从哪儿来?第一,在工作中有个人利益、有实惠;第二,让他觉得有价值、能认同

愿景规划——另类的激励

◆ 当团队领导的,得让他们看到砖和烂泥背后壮丽的大厦。

◆ 你有资源。还要拿到开采证或者正好有一个团队,他们要开采。这就是资源加机遇,保证你赚第一桶金。从第二桶金到第五桶金靠坚固的社会平台、稳定的人脉关系。从第五桶金到第十桶金靠有效的内部管理

◆ 题。第十桶金以后靠文化建设

◆ 普通员工有实惠了,那么他就因自己已经拿得很多了,出于良心、出于回报、出于情感,会帮忙盯着。高层的人因为有理想了,他会出于责任、价值观、个人主动性而帮你盯着

◆ 管理中有一句话,“一个人的问题是个人问题,几个人的问题是领导问题,一群人的问题是制度问题”。

◆ 实在的领导做小事,高明的领导做大事

◆ 光实在不行,把实在的问题展示了,把眼前实惠讲完了,只能调动低层次的人,不是思想水平低,而是生活层次低的人

◆ 高明的领导,在实在的基础上,还得设计一个远大的理想

◆ 工作如同建设一栋大厦,一到十楼叫实惠,十楼以上那叫境界。如果没有实惠,上来就讲境界,讲理想,那是空中楼阁。但是一直在讲一到十楼的事,没有更高的境界,只能是低矮的楼房

第三章 找对人才能做对事——领导必备的小班底

◆ 和,即条件相容,和平共处。同,即结论一致,观点一致

◆ 高水平的人,追求条件相容,和平共处,不强求观点一致,结论一致;低水平的人不考虑条件、对象和场景,非得强求结论一致,观点一致,结果会引发更多的矛盾冲突

◆ 宋江在给英雄排位的时候,实际上搭了两个班子:一个小班子,一个大班子

做不好人事就做不了大事

◆ 一个领导在带队伍、做工作、做事业的时候,一定既要有大班子,又要有小班子。大班子管工作,小班子管生活;大班子管战略,小班子管事务

◆ 所以,不谋心、不利私、不越权,这样的人去给我们当替身是最合适的

◆ 领导用贴身助手,就得用背景简单、资历比较浅的年轻人

◆ 所以领导用助手,要用资历浅、背景简单的年轻人,又安全又好用,过两年就提拔他,提拔起来之后再用一个,这是正确的方法

◆ 二把手给一把手管事务,一把手安排自己的人给二把手管事务,再找个年轻、没背景的人给自己管事务,这就是梁山在班子搭建上的智慧,这智慧很值得玩味呀。

◆ 宋江在选择替心、替口、替身、替手、替脸这五种角色上,颇费心思,但收获颇丰。其中,替心、替口这两种人最重要,也最难找。

摇扇子的做领导不会做的事

◆ 聪明的人只是聪明,高明的人会使用自己的聪明

◆ 聪明人会做傻事,这叫智慧。努力的人会慢半拍,这叫高明。聪明容易,高明难

◆ 在现代团队管理中,做事业就像开车,你会踩油门,那是力量,你会踩刹车,那叫智慧

◆ 孔子的学生里,道德风范最高的是颜回,最有勇气的是子路,最聪明的要属子贡。

◆ 垂头丧气的人和得意扬扬的人,都有问题。中医讲,草木得天地之偏气而茂盛,人得天地之和气而茂盛。得正气人才能不卑不亢,喜气洋洋,一团和气,而且,才能守得住富贵

◆ 主张一种东西,但是,完全不按这种主张去做,这种人就不是什么好人,而且早晚得给自己惹祸

◆ 在团队管理中,有些事情我们做不到,可以不主张,但主张了就一定得做到,怕就怕自己主张自己做不到,还让别人去做,这种人在领导岗位上早晚得出问题。

◆ 这就叫“主张一套,自己做的是另一套”,这样的领导都会出事

◆ 一般来说,聪明人最大的优点是聪明,最大的缺点是太聪明

◆ 聪明人有三道难关:第一道叫认同关;第二道叫感情关;第三道叫安全关。这三道关都过了,你才能活下来。笨人只需要过感情关,跟人家成为好朋友就可以了,聪明人却要过三关。所以看看我们周围,笨人活下来的可能性大,聪明人活下来的可能性小

◆ 真正聪明的人,真正有水平的人,面临一个共同问题就是,做事情在哪个节骨眼上入手,最能得到认可

◆ 我们生活在一个认可的年代,不让员工认可你得“死”,不让领导认可你得“死”,不让客户认可你得“死”。所以获得认可非常重要

◆ 从古到今的所有厉害的人,都要过认可关,再有本事,没人认可也是白搭,会委屈死的。

◆ 。《论语》中也说过:“不愤不启,不悱不发。”即你不着急我不给你提意见,我提了意见,你不认可,你的评价就比较低。我非得等你着急了,才给你提意见,而且我的意见特别到位,一句话就把你的火给扑灭了,那你就认可我了。

◆ 这叫你不疼,我不给你下药;你不苦,我不给你下糖;你不发烧,我不给你退烧,你没烧,我给你吃两片退烧药,你还说我害你呢。所以,吴用就等着关键时刻

◆ 关于掌握工作节奏,给大家引述另外一个反面例子,他俩一对照,你就知道工作节奏的重要性。这个反面例子也是四大名著里的名人,就是《西游记》里的孙悟空

◆ 关于工作节奏,总结两句话:第一句,积极但不能着急。

◆ 第二句,被动而不主动。

◆ 你再有本事,你也是个下属,中国古人有一句话叫“干活不由东,累死也无功”。

◆ 聪明人的第二道难关是感情关

◆ 举个例子,我们雇了一个财务经理,如果是自己人,水平越高,我们越放心;如果是个外人,水平越高,我们越不放心。这叫感情引路,聪明跟进

◆ 聪明人的第三道难关是安全关。

◆ 聪明的人不跟公司的上级领导们谈领导艺术,你谈完了,他们就该担心你的谋略那么多,万一跟他们玩心眼,怎么办?

◆ 其实吴用是在偷偷地帮宋江,但是吴用不把这事说明了,他从来没有让宋江感觉过他知道宋江愿意当一把手,这就叫装傻

◆ 聪明人能做傻事,聪明人能躲在暗处,聪明人做事能慢半拍,这叫智慧。

抡板斧的做领导不能做的事

◆ 一个领导,身边最好有两个人,一个是出主意的,我们叫摇扇子的人;另一个是帮你出头,拍桌子,瞪眼睛的,叫抡板斧的人

◆ 作为一把手争得太急、说得太绝,又会伤害自己的面子和尊严。这时你身边就得准备一个李逵这样的人,让他站出来替你说,帮你争

◆ 这种人应该有三个特点:第一,是鲁,敢发脾气,拍桌子,能使用差异化的手段震慑对手。第二,是忠,一心干事业,一心对领导,怎么骂,怎么打,都不留阴影,他能理解领导。第三,是直,竹筒倒豆子,装什么子弹开什么枪,让怎么说就怎么说。

◆ 吴用好找,李逵不好找,管理学就是这样,团队当中每个岗位都有合适的人,每个岗位都有合适的事。你看这人鲁莽,用好了就是优点。没有不合适的人,只有不匹配的工作,人在匹配的岗位上工作就会很合适。

晁盖的第一桶“人”

◆ 所以建团队,第一条,也是最根本的一条,叫作彼此的信任

◆ 信任成本是团队的最大成本,很多事情本来可以低成本、高效率地完成。为什么完不成?没有信任

◆ 我们现在往往用契约和法律手段来建立信任,但是契约和法律有漏洞,必须还有更深入的东西来弥补信任

◆ 古人有一句话叫“树怕扒皮,人怕见面”。见重要的人谈大事,怎么能发条微信,聊个QQ呢?那叫儿戏。你得亲自去,在正式场合寒暄几句,然后整整衣服,坐下再说

◆ 当天想到的事情得当天去做。在现代管理学中,这叫日落法则,即今天想到的事情,在太阳落山之前得开始行动

◆ 这叫认同公司、认同事业、认同领导、认同任务,只有这四个认同都做到了,此人才能参与我们的工作。

◆ 所以,要上梁山:第一,你喜欢梁山这个公司吗?第二,你喜欢落草当黑社会这份事业吗?第三,你喜欢梁山首领宋江吗?第四,宋头领让你下山抢劫,你去吗?只有这四条都点头了,你才算是我们的人。

◆ 吴用就要考核三阮这四个问题,但是,谈的是风险任务,又不能直接考核,所以,吴用使用了旁敲侧击法。这种方法,我们在选重大人事岗位时可以用,在非正式场合使用闲聊的手段旁敲侧击,问问他这四条行不行

宋江的用人“妙招”

◆ 在重大的商务谈判之前,千万不要接受人家的礼物、吃人家的饭,否则你心里就解除武装了,就已经投降了。等你再说话的时候,你的谈判能力、你的原则立场,都会不自觉地发生变化

◆ 对有才华、有水平的年轻人才,可以给权力,给重担,但是级别一定要逐级提升。

亲贤治小,胜过“高人”的管理之道

◆ 多样化团队中,既要有君子,也要有小人;既要有神,也要有鬼。你看庙里,菩萨、佛祖慈悲为怀,普度众生,慈眉善目。但是庙门口的护法面目非常凶。

◆ 第三步,换副笑脸劝住他。最后还得用温暖的手段解决,不能激化矛盾,不能真动手打,但你要不镇他、不吓他,他不服气。你要是上来就赔个笑脸,他觉得你软弱可欺,就可能抡圆了给你一嘴巴。不能做软弱可欺的人,实力还是很重要的

◆ 这叫君子做不了小人的事,高人做不了低事。管得住一个国、一个省,不见得能管得住一个村。小人的事由谁来做?由车老板来做,正合适

◆ 当领导的得记住,你虽有很多文明手段,但不能没有野蛮手段。当你遇到野蛮人,用文明手段解决不了,得靠别的手段。你碰见狗,狗冲你叫,你不能冲狗叫。你的员工抬手给你一个嘴巴,你还不能还手,挨打不还手,丢人,还手更丢人。这时候怎么办?很简单,我们身边养条藏獒,就能管住天下的狗。怕挨打,我们身边带一个大汉,谁打你他挡着。宋江为什么走到哪儿,都带着李逵?在黑社会下基层,身边要带一更黑的,这叫震慑力。

◆ 所以,小人不好管,你得管着;小人不顺眼,你得忍住;小人不爱干,你得把他激励起来

◆ 做大事,能用君子叫人品,会用小人叫水平。我们要做既有人品,又有水平的领导,才能把天下大事摆平

◆ 小人虽然有他的用处,但对小人必须小心。让小人办事,小人就可能会提许多无理的要求。你若是满足,他会贪得无厌;你若是不满足,就会招小人忌恨。怎么办?该满足的就满足,不该满足的就委婉拒绝。拒绝的技巧是特别重要的

◆ 在团队冲突当中,大量的冲突原因都是人家向你提要求,你拒绝,而且拒绝得太直接、太生硬。

◆ 其一,龙颜无恩。真正做大事的领导只记你的坏处,不记你的好处,不管有多大的贡献,只要你敢违反纪律,都会动狠手杀你。不要以为人家会念旧情,要看眼前,看表现。其二,小人可怕。宁得罪君子,不得罪小人。君子相斗狠在当前,小人相斗狠在背后。小人要是恨上你,他眼前笑呵呵的,但是,过二三十年,再给你下狠招儿,这叫不怕贼偷,就怕贼惦记。其三,领导身边的人不能得罪。一个看大门的,你把他得罪了,说不定啥时候,他告你一状,你的人生和事业就崩溃了。所以,领导身边的人要敬而远之。不能给他好处,也别跟他有冲突,离远点比较好

◆ 夷射,不是死在龙颜无恩上,不是死在小人可怕上,也不是死在领导身边的人不好应付上,而是死在他自己没有拒绝的技巧上

◆ 他只会用简单粗暴的方法拒绝别人,那一定会出事的。人家跟你要酒,你不愿意给,有很多方法拒绝

◆ 第一,可以无限期拖延。“这个酒现在不能给你,人多眼杂,等将来有机会我一定给你”,什么时候有机会,不知道。第二,找个东西替代。“这个酒不能给你,没关系,赵老师那儿有酒,他又不喝酒,你去朝他要。”第三,设个条件来转移。“我现在不能给你,明天早上你过来,我再给你。”第二天早上跟秘书说:“那人来朝我要酒,就说我不在啊。

◆ 现在的人际沟通理论,给了我们拒绝的六个字口诀,适用于各种人际冲突。这六个字是:承认,苦衷,出路

◆ 承认,就是我不能给你东西,但我要承认你的要求是正当的,承认咱俩的关系是亲密的,承认你是有贡献、需要关照的。承认之后讲苦衷,说我愿意给你,但是,我不能给你,原因是……最重要的是出路,拒绝别人,不给别人指明出路,你的拒绝只能打一半分,必须指明出路

◆ 现代行为学给了我们一个基本框架,当我们跟一个人发生冲突的时候,应当使用以下几个手段:竞争,又叫直接拒绝,毫不犹豫,斩钉截铁,只关注自己,不关注他人。妥协,又叫答应,没有任何条件,无条件答应,只关注对方,不关注自己。回避,“这事现在说不清,咱别说了,过些日子再说”

取长补短,善待有缺点的人

◆ 一个团队要解散时,男人眼泪汪汪,女生稀里哗啦,都掉眼泪。但是原因不一样,这叫动情机制。女孩子动感情掉眼泪,一定是舍不得一个氛围、一个团队。所以团队解散时,女孩子掉眼泪,我们人人都感动,她的眼泪中有我们的一份。男人稀里哗啦掉眼泪,绝对是因为舍不得一个人,你甭感动,那人不是你。

◆ 我们常说,似水柔情。让一个男人脑子进水的感情,就叫似水柔情;而让一个男人脑子进开水的感情,就叫火热的似水柔情

◆ 真实的世界是有很多缺点、毛病和遗憾的。但因为真实,才稳定,要不真实,就反常

◆ 现代管理学有个基本结论:稳定的人际关系,是基于缺点展示和缺点认同的

◆ 不能光看优点,缺点是稳定的基础,优点是发展的基础。先有稳定才能发展,没地基哪行?

越是亲信越不能亲近

◆ 我们一定得记住,在团队管理中,就算有过命的交情,就算是掏心窝子的兄弟,你在他面前该讲理想就讲理想,该正襟危坐就正襟危坐,该谈人生就谈人生

◆ 管好自己身边亲信的人,有一个管理原则,叫近严远宽,先严后宽

错位的王伦:庸人能否当领导

◆ 领导就要像宋江那样,像刘邦那样,通过良好的待遇政策和激励政策,通过给理想、给实惠,调动比你强的专家、人才给你干工作。

◆ 从王伦这个小聪明,我们看到了狭隘领导的第二个错误——人为制造内耗,刻意增加自己的权力

◆ 现代团队管理认为权力有两类:一类叫职位权力,是上级给你的奖罚资源;另一类叫个人权力

◆ 个人权力,第一,看本事;第二,看人品、看风格。

◆ 关于获得人心,《孙子兵法》上有一种说法,当领导要做到四个字,“上下同欲”。什么叫上下同欲?上是上级,下是下属,同是相同,欲是个人的动机

不到位的晁盖:鸠占鹊巢的悲哀

◆ 晁盖犯了三大基本错误:第一,让二把手管重大人事安排;第二,让二把手主外,一把手主内;第三,在做事情上,跟二把手争一时之短长。

梁山公司的人与事

◆ 有本事的人发脾气,那叫个性;没本事敢发脾气,那叫找死。

— 来自微信读书

《凤凰架构:构建可靠的大型分布式系统》

阅读感悟

暂略

阅读摘录

《凤凰架构:构建可靠的大型分布式系统》

 周志明

 41个笔记

 自序

  • 软件工程小说《凤凰项目》(见图1)讲述了徘徊在死亡边缘的凤凰项目在精益方法下浴火重生的故事

 5.2.2 OAuth 2

  • [插图]

 第10章 可观测性

  • 日志收集和分析大多被统一到Elastic Stack(ELK)技术栈上,如果说未来还能出现什么变化的话,也就是其中的Logstash有被Fluentd取代的趋势,让ELK变成EFK,但整套Elastic Stack技术栈的地位已是相当稳固
  • 度量方面,跟随Kubernetes统一容器编排的步伐,Prometheus也击败了度量领域里以Zabbix为代表的众多前辈,即将成为云原生时代度量监控的事实标准,虽然从市场角度来说Prometheus还没有达到Kubernetes那种“拔剑四顾,举世无敌”的程度,但是从社区活跃度上看,Prometheus已占有绝对的优势,在Google和CNCF的推动下,未来可期。
  • Kubernetes是CNCF第一个孵化成功的项目,Prometheus是CNCF第二个孵化成功的项目。Kubernetes起源于Google的编排系统Borg,Prometheus起源于Google为Borg做的度量监控系统BorgMon。

 11.1.4 封装系统:LXC

  • LXC的出现肯定受到了OpenVZ和Linux-VServer的启发,站在巨人的肩膀上过河并没有什么不对。可惜的是,LXC在设定自己的发展目标时,也被前辈们的影响所局限住了。LXC眼中的容器与OpenVZ和Linux-VServer定义的并无差别,是一种封装系统的轻量级虚拟机,而Docker眼中的容器则是一种封装应用的技术手段。这两种封装理念在技术层面并没有什么本质区别,但应用效果差异巨大
  • 以封装系统为出发点,仍是按照先装系统再装软件的思路,就永远无法在一两分钟甚至十几秒钟就构造出一个合乎要求的软件运行环境,也决定了LXC不可能形成今天的容器生态,所以,接下来舞台的聚光灯落到了Docker身上

 11.1.5 封装应用:Docker

  • 至少对早期的Docker而言,确实没有什么能构成壁垒的技术,它的容器化能力直接来源于LXC,它的镜像分层组合的文件系统直接来源于AUFS。在Docker开源后不久,有人仅用一百多行Shell脚本便实现了Docker的核心功能(名为Bocker[插图],提供了docker build/pull/images/ps/run/exec/logs/commit/rm/rmi等功能)。
  • 那为何历史选择了Docker,而不是LXC或者其他容器技术呢?对于这个问题,笔者将引用(转述非直译,有所精简)DotCloud公司(当年创造Docker的公司,已于2016年倒闭)创始人Solomon Hykes在Stackoverflow上的一段问答来回应。
  • 为了符合OCI标准,Docker推动自身的架构继续向前演进,首先将libcontainer独立出来,封装重构成runC项目,并捐献给Linux基金会管理。runC是OCI运行时的首个参考实现,提出了“让标准容器无所不在”的口号。
  • 为了能够兼容所有符合标准的OCI运行时实现,Docker进一步重构了Docker Daemon子系统,将其中与运行时交互的部分抽象为containerd项目,这是一个负责管理容器执行、分发、监控、网络、构建、日志等功能的核心模块,内部会为每个容器运行时创建一个containerd-shim适配进程,默认与runC搭配工作,但也可以切换到其他OCI运行时实现上(然而实际并没做到
  • 2016年,Docker把containerd项目捐献给CNCF管理。runC与containerd两个项目的捐赠托管,既是Docker对开源信念执着的追求,也是Docker在众多云计算大厂夹击下无奈的自救,这两个项目将成为未来Docker消亡和存续的伏笔。(看到本节末尾你就能理解这句矛盾的话了。)

 11.1.6 封装集群:Kubernetes

  • Kubernetes Master→kubelet→DockerManager→Docker Engine→containerd→runC
  • ,现在连LXC都还没有被淘汰,反倒发展出了更加专注于与OpenVZ等系统级虚拟化竞争的LXD,相信Docker本身也很难彻底消亡,如已经习惯使用的CLI界面,已经形成成熟生态的镜像仓库等都应该会长期存在,只是在容器编排领域,未来的Docker很可能只会以runC和containerd的形式存续下去,毕竟它们最初都源于Docker

 11.2.1 隔离与协作

  • Docker设计的Dockerfile只允许有一个ENTRYPOINT,这并非无故添加的人为限制,而是因为Docker只能通过监视PID为1的进程(即由ENTRYPOINT启动的进程)的运行状态来判断容器的工作状态是否正常,然后根据状态决定是否执行清理自动重启等操作。

 11.2.2 韧性与弹性

  • 故障恢复、滚动更新、自动扩缩这些特性,在云原生时代里常被概括成服务的韧性(Resilience)与弹性(Elasticity),ReplicaSet、Deployment、Autoscaling的用法,也属于所有Kubernetes教材资料都会讲到的“基础必修课”
  • 如果你准备学习Kubernetes或者其他与云原生相关的技术,建议最好不要死记硬背地学习每个资源的元数据文件如何编写、有哪些指令、有哪些功能,而是站在解决问题的角度去理解为什么Kubernetes要设计这些资源和控制器,为什么这些资源和控制器会被设计成现在这种样子

 11.3.1 Kustomize

  • 最初,由Kubernetes官方给出的“如何封装应用”的解决方案是“用配置文件来配置配置文件”,这不是绕口令,你可以理解为一种针对YAML的模板引擎的变体
  • Kubernetes官方认为应用就是一组具有相同目标的Kubernetes资源的集合
  • 如果逐一管理、部署每项资源元数据过于烦琐的话,那就提供一种便捷的方式,把应用中不变的信息与易变的信息分离开以解决管理问题,把应用所有涉及的资源自动生成一个多合一(All-in-One)的整合包以解决部署问题

 11.3.3 Operator与CRD

  • 有状态应用(Stateful Application)与无状态应用(Stateless Application)是指应用程序是否要自己持有运行所需的数据,如果程序每次运行都跟首次运行一样,不会依赖之前任何操作遗留下来的痕迹,那它就是无状态的;反之,如果程序推倒重来之后,用户能察觉到该应用已经发生变化,那它就是有状态的
  • Operator提供了一种kind:Elasticsearch的自定义资源,在它的帮助下,仅需十行代码,将用户的意图是“部署三个版本为7.9.1的ES集群

 12.1.1 网络通信模型

  • 从整体上看,Linux系统的通信过程无论按理论上的OSI七层模型,还是以实际上的TCP/IP四层模型来解构,都明显呈现出“逐层调用,逐层封装”的特点,这种逐层处理的方式与栈结构,譬如程序执行时的方法栈很类似,因此它通常被称为“Linux网络协议栈”,简称“网络栈”,有时也称“协议栈”
  • [插图]图12-1 Linux系统下的网络通信模型

 12.1.2 干预网络通信

  • 这套名为Netfilter的框架是Linux防火墙和网络的主要维护者Rusty Russell提出并主导设计的,它围绕网络层(IP协议)的周围,埋下了五个钩子(Hook),每当有数据包流到网络层,经过这些钩子时,就会自动触发由内核模块注册在这里的回调函数,这样程序代码就能够通过回调函数来干预Linux的网络通信
  • [插图]

 12.1.3 虚拟化网络设备

  • 普通交换机只会单纯地做二层转发,Linux Bridge却还支持把发给它自身的数据包接入主机的三层协议栈中
  • 对于通过brctl命令显式接入网桥的设备,Linux Bridge与物理交换机的转发行为是完全一致的,都不允许给接入的设备设置IP地址,因为网桥是根据MAC地址做二层转发的,就算设置了三层的IP地址也毫无意义。然而Linux Bridge与普通交换机的区别是除了显式接入的设备外,它自己也无可分割地连接着一台有着完整网络协议栈的Linux主机,因为Linux Bridge本身肯定是在某台Linux主机上创建的,可以看作Linux Bridge有一个与自己名字相同的隐藏端口,隐式地连接了创建它的那台Linux主机
  • Linux Bridge允许给自己设置IP地址,比普通交换机多出一种特殊的转发情况:如果数据包的目的MAC地址为网桥本身,并且网桥设置了IP地址的话,那该数据包即被认为是收到发往创建网桥那台主机的数据包,此数据包将不会转发到任何设备,而是直接交给上层(三层)协议栈去处理
  • 此时,网桥就取代了eth0设备来对接协议栈,进行三层协议的处理。设置这条特殊转发规则的好处是:只要通过简单的NAT转换,就可以实现一个最原始的单IP容器网络。这种组网是最基本的容器间通信形式,
  • 单臂路由不属于任何VLAN,它与交换机之间的链路允许任何VLAN ID的数据包通过,而这个通信的接口也被称为TRUNK。这样,A1要和B2通信,就要将数据包先发送给路由(只需把路由设置为网关即可),然后路由会根据数据包上的IP地址得知B2的位置,去掉VLAN-A的VLAN Tag,改用VLAN-B的VLAN Tag重新封装数据包后再发回给交换机,交换机收到后就可以顺利转发给B2了

 14.2 服务质量与优先级

  • Kubernetes选择哪个节点运行Pod,只会根据requests的值来进行决策;limits才是供cgroups使用的,Kubernetes在向cgroups传递资源配额时,会按照limits的值来进行设置。
  • 用户提交工作负载时设置的资源配额,并不是容器调度必须严格遵守的值,因为根据实际经验,大多数的工作负载在运行过程中真正使用到的资源,其实都远小于它所请求的资源配额。
  • 要进行驱逐,首先Kubernetes就必须制定资源不足时该先牺牲哪些Pod、保留哪些Pod的明确准则,由此就形成了Kubernetes的服务质量等级(Quality of Service Level,QoS Level)和优先级(Priority)的概念
  • Kubernetes目前提供的服务质量等级一共分为三级,由高到低分别为Guaranteed、Burstable和BestEffort
  • 如果Pod中所有的容器都设置了limits和requests,且两者的值相等,那此Pod的服务质量等级便为最高的Guaranteed;如果Pod中有部分容器的requests值小于limits值,或者只设置了requests而未设置limits,那此Pod的服务质量等级为第二级Burstable;如果是上文说的那种情况,limits和requests两个都没设置则属于最低的BestEffort

 14.3 驱逐机制

  • Pod的驱逐机制是通过kubelet来执行的,kubelet是部署在每个节点的集群管理程序,由于本身就运行在节点中,所以最容易感知到节点的资源实时消耗情况。kubelet一旦发现某种不可压缩资源将要耗尽时,就会主动终止节点上较低服务质量等级的Pod,以保证其他更重要的Pod的安全。被驱逐的Pod中的所有容器都会被终止,Pod的状态也会被更改为Failed
  • ·软驱逐:通常配置一个较低的警戒线(譬如可用内存仅剩20%),触及此线时,系统将进入一段观察期。如果只是暂时的资源抖动,在观察期内能够恢复到正常水平的话,那就不会真正启动驱逐操作。否则,若资源持续超过警戒线一段时间,就会触发Pod的优雅退出(Grace Shutdown),系统会通知Pod进行必要的清理工作(譬如将缓存的数据落盘),然后自行结束。在优雅退出期结束后,系统会强制杀掉还未曾自行了断的Pod
  • 硬驱逐:通常配置一个较高的终止线(譬如可用内存仅剩10%),一旦触及此红线,立即强制杀掉Pod,而不会优雅退出
  • 软驱逐是为了减少资源抖动对服务的影响,硬驱逐是为了保障核心系统的稳定
  • 总而言之,在Kubernetes还没有成熟到变为“傻瓜式”容器编排系统之前,因地制宜地配置和运维是非常必要的

 来自微信读书

《芯片战争:世界最关键技术的争夺战(财之道丛书)》

阅读感悟

暂略

阅读笔记

《芯片战争:世界最关键技术的争夺战(财之道丛书)》

 克里斯·米勒

 17个笔记

 推荐序 国运升级点

  • 客观地说,当今中国的经济和科技发展水平,不但比不了美国,而且连20世纪80年代正在崛起中的日本都比不了。当时日本已经有索尼、夏普、丰田、本田、东芝、佳能、尼康等一系列拥有自己的核心技术、自己的设计、自己的品牌,且受到全世界消费者追捧的公司。日本曾经在芯片上把美国打到近乎绝望。就连韩国,早在一二十年前,也已经有了三星、LG、现代这样的全球知名企业。而当今中国除了华为和字节跳动,全球品牌还很少,独家技术也很少
  • 中国排在前列的大公司都是像石油、银行、电网和电信这些国有企业,一些国产品牌只在中国能做到家喻户晓。中国经济体量大、数字好看,而我们的真实经济实力,特别是创新能力,距离发达国家还很远
  • 中国被“卡脖子”的绝不仅仅是芯片。我们在工业母机、医疗仪器、农牧业育种等很多领域都受制于人。我们的产业升级远远没有完成。芯片只是一个聚焦点,但是透过芯片,我们也许可以反思一下一些人过去那种比较幼稚的世界观
  • 简单说,米勒这本书能纠正我们三个错误认知。
  • 第一个错误认知是任何高科技都可以用“堆积”的方法获得。
  • 不可堆积的东西往往要求高水平人才的奇思妙想,要求复杂的环境,要求可遇不可求的机遇。
  • 而要造芯片,从芯片设计软件到光刻机,再到硅材料,每一个步骤都需要很多个聪明人的奇思妙想,这里没有“大力出奇迹”。你需要成千上万个“邓稼先”和“于敏”,而且他们必须都在自己的行业里做得树大根深。
  • 芯片的科学原理没有秘密,都是公开的。但是要做到技术上的可行性,尤其是商业上的可盈利性,那可就太难了。花一亿元人民币造出一颗芯片是没有意义的,我们必须保证大规模制造、保证良品率、保证更新速度,还得保证做出来很便宜才行
  • 现实是中国制造从未离开过全世界的技术支持
  • 是,我们现在有一些像量子信息、碳纳米管芯片之类的领先研究,但是这些都还处于探索科学可能性的阶段——全世界有无数个类似的研究在赌,它们距离技术可行性、商业可盈利性还差十万八千里,根本谈不上“第四次工业革命”。
  • 第二个错误认知是创新应该由政府来主导。
  • 第三个错误认知是我们应该独立自主。
  • 现实是就芯片技术而言,连美国都不能独立自主。美国必须依赖荷兰的光刻机、日本的硅片和中国台湾的制造
  • 所谓的芯片战,美国卡中国脖子,恰恰就是逼着中国去独立自主。这叫作“把互相依赖武器化”:为了打击你,我不让你依赖我
  • 互相依赖是一种生存条件,被孤立是一种打压。现代化已经形成了一个全球“圈子”,只有圈里人得到这个圈的各种好处和帮助,互相依赖,才能把事情做成,独立于圈外没有任何前途。
  • 不被卡脖子的正确做法不是独立自主,而是让自己变得更值得依赖,让包括美国在内的全世界不得不依赖我们,以此跟美国讨价还价,若你要再敢卡我脖子我就卡你脖子
  • 然而进入21世纪,中国经济就到了下一个阶段。允许引进的技术已经引进完毕,剩下的得自己研发,创新走到了无人区。人口步入老龄化,劳动力越来越贵。全球化在退潮,美国等国家已经不拿中国当发展中国家,开始对抗

 来自微信读书