#576 HTML5 Boilerplate

2021-07-26

可以保证跨浏览器兼容性的一套 HTML 模板,我决定将其应用在本站。

HTML5 Boilerplate is an HTML, CSS and JavaScript template (or boilerplate) for creating HTML5 websites with cross-browser compatibility.

#575 广告拦截器太过分了

2021-07-23

《科技爱好者周刊》第 167 期中,阮一峰十分愤慨的说广告拦截器太过分了。

因为他接到读者反馈,有篇文章中间的两段上下语义似乎不能接上,是否有写内容遗漏。然后检查之后发现是被广告拦截器拦截了。

阮一峰从使用非常广泛的规则集 EasyList (AdBlock 默认开启订阅,每 4 小时更新一次) 发现了很多针对他的规则,并从中摘抄了几句特别狠的:

! 拦截脚本 checker.js
ruanyifeng.com/blog/checker.js

! 隐藏指向 t.cn 的链接
ruanyifeng.com##a[href^="http://t.cn/"]

! 隐藏含有"培训"的段落
ruanyifeng.com##p:-abp-contains(培训)

# 曾经出现过,禁用所有 JS 代码:
ruanyifeng.com^$csp=script-src 'none'

我才知道,广告拦截可以做到这么精准的匹配,真心服!
阮一峰检测到用户开启广告拦截之后,就会不显示所有内容,取而代之的是这个提示:

您使用了广告拦截器,导致本站内容无法显示。
请将 www.ruanyifeng.com 加入白名单,解除广告屏蔽后,刷新页面。谢谢。

其他:

  1. 其实除了浏览器的广告拦截插件之外,HTTP 网关/代理、路由器也可以通过预设规则拦截广告。
  2. 我用过的广告拦截插件:AdBlock (getadblock.com),AdBlockPlus (adblockplus.org),uBlockOrigin (gorhill/uBlock)
  3. AdBlock (下载量 296278) 和 AdBlockPlus (下载量 174432) 的渊源:最早是有一个 Adblock 项目,由于 Adblock 停止更新,一位开发者启动了 AdblockPlus 项目,再后来又有公司基于 AdblockPlus 项目创建 AdBlock。

  4. uBlock (下载量 1658) 和 uBlock Origin (下载量 24666) 的渊源:uBlock 的创始人将项目转移给别人维护之后,好像对后来接收捐款的分配不满,自己又开了一个分支 uBlock Origin。

#574 BIMI

2021-07-22

看到新闻,谷歌正式开始在 Gmail 中启用 BIMI,显示经过验证的图标。
BIMI 全名 Brand Indicators for Message Identification,可能是邮件品牌标识的意思,作用就是在邮件中显示一个图标,用来标识邮件是否是品牌邮件。
当然这个 BIMI 就需要邮件能够通过一些手段的检测,比如 SPF, DKIM, DMARC,来保证其可靠性。

#573 使用 utterances 开源评论框

2021-07-22

本站的评论框(Disqus)不知道为啥死活打不开(net::ERR_SSL_PROTOCOL_ERROR),不知道是被某种特殊力量给控制住了,还是别的什么原因。
不想折腾它了,直接切到 utterances 算了。
我本来想,反正也没有人评论,无所谓啦。但时间一长,总在心里挂念这件事,万一有个热心读者想要评论呢!

utterances 是一款依托 GitHub Issue 的开源评论框,所有评论数据都在 GitHub 上。
然后这个仓库是我的,数据当然完全掌握在自己手上,用的放心。

我写博客好些年了,评论框换过好多次,总是垮,最后换 Disque 就是图稳定,但还是不让人省心,这回换到 GitHub 上来了,应该绝对不会有问题了吧!

第一步:安装

utterances app,应该是允许它访问我的某个仓库,读写 Issue。

第二步:<script> 标签

从官网点几个选择框,然后就可以复制出来,粘贴到需要显示评论框的地方(会在那个后面插入一个 iframe)。

<script
  src="https://utteranc.es/client.js"
  repo="markjour/markjour.com"
  issue-term="pathname"
  theme="github-light"
  crossorigin="anonymous"
  async
></script>

不完美的地方:框子太窄

好在是根据最外围的 div 控制的,直接加一行:

div.utterances {
  max-width: none !important;
}

补充

wget https://utteranc.es/client.js -O static/utteranc/client.js
wget https://utteranc.es/client.js.map -O static/utteranc/client.js.map

#572 排列与组合

2021-07-22

吐槽:现在网上各种讲解,不要太贴心。
要是我读书那会儿有这么多资源,我能上清华(手动狗头)。

#571 使用 nohup 的一个小问题

2021-07-22
function fq () {
    # ...
    nohup qv2ray &>> /tmp/qv2ray.log &
    # ...
}

每次关闭终端,qv2 就一起崩溃了。

然后我改成:

nohup sh -c "qv2ray &>> /tmp/qv2ray.log &"

就好了。

分析:可能是后面日志和改后台运行的部分被 nohup 拿走的缘故吧。

#570 转载:如何将 Numpy 加速 700 倍?用 CuPy 呀

