#763 HTTP 超时相关的疑问

2022-02-25

今天发现一个奇怪的现象,相同的代码在 CentOS 7 服务器上发起 HTTP 请求 3 秒之后超时,报 “TimeoutError: [Errno 110] Connection timed out”。
在我本地就按我们的定义的超时时间 5 秒超时,报 “tornado.simple_httpclient.HTTPTimeoutError: Timeout while connecting”。

#762 Nginx proxy-set-header

2022-02-22

线上环境 HTTP 服务有报获取不到 Host 信息。应该是域名,但是拿到的是 backend 这样的名字。

我第一反应肯定是 Nginx 的配置问题,我印象中 Nginx 给 upstream 转发的请求就会设置 Host 为 upstream 名称。

但是我检查了 Nginx 的配置 (nginx.conf), 在 http 这一层的配置中有 proxy_set_header Host $http_host;

不过搜索 proxy_set_header 的时候发现,在 server 这一层设置了 proxy_set_header X-Forwarded-For $remote_addr;, 这个头在 http 这一层也设置了,没必要在设置的。

我就想到,会不会下层设置头会覆盖上一层的所有头设置。

在 Nginx 文档 (proxy_set_header) 中查到:

Allows redefining or appending fields to the request header passed to the proxied server.
The value can contain text, variables, and their combinations.
These directives are inherited from the previous configuration level if and only if there are no proxy_set_header directives defined on the current level.

当本层没有 proxy_set_header 指令的时候,才会继承上一级的 proxy_set_header 指令。

将 server 层重复的这个设置拿掉,果然就好了。

#761 我的中年危机

2022-02-21

已经 30 几了,生活一团糟。

如果能有些事业,好歹有一个方面可以让我感受到自己的价值,至少可以分散一下注意力。可是,也没有,反而也是个令人糟心的事,让我常有髀肉复生之叹,感觉人生就要荒废。

我感觉我在一个漩涡中无法自拔,想逃离却又不能逃离。我不知道该怎么做。我时常想起《阿甘正传》中珍妮的祈祷。

这个可能就是中年危机的前兆吧!

#760 公共库 CDN

2022-02-19

公共库 CDN,也叫静态资源库。

我想了一下,国内公共库 CDN 全面挂了可能是由于监管压力。
在国内,如果 CDN 提供的静态资源被一些恶意网站引用,会被问责。如果要全面审核使用 CDN 的网站,肯定是会增加太多的管理成本。
感觉这真是太不合理了!

如果是线上项目,不要用公共库 CDN。
小项目自己管理静态资源,大项目可以用付费的 CDN 服务。

BootCDN

https://bootcdn.cn

我之前用的最多的 CDN,但是总是挂,心累。现在开始用字节跳动 CDN 了。

字节跳动 CDN

http://cdn.bytedance.com/

CloudFlare CDN

https://cdnjs.com/

JsDelivr

https://www.jsdelivr.com/

不可用清单

百度 [不可用]

http://cdn.code.baidu.com

已经挂了。

腾讯 [不可用]

https://libs.qq.com/

打不开。

新浪 [不可用]

http://lib.sinaapp.com/

只有很少的资源。

360 [不可用]

http://libs.useso.com/

又拍云 [不可用]

http://jscdn.upai.com/

只有很少的资源。

七牛云 [不可用]

http://www.staticfile.org/

感觉很久没有维护了。一些常用库都非常老了。

参考资料与拓展阅读

#759 数据库排行

2022-02-15

https://db-engines.com/en/ranking
https://db-engines.com/en/ranking_trend

关系型 Relational

https://db-engines.com/en/ranking/relational+dbms

  1. Oracle
  2. Microsoft SQL Server
  3. IBM Db2
  4. Microsoft Azure SQL Database,应该就是 SQL Server 的云版
  5. Snowflake
  6. Microsoft Access
  7. FileMaker 类似 Access 的数据库产品

