This blog is rated 🔞, viewer discretion is advised

如何摆脱霉运

悲观者总是正确,乐观者总是成功。摘抄一些好玩的:

有一个英国的心理学家,叫理查德·怀斯曼,他也是英国赫特福德大学的教授。他曾经做过一个实验。大概过程是,招募两组人。第一组人,总是觉得自己最近有点倒霉。第二组人,正好相反,总觉得自己运气不错。怀斯曼就请这两组人看同一份报纸,并且给他们一个任务,数数这张报纸里面,一共有多少张照片。结果发现,倒霉组的平均耗时是,两分钟。而幸运组的平均耗时,还不到十秒钟。是幸运组的人眼力特别好吗?并不是。是因为在这张报纸的第二页,有一个巨大的广告,上面写着一行字,别数了,这张报纸上一共有43张照片。为什么倒霉组的人大都没注意到这个信息?有可能是,大家都想着,我本来运气就不太好,可不能分心大意。而幸运组人可能会觉得,四处多看看,又没什么损失,万一有意外发现呢?后来,怀斯曼又做了一组类似的实验。在报纸中间放了一个大广告,上面写着,别数了,告诉实验人员你看到了这行字,就能赢得250英镑。结果,倒霉组的多数人,又错过了这个信息。怀斯曼就得出一个结论,不幸的人,因为太专注于寻找特定的东西,而错过了别的机会。把这个结论再延伸一步,所谓倒霉,就是太执着于某个单一路线,对周围的好事视而不见。而所谓走运,就是不执着于特定目标,并在沿途发现更多的新机会。
借用马斯克的说法,很多人花了很长时间试图解决某个领域的棘手问题。但他们不会去思考有没有办法把这个解决方案应用到其他领域。简单说就是,越开放,越幸运;越执拗,越倒霉。
你可能会说,假如没有目标,没有计划,万一遇到什么变动,岂不是容易做应激反应,到时候损失更大?别着急,目标和应激反应之间,其实有一个折中的思维方式,叫做,踏脚石思维。也就是,在你前往一个目标的路上,你会遇到很多阶段性的发现,每个发现,就像一个踏脚石。但是,这个踏脚石未必会引导你前往最初的目标,它可能指向别的地方。这时,你就得顺着这个踏脚石,往新奇的地方前进。很多了不起的成就,就是这么来的。比如,YouTube最初的设想是,做一个视频约会网站。后来他们发现大家喜欢在上面发五花八门的视频,就干脆改做视频网站。再比如,当年很多人尝试造飞机,发明了专门的飞机技术,但最后造出飞机的莱特兄弟,使用的是造自行车的技术。再比如,雷达上有个驱动磁控管的部件,后来成了微波炉的核心技术。你看,这些成果都不来自最初的计划,而是中途出现的踏脚石。换句话说,面对不确定的环境时,长期的焦虑可以有。但眼前的开放与好奇,也许更重要。什么是面对世界的最好的姿态?借用理查德·费曼的一句话,以最散漫、最不羁、最原创的方式,努力学习你最感兴趣的东西。

成功大概有两条路,一个是按照悲观者做预期准备,排除一切可能出错的因素,那么剩余的结局必然成功;

另一种就是在成功的边缘疯狂试探,一不小心中了呢?

Posted

stdin

Start positive

看了 Tom Scott 的 After ten years, it's time to stop making videos.
很是感动。他2014年1月4日开始传 youtube 科技类的视频,开始每天一集后来定下来每周一更。多么强大的毅力,才能把一件事坚持10年 streak。

恰好今天也在各种群和X 上看到说「时间的朋友跨年演讲上座率不到1/3」,抛开事实不谈,突然 B站 就给我推了这个 我是罗胖罗振宇,我来B站了
。罗胖说他坚持了10年每天在微信上发一段60s语音。我觉得也挺神的。

恰好看到@Fenng说这个事,他的论点还选还挺好的,这世界终究是由乐观者推动的。虽然贴那个 ChatGPT 作为论据有点。。。。。

2024年对我而言开局十分糟糕。12月孩子陆陆续续咳嗽了快一个月了,最后查出来支原体感冒做雾化、请假输液折腾得不轻,元旦三天假期我也陪着感冒鼻涕(positive了),感觉人到中年身体一年不如一年了,破事一堆接着一堆。今天第一天上班也是头昏眼花无精打采。Tom Scott,罗振宇,Fenng 都不是我最欣赏的那类influencer/大V,你要挑毛病那我也能罗列一大堆,但是不妨碍我觉得这是2024年开端最好的启发:做一个乐观的人,努力去改变。反正都中年老男人了,也没啥好失去的。不要活得那么战战兢兢。

Posted

stderr

这个糟糕的世界对中年男人的恶意

人到中年,形败气衰,总有一种有心无力的感觉。到了这个年龄段,最大的不舒服就是和这个世界从愈演愈烈的摩擦,变成轰轰烈烈的冲突。

前几天工作上,快下班的时候甲方紧张兮兮找到我要支持一些业务,问就是急。结果我这边调整之后,另外的对接方放鸽子了。我也因为手头忙别的没在意。好巧不巧几天过后,调整导致了业务问题。全责,惨。

你说我懒吧,我它妈居然愿意在下班的时候接需求;你说我不负责吧,不负责我为啥不找个冠冕堂皇的理由推脱掉或者至少拖延一点时间。你说我为啥不仔细一点吧,犯错真的是个概率问题,而且我在对接方跑路的情况下独自也干预不了什么。为啥调整导致了问题,屎山呗。屎山怎么来的?还不都是「紧急需求」日积月累来的。为啥屎山不缝缝补补熨平一点?这玩意不记劳也不记功啊。但归根结底,谁理会这些苦衷啊,只能默默承担。

21点回家吃饭,拿起手机刚刷到学校「抵制洋节」,刚想嗤之以鼻问孩子怎么看,还没开口,孩子就嘀咕,看到国外过节,家家户户在客厅摆一颗圣诞树,抱怨说老爸太抠了。我语塞。想起来之前(实际上是2012年)淘宝买一颗1.5的也就30元也不贵,结果现在一搜,好家伙普遍70、80了。没买到便宜货,比亏钱还难受。顿时心里非常不爽!

