HTTPS

连接建立过程 基本流程如下: 客户端向服务器索要并验证服务器的公钥 双方协商产生对称密钥 双方采用对称密钥进行加密通信 TLS 的握手阶段涉及四次通信,使用不同的密钥交换算法,流程也会有所不同。下面以 RSA 密钥交换算法为例,介绍 HTTPS 连接建立过程。 Client Hello 客户端向服务器发送一个 Client Hello 消息,包含以下信息: 客户端支持的 TLS 版本 客户端生成的一个随机数(Client Random) 客户端支持的加密算法,如 RSA、DH、ECDH 等 Server Hello 服务器收到 Client Hello 消息后,向客户端发送一个 Server Hello 消息,包含以下信息: 服务器选择的 TLS 版本 服务器生成的一个随机数(Server Random) 服务器选择的加密算法 服务器的数字证书 客户端回应 客户端收到 Server Hello 消息后,会执行以下操作: 通过浏览器或者 OS 中的 CA 公钥验证服务器的数字证书 从服务器的数字证书中提取公钥,用于后续的通信 生成一个随机数(Pre-Master Secret),并使用服务器的公钥加密该随机数 向服务器发送消息,包含以下信息 加密后的 Pre-Master Secret 加密算法改变通知,表示后续的通信将使用对称密钥加密 客户端的 Finished 消息,包含一个验证数据,用于验证双方是否使用相同的密钥 服务器回应 服务器收到客户端的 Pre-Master Secret 后,会执行以下操作: 使用私钥解密 Pre-Master Secret 使用 Client Random、Server Random 和 Pre-Master Secret 生成对称密钥(Master Secret) 向客户端发送消息,包含以下信息 加密算法改变通知,表示后续的通信将使用对称密钥加密 服务器的 Finished 消息,包含一个验证数据,用于验证双方是否使用相同的密钥

March 1, 2024

HTTP 状态码

权威文档:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status 以下为补充的一些状态码 499 Client Closed Request 由 Nginx 定义的非标准状态码,表示客户端主动关闭了请求,导致服务器无法完成请求

March 1, 2024

Redis SDS 结构

介绍 SDS 全称为 Simple Dynamic String,用于存储二进制数据的一种结构,具有动态扩容、空间预分配、二进制安全等特性。Redis 中的字符串对象就是使用 SDS 结构实现的。 总体结构 struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; /* used */ uint8_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; }; struct __attribute__ ((__packed__)) sdshdr16 { uint16_t len; /* used */ uint16_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; }; struct __attribute__ ((__packed__)) sdshdr32 { uint32_t len; /* used */ uint32_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; }; struct __attribute__ ((__packed__)) sdshdr64 { uint64_t len; /* used */ uint64_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; }; len:记录 buf 中已使用的字节数 alloc:记录 buf 中已分配的字节数 flags:记录 buf 的类型,低 3 位表示类型,高 5 位未使用 buf:存储字符串的字节数组 优势 常数复杂度获取字符串长度:读取 len 字段即可,不需要遍历整个字符串 不存在缓冲区溢出问题:根据 len 和 alloc 字段,判断是否需要扩容 空间预分配,减少内存重分配次数:每次扩容都会多分配一些内存,减少内存重分配次数 二进制安全,可以存储任意二进制数据:以 len 字段的长度来判断字符串的结束,而不是以空字符 \0 来判断 兼容部分 C 字符串函数:遵从每个字符串的最后一个字节是空字符 \0 的惯例,可以使用部分 C 字符串函数 空间预分配 当执行字符串拼接操作时,如果字符串的长度超过了已分配的内存,Redis 会对字符串进行扩容。扩容的规则如下:...

February 29, 2024

Redis IntSet 结构

