#7 转载:康威定律

2024-03-04

几乎所有我喜欢的软件架构师,都认同康威定律(Conway Law),认为这个定律非常重要,足以影响到所有系统。而且,你没法与之抗争,想要抗拒康威定律注定要失败。

康威定律的最好表述是:“任何系统的构成,都反映了设计这个系统的组织结构。”

它的出处是 Melvin Conway 在 1968 年写的一篇文章。后来,弗雷德·布鲁克斯(Fred Brooks)在著名的《人月神话》(The Mythical Man-Month)引用了这条定律。

Melvin Conway 观察到,软件系统的架构看起来与构建它的开发团队的组织结构非常相似。

最初的描述是,如果一个团队编写一个编译器,那么它将是一个单通道编译器;但是,如果两个团队共同开发,那么它将是一个双通道编译器。这个描述后来被发现,广泛适用于大量系统。

正如我的同事 Chris Ford 对我说的:“软件耦合是由人类交流促成的。” 如果我可以轻松与代码作者交谈,那么我就更容易对代码有更深入的了解,因此我的代码更容易耦合到该代码。

应对康威定律的第一步是不要与之抗争。我仍然记得一位技术主管,他刚刚被任命为 ​​ 一个大型新项目的架构师,该项目由分布在世界各地不同城市的六个团队组成。“我做出了第一个架构决定”,他告诉我:“就是这个系统将有六个主要的子系统。我不知道它们会是什么子系统,但肯定会有六个。”

为了适应康威定律,现在有一种策略,就是一旦定下软件架构,就相应改变组织结构,让紧密耦合模块的开发者更容易沟通。

#5 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

参考资料与拓展阅读

#3 容错、高可用、灾备

2019-11-19

阮一峰的博文(容错,高可用和灾备)中说:

  • 容错:发生故障时,如何让系统继续运行。
    飞机的四个引擎坏了一个还能继续飞行,汽车的四个轮子坏了一个也能将就驾驶。
  • 高可用:系统中断时,如何尽快恢复。
    汽车的备胎,用于快速恢复正常驾驶(允许短暂的业务中断)。
  • 灾备:系统毁灭时,如何抢救数据。
    飞机的弹射装置,保证最核心的“资产” —— 驾驶员能够存活。

#2 关于缓存

2019-02-12

什么地方需要缓存?

  • DB 查询,加上缓存可以减小数据库压力,同时提升性能。
  • 某些费时费资源的操作,加上缓存可以避免重复计算。

缓存方式

  1. 本地缓存
  2. 进程内缓存(内存)
  3. 磁盘缓存
  4. 缓存服务(redis/memcache)
  5. 数据库:有些任务可以提前进行计算,将结果存在数据库中。

本地缓存的问题是多个节点之间容易出现数据不一致的情况。我听说过 Java 的一些本地缓存组件,应该其中有一些可以做到多个节点之间的数据同步。如果要是自己实现的话,可以在服务中增加一个刷新缓存的接口调用,其中一个节点刷新缓存时,调用其他节点的刷新接口。也可以引入 MQ,避免这个调用造成的耦合和可能的性能损耗。

缓存策略

  1. 提前进行一些计算,将内容缓存起来。如有必要,可以选择合适的时间间隔进行数据刷新。
  2. DB 缓存可以由数据库中间层来做,也可以有客户端库来做,或者就在应用的数据库层中实现。
    查询时,先尝试本地缓存,再尝试缓存服务(两道缓存,避免击穿),最后再进行数据库查询。
    缓存的数据应该是这样的:
  3. 高命中(缓存命中率需要做好监控)
  4. 较少变更
  5. 尽可能保证数据变更之后(不一致问题)不会产生严重影响
    如果一致性要求很高的话,要反复确实是否必须使用缓存,如果确定的话,缓存刷新策略需要考虑清楚。

穿透

数据库没有数据,缓存也没有数据。这样的请求直接穿过缓存读数据库,给数据库照成压力。

  1. 一般是非法请求所致,对于这一部分请求应该有机制可以进行过滤掉
    恶意请求,或者高频请求
  2. 可以对没有数据的请求也进行缓存,可以给一个相对小一点的 TTL
  3. 布隆过滤器 参考:2021/03/07, 布隆过滤器
  4. 将空 Key 单独存到一个 Redis SET 中应该也可以
    比如 system:cache:empty_keys:hhmm, TTL = 123,每次用 SISMEMBER 查询当前分钟和上一分钟

