WebDev DNS
2021-12-14
- 浏览器突然打不开 zhihu.com, 报
DNS_PROBE_FINISHED_NXDOMAIN
。
- Windows 网络诊断之后说是 DNS 不可用。
- 经过检查,使用 DHCP 获取到的 DNS
172.16.0.1
。
- 改成 AliDNS:
223.5.5.5
, 223.6.6.6
之后就好了。
- 然后再改回默认的 DNS 发现也能正常访问了。
我应该在出现问题的时候先尝试 nslookup 一下,看看 DNS 解析出来的到底是个什么结果。
下次遇到再继续更新。
C:\Users\Administrator>ipconfig /all | findstr DNS
主 DNS 后缀 . . . . . . . . . . . :
连接特定的 DNS 后缀 . . . . . . . :
DNS 服务器 . . . . . . . . . . . : 172.16.0.1
连接特定的 DNS 后缀 . . . . . . . :
DNS 服务器 . . . . . . . . . . . : fec0:0:0:ffff::1%1
连接特定的 DNS 后缀 . . . . . . . :
连接特定的 DNS 后缀 . . . . . . . :
连接特定的 DNS 后缀 . . . . . . . :
连接特定的 DNS 后缀 . . . . . . . :
C:\Users\Administrator>nslookup zhihu.com
服务器: UnKnown
Address: 172.16.0.1
非权威应答:
名称: zhihu.com
Address: 103.41.167.234
Tornado
2021-12-13
- 一个请求会先到 A 服务,然后通过 HTTP 调用的方式传到 B 服务 (基于 Tornado)
- 面对突然涌来的大量请求,B 服务负载迅速升高,响应时间拉长
- A 服务的部分请求因为超时断开,然后重发
也就是说,在负载较高的时候,B 会收到一些重复请求。因为负载高的时候,B 其实已经接收过一遍,只是没有响应。
B 服务处理任务结束之后,会将请求加入幂等。但是在 B 服务正在处理的时候,还是会有重复处理的可能性。
如果是一些对数据可靠性有一定要求的场景,这样重复的处理就不能接受。
比较合适的设计应该是 A 和 B 之间通过 MQ 通讯,根本不可能有这样的情况发生,哪怕请求再多也可以按照自己的速度消费,使系统保持最佳状态运行。而且,服务之间解耦之后,可以避免相互影响。
但是,如果不能改变 A B 两个服务的现有设计(调用关系),可以做的事情:
- 通过请求的正常返回来当 ACK 机制 (本文原本想重点说的内容)
- 将请求的接收和处理拆开
- 请求收到,加入队列,然后就可以返回了,这个时间非常短,这个过程出现问题的可能性非常低
- 如果在处理请求之前,连接断开,那就结束流程,抛弃任务
- A 服务的重试机制延迟一个合适的时间(比如 3 分钟)处理,可以通过 MQ, DB, Redis 实现
两个方案都应该能大幅减小出现重复请求的情况,双管齐下效果会更好。
方案一, 如果引入 MQ 或 DB 的话,就会觉得为什么不在 A 服务做;如果不引入新组件的化,复杂是需要保证服务突然挂掉之后,队列数据的恢复。只能在启动服务时通过扫描日志来恢复数据。
方案二,无论是业务还是代码,影响范围比较小,易于实现。
Tornado 实现 ACK 机制
客户端在正常流程处理完成之前,断开连接,会触发 on_connection_close
调用,可以在这个里面做手脚。
class MainHandler(tornado.web.RequestHandler):
def post(self):
if self._finished:
return
# do something
def on_connection_close(self):
LOG.debug('connection closed')
self._finished = True
super().on_connection_close()
Java
2021-12-13
不要用 xx != null && xx.length() > 0
来判空,可以使用 apache 的 commons 包。
Golang GoGenerics
2021-12-12
Golang GitHub 仓库上的 Issue 49884 all: rewrite interface{}
to any
值得吃瓜。
由于新的泛型机制中引入了 any
关键字, 有人提议将代码中的所有 interface{}
替换为 any
。
相关连接:all: gofmt -w -r 'interface{} -> any' src
目前这个 Issue 还是 Open 状态。
Linux
2021-12-12
stat /tmp/edge_shutdown_crash.txt
文件:/tmp/edge_shutdown_crash.txt
大小:2 块:8 IO 块:4096 普通文件
设备:801h/2049d Inode:9306564 硬链接:1
权限:(0664/-rw-rw-r--) Uid:( 1000/ markjour) Gid:( 1000/ markjour)
最近访问:2021-12-12 10:24:17.318846619 +0800
最近更改:2021-12-12 10:42:27.982887293 +0800
最近改动:2021-12-12 10:42:27.982887293 +0800
创建时间:2021-12-12 10:24:17.318846619 +0800
# %w time of file birth, human-readable; - if unknown
# %W time of file birth, seconds since Epoch; 0 if unknown
# %x time of last access, human-readable
# %X time of last access, seconds since Epoch
# %y time of last data modification, human-readable
# %Y time of last data modification, seconds since Epoch
# %z time of last status change, human-readable
# %Z time of last status change, seconds since Epoch
stat -c "%w" /tmp/edge_shutdown_crash.txt
2021-12-12 10:24:17.318846619 +0800
sudo debugfs -R 'stat /tmp/edge_shutdown_crash.txt' /dev/sda1 | cat
debugfs 1.46.3 (27-Jul-2021)
Inode: 9306564 Type: regular Mode: 0664 Flags: 0x80000
Generation: 3654863154 Version: 0x00000000:00000001
User: 1000 Group: 1000 Project: 0 Size: 2
File ACL: 0
Links: 1 Blockcount: 8
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x61b56193:ea56adf4 -- Sun Dec 12 10:42:27 2021
atime: 0x61b55d51:4c04da6c -- Sun Dec 12 10:24:17 2021
mtime: 0x61b56193:ea56adf4 -- Sun Dec 12 10:42:27 2021
crtime: 0x61b55d51:4c04da6c -- Sun Dec 12 10:24:17 2021
Size of extra inode fields: 32
Inode checksum: 0xd223c644
EXTENTS:
(0):39635628
架构 微服务 etcd
2021-12-09
简介
etcd 是 CoreOS 的一个子项目,KV 数据库,原是设计用于存储集群共享配置数据,基于 Apache 协议开源。
PS: CoreOS 2018 年被 RedHat 收购,然后更名为 Container Linux, 后来和 RedHat 的 Project Atomic 合并,形成 Fedora CoreOS 项目。
本质是一个 KV 存储,从这个角度看和 Redis 很像。但:
Item |
etcd |
Redis |
性能 |
高 |
非常高 |
存储方式 |
磁盘 + 内存缓存 |
内存 |
持久化 |
boltdb |
AOF + rdb |
数据类型 |
string |
丰富 |
API |
gRPC |
RESP |
一致性 |
Raft |
复制 |
PS:性能:更高 QPS + 更低延迟
PS:RESP:REdis Serialization Protocol(TCP 文本协议)
作用
本质就是一个轻量级的 KV 存储系统,由于其高并发、支持分布式(强一致性)、支持版本控制、支持实时更新通知等特点,所以常用于:
- 服务发现:帮助分布式系统中的服务相互找到并建立连接。
- 配置管理:集中管理和动态更新分布式系统的配置信息。
- 分布式锁:确保在分布式环境中,同一时间只有一个客户端能获取锁,避免资源冲突。
概念
-
Raft 算法
Raft 是一种用于管理复制日志的一致性算法,节点之间通过复制日志来达成一致状态。
当客户端发起写请求时,领导者(Leader)节点接收请求并将其记录到日志中,接着把日志条目复制到其他跟随者(Follower)节点。
只有当多数节点都成功复制了该日志条目后,该条目才会被提交,数据更新才会被应用。
-
角色相关概念
- Node(节点)/Member(成员):指 etcd 集群中的单个服务器实例,每个节点都有一个唯一标识,存储着部分或全部的数据副本,并且参与集群的一致性协议。
- Leader(领导者):负责处理客户端写请求和协调日志复制的节点。同一时刻集群中只有一个领导者,它会接收客户端的写操作,并将这些操作广播给其他跟随者节点。
- Follower(跟随者):跟随领导者的指令,接收并复制领导者发送的日志条目。跟随者不直接处理客户端的写请求,只响应领导者的请求。
- Candidate(候选人):在 Raft 算法的选举过程中,当跟随者在一定时间内没有收到领导者的心跳信息时,它会转变为候选人状态。
候选人会发起新一轮的选举,向其他节点请求投票。如果获得多数节点的投票,候选人就会成为新的领导者。
- Peer(对等节点):指集群中地位平等的其他节点,节点之间相互通信以实现数据复制、选举等功能。
-
Term(任期)
逻辑时间概念,用于划分不同的选举周期,确保选举的正确性和防止过期的领导者重新掌权,同时在日志复制和消息传递中也起到重要的同步和协调作用。
每个任期从一次选举开始,有一个唯一的递增编号。
在一个任期内,最多只能有一个领导者。
如果选举失败或领导者故障,会开启一个新的任期。
结构