这几种大型商用数据库就不提了,除了 Access 和 SQL Server 稍微熟悉一点点之外,其他几个碰都没碰过。

开源:

  1. MySQL
  2. PostgreSQL
  3. MariaDB 排 RDBMS 第 8 名
  4. Percona Server for MySQL 排 RDBMS 第 56 名
  5. SQLite
  6. Firebird

文档型 Document

MongoDB 遥遥领先。

键值型 Key-Value

  1. Redis
  2. Memcached
  3. etcd

搜索引擎 Search Engine

排除两个商业服务 Splunk, Algolia。

  1. Elasticsearch
  2. Apache Solr
  3. Sphinx

Elasticsearch 和 Solr 都基于 Apache Lucene

列式存储 Wide Column

熟悉的 RDB 基本上都是行式存储。

Cassandra 遥遥领先, 第二是 HBase。

图 Graph

  1. Neo4j
  2. Microsoft Azure Cosmos DB

时序型 Time Series

  • InfluxDB
  • Kdb+
  • Prometheus
  • Graphite
  • TimescaleDB
  • Apache Druid
  • RRDtool
  • OpenTSDB

参考资料与拓展阅读

#758 setcap

2022-02-08

set file capabilities, 设置文件权限

setcap
usage: setcap [-q] [-v] [-n <rootid>] (-r|-|<caps>) <filename> [ ... (-r|-|<capsN>) <filenameN> ]

 Note <filename> must be a regular (non-symlink) file.

#757 Go 枚举

2022-02-07

编程语言中一般都有枚举类型。可以用来替换代码中的那些有一定范围的常量,减少幻数的使用,提升代码可读性。
有些语言的枚举支持遍历等操作,有一些语言的枚举还支持枚举值和枚举名字的映射。

Go 没有在语言层面实现枚举,只能通过定义一组变量。

比如(src/runtime/time.go):

const (
        timerNoStatus = iota
        timerWaiting
        timerRunning
        timerDeleted
        timerRemoving
        timerRemoved
        timerModifying
        timerModifiedEarlier
        timerModifiedLater
        timerMoving
)

官方代码(src/time/time.go)中有些地方会给这些变量加一个自定义类型:

// A Weekday specifies a day of the week (Sunday = 0, ...).
type Weekday int

const (
        Sunday Weekday = iota
        Monday
        Tuesday
        Wednesday
        Thursday
        Friday
        Saturday
)

参考资料与拓展阅读

#756 OpenWrt 开源路由器

2022-01-31

历史

Linksys WRT54G 系列路由器(2002 年 12 月首次发布)的操作系统是基于 Linux 内核开发。
2003 年,思科旗下的 Linksys 公司面对各方面压力,开源了 WRT54G 源代码。

PS: Linksys 是美国加州的台湾移民创建, 2003 年被思科收购, 2013 年又被 Belkin 收购。2018 年 Belkin 被台湾的富士康收购。

  • 2002 年,开发者基于 WRT54G 开发了 Alchemy 项目,成为相关领域最热门的项目。
  • 2004 年,OpenWrt 项目启动。
  • 2005 年,由于 Alchemy 开发者急于商业化,社区分叉出了 DD-WRT 项目。
    现在依然是一个积极开发中的项目:https://svn.dd-wrt.com/ GitHub
    最后一个稳定版本 24 发布于 2008 年,之后十几年高频度发布 beta 版本:
    https://download1.dd-wrt.com/dd-wrtv2/downloads/betas/
  • 2006 年,Jonathan Zarate 创建 Tomato 项目。
    之前有一个 HyperWRT 项目(2004 - 2006),维护 WRT54G 代码,并支持 Linksys WRT54GS 无线路由器。
    Tomato 项目就是在 HyperWRT 的基础之上继续开发和维护。
    比较独创的是开发了一个 Web 管理界面。
    2014 年之后就没有继续更新,但是有很多子项目,包括现在仍在开发中的 FreshTomato 和 Asuswrt。
  • 2011 年 Asuswrt 项目启动,至今还在积极开发中。
  • 2013 年,Asuswrt-merlin(梅林固件)启动。
    https://www.asuswrt-merlin.net/
    https://github.com/RMerl/asuswrt-merlin
    https://github.com/RMerl/asuswrt-merlin.ng
  • 2016 年,俄罗斯开发者 Padavan 基于华硕固件(Asuswrt)开发 rt-n56u 项目,目标是寻求对华硕路由器设备硬件的绝对控制。
    人们用开发者的名字来当做项目名字 Padavan,俗称“老毛子”。
    和梅林固件专注的是华硕高端机型(封闭的博通芯片)不同,老毛子主要针对的是华硕低端设备(联发科芯片),资源要求低。
  • 2016 年,因为内部分歧,部分核心开发者出走,新起 LEDE 项目(Linux 嵌入式开发环境)。
    两年之后,双方和解,两个项目又合并到一起,采用 LEDE 的制度,继续使用 OpenWrt 品牌。
  • PandoraBox,国内项目,基于 OpenWrt,早期叫做 DreamBox。
    已经很久没有听到相关声音了。