吃完饭,丈母娘在抱怨为啥昨晚(因为穷,所以定时在谷电时段启动的)洗碗机没工作。我看了下洗碗机的门没关好,当然不会启动。这件事从来都是反复抱怨,就是舍不得去关一下门。合着因为洗碗机是我买的和推广的,所有鸡毛蒜皮的事都得我来兜底呗。

真是备受打击的一天啊。长大的过程,从来没在乎来自身边的关爱和鼓励,到现在逐渐沦落成指责和嫌弃,才追悔莫及。似乎全世界终究会剥夺老男人的一切,只留下沉甸甸的责任,说的好听点,中年男人是很多人的仰仗和依赖,说得难听点,中年男人唯一的价值就是被索取。或许这就是中年男人的命吧。

今天政策方面发生了大事,《网络游戏管理办法(草案征求意见稿)》搞得鸡飞狗跳,对整个行业的悲观,加剧了心情的低落。

于是又观察到一个点,可能很多聪明的人早早就察觉了老男人迟早被吃干抹净这个事实,所以默契的联合起来形成父权,对关键资源和社会关系形成垄断和卡位。自己辛辛苦苦创造/掠夺/搏取的财富,凭啥要被傻逼们摆布?为什么很多职场女性觉得自己遭到了歧视和不公?因为你被当男人对待了。

喜欢 ownership 是吧,得,职责对等,那就所有权极端化,私有制神圣不可侵犯,最终卷出来几个阶级顶端的胜利者老男人 own 所有人。这可能就是成年人对全世界的一份报复吧。Screw y'all! Fuck this world!

Posted

stderr

不能忘却的记忆

由于小爱同学不能导出就挨个手打出来。纪念这个日子。

一年前这几天,孩子和爱人相继病倒,我本来以为苟住了能以幸免结果在家人好转的时候一测体温发现偏高。于是赶紧洗了个澡,把小卧室腾出来,弄好电暖气、电吹风,装满开水壶,把mbp和其它个人物品搬进去,因为我知道接下来的日子将会无比煎熬。

date
2022-12-18 16:41 37.3
2022-12-18 16:43 37.3
2022-12-18 17.31 38.1
2022-12-18 17:46 38.3
2022-12-18 18:33 39.3
2022-12-18 18.41 38.7
2022-12-18 19:07 38.3
2022-12-18 19:15 38.7
2022-12-18 20:01 38.6
2022-12-18 21:11 39.5
2022-12-18 21:20 38.9
2022-12-18 21:26 39.1
2022-12-18 21:41 39.2
2022-12-18 23:30 38.4
2022-12-19 02:38 39.3
2022-12-19 02:48 38.3
2022-12-19 10:05 38.5
2022-12-19 10:12 37.9
2022-12-19 10:36 37.4
2022-12-19 11:19 37.7
2022-12-19 14:03 37.7
2022-12-19 14:29 37.0
2022-12-19 15:18 37.7
2022-12-19 16:33 37.6
2022-12-19 18:05 38.1
2022-12-19 18:44 37.8
2022-12-19 19:12 37.2
2022-12-19 19:47 37.8
2022-12-19 20:00 37.6
2022-12-19 21:09 37.2
2022-12-19 21:44 37.2
2022-12-19 22:57 36.9
2022-12-20 00:19 37.2
2022-12-20 06:55 37.1
2022-12-20 09:03 37.0
2022-12-20 09:34 37.0
2022-12-20 10:49 36.8
2022-12-20 11:41 38.0
2022-12-20 11:59 38.4
2022-12-20 14:47 38.6
2022-12-20 15:51 38.1
2022-12-20 16:32 38.0
2022-12-20 18:22 38.3
2022-12-20 22:31 38.4
2022-12-21 09:12 38.1
2022-12-21 09:59 38.2*
2022-12-21 11:57 38.2
2022-12-21 13:46 37.5
2022-12-21 15:10 37.5
2022-12-21 16.44 37.2
2022-12-21 17:07 37.0
2022-12-21 20:37 37.2
2022-12-21 20:54 37.7
2022-12-21 22:54 38.3
2022-12-21 23:05 38.4
2022-12-22 09:23 36.7
2022-12-22 09:43 37.3
2022-12-22 12:44 37.6

*: 布洛芬已用完

可能大家只是看到一串冷冰冰的数字,但是我打字都是颤抖的,每一刻时间和体温都是刻骨铭心的记忆。

NEVER FORGET!

Posted

stdout

京加高铁

今天了解到这么一件事,白令海峡原来也就 82.5km 宽,水深55米,对于现代工程技术来说修桥和隧道都不是问题。

如果中间那个岛做个中转站,那么两侧都各自30km左右。所以理论上从北京修一条到西雅图/加州的高铁完全没有问题。。

Posted

stdout

query git notes with Github GraphQL

git notes is an interesting feature, you can use it like

git notes add -m "hello test git notes"
git push origin 'refs/notes/*'

Github supported them back in 2010 then gave up LMAO.