和 Redis 对比
- 两者都是 key-value 类型存储
- Redis 是 C 写的,而 etcd 是 Go 写的
- Redis 支持多种数据类型,而 etcd 不支持
- Redis 支持 Lua 编程,而 etcd 不支持
- Redis 支持简单的权限控制,而 etcd 不支持
- Redis 采用 RESP 私有协议,而 etcd 采用 GRPC 或 HTTP (JSON)
- Redis 支持复制集的方式同步数据,而 etcd 则是通过 Raft 实现强一致性
- Redis 通过 RDB (快照) / AOF (增量) 的方式持久化
部署 (Ubuntu)
sudo apt install -y etcd
etcd --version
sudo systemctl start etcd
sudo systemctl status etcd
部署 (Docker)
docker-compose.yml
:
version: "3"
networks:
etcd_net:
driver: bridge
ipam:
driver: default
config:
- subnet: ${NETWORK_CONFIG_SUBNET}
services:
etcd-0:
networks:
etcd_net:
ipv4_address: ${ETCD_01_NETWORKS_ETCD_NET_ADDRESS}
image: quay.io/coreos/etcd:latest
ports:
- ${ETCD_01_NETWORKS_ETCD_NET_ADDRESS}:4001:4001
- ${ETCD_01_NETWORKS_ETCD_NET_ADDRESS}:2380:2380
- ${ETCD_01_NETWORKS_ETCD_NET_ADDRESS}:2379:2379
hostname: etcd-0
environment:
- GOMAXPROCS=2
command: >-
/usr/local/bin/etcd
-name etcd-0
-advertise-client-urls http://etcd-0:2379,http://etcd-0:4001
-listen-client-urls http://${ETCD_01_NETWORKS_ETCD_NET_ADDRESS}:2379,http://${ETCD_01_NETWORKS_ETCD_NET_ADDRESS}:4001
-initial-advertise-peer-urls http://etcd-0:2380
-listen-peer-urls http://${ETCD_01_NETWORKS_ETCD_NET_ADDRESS}:2380
-initial-cluster-token etcd-cluster
-initial-cluster etcd-0=http://etcd-0:2380,etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380
-initial-cluster-state new
etcd-1:
networks:
etcd_net:
ipv4_address: ${ETCD_02_NETWORKS_ETCD_NET_ADDRESS}
image: quay.io/coreos/etcd:latest
ports:
- ${ETCD_02_NETWORKS_ETCD_NET_ADDRESS}:4001:4001
- ${ETCD_02_NETWORKS_ETCD_NET_ADDRESS}:2380:2380
- ${ETCD_02_NETWORKS_ETCD_NET_ADDRESS}:2379:2379
hostname: etcd-1
environment:
- GOMAXPROCS=2
command: >-
/usr/local/bin/etcd
-name etcd-1
-advertise-client-urls http://etcd-1:2379,http://etcd-1:4001
-listen-client-urls http://${ETCD_02_NETWORKS_ETCD_NET_ADDRESS}:2379,http://${ETCD_02_NETWORKS_ETCD_NET_ADDRESS}:4001
-initial-advertise-peer-urls http://etcd-1:2380
-listen-peer-urls http://${ETCD_02_NETWORKS_ETCD_NET_ADDRESS}:2380
-initial-cluster-token etcd-cluster
-initial-cluster etcd-0=http://etcd-0:2380,etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380
-initial-cluster-state new
etcd-2:
networks:
etcd_net:
ipv4_address: ${ETCD_03_NETWORKS_ETCD_NET_ADDRESS}
image: quay.io/coreos/etcd:latest
ports:
- ${ETCD_03_NETWORKS_ETCD_NET_ADDRESS}:4001:4001
- ${ETCD_03_NETWORKS_ETCD_NET_ADDRESS}:2380:2380
- ${ETCD_03_NETWORKS_ETCD_NET_ADDRESS}:2379:2379
hostname: etcd-2
environment:
- GOMAXPROCS=2
command: >-
/usr/local/bin/etcd
-name etcd-2
-advertise-client-urls http://etcd-2:2379,http://etcd-2:4001
-listen-client-urls http://${ETCD_03_NETWORKS_ETCD_NET_ADDRESS}:2379,http://${ETCD_03_NETWORKS_ETCD_NET_ADDRESS}:4001
-initial-advertise-peer-urls http://etcd-2:2380
-listen-peer-urls http://${ETCD_03_NETWORKS_ETCD_NET_ADDRESS}:2380
-initial-cluster-token etcd-cluster
-initial-cluster etcd-0=http://etcd-0:2380,etcd-1=http://etcd-1:2380,etcd-2=http://etcd-2:2380
-initial-cluster-state new
etcdctl
etcdctl - A simple command line client for etcd.
ENDPOINTS=ip:port,ip:port,ip:port
# set
etcdctl --endpoints=$ENDPOINTS put test1 111
etcdctl --endpoints=$ENDPOINTS put test2 222
etcdctl --endpoints=$ENDPOINTS put test3 333
# get
etcdctl --endpoints=$ENDPOINTS get test1
etcdctl --endpoints=$ENDPOINTS get test1 --write-out="json"
etcdctl --endpoints=$ENDPOINTS get test --prefix
# delete
etcdctl --endpoints=$ENDPOINTS del test1
etcdctl --endpoints=$ENDPOINTS del test --prefix
# 集群信息
etcdctl --write-out=table --endpoints=$ENDPOINTS endpoint status
etcdctl --write-out=table --endpoints=$ENDPOINTS member list
示例 (Golang)
go get go.etcd.io/etcd/client/v3
package main
import (
"context"
"fmt"
"time"
etcd "go.etcd.io/etcd/client/v3"
)
func main() {
cli, err := etcd.New(etcd.Config{
Endpoints: []string{"192.168.31.204:2379"},
DialTimeout: 3 * time.Second,
})
if err != nil {
fmt.Printf("etcd ConnectError: %v\n", err)
return
}
defer cli.Close()
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
resp, err := cli.Get(ctx, "test1")
cancel()
if err != nil {
fmt.Printf("etcd GetError: %v\n", err)
}
for _, kv := range resp.Kvs {
fmt.Printf("etcd KeyVlue: %s, %s\n", kv.Key, kv.Value)
}
}
示例 (Python)
python3 -m pip install -u etcd3
import etcd3
etcd = etcd3.client(host='etcd-host-01', port=2379)
testkey = '/key'
testval = '1234'
etcd.put(testkey, testval)
v = etcd.get(testkey)
print(v)
assert v == testval
etcd.delete(testkey)
参考资料与拓展阅读
开发者
2021-12-08
公众号四猿外的文章《这 10 个程序员的好习惯,让我变强了》,很有同感,这里总结一下:
- 要看官方文档,网上的资料鱼龙混杂
- 开发者的可靠性:自测没有问题了再交付
- 日志设计合理,保证调试时,有恰当的信息来定位问题
- 精通 Git
- 优先功能实现,别急着优化
过早优化是魔鬼,应该根据实际运行的情况来优化
- 先做明确的需求,不确定或者模糊的需求先往后放
- 积极主动:发现问题,提出方案,解决问题
- 开发时间评估,保证有冗余时间,以处理可能的意外
- 学习编程主要靠上手写代码,不要光看文档
- 英语。不需要很好,能看懂英文文档就行
科学
2021-12-05