LibreCMC

2014 年,作为 Linux 嵌入式发行版发行。
2015 年,与 LibreWRT 项目(研究项目)合并。
2017 年,基于 LEDE 17。
2020 年,基于 OpenWrt 19。

爱快

iKuai / iKuic (海外)

北京丰台一家网络设备厂商的闭源路由器系统,有商业版和免费版。
https://www.ikuai8.com/component/download

附:List of router firmware projects

  • Linux-based
  • entirely free
    • Endian
    • Floppyfw
    • IPFire
    • LEDE
    • libreCMC
    • OpenWrt
    • VyOS
    • Zeroshell
  • partly proprietary
    • AirOS & EdgeOS
    • Alliedware Plus
    • DD-WRT
    • ExtremeXOS
    • FRITZ!Box
    • RouterOS
    • SmoothWall
    • Tomato
    • Vyatta
  • FreeBSD-based
  • entirely free
    • m0n0wall
    • pfSense
    • OPNsense:pfSense 分叉
  • partly proprietary
    • Junos OS
  • proprietary
  • Cisco IOS
  • ExtremeWare
  • NX-OS
  • TiMOS
  • VRP

参考资料与拓展阅读

#755 Go HTTP 客户端

2022-01-29

原生

之前的文章:Golang HTTP 以及 HTML/XML 解析

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    resp, err := http.Get("https://www.baidu.com")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer resp.Body.Close()
    fmt.Printf("%#v\n", resp.Status)           // string, "200 OK"
    fmt.Printf("%#v\n", resp.StatusCode)       // int, 200
    fmt.Printf("%#v\n", resp.Header)           // http.Header, map[string][]string
    fmt.Printf("%#v\n", resp.Request)          // *http.Request
    fmt.Printf("%#v\n", resp.ContentLength)    // int64
    fmt.Printf("%#v\n", resp.TransferEncoding) // []string(nil)
    fmt.Printf("%#v\n", resp.Trailer)          // http.Header(nil)
    fmt.Printf("%#v\n", resp.Uncompressed)     // bool
    fmt.Printf("%#v\n", resp.TLS)              // *tls.ConnectionState
    fmt.Printf("%#v\n", resp.Body)             // *http.bodyEOFSignal => io.ReadCloser => io.Reader

    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

multipart