什么时候会使用 IntSet 集合中的元素都是整数 元素数量不超过 set-max-intset-entries 参数设置的阈值 set-max-intset-entries:这个参数用于设置 intset 的最大元素数量。当集合的元素数量超过这个阈值时,Redis 会将底层实现从 intset 转换为 hashtable。默认值通常为 512 数据结构 typedef struct intset { uint32_t encoding; // 编码方式 uint32_t length; // 元素数量 int8_t contents[]; // 元素数组 } intset; contents 数组中的元素是有序的,且不重复。encoding 用于标识 contents 数组中的元素是什么类型的整数。encoding 的值有三种: INTSET_ENC_INT16 16 位整数 INTSET_ENC_INT32 32 位整数 INTSET_ENC_INT64 64 位整数 升级操作 如果插入的元素超过了 intset 的编码方式,Redis 会将 intset 升级为更大的编码方式。例如,如果 intset 的编码方式是 INTSET_ENC_INT16,而插入的元素是 32 位整数,那么 Redis 会将 intset 的编码方式升级为 INTSET_ENC_INT32 步骤如下: 扩容 intset,将 intset 的编码方式升级为更大的编码方式 从后往前,将 intset 中的元素转换为更大的编码方式 插入新的元素 图解如下: 不支持降级操作,一旦升级,就不会再降级!!!...

February 16, 2024

【译】系统设计面试

原文链接:https://github.com/karanpratapsingh/system-design?tab=readme-ov-file#system-design-interviews 系统设计涉及广泛的主题,系统设计面试旨在评估您为抽象问题提供技术解决方案的能力,因此,它们不是为了寻求特定答案。系统设计面试的独特之处在于候选人和面试官之间的双向互动。 不同工程级别的期望也有很大差异。这是因为具有丰富实践经验的人与行业新手的处理方式完全不同。因此,很难想出一个单一的策略来帮助我们在面试过程中保持条理。 让我们来看看一些常见的系统设计面试策略: 需求澄清 系统设计面试问题本质上是模糊或抽象的。在面试初期就询问有关问题确切范围的问题,并澄清功能需求至关重要。通常,需求分为三个部分: 功能需求 这些是最终用户特别要求的基本功能,系统应提供这些功能。根据合同,所有这些功能都需要纳入系统。 例如: “我们需要为这个系统设计哪些功能?” “在我们的设计中,我们需要考虑哪些边缘情况(如果有的话)?” 非功能性需求 这些是系统根据项目合同必须满足的质量约束。这些因素的实施优先级或程度因项目而异。它们也被称为非行为需求。例如,可移植性、可维护性、可靠性、可扩展性、安全性等。 例如: “每个请求都应以最低延迟处理” “系统应具有高可用性” 扩展需求 这些基本上是“好有”的需求,可能超出了系统的范围。 例如: “我们的系统应记录指标和分析数据” “服务健康和性能监控?” 估算和约束 估计我们要设计的系统的规模。询问诸如以下问题很重要: “这个系统需要处理的目标规模是多少?” “我们的系统的读 / 写比例是多少?” “每秒有多少请求?” “需要多少存储空间?” 这些问题将有助于我们后期扩展设计。 数据模型设计 一旦我们有了估算,我们可以开始定义数据库模式。在面试的早期阶段这样做将有助于我们了解数据流,这是每个系统的核心。在这一步中,我们基本上定义了所有实体及其之间的关系。 “系统中有哪些不同的实体?” “这些实体之间的关系是什么?” “我们需要多少张表?” “NoSQL 在这里是否更好?” API 设计 接下来,我们可以开始为系统设计 API。这些 API 将帮助我们明确地定义系统的预期。我们不需要编写任何代码,只需简单地定义API需求,如参数、函数、类、类型、实体等。 例如: createUser(name: string, email: string): User 建议尽可能简化接口,并在后续处理扩展需求时返回。 高级组件设计 现在我们已经建立了数据模型和 API 设计,是时候确定解决问题所需的系统组件(如负载均衡器、API 网关等),并绘制系统的初步设计。 “设计单体架构还是微服务架构更好?” “我们应该使用什么类型的数据库?” 一旦我们有了一个基本的图表,我们可以开始与面试官讨论如何从客户端的角度使系统正常工作。 详细设计 现在是时候详细讨论我们设计的系统的主要组件。与面试官讨论哪个组件可能需要进一步改进。 这是一个很好的机会来展示您在专业领域的经验。提出不同的方法,优缺点。解释您的设计决策,并用实例支持。这也是讨论系统可能支持的其他功能的好时机,尽管这是可选的。 “我们应该如何划分数据?” “关于负载分配?” “我们是否应该使用缓存?” “如何处理流量激增?” 此外,尽量不要对某些技术持有强烈意见,像“我认为 NoSQL 数据库更好,SQL 数据库不可扩展”这样的说法给人的印象不好。作为多年来面试过很多人的人,我在这里的建议是要谦虚地对待你所知道的和你不知道的。在面试的这个部分,用实例运用你现有的知识来引导。...

February 8, 2024