架构 日志 Loki
2021-12-05
Loki
Grafana 公司出品的一个日志系统。才出来没两年,是一个相对较年轻的项目,不过已经有一定知名度了。
业界最为知名的日志系统是 ELK,它对日志做全文索引,搜索起来最快、最灵活,同时大量索引导致存储成本相对较高。
Loki 则将日志分成时间戳、标签、正文三部分,标签就是索引,存储在
Promtail
Grafana
Grafana 是一个数据面板,常用于监控系统。它本身不会收集和存储数据,而是通过接入其他数据源来实现。
通过内置的插件,Loki 可以支持各种关系型数据库和时序数据库(Zabbix 一般配套使用 MySQL 做存储,Prometheus 本身就可以认为是一个时序数据库),也支持 Loki,Elasticsearch 这样的数据源。
实验
Install Loki & Promtail
# 获取最新版本号
# LOKI_VERSION=$(curl -s https://api.github.com/repos/grafana/loki/releases/latest | jq -r .tag_name)
LOKI_VERSION=$(curl -s https://api.github.com/repos/grafana/loki/releases/latest | grep -Po '"tag_name": "\Kv[0-9.]+')
# 下载 loki & promtail
curl -O -L "https://github.com/grafana/loki/releases/download/${LOKI_VERSION}/loki-linux-amd64.zip"
curl -O -L "https://github.com/grafana/loki/releases/download/${LOKI_VERSION}/promtail-linux-amd64.zip"
# loki : 18M -> 57M
# promtail: 21M -> 74M
# 解压 & 设置
unzip loki-linux-amd64.zip promtail-linux-amd64.zip
sudo mv -n loki-linux-amd64 /usr/local/bin/loki
sudo mv -n promtail-linux-amd64 /usr/local/bin/promtail
# chmod a+x /usr/local/bin/{loki,promtail} # already 755
# 下载配置文件
sudo -E wget -qO /etc/loki.config.yaml "https://raw.githubusercontent.com/grafana/loki/${LOKI_VERSION}/cmd/loki/loki-local-config.yaml"
sudo -E wget -qO /etc/promtail.config.yaml "https://raw.githubusercontent.com/grafana/loki/${LOKI_VERSION}/clients/cmd/promtail/promtail-local-config.yaml"
ls -l /etc/{loki,promtail}.config.yaml
# 启动 loki
loki -config.file /etc/loki.config.yaml
# 在另一个终端查看
browse http://localhost:3100/metrics
# 启动 promtail
Install Grafana
Install on Debian or Ubuntu
sudo apt-get install -y apt-transport-https software-properties-common wget
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
echo "deb https://packages.grafana.com/oss/deb stable main" | sudo tee -a /etc/apt/sources.list.d/grafana.list
# Bate 版本
# echo "deb https://packages.grafana.com/oss/deb beta main" | sudo tee -a /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get install -y grafana
# 无法创建主目录"/usr/share/grafana"
# sudo systemctl daemon-reload
# sudo systemctl enable grafana-server
sudo systemctl start grafana-server
browse http://localhost:3000
参考资料与拓展阅读
操作系统 Fedora
2021-12-04
牺牲:灵活性
优势:稳定性和安全性(只读文件系统,不容易被破坏,方便回滚)
- 桌面使用
- Fedora Silverblue:基于 Fedora,使用 Flatpak 管理应用程序,具有原子更新功能.
- Vanilla OS:基于 Debian Sid,使用 Ext4 文件系统,通过 Apx 工具安装软件.
- openSUSE Aeon:简化了 Btrfs 的复杂性,适合桌面用户,结合了 Flatpak 和稳定的系统基础.
- NixOS
- Endless OS
- 服务器使用
- Fedora CoreOS:注重最小化和自动原子更新,适合容器托管或 Kubernetes 集群.
- Flatcar Container Linux:继承了 CoreOS Container Linux 的遗产,专注于容器化环境.
- openSUSE MicroOS (Server Edition):基于 openSUSE Tumbleweed,提供事务性更新和基于 Btrfs 的不可变根文件系统.
- AWS Bottlerocket
- Talos Linux: 专注于 Kubernetes 集群.
- 其他
- Ubuntu Core
- Photon OS
- blendOS