击穿

某个热 Key 失效,导致大量请求打到数据库。
热 Key 应该预热,然后有一个比较大的 TTL,甚至没有过期时间。最后,通过定时刷新任务来更新这些热 Key。

雪崩

大量 Key 同时过期, 导致请求直接打到数据库,然后影响整个系统。
大面积的击穿。

  1. 比如系统刚启动的时候,批量写入大量数据,这些数据有相同的 TTL,就会同时过期。我们应该给过期时间加入一些随机,将过期时间点分散在一个区间内。
  2. 对于热 Key 的处理, 同击穿部分。

其他:

  1. 为了防止 Redis 奔溃,导致系统崩溃,应该在本地进程中也设置一个缓存。
    LocalCache -> RedisCache -> DB
  2. 为了防止数据库奔溃,数据库请求应该由一个队列来处理。
  3. 网关部分对于大量来不及处理的请求应该丢弃。
  4. 缓存应该能够按重要性划分一下级别,如果遇到问题能够快速丢弃不重要的数据
    还应该可以快速丢弃指定服务的所有缓存。
    这应该叫做缓存降级。

预热

根据之前的经验,或者开发者预判,将部分数据事先写入缓存。
如果提供相关工具,让系统维护人员能够方便快捷地管理缓存数据,能够手动介入缓存的生命周期就更好了。至少在后台提供一个 缓存预热 的按钮。

  1. 提供指定热 Key,让定时任务负责刷新。
    智能预热:给访问量大的 Key 延长 TTL, 启动定时刷新。
  2. 需要对缓存的访问有一个简单的监控,方便作为之后预热的依据。

缓存更新

另起一篇:缓存更新策略

#1 关于 DNS(历史和未来)

2018-05-02

历史

从阿帕网 (ARPANET) 时代一直到互联网的早期,网络节点比较少,都是通过本地 hosts 文件来实现主机名到 IP 地址的映射。
根据维基百科的信息,斯坦福研究所负责维护了一个公共 hosts 文件,大家会找他同步 (rfc606, rfc608)。
PS: 这个时候如果有主机名重复了谁来管?打电话过去让他们改名?

这套机制一直运行了十几年,公共 hosts 文件已经变的很大了,变化也很频繁(IP 可能已经不再那么固定了),需要经常同步。这个时候,斯坦福研究所的网络压力也越来越大了。

后来人们开始设计域名和域名相关的公共设施 (rfc805, rfc830)。最后,在 1983 年,形成了下面两个 RFC 文档:

  • RFC 882, DOMAIN NAMES - CONCEPTS and FACILITIES
  • RFC 883, DOMAIN NAMES - IMPLEMENTATION and SPECIFICATION

几年后(1987),正式的 DNS 标准 RFC 1034 和 RFC 1035 推出。
这套标准一直运行到现在,可能有对其进行拓展(比如 DNS 记录类型不断添加,Unicode 字符引入),但是基本技术设计没有改变。

DNS 的管理权问题

https://zhidao.baidu.com/question/1386069665602139980.html

基本流程

比如本站域名 www.markjour.com, 其完整形式应该是 www.markjour.com. (后面多一个小数点)

DNS 软件

  • BIND
  • PowerDNS
  • dnsmasq
  • Unbound
  • CoreDNS
  • SmartDNS

Cache-Only DNS Server

新的发展

  1. 标准的 DNS 是运行在 UDP 53 端口上的。后来的 RFC 1123 增加了 TCP 的支持, 这个方案叫做 DNS over TCP, 还是在 53 端口。
  2. DNSCrypt, 2011 年设计的, 实现 DNS 的加密和验证,运行于 443 端口。注意:存在于 IETF 框架之外,但是好像有很多服务器支持。
  3. DNS over TLS (DoT), 2016 年 5 月成为规范。
    RFC 7858 Specification for DNS over Transport Layer Security (TLS)
    主要作用是加密传输,防止窃听。
  4. DNS over HTTPS (DoH), 2018 年 10 月成为规范。
    RFC 8484 DNS Queries over HTTPS (DoH)
    作用和 DoT 一样。
  5. DNS over TOR, 2019 年。
  6. Oblivious DNS-over-HTTPS (ODoH), 透过代理的方式,让 DoH 服务器无法获取客户端的真实 IP。同时代理无法获取 DNS 请求的内容。

参考资料与拓展阅读