2021-07-21

作为 Python 语言的一个扩展程序库,Numpy 支持大量的维度数组与矩阵运算,为 Python 社区带来了很多帮助。借助于 Numpy,数据科学家、机器学习实践者和统计学家能够以一种简单高效的方式处理大量的矩阵数据。那么 Numpy 速度还能提升吗?本文介绍了如何利用 CuPy 库来加速 Numpy 运算速度。
就其自身来说,Numpy 的速度已经较 Python 有了很大的提升。当你发现 Python 代码运行较慢,尤其出现大量的 for-loops 循环时,通常可以将数据处理移入 Numpy 并实现其向量化最高速度处理。
但有一点,上述 Numpy 加速只是在 CPU 上实现的。由于消费级 CPU 通常只有 8 个核心或更少,所以并行处理数量以及可以实现的加速是有限的。
这就催生了新的加速工具——CuPy 库。

#569 思考:八进制的应用场景

2021-07-21

常见的进制:

  • 二进制, Binary /ˈbaɪnəri/, bin /bɪn/
    除了苏联设计过的一种计算机系统采用了平衡三进制(-1, 0, 1), 所有计算机系统都是采用的二进制, 二进制计算是程序员的一种必备技能, 其重要性不言而喻。
    常见的数字 16(四位), 256(八位), 1024(十位)等。
  • 八进制, Octal /ˈɒktl/, oct /ɒkt/
  • 十进制, Decimal /ˈdesɪm(ə)l/, dec /dek/
    十进制普遍认为是基于人类手指数量来设计的, 其深深的影响了我们的计算方式, 已经作为人类基本的数学认知。
  • 十六进制, Hexadecimal /ˌheksəˈdesɪml/, hex /heks/
    二进制计算机系统中, 一个字节定义为八位, 那么通常的选择是采用两个十六进制数来表示, 在记忆成本和便捷性方面达到一个最好的平衡。
    CPU 位数、地址总线宽度等, 通常是 4 的倍数, 比如:16 位的 8086 / 8088 有 20 位地址总线, 32 位的 386 / 486 / 奔腾 有 32 位地址总线, 64 位酷睿系列有 64 位地址总线。

那么,八进制用来干嘛?

刚才在维基百科上找到了答案:

Octal became widely used in computing when systems such as the UNIVAC 1050, PDP-8, ICL 1900 and IBM mainframes employed 6-bit, 12-bit, 24-bit or 36-bit words.

就是说早期大量机器采用了 6 位,12 位,24 位,36 位的实现,都是 3 的倍数,所以取八进制(3 位二进制数一组)来表示比较通用。

#568 Python 源码学习 04: int

2021-07-20

经过源码分析,可以得知,所有类型的定义都是通过 object.c 中的 INIT_TYPE 语句,比如 INIT_TYPE(&PyLong_Type, "int");

然后,可能是通过 SETBUILTIN("int", &PyLong_Type); 设置成内置方法。

int 类型最后就指向了一个叫做 PyLong_TypePyTypeObject 类型变量。

PyTypeObject

Objects/longobject.c

PyTypeObject PyLong_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "int",                                      /* tp_name */
    offsetof(PyLongObject, ob_digit),           /* tp_basicsize */
    sizeof(digit),                              /* tp_itemsize */
    0,                                          /* tp_dealloc */
    0,                                          /* tp_vectorcall_offset */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_as_async */
    long_to_decimal_string,                     /* tp_repr */
    &long_as_number,                            /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    (hashfunc)long_hash,                        /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
        Py_TPFLAGS_LONG_SUBCLASS,               /* tp_flags */
    long_doc,                                   /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    long_richcompare,                           /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    long_methods,                               /* tp_methods */
    0,                                          /* tp_members */
    long_getset,                                /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    long_new,                                   /* tp_new */
    PyObject_Del,                               /* tp_free */
};

其中所有的方法就在 PyMethodDef long_methodsPyNumberMethods long_as_number 中。
尤其是 long_as_number 可能就是那些重载操作符的基础。

long_newlong_new_impl

可能是 int 方法对应的实现。

#567 OpenStack 实验环境的快速搭建

2021-07-19

部署方式汇总

  1. 手动部署
  2. 部署脚本,也就是将手动部署的过程转化为脚本
  3. 使用自动化工具部署

而工具又有很多,比如:

  • openstack-chef shields.io
  • openstack-ansible shields.io
    简称 osa
  • puppet
  • Fuel 很早的一个解决方案,
  • Kolla shields.io
    采用 k8s 技术
  • DevStack shields.io
  • PackStack shields.io
    来自红帽的 rdo 项目,用于 OpenStack 在 RHEL 系统上的部署。

以最新的 wallaby 为例。

通过 DevStack 来弄。

就体验来说,在 devstack 的使用上,有需要改进的地方:

  1. 离线安装(提前准备好安装包和仓库)
  2. 出现异常的重试
  3. 日志还是写到文件中去的好
  4. 进度不友好

如果要开发有一定规模的相关项目,还是自己维护一组脚本,或者定制某个部署工具比较合适。