If you need to retrive them with Github API, try GraphQL like this

  {
    repository(owner: "est", name: "snippets") {
      refs(refPrefix:"refs/notes/",first:1) {
        nodes{
          ... on Ref{
            target {
              oid
              ... on Commit{
                changedFilesIfAvailable
                message
                tree{
                  oid
                  entries{
                    oid
                    path
                    size
                    lineCount
                    mode
                    object{
                      ... on Blob{
                        byteSize
                        text
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }

It will return something like:

  {
    "data": {
      "repository": {
        "refs": {
          "nodes": [
            {
              "target": {
                "oid": "bfdfda121f3d8a2d6aa399e3e8f0d58b3db0a543",
                "changedFilesIfAvailable": 1,
                "message": "Notes added by 'git notes add'",
                "tree": {
                  "oid": "2ab3e6a8b0dff6596fa60ecfb3c61bf91e5b4e1f",
                  "entries": [
                    {
                      "oid": "404d353a254ffbc97b2e16d17b8c100461aef586",
                      "path": "4fb1272f1e235580f87e3ec071984658de050dc9",
                      "size": 21,
                      "lineCount": 1,
                      "mode": 33188,
                      "object": {
                        "byteSize": 21,
                        "text": "hello test git notes\n"
                      }
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    }
  }

Here "path": "4fb1272f1e235580f87e3ec071984658de050dc9" is the commit SHA.

If you add more filter parameters in refs() you can fetch all the notes.

Posted

stdout

VPS推荐:Hosteon

继续上次 Racknerd 之后发现这家还行。写了个爬虫扒了一下各色配置,独立服除外,有需要的自取

pid spec Disk Bandwidth Annual $
1 1C512M 5GB 100Mbps $27.00
7 6C8G 100GB 100Mbps $270.00
77 1C1G 10GB 1Tbps $21.00
78 2C1.5G 20GB 2Tbps $36.00
79 2C2G 25GB 3Tbps $45.00
80 2C2.5G 30GB 4Tbps $54.00
81 3C3G 40GB 5Tbps $63.00
83 3C5G 50GB 8Tbps $90.00
84 6C10G 120GB 12Tbps $180.00
85 10C12G 150GB 15Tbps $270.00
86 14C20G 200GB 20Tbps $450.00
87 20C30G 300GB 30Tbps $675.00
110 1C1G 15GB 2Tbps $21.00
111 2C2G 20GB 4Tbps $39.99
112 3C3G 30GB 6Tbps $49.50
113 4C4G 40GB 8Tbps $59.99
119 1C512M 10GB 500Gbps $24.00
120 1C1G 15GB 1Tbps $36.00
121 2C2G 30GB 2Tbps $72.00
122 2C3G 50GB 3Tbps $108.00
123 2C4G 70GB 4Tbps $144.00
124 4C5G 90GB 5Tbps $180.00
126 6C6G 100GB 6Tbps $216.00
127 6C7G 120GB 7Tbps $252.00
128 8C8G 140GB 8Tbps $288.00
129 8C9G 150GB 9Tbps $324.00
130 10C10G 170GB 10Tbps $360.00
131 10C11G 180GB 11Tbps $396.00
132 12C12G 200GB 12Tbps $432.00
154 1C1G 20GB 4Tbps $33.00
155 2C2G 30GB 6Tbps $40.00
156 3C3G 40GB 8Tbps $60.00
157 4C4G 50GB 10Tbps $84.00
158 5C5G 60GB 12Tbps $105.00
159 6C6G 70GB 14Tbps $130.00
160 7C7G 80GB 16Tbps $145.00
161 8C8G 90GB 18Tbps $170.00
162 9C9G 100GB 20Tbps $190.00
163 10C10G 110GB 22Tbps $210.00
164 11C11G 120GB 24Tbps $240.00
165 12C12G 130GB 26Tbps $260.00
166 8C8G 50GB 30Tbps $98.99
167 4C4G 25GB 15Tbps $56.99
168 4C4G 25GB 15Tbps $79.00
169 8C8G 50GB 30Tbps $158.00
170 12C12G 75GB 30Tbps $235.00
171 16C16G 100GB 30Tbps $318.00
172 20C20G 125GB 30Tbps $399.00
173 24C24G 150GB 30Tbps $480.00
174 28C28G 175GB 30Tbps $575.00
175 32C32G 200GB 30Tbps $660.00
176 36C36G 225GB 30Tbps $730.00
177 40C40G 250GB 30Tbps $810.00
178 1C1G 20GB 2Tbps $21.00
179 32C32G 1TB 1Gbps $540.00
180 64C64G 1TB 1Gbps $725.00
181 128C128G 2x 1Gbps $1850.00

Posted

stdout

太阳系天体的 unicode 符号

发现Astronomical symbolsPlanet symbols
Alchemical symbol

IAU 七金 符号 name 星期 七曜 备注
☉︎ 太阳 Sun 星期天 日曜
月球 Moon 星期一 月曜 也可以画成 ☽︎
或者月相 🌑︎🌒︎🌓︎🌔︎🌕︎🌖︎🌗︎🌘︎,🌚︎🌛︎︎🌝︎︎🌜︎︎
H 水星 Mercury 星期三 水曜 赫耳墨斯(Hermes),罗马人称 Mercury
现代人称「爱马仕」的双盘蛇带翼权杖 Caduceus
V 金星 Venus 星期五 金曜 维纳斯
E 🜨 地球 Earch Bible 里说的四条河把地球分成四洲
另外的符号是 ♁ 表示十字架钉个球 globus cruciger
M 火星 Mars 星期二 火曜 符号是战神盾牌和长矛
J 木星 Jupiter 星期四 木曜 字母 Zeta(大写Ζ小写ζ)加一杠,代表宙斯/朱庇特
S 土星 Saturn 星期六 土曜 字母 kappa-rho ,表示 Kronos 加一杠
泰坦 克罗诺斯,宙斯他爹
U 天王星 Uranus
N 海王星 Neptune

几点:

  • 其中 金星/火星 符号被拿来代表 女/男 是1750年代Carl Linnaeus干的。
  • 冥王星 Pluto 发现的晚,占星术还没来得及改版就惨遭除名成矮行星。
  • 星期和对应的天体意义,全世界惊人的一致

其它各种卫星,小行星的名字就懒得贴了。

有了这些符号,我们可以画一个比如托勒密的简化版太阳系模型:

本文源自《哥白尼、伽利略、开普勒都是脱离证据拍脑袋撕逼》一文。HN

Posted

stdout

Getting started blogging on Win10

I am planning to setup by blogging environment on my Windows PC.

  • OS: Windows 10
  • Shell: Bash on WSL1
  • Program: pelican on Miniconda3 with Python 3.11

My objective today is get rid of the static/js/core.js from aether-pelican theme and replace it with a simple CSS, and it turns out quite challenging.

WSL1 and file system

As it turns out, the installed Ubuntu root filesystem on WSL1 is located here:

%localappdata%\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs

I plan to move my home dir (at least) to another SSD on my PC, as the C:\ drive is an old SanDisk Plus240, which is unforgivablly slow these days. I tried to mv ~ /mnt/d/home, ok but the permission gets weird.

So I tried to copy the folder on Windows to D:\home, and mklink back. I tried both mklink /D and mklink /J ( the /D switch works on a remote drive whereas the /J junction works on local NTFS disks only) and they both failed be recognized by WSL1. The bash yields Broken stdout/stdin upon launch

So OK, default home dir then. Bad luck for me.

Ubuntu 22

I just found out my cat /etc/lsb-release shows Ubuntu 18, which is the default distro installed on WSL1 I assume. I tried to install Ubuntu 22 from the Microsoft Store, after several wasted minutes (curse the slow Internet in Mainland!) it turns out for WSL2 only. Luckily the Windows Subsystem for Linux was successfully upgraded to latest version:

C:\> wsl --version
WSL version: 1.2.5.0
Kernel version: 5.15.90.1
WSLg version: 1.0.51
MSRDC version: 1.2.3770
Direct3D version: 1.608.2-61064218
DXCore version: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windows version: 10.0.19045.3570

the wsl --list --online commands stuck for a total of 10 minutes and returns nothing (curse the AS4134 163NET!)

Getting Python, Miniconda and Pelican

So linux distro setup was back to where started, I only have Python to mess with. Miniconda3 gets insalled easily, now that's a some progress. The static site generator I am using pip install pelican[markdown] finishes ok, the pypi was not blocked? Keke.

Then I typed make. It was not part of WSL1? Blaspheme!

Manually running python -m pelican.server, and voila! The Windows asks for firewall listening permission, granted, open up browser, BAM! My blog setup process is done and now I can write and tweak it on Windows.

Oh, and there's git config core.fileMode false needs to be checked!

Posted

stdout

火星上怎么结算工资

看到个段子

突然让我想起个事,如果去火星打工,薪资该怎么计算?摘录 wikipedia

火星的恒星时长 24小时37分钟22.663秒,而太阳日则长 24小时39分钟35.24409 秒。而地球的恒星时和太阳日则分别是 23小时56分钟4.0916秒24小时。在比较之下,一火星太阳日等于1.027491地球太阳日,即比地球太阳日长2.7%。

怎么有两个天?继续搜

太阳日(英语:solar day)是依据太阳运动,所定义的时间,可以分为视觉太阳日和平均太阳日。一太阳日传统称为一“日”、一“天”或一“昼夜”。

英语里把火星的一天叫做一个 Sol。一个火星年在英文的 wikipedia 有讲

sidereal year ... is about 686.98 Earth solar days (≈ 1.88 Earth years), or 668.5991 sols 。也就是说火星一年有 668.5991 火星天。。。

火星因为有25.19°的自转倾角,跟地球相近,所以也可以划分四季。但问题是,火星的轨道长这样:

所以如果按照四个季节划分,最长的季节有 194 个 Martian Sols,最短的 142 Martian sols

那么问题来了。在火星上怎么算工资呢?我觉得得安天结了。还有,假如亲爱的读者小伙伴将来到火星务工,应聘的时候一定确认好是按地球年结算还是火星年哦。谨防火星历诈骗!

Posted

stdout

跟孩子掰扯活着的意义

今天趁着11·11买了个 GZ104 黄轴键盘,感觉之前随便买的青轴的好了很多,于是手痒想打字。哈哈

人的意义 是什么

娃今天吃饭的时候突然问了我一个问题:「人活着的意义是什么呀」,很不凑巧我也闲来无事琢磨过这事,但是想要在孩子面前把这事说清楚可能没那么容易。

于是我换了个思路,我问他,如果你是一个动物,你觉得意义是什么?他没回答,我又问,如果你是一块石头,石头会考虑自己的意义嘛?

孩子也没回答。最后我说,人活着的意义呀,可能很多人也不知道,但是有一点可以确定,正因为你有一个聪明的小脑袋,才会去思考「什么是意义」这样的问题

国家是什么

他又问到,为什么世界上有那么多国家呢,为什么联合国不是一个国家。怎么样才能统一全世界成为一个国家。

我的回答又把刚才的套路重复了一遍。如果你是一只动物,你会觉得自己属于某个国家吗?娃想了下,可能不会吧。我看到碗里的饭说,如果你是一粒大米,你会觉得自己有国家吗,孩子也不说话。最后我说,国家有很多个定义,但是正是因为人有了大脑,有了想象力,才会产生国家这个概念。所谓国家,就是一个想象的共同体。

反思

这可能是我会记录的最无聊的两件事。因为我真的想拿着键盘痛快的打一打字哈哈。

或许我的回答都不是最好的,现在想起来真的有很多改进之处。比如没能鼓励孩子进一步对这个问题进行探索。

蹲茅坑的时候想了一下,我们小时候哪里会思考这些问题呀。小时候都在为了作业发愁,心思都花在了怎么玩上面。现在的孩子能思考这些问题说明生活条件比较好了。人在穷的时候,物质匮乏的时候是不会去思考存在的意义的,都忙着奔波生活去了。生存都照顾不过来,如何去顾及意义呢。

生命或许最大的意义就是生存。仅仅丰衣足食之后,才有闲心来考虑一下奋斗折腾这么大半辈子,究竟是图了个啥。

后记

本文完成于 2023-11-05 22:39:59。

Posted

stderr

FastAPI/Starlette graceful shutdown server-sent events

缘起

假如你用 FastAPI/Starlette框架,写了一段 Server-sent Events

@app.get('/api/my_stream')
async def api_stream():
  async def gen():
    while 1:
      yield "data: {}\n\n"
      await asyncio.sleep(1.0)

  return StreamingResponse(gen, media_type="text/event-stream", headers={
    'X-Accel-Buffering': 'no',
    'Cache-Control': "no-cache",
    'Connection': "keep-alive",
  })

FastAPI 事件

然后你重启了服务器,比如 gunicorn+uvicorn,你会发现这个连接不会断开,一直输出结果。直到 worker 进程超过配置里的 timeout 参数(默认60秒),被 master 强行杀死然后重启。

一开始尝试了 FastAPI 官方的 shutdown 事件,再在代码的 while 1 加一个 if 判断。发现不管用

@app.on_event('shutdown')
def on_server_shutdown():
    app.state.running = False

发现这段代码执行是执行了,但是是在杀死worker的时候生效的。得更加提前。用ASGI推荐的lifespanhttp.disconnect 太复杂,放弃。

Linux signal

尝试暴力监听 worker signal

import signal
signal.signal(signal.SIGINT, on_server_shutdown)  # ctrl+c
signal.signal(signal.SIGUSR2, on_server_shutdown)
signal.signal(signal.SIGTERM, on_server_shutdown)
signal.signal(signal.SIGWINCH, on_server_shutdown)
signal.signal(signal.SIGHUP, on_server_shutdown)
signal.signal(signal.SIGQUIT, on_server_shutdown)

发现也不管用。研究了半天,似乎是 UvicornWorker.init_signals 的时候signal.SIG_DFL。无语ing

uvicorn shutdown 流程

看gunicorn日志:

[2023-11-02 09:50:38 +0800] [25918] [INFO] Shutting down
[2023-11-02 09:50:38 +0800] [25918] [INFO] Waiting for application shutdown.
[2023-11-02 09:50:38 +0800] [25918] [INFO] Waiting for connections to close. (CTRL+C to force quit)
....
[2023-11-02 09:51:18 +0800] [25918] [INFO] Application shutdown complete.
[2023-11-02 09:51:18 +0800] [25918] [INFO] Finished server process [25918]
[2023-11-02 09:51:18 +0800] [25918] [INFO] Worker exiting (pid: 25918)

观察到 Waiting for connections to close 之后卡住。

属于 uvicorn 的 Server.shutdown() 方法

这个方法调用链:

  1. Server.install_signal_handlers 里注册 signal
  2. Server.handle_exit 里设置 Server.should_exit = True
  3. 每秒一次的 Server.on_tick 就会打断 Server.main_loop() 死循环
  4. 调用 Server.shutdown

如果能拿到 Server.should_exit 自行判断就好了,但是 ASGI 是容器无感的,只继续研究

断开http连接

上面日志有一个比较关键:

# Request shutdown on all existing connections.
for connection in list(self.server_state.connections):
    connection.shutdown()
    await asyncio.sleep(0.1)

其http连接关闭方法实现为:

def shutdown(self):
    """
    Called by the server to commence a graceful shutdown.
    """
    if self.cycle is None or self.cycle.response_complete:
        self.transport.close()
    else:
        self.cycle.keep_alive = False

这里应该走的是 else 分支。所以解决方法就是去代码里拿到 cycle.keep_alive 这个属性。

ASGI 接口

跟了一会儿,发现ASGI 在 starlette 里如下流程:

  1. Route() 初始化 self.app = request_response(endpoint)
  2. Route().handle() 的时候会调用 self.app(scope, receive, send)
  3. unicornrun_asgiresult = await app(self.scope, self.receive, self.send) 。其中 app 就是 starlette 的 Route() 实例

所以解决方案逐渐明朗了

黎明的前夜

通过 FastAPI/Starlette 请求的 .receive 属性的 __self__ 拿到 unicorn 的 cycle 实例,然后定时判断上面 shutdown 赋值的 self.cycle.keep_alive = False

@app.get('/api/my_stream')
async def api_stream(req: Request):
  async def gen():
    while 1:
      yield "data: {}\n\n"
      await asyncio.sleep(1.0)
      if getattr(req.receive.__self__, 'keep_alive', None) is False:
        break

  return StreamingResponse(...

这里用了个 getattr() 是保证这个 hack 在非 unicorn 下代码也能正常跑

git commit . -m "haha" 上机联调,发现坑了。。。我特么。。。。这个 cycle.keep_alive 默认就是 False

扭转 keep_alive

这玩意一直为 False 的原因是,unicorn 的 HttpToolsProtocol.on_headers_complete 赋值过程:

self.cycle = RequestResponseCycle(..., keep_alive=http_version != "1.0")

这里 http_version 可以通过 req.scope['http_version'] 得到,打印了一下,你猜怎么着,还真tm是。。。原因就是厂里反代 nginx的proxy_http_version没配置。这里是个常见的坑,非常影响性能,因为每个请求会生成一个新的 http 连接。

但是也等不急SA去改配置了。于是直接在代码 async def gen(): 前面写死:

req.receive.__self__.keep_alive = True 。提交,再试。。。咦,怎么 keep_alive 依然为 False????明明刚刚赋值了?

继续读代码,发现第二个坑,发现 unicorn 的 RequestResponseCycle.send() 方法里,在构造返回的时候,有一句

for name, value in headers:
    elif name == b"connection" and value.lower() == b"close":
        self.keep_alive = False

好吧。。。那么解决方法就是,把 keep_alive = True 挪到返回内部。先完成返回构造,再强行改值。

最后的代码

@app.get('/api/my_stream')
async def api_stream(req: Request):
  # req.receive.__self__.keep_alive = True  # doesn't work here
  async def gen():
    req.receive.__self__.keep_alive = True  # works here
    while 1:
      yield "data: {}\n\n"
      await asyncio.sleep(1.0)
      if req.receive.__self__.keep_alive is False:
        break

  return StreamingResponse(gen, media_type="text/event-stream", headers={
    'X-Accel-Buffering': 'no',
    'Cache-Control': "no-cache",
    'Connection': "keep-alive",
  })

测试重启 worker,该连接在1秒后断开,worker平稳重启。完美。

苦逼撸业务的一天又开始了。

Posted

stdout

浏览器拉起钉钉客户端并跳转OA工单

又是 corporate software engineering 吃屎的一天。用户在内部系统走流程,厂里用的是钉钉自带的OA工单审批。已经接入了API创建、完成工单

问题在于发起工单之后,用户并没有感知,不知道工单去哪里了,流程就断了,向钉钉官方售后发起咨询,对方丢了一句:不支持。无语

只能自己鼓捣。已知工单链接,在钉钉聊天窗口内部的最短的网址为:

  https://aflow.dingtalk.com/dingtalk/mobile/homepage.htm?corpid=...&procInstId=...#approval

对方丢了一个 dingtalk:// 跳转协议

反复尝试,发现一个很有用:

桌面端打开URL
dingtalk://dingtalkclient/page/link
侧边栏: pc_slide=true
大容器(类似工作台容器)ddtab=true

于是构造一个试试:

  dingtalk://dingtalkclient/page/link?url=https%3A%2F%2Faflow.dingtalk.com%2Fdingtalk%2Fpc%2Fquery%2Fpchomepage.htm%3Fcorpid%3D...%26procInstId%3D...%23%2Fapproval

然后果然ok了。

钉钉给人的感觉就是各个部门 hack 强行揉合起来一个怪物。各种不完善、不一致的问题。有空写一下吐槽。

Posted

stdout

SVG放在 img 中不能加载图片/字体

最近想在 markdown 里搞图文混排,遇到一个大图把版面占完了,想缩小一些,由于 .md 渲染器安全限制,没法直接指定宽高

只能从图片本身想办法,首先最直接的手段就是服务器再缩放一次,但是蛋痛的问题是缩放之后文件怎么存,怎么给静态文件路由,怎么保障图片可用性,怎么兼容各种尺寸,想想都头大。

想到一个 hack,要不直接放一个 .svg 进去,因为 svg 支持加载位图资源,所以把 svg 位图缩小一点,让浏览器渲染 svg 不就行了。

没想到就被这个 hack 坑了。原因是 svg 如果被 <img> 标签渲染,那么禁止加载任何外部资源,脚本也禁止执行。

为了证明这个限制,我写了个 demo 放在 https://lab.est.im/shit_svg/

这个问题太隐蔽了。stackoverflow 上只有 Robert LongsonThangLeQuocTheHippo
的答案提到隐约线索:

后来搜了到 skychx 把这个问题研究透了。罪魁祸首 SVG Security

If an SVG file is fetched as image, then certain requirements apply to this document:

  • The SVG document is not allowed to fetch any resources. This also applies to scripts, stylesheets or images.
  • Fonts shouldn't be loaded as well. The situation in UAs seems to still be unclear though.
    Scripts must not be executed.
  • Event listeners must be disabled at all times.

浪费我好多时间。SVG2里把这个说得很明白了 https://www.w3.org/TR/SVG2/conform.html#processing-modes

When external references are disabled in an SVG document, any attempt to fetch a document through an external reference must instead be treated as if a network error occurred and no data was received.

不过查阅资料的时候发现个好玩的,SVG 1.1 支持 URL fragments

比如:

Posted

stdout

市以上行政区划去掉后缀

今天才知道,中国一共333个市一级行政单位。数据来源是 民政部首页 ➡ 民政数据

import re

def short_name(name):
    m = re.search(r'''
        (\S+?)
        (?:
            壮族|回族|维吾尔|省|自治区|特别|  # 省
            (?:侗|傈僳|傣|哈尼|回|土家|壮|布依|彝|景颇|朝鲜|白|羌|苗|藏)族|
            蒙古|柯尔克孜|哈萨克|
            地区|市|盟|自治州|自治县
        ).*$
    ''', name, re.VERBOSE)
    if m:
        return m.group(1)
    return name

这段代码的作用是把比如 "黔南布依族苗族自治州" 这样的名字,缩短成 "黔南"

有一个小坑是,市一级行政区包含 蒙古|柯尔克孜|哈萨克 这三个名字后面没有 字。

有一些 XX林区 XX矿区 什么的,我觉得保留比较好。

Python的 re.VERBOSE 真是好东西!

Posted

stdout

B站百大up 和 国拟

bilibili.com 是国内热门长视频网站,俗称B站,百大UP是头部百万粉丝的视频上传者,前段时间无聊看了下《征集两万份问卷!全站最受欢迎的UP主居然是..?》,这部视频作者 -LKs- 抛出了一个惊世骇俗(对我而言)的理论:

频道和频道之间,天生就有占领心智和认知上的高低之分
记忆点排名:人设≥情绪≈观点>颜值>知识/新闻`
一般来说输出情绪和观点的频道,它一定会比那些输出知识和资讯的频道,要更容易被人记住和喜欢。因为前者的拟态更接近人,后者的拟态更接近「书」

注意它这句话有前提:

  1. 第一他说的是,一个频道作为一个品牌如何被人记住和喜欢
  2. 第二他说是记住和喜欢,并没有说频道的内容一定正确或者有效
  3. 第三它不是说一个频道对你有多少帮助。完全有可能你偶然遇到或者搜到一个频道帮你解决了一个问题但是你把频道的名字得一干二净

想起来,这也是为啥youtube/bilibili上很多视频封面都是各种大头照,以 LinusTechTips为例:

让人们记住一个鲜明的大活人,人设最重要啊。

无独有偶,今天又看到一则奇闻《不得不出“国区特供版”的波兰球,是怎么火进小学生圈子里的?》,简单的说 Steam 上出了一款波兰球游戏「Bang On Balls Chronicles」结果波兰球这个亚文化早就在国内流传开并在小学生里成功的「娘」化了

得出一个道理:自己对世界的理解,要去人化,祛魅脱敏;对品牌和迷因的传播,要拟人化萌娘化。

Posted

stderr

核心阶级和国家认同

最近看到个巨扯谈的视频《个人主义盛行的美国是怎么建立国家认同的

剧透 美国早期本来是一个松散的13邦联盟,互相尿不到一壶里去,但是联邦政府作为一个可以名义上筹钱筹人的机构,在1803年的Louisiana Purchase得到了大量的未开发土地,种植园主和牧场主为了个人的发财梦,去和北美土著诸部火并,拿起枪杆子保卫自己亲自或者祖辈抢来的土地,13邦的边界虽然明确,但是联邦政府在默许拓边这一行为,并且应允境外白人用 税款 换取 联邦指派治安官和联邦武力的庇护。在对北美土著采取离间驱逐的过程中逐渐形成国家认同。

实际上,美国这一血腥的国家认同,正是在上个世纪30年代纳粹主义的渊源之一(wapo有讲)。简单的说,发现美洲之后,敢离开老欧洲漂洋过海的人都是胆大包天的亡命之徒。它们信奉的昭昭天命就是「应许之地」,加上新教核心理念就是「sola fide」,我强我有理,有理我就强,所以我就是比其他物种优越。最后发展成种族主义,实施对其他 race 的压迫。

这个视频的 up 主是「思维实验室」,他们家的视频有一些说不清道不明的诡异但的地方但是题材比较有趣。这个视频很方便的忽略了一个更有趣 whataboutism 的问题:太平洋对岸的国家认同是怎么建立的?

有理由相信,华夏的国家认同建立正是武装殖民

白寿彝版《中国通史》在论述周初分封制直接说这就是武装殖民。

周初的分封是一种武装驻防事业,其目的主要在于作为王室的助手,以监视被征服的各族人民,实际上它具有武装部落殖民的性质。我们知道,周王当时所能直接统治的只有王巍之地。王畿是以镐京和洛邑为两个焦点,其范围现在不能确考,但可知其北不过黄河,南不到汉水,东不达淮水,西则镐京已接近边陲。王畿之外,周室先后封立了很多诸侯国,诸侯对王室的义务不过按期纳贡朝觐,出兵助王征伐,以及救济畿内的灾患而已。诸侯国的内政几乎完全自主,而王室开国初年的武王成王过去以后,诸侯对王室的义务也就成了具文。
另一方面,所有新建的封国大都是以少数周族奴隶主贵族统治多数被征服族人,其土既非周人所有,其民也与周人不类,这些新来者的统治地位,如果没有坚强的武力作其后盾是支持不住的。姜太公封到东方的营邱,史书说太公就东国,“夜衣而行,犁(黎)明至国。莱侯来伐,与之争营丘。”“营丘边莱,莱人,夷也”(均见《史记·齐太公世家》),新来的统治者占有其土地,拥有其人民,因而起来反抗。《礼记·檀弓上》云:“太公封于营丘,比及五世,皆反葬于周”,可见当时武装殖民之不易。当时被封的周族奴隶主贵族及其所率领的周族公社农民进入广大占领区后,首先建立一个军事据点,这在古代文献中名之曰“城”,只有如此,才能进行武力镇压。《诗经·小雅·黍苗》中召穆公营谢,诗人描述军族集镇之况云:“我任我辇,我车我牛,我行既集,盖云归哉!我徒我御,我师我旅,我行既集,盖云归处!”周人及其姻戚联盟,造邦筑城,镇戍征服地区,也见于周金铭文,例如《中甗》虽然铭文残泐,颇难卒读,但其大意,尚可通晓。意谓:王派中巡省南国,筑城殖民,并派兵镇戍。《班毁》记毛公伐东国■戎,■令班“■(以)乃族从父(毛父)征,■(出)■(城)卫。”毛公东征“三年静东国”。这里的■(城)就是征服筑城之证。

这个段子说,姜子牙去山东「上任」,实际上也就给了一点亲兵和战车,地盘要自己去抢,城要自己去建,太公当年差点被山东当地一个叫「莱」的土著部落给搞死……

为什么少数民族总是「能歌善舞」,并且大多住在偏远的山区?是不是换个译法就是 …… tribal reservation?

这让我想起另一个段子:《封神》导演乌尔善说【汉族称谓】形成于元代。

下面有个评论就很应景:「汉族是不是起源自元代,我不知道,但是我知道,“我是汉人”的意识是这些其它民族一遍一遍给我加强的」

这里有个感觉,无论是「通天纹大妈」,还是玩梗的「正米字旗」老不列颠,还是「红旗下长大」的大院子弟,对阶级的认同度远远大于普通国民。因为天下是他们或者他们的祖辈打下来的,有一种「股东感」。如果是普通人,和平年代的国籍无非是身份证件上的可以花代价更改的一行字罢了。

如果你读到这里,一定要暂停一下。如果你觉得这个太宏观,那么假想一下你去了个新公司。你跟普通同事交谈的是,今天做了哪些工作,明天又有哪些该死的任务下来;但是公司里总有那么一戳人,他们跟你讲公司的视角完全不同。他们会告诉你 A 这个市场是当年谁谁大干30天拿下的,B 这个股份是谁谁奋斗了几年才成为合伙人得到的,C 这个部门之前没有,是谁谁带资进组专门成立的。D 这个烂摊子之所以一直没解决是因为负责人跑路到友商,等等。那么把同等的视角换到国家,统治者或者他们的家属后代会明确告诉你,某某领土的由来就是谁谁当年打下来的,而且他们在这样的一种「传承」体系中,非常自豪。

这就是为啥平时很多 XX盟 XX旗 XX喇嘛教 会看不起汉人的根本原因。别人对某一篇山川有「ownership」的感觉。汉文化一个比较短视缺陷就是把天下看成一种零和博弈,「百姓」需要把自己一部分权力让渡给「君主」,人权需要迁就主权,但从唯物的角度来说,国家总是由一小部分人打下来的,大多数被土地绑定的人,并没有太多选择,作为 X国人 或 X朝人 的资格,只能是来自统治者的「施舍」。所以鱼肉百姓也是理所当然顺理其章的事了。因为人口就脱离了人的属性,只是附属和资产。

又联系起最近看马督工的 睡前消息652期《只靠民间热情,“山河大学”建不起来》,里面谈到高中 - 职校分流问题,现实中全世界成功的职业教育有且只有德国一家。职校前身实际上是行业 (学术概念:基尔特) 里师徒制的正规化(1969 Berufsausbildungsgesetz)。我有一个偏激的观点,职校早期雏形是条顿军官团拿来生产军需屯田和筹款的产物。容克军户在和平年代转型生产制造和银行业,大量的德意志职校几乎都是围绕汽车产业进行布局,归根结底就是一堆战备产业。

所以谁才是国家的主人?血与火铸成的核心利益阶级(class)

Class 是一个神奇的词汇,OOP的「类」和社会的「阶级」有异曲同工之妙。如果按照本篇思路推演下去,可以拿历朝历代作为例子总结一下朝代的消亡的实质,就是核心class的消解:

  • 周人的核心阶级是老姬家。三家分晋之后gg。公卿代替天子
  • 大秦是一个神奇的国度,变法之后不分种族和阶级,以法治世,但是功勋升级系统在大一统之后达到武力扩张极限被玩坏反噬了。汉王定三秦,算老秦人第二次统一中国。
  • 老刘家彻底扫平沛县集团还得等到刘彻的大手笔。当时看 渤海小吏 的 《汉武大帝》系列就有如同看开篇第一个视频讲的美国西部精神,牛仔风范那种爽片一样。李广难封,老刘家对野心家封无可封就造就了曹阿瞒加九锡这种操作
  • 老李家核心阶级是关陇集团。安世之乱就是河北土著(主要是汉人)和陕西外籍雇佣军团冲突
  • 辽金的核心阶级显然是拓跋部和猛安谋克,大怂国本质是一群胸无大志但是筹款能力比其他朝代强的节度使混日子
  • 元的核心阶级就是千户制达鲁花赤老爷们
  • 明的核心阶级是军户供养皇族养猪。军户跑光了也该亡国了
  • 大清的核心阶级是八旗。满城沦陷就完蛋
  • 本朝的核心阶级是 ☭

除了核心阶级所形成的可以继承的利益集团,其他人要么是归顺的食邑劳动力,要么是“穷凶极恶”的印第安人。

Posted

stderr

无为,智力和因果论

最近看渤海小吏讲汉文帝、汉景帝的《秦汉帝国完全体99%》很有收获,特别是 23:00 这段:

总体而言,汉代初期的官僚人数少,制度简单,政府没有整体干预经济的想法,所以超级省钱。养活当时的整个官僚,每年所需的粮食不过几十万石。哪怕按一百万石算,只用全国大约八百分之一的土地就可以养活这个官僚系统。这是整个中国历史上政府运营成本自成一档的优秀存在。
这套体制虽然简约,但却并不简单。中央对于局势的控制力度一点也没有弱化。这段时间,反而是整个有汉一朝,刑事案件与社会矛盾最少的年代。
《道德经》曾经讲过这么一句话:“太上,下知有之。其次,亲而誉之。其次,畏之。其次,侮之。”
- 最牛的水平,是下面的人仅仅知道有这么个领导。
- 水平差点的,是下面的人赞美夸奖这个领导。
- 再差点的,是所有人害怕这个领导。
- 最次的,是所有人都在骂这个货。

总觉得这段话在骂谁呢?没想到汉景帝的七国之乱也是折腾出来的。看完这视频我又在琢磨,皇权、王权、相权三权分立是什么?

看这一系列的视频总给人一种强烈 黄老之术讲究的一个「无为」,也不是很懂;


周末又昏昏沉沉看了了《长安三千里》,给我感动得,然后重新认识 李白 那种商人之子想通过刷干谒文当官,我心里一直在嘀咕,李白家里有钱,为啥非得要去当官?可能是,在那个时代,你有钱也没有安全感。权力一张纸就可以让你的财富化为灰烬。但是李白明明修道,却一辈子没能实现自己的政治抱负,最终取了功名的反而是 高适。


然后又看到一篇《gpt4开发人员拿柯尔莫哥洛夫复杂度数学上严格约束并解释gpt4的智力产生原因》。让我想起来前段时间看到的离谱故事:Gzip+ kNN文本分类竟然击败Transformers:无需预训练、14行代码实现


看起来风马牛不相及的三个生活经历,在今天产生了奇妙的化学反应。我突然想明白,无为的意思,就是承认因果律的局限性。

人的主观能动性——作为,有一个经常被忽略的大前提,你得抱着一定的「目的」才可能去作为,也就是拿着预设的最终结果去改变现状。准确的目的设定,需要对真实世界的反复推演和总结因果律。因果律的掌握程度就是智力水平,智力的高低就看你掌握的是有损压缩还是无损压缩,因为压缩的本质就是用更少的信息去模拟真实的全部信息。聪明就是用更小的成本去还原更多的事物,笨就是还原成本更高。这个成本,在学术上的定义就是 Kolmogorov复杂度。它有个上限但是不可及算,某些可计算的变体,比如conditional time-bounded Kolmogorov 复杂度是 NP 问题,不可知也。

以前一直不知道信息论学了有什么用,现在似乎摸到了一些门道。信息是中性的,但是因果就有得失和优劣之分。以前不会用,是因为不知道武器的哪一端是剑柄才能握住挥舞。

生活中许多东西是需要去「作为」的,但是需要区分哪些是自然事物,那些是人为的设置。人这个神奇的动物可以把不存在的实体看成实体

胡思乱想一些心得,记录一下,或许将来可以整理得更清晰。

Posted

stderr

为啥Linux下病毒比较少

最近下载了一个Linux的病毒,把它解包了。
尝试用root身份运行,发现啥也没有。
Google了俩小时,发现这病毒没解包到 /usr/local/bin 而是解压到了 malware 用户没有写入权限的 /usr/bin,于是病毒创建不了进程文件。
从某个中文论坛里找到了修改过的 .configure 和 .make 文件,重新编译重新运行了一下。
病毒说它需要 cmalw-lib-2.0,结果发现这玩意是 CentOS 里的,Ubuntu 里没有。Google 了好几个小时找到了从源码构建 .deb 包的方法。
这病毒总算开始跑了,然后打出来几行日志,吐了个 core dump 就 crash 了。翻了一个小时日志发现这病毒假定自己跑在 ext4 文件系统上然后调用了 ext4 的加密 API。但是在 btrfs 里这个 API 已经弃用了,内核发现了这个问题然后把这个分区变成只读的了。
打开源文件,grep 搜索出病毒里的 BTC 钱包地址然后出于同情给它打了 $5。

哈哈虽然现在linux病毒也多,但是这个也太搞笑了 via

Posted

stdin

北京,为什么会没有大家说的那么繁华?

看了这篇文章之后,感受颇深。强烈推荐阅读

https://mp.weixin.qq.com/s/hqIMDADiPDVSWp8EKh5gbw

之前看到行业说法,北京便利店少的原因是“三个半”,即由于纬度高,天气寒冷,只能做“半年”生意,每日只能做“半天”生意,马路宽隔绝了人流,只能做“半条路”的生意。

当时不知道为啥北京有这么多栏杆,现在知道了,原来是模仿苏联的城市规划。

文章里提到

“窄马路、密路网、路口多”这种在风水上叫做“雍聚”,是上上之选,有利于聚财、聚气、增加流动性、繁荣商业、降低拥堵。那种“宽马路、大广场、路口少”叫做“散迭”,会造成西北风煞冲散街区的热气,导致地气不旺、商业冷落萧条。城市变成睡城、死城,民众精气散迭,日渐贫穷衰落。

搜了下,居然这个说法是公众号里早就有之的洗稿文。不过从另一个角度想,你分析blah那么一大堆,给没耐心或者没文化的小学生讲这个道理,不如就简化成:雍聚是上上之选。有的时候「道理」被过度复杂化了。不如直接给出上 - 中 - 下这种线性结论杀伤力更大。

想到现在政策层面反复炒作的「烟火气」,又想起2016年那个「不再建设封闭式小区」,当时被好多人骂来着。

最后感觉这篇文章讲的故事,如果放在网络社区上,也是同样的结论。把流量都往「主干道」「主旋律」上赶,必然会有大量的舆论翻车事故,最后得到的一定是商业上的萧条。

Posted

stdin