admin@victus:~$ cd /C/Program\ Files/Go/src/mime/multipart
nosch@victus:/C/Program Files/Go/src/mime/multipart$ grep -ER 'func.+\) [A-Z]\w+' .
./formdata.go:func (r *Reader) ReadForm(maxMemory int64) (*Form, error) {
./formdata.go:func (f *Form) RemoveAll() error {
./formdata.go:func (fh *FileHeader) Open() (File, error) {
./formdata.go:func (rc sectionReadCloser) Close() error {
./formdata_test.go:func testFile(t *testing.T, fh *FileHeader, efn, econtent string) File {
./formdata_test.go:func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) {
./multipart.go:func (p *Part) FormName() string {
./multipart.go:func (p *Part) FileName() string {
./multipart.go:func (r *stickyErrorReader) Read(p []byte) (n int, _ error) {
./multipart.go:func (p *Part) Read(d []byte) (n int, err error) {
./multipart.go:func (pr partReader) Read(d []byte) (int, error) {
./multipart.go:func (p *Part) Close() error {
./multipart.go:func (r *Reader) NextPart() (*Part, error) {
./multipart.go:func (r *Reader) NextRawPart() (*Part, error) {
./multipart_test.go:func (mr *maliciousReader) Read(b []byte) (n int, err error) {
./multipart_test.go:func (s *slowReader) Read(p []byte) (int, error) {
./multipart_test.go:func (s *sentinelReader) Read([]byte) (int, error) {
./writer.go:func (w *Writer) Boundary() string {
./writer.go:func (w *Writer) SetBoundary(boundary string) error {
./writer.go:func (w *Writer) FormDataContentType() string {
./writer.go:func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, error) {
./writer.go:func (w *Writer) CreateFormFile(fieldname, filename string) (io.Writer, error) {
./writer.go:func (w *Writer) CreateFormField(fieldname string) (io.Writer, error) {
./writer.go:func (w *Writer) WriteField(fieldname, value string) error {
./writer.go:func (w *Writer) Close() error {
./writer.go:func (p *part) Write(d []byte) (n int, err error) {

第三方库

GitHub: http client stars:>1000

  1. go-resty/resty shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    Simple HTTP and REST client library for Go
  2. parnurzeal/gorequest shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    GoRequest -- Simplified HTTP client ( inspired by nodejs SuperAgent )
  3. gojek/heimdall shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    An enhanced HTTP client for Go
  4. imroc/req shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    Simplified Golang HTTP client library with Black Magic, Less Code and More Efficiency
  5. dghubble/sling shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    A Go HTTP client library for creating and sending API requests
  6. hashicorp/go-retryablehttp shields.io:github/stars shields.io:github/languages/code-size shields.io:github/commit-activity/w shields.io:github/license
    Retryable HTTP client in Go

简单的了解:

  1. resty 看起来确实不错,链式调用,清晰明了,而且有不错的调试信息。
  2. gorequest 是在原生库上做了一点简单的封装,优化调用体验。有篇中文文档可以参考:gorequest中文文档(非官方)
    需要学习一下他的设计。官方文档说是借鉴 Node.js 的 SuperAgent。
  3. sling 也挺有特色的,使 API 变得结构化,调用变得像普通的 Go 函数一样。
  4. go-retryablehttp 在原生库上加了一个自动重试机制。
  5. heimdall, req, 简单一看,还看不出来有什么特别的地方。

#754 2022 BUG

2022-01-28

前些天应该都看过微软 Exchange Server 开发者跨年改 BUG 的新闻了吧 (相关链接)。

Exchange Server 的邮件过滤器采用了 yymmddHHMM 格式的时间,存储在 long 类型字段中。
PS: 微软的 C++ Compiler 会将 long 当做 32 位 int。

32 位有符号整形能够表达的范围:[$-2^{31}$, $2^{31} - 1$],也就是 [-2147483648, 2147483647]。

到了 2022 年,就会超出范围,比如 2022-01-01 12:00:00, 会被存储为 2201011200,会超出 signed int 的表达范围。

2147483647
2201011200 # 超出

可能有一些系统采用 int 类型存储 yymmdd + 4 位数字做编号的方式,比如 2101011234。
相同的原因,到了 2022 年,就行不通了。

2147483647
2201011234 # 超出

最好的办法是改成 long long, 或者 unsigned int

PS: Linux 下的 C/C++ 编译器——GCC、CLang/LLVM 都是将 long 当做是 64 位。

参考资料与拓展阅读