apollo简单入门

在现代微服务架构中,服务数量的激增使得传统的配置文件管理方式(如本地文件、Git 仓库)变得越来越难以维护。配置的修改、发布、回滚、权限控制以及实时生效等需求,催生了分布式配置中心这一关键中间件。 Apollo (阿波罗) 是由携程框架部门研发的开源配置管理中心,它功能强大、生态丰富,能够集中化管理应用在不同环境、不同集群的配置。本文将带你深入了解 Apollo 的世界,从核心概念到架构原理,再到 Go 语言的实战集成。 一、Apollo 核心概念 要用好 Apollo,首先需要理解它的几个核心概念,这些概念构成了其灵活、强大的配置管理模型。 1. Application (应用) 这是配置管理的基本单元,代表一个独立的应用或服务。每个应用都有一个全局唯一的 AppId,这是客户端与服务端交互时的身份标识。 2. Environment (环境) 用于隔离不同部署环境的配置,如 DEV (开发环境)、FAT (测试环境)、UAT (预发布环境)、PRO (生产环境)。Apollo 的权限管理也是基于环境的,可以精细控制不同环境的配置修改和发布权限。 3. Cluster (集群) 一个应用在同一个环境下,可以部署在不同的集群中。这个概念主要用于支持多数据中心(IDC)或者隔离部署。例如,你可以为上海机房和北京机房创建两个不同的 Cluster,为它们配置不同的数据库地址。默认情况下,会有一个 default 集群。 4. Namespace (命名空间) 这是配置项的集合,是配置隔离的最小单元。通过 Namespace,可以把不同类型的配置分门别类地管理。 私有 Namespace:其作用域仅限于当前应用。每个应用创建时都会有一个默认的 application 命名空间。 公共 Namespace:可以被多个应用关联和共享。常用于存储一些公共的配置,如数据库连接信息、中间件地址等。 关联 Namespace (继承):应用可以关联(继承)公共 Namespace 的配置,实现配置的复用。如果私有配置与公共配置有相同的 Key,私有配置会覆盖公共配置。 格式支持:Namespace 支持多种格式,如 properties (默认)、XML、JSON、YAML、TXT。 5. Release (发布) 当一个 Namespace 的配置修改完成后,需要通过“发布”操作才能被客户端感知到。每一次发布都会生成一个唯一的版本号,且发布后的配置是 不可修改的。如果需要变更,只能创建一个新的发布。这个机制保证了配置的可追溯和快速回滚。 6. Gray Release (灰度发布) 这是 Apollo 的一大亮点。它允许你将新发布的配置先生效于指定的 部分 客户端实例上。你可以根据客户端的 IP 地址或预设的标签来圈定灰度范围。待验证无误后,再进行全量发布。这极大地提高了配置发布的安全性。...

September 14, 2025 · 539 words · Kurong

golang单元测试与冒烟测试入门

概念解析:单元测试 vs. 冒烟测试 1. 单元测试 (Unit Testing) 单元测试是针对程序中最小的可测试单元进行的测试。在 Go 语言中,这个“单元”通常指一个函数(Function)或一个方法(Method)。 核心目标:验证单个函数或方法的逻辑是否正确。它确保给定特定的输入,该函数能返回预期的输出。 关键原则:隔离。单元测试应该与其依赖项(如数据库、文件系统、外部API等)隔离开来。如果一个函数依赖了这些外部服务,我们通常会使用“模拟(Mock)”技术来创建一个假的依赖项,从而保证测试的稳定性和速度。 特点: 速度快:因为不涉及外部I/O操作,通常毫秒级就能完成。 粒度细:精确到每个函数,能够快速定位问题。 编写频繁:是开发过程中最常编写的测试类型。 2. 冒烟测试 (Smoke Testing) 冒烟测试,又称“构建验证测试(Build Verification Test)”,是一种非常基础的测试,用于确定软件的关键功能是否能够正常工作。它像是在给一个新设备通电后,看看它是否会冒烟——如果不冒烟,说明最基本的功能没问题,可以进行更详细的测试。 核心目标:验证应用程序的核心、关键流程是否能够跑通,确保程序在主要路径上不会崩溃。 关键原则:快速、宽泛、但不深入。它不关心业务逻辑的细枝末节,只关心“主干”是否健康。 特点: 速度较快:比完整的端到端测试快,但比单元测试慢。 覆盖面广:覆盖的是最关键的业务流程。 执行时机:通常在新版本构建完成后、进行全面测试之前执行。对于后端服务来说,可能是在服务启动后,立即调用几个核心API(如健康检查/health,登录接口等)看看是否返回200 OK。 Go 中的测试库 标准库 Go 语言内置了强大的测试支持,主要通过 testing 包来实现。这是所有 Go 测试的基础,你必须首先掌握它。 testing: 提供了编写测试的基本框架,例如 *testing.T 类型用于报告测试状态。 net/http/httptest: 专门用于测试 HTTP 客户端和服务器,无需真正监听端口即可模拟 HTTP 请求和响应,非常适合后端 API 测试。 流行的第三方库 虽然标准库很强大,但在某些方面,社区提供了更高效、更易读的工具。 stretchr/testify: 这是 Go 社区中最受欢迎的断言库。它提供了丰富的断言函数(如 assert.Equal(), assert.NoError()),让你的测试代码更简洁、更具可读性。 golang/mock (Gomock): Google 官方开发的 Mock 框架。当你需要隔离测试单元与它的依赖时,Gomock 可以根据接口自动生成模拟对象,让你完全控制依赖项的行为。 h2non/gock: 如果你的代码需要调用外部 HTTP API,这个库可以轻松地模拟这些外部请求,让你的测试不依赖于网络和第三方服务的稳定性。 从单元测试到冒烟测试入门 Part 1: 什么是单元测试?为什么需要它? 想象一下你正在组装一台复杂的机器,比如一台汽车。单元测试就像是分别检查每一个零件(螺丝、齿轮、活塞)是否合格。只有确保每个最小零件都工作正常,你才能对最终组装好的汽车有信心。...

September 14, 2025 · 569 words · Kurong

Kafka简单入门

Apache Kafka 是一个开源的分布式事件流平台,被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用。无论你是后端开发者、数据工程师,还是希望构建实时数据系统的架构师,掌握 Kafka 都是一项至关重要的技能。 本文将带你系统地学习 Kafka,从核心概念到命令行实战,再到面试高频考点,最后用 Go 语言构建一个简单的生产者和消费者应用。 一、Kafka 核心概念解析 要理解 Kafka,首先必须掌握其核心术语。我们可以把 Kafka 想象成一个高度有序、可分区、可复制的数字“邮局系统”。 1. Broker Kafka 集群中的一台服务器就是一个 Broker。你可以把它想象成邮局系统中的一个“邮局分拣中心”。多个 Broker 共同组成一个 Kafka 集群,协同工作,提供高可用和负载均衡。 2. Topic(主题) Topic 是消息的类别。所有发布到 Kafka 集群的消息都有一个指定的 Topic。这就像邮局里的不同“信件类型”,比如“平信”、“挂号信”或“国际邮件”。生产者将消息发送到特定的 Topic,消费者订阅特定的 Topic 来接收消息。 3. Partition(分区) 每个 Topic 都可以被分成一个或多个 Partition。Partition 是 Kafka 实现高吞吐量和水平扩展的关键。你可以将 Partition 理解为某个信件类型(Topic)的专用“处理通道”。 有序性:在单个 Partition 内,消息是严格有序的。消息被追加到 Partition 的末尾,并被分配一个唯一的、递增的 ID,称为 Offset。 并发性:不同的 Partition 可以分布在不同的 Broker 上,这使得多个消费者可以并行地从一个 Topic 中读取数据,极大地提高了读取效率。 4. Offset(偏移量) Offset 是 Partition 中每条消息的唯一标识符,是一个单调递增的整数。消费者通过 Offset 来追踪自己已经消费到 Partition 中的哪个位置。这个机制非常强大,因为它允许消费者自由地控制消费进度,可以重复消费或跳过某些消息。 5. Producer(生产者) 负责创建消息并将其发布(发送)到 Kafka Topic 的应用程序。生产者在发送消息时,可以指定 Topic,也可以指定消息的 Key。如果指定了 Key,Kafka 会使用哈希算法将这个 Key 映射到某个特定的 Partition,确保所有具有相同 Key 的消息都进入同一个 Partition,从而保证了这部分消息的顺序性。...

September 13, 2025 · 1137 words · Kurong

xxl-job简单入门

在任何复杂的业务系统中,定时任务、延时任务、周期性报表生成等调度需求都不可或令。虽然 Linux 的 cron 和编程语言内置的定时器可以解决简单问题,但在微服务架构下,它们面临着单点故障、无法统一管理、不支持集群和分片等诸多挑战。 XXL-JOB 是一个由大众点评开源的轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。凭借其清晰的架构、丰富的功能和极低的接入成本,XXL-JOB 已成为国内最流行的任务调度解决方案之一。 本文将带你全面解析 XXL-JOB,从核心概念、架构原理,到面试中的高频问题,并最终使用 Go 语言构建一个可以实际运行的执行器。 一、XXL-JOB 核心概念 理解 XXL-JOB 的工作模式,需要先掌握它的几个基本组件和术语。 1. 调度中心 (Admin) 调度中心是 XXL-JOB 的“大脑”,是一个独立部署的 Web 应用。它负责: 任务管理:提供可视化的 UI,用于创建、编辑、启动/停止任务。 调度触发:内置调度器,根据任务配置的 Cron 表达式或固定延时,准时地向执行器发起调度请求。 注册管理:维护在线的执行器列表,感知执行器的上下线。 日志监控:汇集所有任务的执行日志,方便开发者查看任务执行情况和排查问题。 2. 执行器 (Executor) 执行器是集成在业务应用中的一个组件(通常是一个 SDK 或库)。它负责: 自我注册:启动后,执行器会周期性地向调度中心发送心跳,报告自己的地址,完成“服务注册”。 接收调度:内置一个嵌入式的 HTTP 服务器,用于接收来自调度中心的任务触发请求。 执行任务:根据请求中的参数,调用应用内具体的业务代码(JobHandler)。 上报结果:将任务的执行结果(成功/失败)和执行日志回调给调度中心。 3. 任务 (Job) 与 JobHandler 任务:在调度中心 UI 上创建的一个调度单元,包含了 Cron 表达式、路由策略、负责人、超时时间等元数据。 JobHandler:在执行器项目中,具体承载任务业务逻辑的代码块。在 Java 中通常是一个方法,在 Go 中则是一个注册的函数。一个任务必须绑定一个唯一的 JobHandler 名称。 4. 路由策略 (Route Strategy) 当一个执行器以集群模式部署(即多个应用实例)时,路由策略决定了调度中心如何从集群中选择一个或多个实例来执行任务。常用策略包括: 第一个/最后一个:选择集群中第一个或最后一个在线的实例。 轮询 (Round Robin):按顺序轮流选择。 一致性哈希 (Consistent HASH):根据任务 ID 计算哈希值,选择固定的一个实例,可用于实现粘性调度。 分片广播 (Sharding):核心功能。将任务“分片”给集群中的所有实例并行执行。例如,处理 100 万条数据,可以分给 10 个实例,每个实例处理 10 万条。调度中心会向每个实例传递分片参数(分片总数、当前分片索引)。 5....

September 13, 2025 · 493 words · Kurong

sync包详解

一、sync 包概述 sync 包是 Go 标准库中用于处理并发和同步的核心工具包。在 Go 语言中,我们通过 Goroutine 实现并发,而当多个 Goroutine 需要访问共享资源时,为了避免数据竞争(Data Race)等问题,就需要使用 sync 包提供的同步原语来保证并发安全。 面试官通过 sync 包相关的问题,主要考察你对 Go 并发模型的理解、处理并发问题的能力以及对底层原理的掌握程度。 二、sync.Mutex与sync.RWMutex (互斥锁与读写锁) 1. 作用与目的 这是最基础和最常用的同步原语。 sync.Mutex (互斥锁): 保证在同一时刻,只有一个 Goroutine 可以访问被其保护的共享资源。当一个 Goroutine 获取了锁,其他试图获取该锁的 Goroutine 都会被阻塞,直到锁被释放。 sync.RWMutex (读写锁): Mutex 的一种更细粒度的实现,它将访问分为“读”和“写”。 多个读:可以共存,多个 Goroutine 可以同时获取读锁。 写与任何操作:互斥,当一个 Goroutine 获取了写锁后,其他任何 Goroutine(无论是读还是写)都必须等待。 核心解决的问题: RWMutex 旨在优化“读多写少”的场景。如果共享资源的读取频率远高于写入频率,使用 RWMutex 可以显著提高并发性能,因为它允许多个读操作并行执行。 2. 实现原理简介 Mutex: 内部通过一个 state 字段和信号量 sema 实现。state 字段的比特位分别表示锁的锁定状态、是否有被唤醒的 Goroutine、是否有等待的 Goroutine,以及是否处于饥饿模式。 正常模式: 新来的 Goroutine 会和等待队列头部的 Goroutine 竞争锁,这种模式吞吐量高,但可能导致队列中的 Goroutine 长时间等待(饥饿)。 饥饿模式: 当一个 Goroutine 等待锁超过 1ms,锁会切换到饥饿模式。在此模式下,锁会直接交给等待队列头部的 Goroutine,保证公平。 RWMutex: 内部组合了一个 sync....

August 30, 2025 · 605 words · Kurong

TCP的三次握手与四次挥手

深入理解TCP:三次握手与四次挥手(含面试高频考点) TCP (Transmission Control Protocol) 作为互联网核心协议簇中的基石,保证了数据传输的可靠性和顺序性。而要理解 TCP 的可靠性,就必须掌握其经典的连接管理机制——三次握手与四次挥手。 本文将用通俗易懂的比喻和清晰的技术图解,带你彻底搞懂这两个过程,并深入剖析面试中关于它们的高频考点。 一、三次握手:建立可靠的连接 在进行数据传输之前,通信双方(客户端和服务器)必须建立一个可靠的连接。这个建立连接的过程,就是“三次握手”。 一个生动的比喻:打电话 想象一下你给朋友打电话确认双方都能正常通信的过程: 你:“喂,能听到我说话吗?” (发送 SYN) 朋友:“我能听到你,你能听到我吗?” (回复 SYN+ACK) 你:“我也能听到你,那我们可以开始聊天了。” (发送 ACK) 经过这三步,双方都确认了彼此的发送和接收能力都正常,通话(连接)才正式建立。 技术流程详解 第一次握手 (SYN) 从哪到哪:客户端 -> 服务器 做什么:客户端发送一个 SYN (Synchronize) 报文段,其中包含一个随机生成的初始序列号 seq = x。 状态变化:客户端进入 SYN_SENT 状态,等待服务器的确认。 第二次握手 (SYN+ACK) 从哪到哪:服务器 -> 客户端 做什么:服务器收到客户端的 SYN 后,如果同意连接,则回复一个报文段。该报文段包含两个关键信息: 自己的 SYN 标志位,以及一个随机生成的初始序列号 seq = y。 一个 ACK (Acknowledgment) 标志位,以及确认号 ack = x + 1,表示已成功收到客户端的第一个报文。 状态变化:服务器进入 SYN_RCVD 状态。 第三次握手 (ACK) 从哪到哪:客户端 -> 服务器 做什么:客户端收到服务器的 SYN+ACK 后,会发送最后一个 ACK 报文段作为确认。该报文段包含确认号 ack = y + 1。 状态变化:此报文发送后,客户端和服务器都进入 ESTABLISHED 状态,标志着 TCP 连接正式建立成功,可以开始双向传输数据。 三次握手的核心目标 确保客户端和服务器双方都确认了自己的发送能力和对方的接收能力都正常。...

August 30, 2025 · 275 words · Kurong

WebSocket面试详解

第一部分:核心概念 1.1 什么是 WebSocket? WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它诞生于 HTML5,旨在解决浏览器与服务器之间低延迟、实时、双向通信的需求。 全双工 (Full-duplex):与 HTTP 的“请求-响应”模式不同,WebSocket 连接一旦建立,客户端和服务器双方可以随时主动向对方发送数据,无需等待对方的请求。 单个 TCP 连接:所有通信都在一个 TCP 连接上完成,减少了重复建立和关闭连接的开销。 协议标识符:WebSocket 的 URL 方案是 ws://(非加密)和 wss://(加密,运行在 TLS 之上)。 1.2 为什么需要 WebSocket?(解决了什么问题?) 在 WebSocket 出现之前,为了模拟实时通信,开发者通常采用以下技术,但它们都有明显的缺陷: 轮询 (Polling):客户端定时向服务器发送 HTTP 请求,询问是否有新数据。 缺点:大量无效请求,浪费带宽和服务器资源;数据延迟高,实时性差。 长轮询 (Long Polling):客户端发送一个请求,服务器“挂起”这个连接,直到有新数据时才返回响应。客户端处理完响应后,立即发送下一个请求。 缺点:仍然有连接开销;服务器长时间持有连接会消耗资源;实现相对复杂。 服务器发送事件 (Server-Sent Events, SSE):一种允许服务器向客户端单向推送数据的技术。 缺点:只能实现服务器到客户端的单向通信,客户端无法向服务器发送数据(除非通过另一个 HTTP 请求)。 WebSocket 解决的核心问题:提供了一种低开销、低延迟、真正意义上的双向实时通信方案,完美适用于即时通讯、在线游戏、实时数据更新(股票行情、体育比分)等场景。 1.3 WebSocket 与 HTTP 的关系和区别 特性 HTTP (HyperText Transfer Protocol) WebSocket 通信模式 半双工 (Half-duplex)。客户端发起请求,服务器响应,一次完整的事务结束。 全双工 (Full-duplex)。连接建立后,双方可随时互相发送数据。 连接状态 无状态 (Stateless)。每个请求都是独立的,服务器不保留之前请求的信息。 有状态 (Stateful)。连接在整个会话期间保持,服务器和客户端都维护连接状态。 协议开销 较高。每个请求都包含完整的 HTTP 头部,通常有几百字节甚至更多。 极低。握手之后,数据帧的头部非常小(通常 2-10 字节),开销很小。 连接建立 每次通信都需要建立 TCP 连接(或在 Keep-Alive 下复用,但仍是请求-响应模式)。 借用 HTTP 协议进行一次“握手”,之后升级为 WebSocket 协议,后续通信与 HTTP 无关。 应用场景 文档、图片、API 数据等一次性资源获取。 即时聊天、在线协作、实时数据推送、在线游戏等需要高实时性的场景。 协议标识 http://, https:// ws://, wss:// 1....

August 30, 2025 · 691 words · Kurong

搞懂BIO与AIO

彻底搞懂 AIO 与 epoll 维度一:必须厘清的两个核心概念 在深入 I/O 模型之前,我们必须先区分两组完全独立的概念,很多人在这里就已混淆: 阻塞 (Blocking) vs. 非阻塞 (Non-blocking) 关注点:应用程序在等待结果时的状态。 阻塞:发起 I/O 操作后,如果数据未就绪,当前线程会被挂起,直到操作完成。 非阻塞:发起 I/O 操作后,如果数据未就绪,函数会立刻返回一个错误码,线程不会被挂起,可以继续执行其他任务。 同步 (Synchronous) vs. 异步 (Asynchronous) 关注点:内核与应用程序之间如何交付 I/O 结果。 同步:应用程序自己负责发起 I/O 操作,并且主动去查询或等待结果。即便是非阻塞轮询,只要是“我”去问内核“好了没”,都算同步。 异步:应用程序发起 I/O 操作后便立即返回。内核会独立完成整个 I/O 过程(包括将数据从内核空间拷贝到用户空间),当一切完成后,内核会通知应用程序。 经典烧水比喻: 同步阻塞 (BIO):你守在水壶边,一直等到水烧开。 同步非阻塞 (NIO):你边看电视边烧水,每隔几分钟就跑去厨房看水开了没。 异步非阻塞 (AIO):你用一个智能水壶,告诉它水开后提醒你,然后你就可以安心做任何事,直到水壶通知你。 I/O 模型的演进之路 模型一:BIO (同步阻塞 I/O) - 一夫当关 这是最古老、最简单的模型:一个连接对应一个处理线程。 工作方式:主线程 accept 一个新连接,然后创建一个新线程来处理这个连接的 read() 和 write()。 致命缺点: 资源开销巨大:一个连接一个线程,线程的创建和上下文切换成本极高。 性能瓶颈:当成千上万个线程大部分时间都在 read() 等待数据而阻塞时,CPU 大量时间被浪费在无效的上下文切换上。它无法应对 C10K (单机一万并发) 的挑战。 模型二:I/O 多路复用 - 一人看多路 为了解决 BIO 的问题,诞生了 I/O 多路复用技术。其核心思想是:用一个(或少量)线程来监视和管理大量的网络连接(文件描述符 File Descriptor, fd)。...

August 30, 2025 · 314 words · Kurong

pprof 指南

Go 性能分析利器 pprof 指南 pprof 是 Go 语言生态中功能最强大的性能分析工具,它内置于 Go 的标准库中,能够帮助开发者精准定位程序中的性能瓶颈,无论是 CPU 的过度消耗、内存的异常增长,还是并发程序中的各种“慢”问题。 本文将在 kurongtohsaka 的 pprof 指南基础上,深入探讨两个额外的关键领域:Goroutine 泄漏 和 锁竞争。 第一部分:PProf 基础与开启 要在 Web 服务中开启 pprof,我们只需引入 net/http/pprof 包。对于使用 Gin 等框架的服务,可以将其 Handler 包装后注册到路由中。 一个关键的准备步骤是,为了能分析到 阻塞 和 锁竞争,我们需要在程序启动时设置采样率。如果不设置,相关的 profile 文件将是空的。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package main import ( "log" "net/http" _ "net/http/pprof" // 关键:匿名导入pprof包,它会自动注册handler "runtime" "github....

August 27, 2025 · 808 words · Kurong

常见linux命令

好的,这个命令非常重要,尤其是在排查 “too many open files” 这类经典问题时。我会把它加上,并放在一个新的“高级调试与诊断”分类里,让整个指南的结构更清晰。 这是更新后的完整版本,你可以直接替换掉之前的博客文章。 后端实习Linux命令指南 一、文件与目录管理 (基础中的基础) 这是最基本的操作,你需要像在Windows图形界面里操作文件夹一样熟练。 命令 完整名称 作用 常用示例 ls list 列出目录内容 ls -lha (列出所有文件,含隐藏文件,以人类可读的格式显示详细信息) cd change directory 切换目录 cd /var/log (切换到日志目录), cd .. (返回上一级), cd ~ (返回家目录), cd - (返回上次所在目录) pwd print working directory 显示当前所在路径 pwd cp copy 复制文件或目录 cp source.txt dest.txt, cp -r sourcedir destdir (递归复制整个目录) mv move 移动或重命名文件/目录 mv oldname.txt newname.txt (重命名), mv file.txt /tmp/ (移动到/tmp目录) rm remove 删除文件或目录 rm file.txt, rm -rf somedir (递归强制删除目录,慎用!) mkdir make directory 创建目录 mkdir logs, mkdir -p /data/app/logs (-p可以递归创建多层不存在的目录) touch touch 创建空文件或更新时间戳 touch app....

August 27, 2025 · 383 words · Kurong

《Redis深度历险》记录

《Redis深度历险》记录 基础与应用 分布式锁 分布式锁用于高并发或分布式事务中保护临界资源。 最常见的实现方式是使用 SET 命令的 NX (不存在时设置)和 PX (设置过期时间)选项: 1 SET lock_name my_random_value NX PX 30000 lock_name : 锁的名称。 my_random_value : 唯一标识(通常使用 UUID)。 NX : 只有键不存在时才设置。 PX 30000 : 设置 30 秒的过期时间。 释放分布式锁时需要验证唯一标识是否匹配,防止误删其他客户端的锁。 超时问题 超时问题有三类: 锁过期,可以通过锁续约机制,即获取锁后启动一个守护线程,定期检查锁是否仍由自己持有,如果是则延长锁的过期时间。 客户端获取锁后崩溃,没有主动释放锁,锁就只能等待超时后才能被其他客户端获取。所以要设置合理的锁超时时间。 Redis 服务器和客户端之间的时钟不同步,可能导致锁提前或延后过期。尽量确保服务器和客户端时钟同步(NTP),同时适当增加锁的超时时间作为缓冲。 分布式锁的实现有 Redlock 算法、go-redis 中的简易 mutex 分布式锁。 延时队列 通过 List 来作为简易的消息队列使用。 通过 rpush , lpush 控制入队,rpop , lpop 控制出队。 使用 List 实现延时队列会出现一个问题,假设 List 为空,那么之后的 pop 操作就是死循环。此时可以使用阻塞读来避免这种情况,即使用 blpop, brpop 两个命令代替之前的出队命令。阻塞读在队列没有数据的时候,会立即进入休眠状态。 同时需要注意,List 长时间为空,Redis 客户端连接可能会因为空闲而被回收。所以需要加入重试机制。...

April 29, 2025 · 914 words · Kurong

《Kubernetes编程》记录

《Kubernetes编程》记录 概论 控制器和 Operator 控制器实现了一个控制循环,通过 API 服务器监测集群中的共享状态,进行必要的变更。Operator 也是一种控制器,但是包含了一些运维逻辑,如应用程序的生命周期管理。 控制循环的基本流程如下: 读取资源的状态,通常采用事件驱动模式,使用 Watch 来实现。 改变集群中的对象状态或集群外部系统的状态。 通过 API 服务器更新第 1 步中所提到的资源的状态,状态放在 etcd 中。 循环执行这些逻辑。 控制器通常会使用以下的数据结构: Informer :Informer 负责观察资源的目标状态,它还负责实现重新同步机制从而保证定时地解决冲突。 工作队列:事件处理器把状态变化情况放入工作队列。 多个相互独立的控制循环通过 API 服务器上的对象状态变化相互通信,对象状态的变化由 Informer 以事件的方式通知到控制器。这些通过 Watch 机制发送的事件对用户来说几乎不可见,在 API 服务器的审计机制中也看不到这些事件,只能看到对象更新的动作。控制器在处理事件时,以输出日志的方式记录它做的动作。 Watch 事件:在 API 服务器与控制器之间通过 HTTP 流的方式发送,用于驱动 Informer 。 Event 对象:是一种用户可见的日志机制,如 kubelet 通过 Event 来汇报 Pod 的生存周期事件。 乐观并发 Kubernetes 采用乐观并发机制,假设多用户操作同一资源的冲突概率较低,允许并发修改,仅在提交时检测冲突。 每个 Kubernetes 资源对象(如 Pod、Deployment)的元数据中都有一个 resourceVersion 字段,用于标识资源的当前版本号(由 etcd 生成)。这是乐观并发的核心: 读取资源时:客户端获取对象的 resourceVersion(例如 123)。 更新资源时:客户端必须在请求中携带该版本号(通过 metadata.resourceVersion 字段)。 服务器端校验:API Server 比较请求中的 resourceVersion 与当前存储中的版本: 匹配:允许更新,resourceVersion 递增。 不匹配:返回 409 Conflict 错误,拒绝更新(说明资源已被其他客户端修改过)。 当发生冲突时,客户端需重新获取资源的最新状态,然后基于最新数据重新提交修改。...

April 9, 2025 · 212 words · Kurong

《Kafka权威指南》记录

《Kafka权威指南》记录 初识 Kafka 消息和批次 消息有一个可选的元数据,也称为键。键是一个字节数组,可以用来实现如分布式键等应用。 为了提高效率,消息会被分批次写入 Kafka,批次包含了一组同属于一个主题和分区的消息。 模式 模式就是消息的格式,如Json、XML。 主题和分区 消息通过主题进行分类,主题又被分为若干个分区。 生产者和消费者 默认情况下,生产者会把消息均衡地分布到主题的所有分区中,也可以通过自定义设置决定消息发布到哪里。 消费者通过检查消息的偏移量来区分已经读取过的消息。 broker 和集群 一台单独的 Kafka 服务器称为 broker,单个 broker 可以处理数千个分区和每秒百万级的消息量。 集群由 broker 组成,同时也有集群中都有的分布式共识算法。Kafka 使用 ZAB 作为共识算法,2.8 版本后支持 KRaft 管理元数据。 Kafka 与其他消息队列的区别 Kafka 与其他消息队列相比,有以下几个显著的区别: 高吞吐量与低延迟 Kafka 设计上支持高吞吐量和低延迟,能够处理海量数据和高并发写入、读取场景。 其他消息队列(如 RabbitMQ、ActiveMQ)通常适用于较小规模或实时性要求较高的场景,但在海量数据和高并发场景下可能会遇到性能瓶颈。 持久化与分布式存储 Kafka 使用磁盘持久化和日志分段技术,并通过分区(Partition)和副本(Replica)机制实现分布式存储和容错。 其他消息队列有的采用内存队列(例如 RabbitMQ 默认使用内存),虽然也支持持久化,但在大规模数据存储和高可靠性需求下不如 Kafka 灵活。 消息模型与消费模式 Kafka 基于发布-订阅模型,采用拉取(Pull)的消费模式。消费者主动拉取消息,可以灵活控制消费速率和消息重放。 其他消息队列(例如 RabbitMQ)多采用推送(Push)模式,消息直接推送给消费者,实时性较好,但可能在处理慢消费者时需要额外机制(如 ACK、流控等)。 数据持久性和消息重放 Kafka 中的消息存储在日志中,并且消息不会在被消费后自动删除(可以根据保留策略删除),这使得消费者可以根据需求重放消息。 其他消息队列通常在消息被消费确认后就删除,无法轻易重放历史消息。 扩展性与容错性 Kafka 天生支持分布式集群部署,通过增加 Broker 节点来横向扩展,且内置副本机制保证容错。 其他消息队列虽然也有集群部署方案,但在大规模扩展和高容错要求下往往不如 Kafka 那样简单高效。 应用场景不同 Kafka 更适用于日志收集、流处理、大数据实时分析等场景。 其他消息队列则更适合需要复杂路由、即时响应、任务队列等场景,比如电商下单、实时通知等。 Kafka 生产者 消息发布方式:...

March 26, 2025 · 132 words · Kurong

《MongoDB 权威指南》记录

《MongoDB 权威指南》记录 MongoDB 简介 MongoDB 是面向文档的非关系型数据库。 MongoDB 具有以下特点: 易于使用:没有“行记录”的概念,而使用“文档”模型取代之。同时也没有预定义模式,文档键值的类型和大小是动态的。 易于扩展:MongoDB 采用了横向扩展方式,面向文档的数据模型使得跨多台服务器拆分数据更加容易。 性能强大:使用了机会锁,提高并发和吞吐量。同时会尽可能多的使用内存作为缓存,并尝试为查询自动选择合适的索引。 入门指南 文档 文档是一组有序键值的集合,如以下形式: 1 {"q": "ans"} 集合 集合就是一组文档,相当于关系型数据库的表结构。 集合具有动态模式的特性,每个文档的长度大小、键数量都可以不一样。不过开发中并不推荐这样做,还是将“形状”相同的文档存在同一个集合中好,因为这样查询起来性能不会受到影响。 在 MongoDB 中通常会用 . 字符分隔不同命名空间的子集合,如 blog.posts 。 数据库 MongoDB 使用数据库对集合进行分组。 数据类型 基本数据类型:除了 int 等,还有 Regex、Array、ObjectID(12字节的文档的唯一ID 标识)、Bytes、JSCode。 Array: {"things": ["pie", 3.14]},数组内可以混有不同类型的数据。 内嵌文档:文档作为值。 ObjectID:每个文档都有一个 _id ,默认是 ObjectID ,这个 ID 在集合内是唯一的。可以看到这个 ID 不是自增的,类似于 UUID,这说明 MongoDB 在设计之初就是作为分布式数据库存在的。以下是它的生成规则: 前 4 字节为时间戳,中间 5 个字节是随机值,后 3 个字节是计数器(起始值随机)。 索引 MongoDB 中可以设置单一索引和复合索引,方式如下: 1 db.users.createIndex({"age": 1, "username": 1}) # 复合索引 MongoDB 的默认存储引擎是 WiredTiger ,索引是 B+ 树实现的。所以在索引的优化上,可以参照相关关系型数据库的索引优化经验与原则。下面是部分经验:...

March 22, 2025 · 143 words · Kurong

《HTTP权威指南》记录

《HTTP权威指南》记录 本书中的 HTTP 协议版本为 HTTP/1.1 。 URL 与资源 URL 语法 在 URL 中以分号 ; 作为参数的分隔符,如下: 1 http://xxx.xxxx.com/hammers;sale=false/index.html;graphics=true 有些资源需要通过查询字符串来缩小查找范围,查询字符串通过 ? 分割,如常见的 GET 请求中的查询参数。 URL 支持使用片段组建来表示一个资源内部的片段,以 # 进行分割: 1 http://xxx.xxx.com/index.html#head Web 服务器 Web 服务器应该做的: 接受客户端连接:客户端收到一条连接之后,那么它将会把新连接添加到现存web服务器连接列表中,用于监视当前连接上的数据传输情况。期间服务器还应该做到通过一定的设备机制阻止未认证或已知恶意黑名客户端的连接。 接收请求报文: 这一步需要解析请求报文: 解析请求行,查找请求方式、URI 、版本号以及 CRLF 分隔符。 读取以 CRLF 结尾的报文首部。 检测到以 CRLF 结尾的,标识首部结尾的空行。 解析得到请求体。 处理请求:其他章节会详解。 对资源的映射及访问:找到客户端请求资源在服务器的上的目录路径。 Web 服务器存放内容的文件夹称为文档的根目录(doc root),Web 服务器会从请求报文中获取 URI ,并将其附加在 doc root 的后面。 在一个服务器上挂载多个 Web 站点,那么这样当请求的资源路径相同时,服务器应该从请求报文首部的 HOST 和 URI 字段找出真正的资源目录,这些目录都可以更改配置。 构建响应: 正确设置响应主体的长度(content-length)。 设置报文的 MIME 类型(content-type)。 有时候资源不在原地,需要进行重定向。 发送响应。 记录事务日志。 代理 Web 的中间实体 Web 代理服务器是代表客户端对事务请求处理的中间人,代理分为私有代理(只代理一个客户端)和公共代理(代理多个客户端)。 代理和网关的对比:代理的两端使用相同的协议,而网关的两端使用不同的协议,网关负责协议转换。 代理应用 内容过滤。 文档访问控制。 安全防火墙。 Web 缓存:缓存资源的副本。 反向代理:反向代理伪装成原始服务器,不过与服务器不同的是反向代理还可以向其他服务器发送请求,以便实现按需定位所请求的内容。 ……....

March 14, 2025 · 315 words · Kurong

《深入理解分布式共识算法》记录

《深入理解分布式共识算法》记录 从 ACID 和 BASE 到 CAP BASE 理论 BASE 包含; BA(Basically Available):基本可用,当分布式系统出现故障的时候,允许损失部分可用性。这里损失指响应时间的损失和功能上降级,前者是在增加响应时间上来提供服务,后者是在高峰期不请求后端而直接返回降级数据、以保证系统的稳定性。 S(Soft State):软状态,允许系统中的数据存在中间状态,并认为该状态不会影响系统的整体可用性,即允许节点之间的数据同步存在延迟。 E(Eventually Consistent):最终一致性。 为应用 BASE 理论,可以采用异步补偿机制。如果用这个机制,需要明确哪些操作属于非关键操作,如果非关键操作失败,应允许业务流程继续执行,然后在异步补偿非关键操作。 补偿指事务补偿:在分布式事务中,由于各个服务可能分布在不同的节点上,无法像单机事务那样简单地通过“回滚”来撤销操作。因此,事务补偿通过执行与原始操作相反的操作来达到类似的效果。 补偿操作需要注意两点: 幂等性:补偿操作可能需要多次执行,因此需要保证其幂等性,即多次执行的效果与一次执行相同。 最终一致性:补偿机制通常用于实现最终一致性,而不是强一致性,因此需要权衡一致性和性能。 CAP 理论 CAP 包含: C(Consistency)一致性:分布式系统的全序线性一致性。 A(Availability)可用性:任何情况都能够处理客户端的每个请求。 P(Partition Tolerance)分区容错性:发生网络分区时,系统仍能提供服务。 任何分布式系统都可分为三类架构: CP(锁定事务资源):所有节点的数据需要实时保持一致性,这需要锁定各分支事务的资源。当发生分区时,此期间为确保返回客户端数据的准确性且不破坏一致性,可能会因为无法响应最新数据而拒绝响应,即放弃 A 。 AP(尽力提供服务):要求每个节点拥有集群的所有能力或数据,能独立处理客户端的每个请求。发生分区时,可以通过缓存后本地副本来处理请求,以达到可用性,但会出现各节点不一致性,即放弃 C 。 CA(本地一致性):在不发生分区时,C、A 正常满足。一旦发生分区,会保证各子分区满足 CA 。 但是网络分区不可避免,CAP 理论就变成了在 C 和 A 中的权衡问题。 2PC、3PC,分布式事务的解决方案 2PC 2PC 中的字母含义是 P(Participant,参与者),C(Coordinator,协调者)。 2PC 即两阶段提交协议,用于解决分布式事务问题。第一阶段用于各个分支事务的资源锁定,第二阶段用于全局事务的提交或回滚: 第一阶段,准备: 开启全局事务。当协调者收到客户端的请求后,它将各个分支事务需要处理的内容通过 Prepare 请求发送给所有参与者,并询问能否正常处理自己的分支事务,然后等待各个参与者的响应。 处理分支事务。当参与者收到 Prepare 请求后便锁定事务资源,然后尝试执行,记录 Undo 和 Redo 信息,但不提交分支事务。 汇报分支事务状态。参与者根据第 2 步执行的结果,向协调者汇报各自的分支事务状态,Yes 表示可以提交,No 表示不能提交。 第二阶段,提交/回滚:...

March 7, 2025 · 298 words · Kurong

《数据密集型系统设计》记录

《数据密集型系统设计》记录 本书的电子版本链接:Vonng/ddia: 《Designing Data-Intensive Application》DDIA中文翻译 3. 存储与检索 本章围绕两大类存储引擎:日志结构(log-structured)的存储引擎,以及面向页面(page-oriented)的存储引擎(例如 B 树)。 数据库核心:数据结构 这里所使用的“日志”是一个更一般性的概念:一个支持 append-only 仅追加操作的记录序列。 为了高效查找数据库中特定键的值,需要用到索引。索引是从主要数据中衍生的额外结构,维护它会产生额外开销,特别是写入时。任何类型的索引通常都会减慢写入速度,因为每次写入数据时都需要更新索引。这是存储系统中一个重要的权衡:精心选择的索引加快了读查询的速度,但是每个索引都会拖慢写入速度。因为这个原因,数据库默认并不会索引所有的内容,而需要程序员或数据库管理员(DBA),基于对应用的典型查询模式的了解来手动选择索引。 SSTable 把键值对的序列按照键进行排序,这个格式称为排序字符串表(Sorted String Table, SSTable)。此时再要求每个键值在每个合并的段文件中出现一次。与使用散列索引的日志段相比,SSTable 有几个大的优势: 即使文件大于可用内存,合并段的操作仍然是简单高效: 一开始并排读取多个输入文件,查看每个文件中的第一个键,复制最低的键(根据排序顺序)到输出文件,不断重复此步骤,将产生一个新的合并段文件,而且它也是也按键排序的。 如果在几个输入段中出现相同的键,该怎么办?请记住,每个段都包含在一段时间内写入数据库的所有值。这意味着一个输入段中的所有值一定比另一个段中的所有值都更近(假设我们总是合并相邻的段)。当多个段包含相同的键时,我们可以保留最近段的值,并丢弃旧段中的值。 为了在文件中找到一个特定的键,你不再需要在内存中保存所有键的索引: 假设你正在内存中寻找键 handiwork,但是你不知道这个键在段文件中的确切偏移量。然而,你知道 handbag 和 handsome 的偏移,而且由于排序特性,你知道 handiwork 必须出现在这两者之间。这意味着你可以跳到 handbag 的偏移位置并从那里扫描,直到你找到 handiwork(或没找到,如果该文件中没有该键)。 由于读取请求无论如何都需要扫描所请求范围内的多个键值对,因此可以将这些记录分组为块(block),并在将其写入硬盘之前对其进行压缩。稀疏内存索引中的每个条目都指向压缩块的开始处。除了节省硬盘空间之外,压缩还可以减少对 I/O 带宽的使用。 那么,可以构建 SSTable 了,可以让存储引擎这样工作: 有新写入时,将其添加到内存中的平衡树数据结构(例如红黑树),这个内存树有时被称为内存表(memtable)。 当内存表大于某个阈值(通常为几兆字节)时,将其作为 SSTable 文件写入硬盘。这可以高效地完成,因为树已经维护了按键排序的键值对。新的 SSTable 文件将成为数据库中最新的段。当该 SSTable 被写入硬盘时,新的写入可以在一个新的内存表实例上继续进行。 收到读取请求时,首先尝试在内存表中找到对应的键,如果没有就在最近的硬盘段中寻找,如果还没有就在下一个较旧的段中继续寻找,以此类推。 后台进程周期性地执行合并和压缩过程,以合并段文件,并将已覆盖或已删除的值丢弃。 这样的索引结构被称为 LSM-Tree(Log-Structured Merge-Tree)。 当查找数据库中不存在的键时,LSM 树算法可能会很慢。为了优化这种访问,存储引擎通常会使用布隆过滤器。还可以使用大小分级和分层压缩。对于大小分级,较新和较小的 SSTables 相继被合并到较旧的和较大的 SSTable 中。对于分层压缩,键的范围被拆分到多个较小的 SSTables,而较旧的数据被移动到单独的层级,这使得压缩能够逐步进行并且使用较少的硬盘空间。 B-Tree 前面看到的日志结构索引将数据库分解为可变大小的段,通常是几兆字节或更大的大小,并且总是按顺序写入段。相比之下,B 树将数据库分解成固定大小的块或分页,传统上大小为 4KB(有时会更大),并且一次只能读取或写入一个页面。这种设计更接近于底层硬件,因为硬盘空间也是按固定大小的块来组织的。 在 B 树的底层中,写操作是用新数据覆写硬盘上的页面,并假定覆写不改变页面的位置。即当页面被覆写时,对该页面的所有引用保持完整。这与日志结构索引(如 LSM 树)形成鲜明对比,后者只追加到文件(并最终删除过时的文件),但从不修改文件中已有的内容。...

February 7, 2025 · 585 words · Kurong

《Kubernetes微服务实战》记录

《Kubernetes微服务实战》记录 面向开发人员的 Kubernetes 简介 部分核心概念 Node:Node 是 Kubernetes 集群中的一个工作单元,表示集群中的一台物理机或虚拟机。Node 的作用是为运行容器化应用提供计算资源,一个 Node 可以运行多个 Pod。Node 分为: Master Node:负责集群的控制平面,管理所有工作节点。 Worker Node:运行实际的应用程序工作负载,实际上就是 Pod。 Pod:Pod 是 Kubernetes 中最小的可部署单位,表示一个或多个容器的集合。有以下特性: Pod 内的容器共享一个 IP 地址,可以通过 localhost 相互通信。 Pod 内的容器可以共享挂载的存储卷(Volume)。 Pod 通常包含运行同一应用程序组件的容器,如主应用程序容器、日志采集容器等。 Namespace:用于逻辑上隔离资源。可以在同一集群中运行多个项目或团队的工作负载。 Service:用于将一组 Pod 的访问暴露给外部或集群内部的其他 Pod。 Deployment:用于声明和管理 Pod 的期望状态,例如副本数、滚动更新等。 Volume:提供持久化存储,允许 Pod 重启后数据仍然保留。 Kubernetes 架构 每个集群都有一个控制平面和数据平面,数据平面由多个节点组成,控制平面将在这些节点上部署并运行 Pod(容器组),监控变更并做出响应。 控制平面包含以下组件: API 服务器。 etcd 存储。 调度器:kube 调度器负责将 Pod 调度到工作节点。 控制器管理器:kube 控制器管理器是包含多个控制器的单个进程,这些控制器监控着集群事件和对集群的更改做出响应。 节点控制器:负责在节点出现故障时进行通知和响应。 副本控制器:确保每个副本集(replica set)或副本控制器对象中有正确数量的 Pod 。 端点控制器:为每个服务分配一个列出该服务的 Pod 的端点对象。 服务账户和令牌控制器:使用默认服务账户和响应的 API 访问令牌对新的命名空间进行初始化。 数据平面是集群中将容器化工作负载转为 Pod 运行的节点的集合。...

January 25, 2025 · 1376 words · Kurong

minikube:Ubuntu部署本地集群踩坑

minikube:Ubuntu 部署本地集群踩坑 官方安装教程 minikube start | minikube 再次感叹 homebrew 的伟大,一行命令就安装、后续也没有问题。 仪表盘 使用 minikube dashboard 启动仪表盘。如果在服务器上启动、本地访问,就运行类似于的下面命令: 1 kubectl proxy --address='0.0.0.0' --disable-filter=true 然后把 http://127.0.0.1:46749/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ 这样的 URL 改为 http://ServerIP:8001/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ ,就可以外网直接访问了(注意服务器开端口)。 Helm 安装 直接使用 snap 安装,方便快捷: 1 sudo snap install helm --classic kubectl 安装 snap 上大分: 1 sudo snap install kubectl --classic 安装可能遇到的问题的解决方案 The “docker” driver should not be used with root privileges 使用命令 minikube start 启动,参数 driver 默认为 docker 。如果以 root 用户运行该命令会出现如标题所示的错误,下面是解决方法: minikube - Why The “docker” driver should not be used with root privileges - Stack Overflow...

January 25, 2025 · 219 words · Kurong

《云原生开发实践》记录

《云原生开发实践》记录 本书会包含大量的实践内容,现阶段应着重看理论内容、实践内容暂且忽略,实践内容在理论补充完毕后用到哪块补哪块。 容器化 DOCKERFILE 的多阶段构建在部署阶段很有用,可以大幅减少镜像的体积。 Docker 可以自建网络,这样就可以把很多容器纳入到同一个网络下,实现容器间按容器名访问对方。 容器编排 对于一个中大型的应用,会有很多的容器组件。而有些容器如Redis、RabbitMQ、Kafka等需要紧密的配合,这时候就需要用到容器编排。 Docker Compose 组件用于实现本地单个节点上的容器编排,它会从 docker-compose.yaml 文件中读取所需的全部容器的定义,然后运行 docker-compose up 命令启动容器编排。Docker Swarm 可以管理多个节点上的容器,即管理 Docker 集群。 云原生软件生产流程 云计算的能力: 弱化了传统 IT 硬件概念,革命性地降低了企业在基础设施上的建设成本。 实现了弹性获取计算资源,用户可以按需使用。 云计算的成熟时云原生发展的基石,使云原生的许多概念得以落地。云原生应用一般有以下特点: 在应用的设计和开发阶段就为部署到云上做适配。 应用由多个松耦合的小模块构成而不是一个庞大的单体项目,即微服务架构。 通过容器来交付和发布应用,应用代码中会加入容器化需要的文件。 和传统的软件生产方式相比,云原生的优势主要在塔提高了应用发布和运维时的效率,显著降低了运维的复杂性。 云原生基础设施 Kubernetes Kubernetes 是开源的容器编排平台,支持集群环境下部署和管理容器化应用。目前已经是容器编排领域的事实标准,成为云原生的操作系统。Kubernetes 也叫 K8s ,8指中间的8个字母。K8s 2013年由 Google 开源,相比于 Docker Swarm K8s 提供更加复杂强大的功能。 K8s 集群中包含两种节点,一种是 Control Plane 节点(master 节点),另一种是 worker 节点。K8s 中的容器运行在 Pod 中,Pod 运行在 Node 中。每个集群默认至少有一个 Pod ,否则 Pod 无法调度和运行。Control Plane 是 K8s 的容器编排层,通过它提供的 API ,可以定义和部署容器及管理容器的整个生命周期。Control Plane 有以下组件:...

January 22, 2025 · 107 words · Kurong

《Go微服务实战》记录

《Go微服务实战》记录 本书包含很多的 Go 基础和部分进阶内容,这里只选取对现阶段有帮助的内容,毕竟 Go 已经入门过一遍了。 Go 基础 关于指针,如果涉及到修改变量本身就使用指针作为函数输入变量等,典型的如单例模式下的变量都是指针。反之如果不修改变量本身,就直接使用复制变量作为函数输入变量等,函数返回值自然也是某个新的变量。 关于 Go 中的循环,请看 code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package main func main() { for i := 0; i < 10; i++ { ... } // 最标准的循环 for { ... } // 就是没有显式跳出条件的 while arr := []int{1, 2, 3, 4, 5} for i, v := range arr { ... } // 这里的 range 是关键字,用法有些类似于 Python 中的 enumerate 函数 } Go 的垃圾回收策略使用的三色标记法,具体内容会单独进行学习。...

January 18, 2025 · 2955 words · Kurong

《PostgreSQL指南》记录

《PostgreSQL指南》记录 Why choose PostgreSQL, or not MySQL ? SQL 标准支持更好:PostgreSQL 对 SQL 标准的支持更加全面,而 MySQL 在某些情况下依赖非标准实现(如分组函数的行为)。 高级功能更强: JOSNB 的性能更优:PostgreSQL 的 JSONB 数据类型支持更高效的索引和操作,而 MySQL 的 JSON 功能较为有限。 复杂查询:支持递归查询、窗口函数等复杂功能,而 MySQL 在这些方面功能较弱或支持有限。 并行查询:PostgreSQL 提供原生的并行查询能力,能够充分利用多核 CPU,而 MySQL 直到 8.0 版本才有一定程度的改进。 扩展性更好: PostgreSQL 支持自定义数据类型、函数和插件,更适合需要扩展或复杂业务逻辑的项目。 PostGIS 插件使其在地理信息领域表现出色,而 MySQL 的 GIS 功能相对简单。 更高的性能和灵活性: 支持多种索引类型(如 GIN、GiST、BRIN 等),适合高性能全文搜索、地理查询等场景。 支持更复杂的查询优化器策略,适合复杂查询和数据分析。 更可靠的数据一致性: PostgreSQL 的 MVCC 机制更成熟,避免了常见的锁争用问题。 MySQL 的 MVCC 在某些情况下(如高并发)性能欠佳,容易导致死锁。 PostgreSQL 适用于有复杂数据分析需求、地理信息系统、高并发和大数据量等应用场景,相比于 MySQL 的应用场景更复杂。 数据库集簇、数据库和数据表 数据库集簇的逻辑结构 数据库集簇是一组数据库的集合,由一个 PostgreSQL 服务器管理。而数据库是数据对象的集合,数据库对象用于存储或引用数据的数据结构。 在 PosgreSQL 中,所有的数据库对象都通过相应的对象标识符(oid)进行管理,这些标识都是无符号 4 字节整型。...

December 22, 2024 · 768 words · Kurong

《高效使用Redis》记录

《高效使用Redis》记录 基础数据结构解析 对象 Redis中的 RedisObject 的 c 定义: 1 2 3 4 5 6 7 8 #define LRU_BITS 24 typedef struct redisObject { unsigned type:4; // 数据类型 unsigned encoding:4; // 底层数据结构 unsigned lru:LRU_BITS; // 缓存淘汰时使用 int refcount; // 引用计数 void *ptr; // 指向实际存储位置 } robj; RedisObject 是 Redis 中数据结构的一个抽象,用它来存储所有的 key-value 数据。 下面是结构体中各个属性的。说明: type:用来表示对象类型; encoding:表示当前对象底层存储所采用的数据结构,如int、字典、压缩列表、跳跃表等等; lru:用于在配置文件中通过 maxmemory-policy 配置已用内存到最大内存限制时的缓存淘汰策略。 以 GET 命令为例,简单看看 Redis 中的 LRU 实现。使用 GET 后,会执行这段代码更新对象的 lru 属性: 1 2 3 4 5 if (server....

December 5, 2024 · 1410 words · Kurong

《gRPC与云原生应用开发》记录

《gRPC与云原生应用开发》记录 gRPC 入门 gRPC 的定义 gRPC 是一项进程间通信技术,可以用来连接、调用、操作和调试分布式异构应用程序。 开发 gRPC 应用程序时,要先定义服务接口:消费者消费信息的方式、消费者能够远程调用的方法以及调用方法所使用的参数和消息格式等。在服务定义中使用的语言叫作接口定义语言(IDL)。 下面是使用 protocol buffers 作为 IDL 来定义服务接口: 服务定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // ProductInfo.proto syntax = "proto3"; package ecommerce; // 防止协议消息类型之间发生命名冲突 // 定义服务接口 service ProductInfo { rpc addProduct(Product) returns (ProductID); rpc getProduct(ProductID) returns (Product); } // 定义消息格式 message Product { string id = 1; string name = 2; string description = 3; } message ProductID { string value = 1; } 上面定义完成之后,就使用 protocol buffers 编译器 protoc 生成服务器端和客户端代码。...

November 30, 2024 · 963 words · Kurong

《深入RabbitMQ》记录

《深入RabbitMQ》记录 RabbitMQ 基础 AMQP 协议 AMQP,即高级消息队列协议(Advanced Meesage Queuing Protocal)。它定义了一个 AMQ 应该有以下三部分: 交换器:一个接收器接收发送到 MQ 中的消息并决定把它们投递到何处; 队列:存储接收到的消息; 绑定:定义队列和交换器之间的关系。在 RabbitMQ 中,绑定或者叫绑定键即告知一个叫交换器应该将消息投递到哪些队列中。 使用 AMQ 协议与 Rabbit 进行交互 AMQP 帧类型 AMQP 规定了五种类型的帧: 协议头帧:用于连接到 RabbitMQ,仅使用一次; 方法帧:携带发送到 RabbitMQ 或从 RabbitMQ 接收到的 RPC 请求或响应; 内容头帧:包含一条消息的大小和属性; 消息体帧:包含消息的内容; 心跳帧:确保连接,一种校验机制。 将消息编组成帧 首先是方法帧、内容头帧,这两种帧比较好理解。 而消息体帧会根据消息体的大小切分成一到多个消息体帧。 要发送消息,首先发送的是方法帧,之后是内容帧,最后是若干个消息体帧。 帧结构 方法帧携带构建 RPC 请求所需的类、方法和参数。 内容头帧包含了消息的大小和其他对消息起描述作用的属性。 消息体帧可以传输很多类型的数据,可以是二进制、文本,也可以是二进制化的图片和序列化后的json、xml。 使用协议 包含下面一个基本流程: 声明交换器 声明队列 绑定队列到交换器 发布消息到 RabbitMQ 从 RabbitMQ 消费消息 消息属性详解 使用 content-type 创建显式的消息契约 在不同的语言的消费者框架中,框架可以自动的根据 content-type 类型将其反序列化为该语言中的数据结构。 使用 gzip 和 content-encoding 压缩消息大小 通过指定 content-encoding 属性可以在消息体上应用特殊的编码。...

November 28, 2024 · 192 words · Kurong

华为云部署踩坑

华为云部署踩坑 Docker 国内镜像源(截止到2024.11.1) DockerHub 镜像仓库 是否正常 hub.xdark.top 正常 hub.littlediary.cn 正常 dockerpull.org 新增 hub.crdz.gq 正常 docker.1panel.live 正常 docker.unsee.tech 新增 docker.m.daocloud.io 正常 docker.kejilion.pro 正常 registry.dockermirror.com 正常 hub.rat.dev 正常 dhub.kubesre.xyz 正常 docker.nastool.de 正常 docker.hpcloud.cloud 失效 docker.hlyun.org 失效 doublezonline.cloud 失效 docker.chenby.cn 失效 ginger20240704.asia 失效 lynn520.xyz 失效 hub.docker-ttc.xyz 失效 noohub.ru 失效 docker.nat.tf 失效 dockerproxy.cn 失效 freeno.xyz 失效 docker.registry.cyou 失效 hub.yuzuha.cc 失效 docker-cf.registry.cyou 失效 docker.mrxn.net 失效 dockerproxy.github.io 失效 docker.wget.at 失效 atomhub.openatom.cn 失效 ccr.ccs.tencentyun.com 失效 dockerproxy.com 失效 dislabaiot....

November 5, 2024 · 327 words · Kurong

《计组KG》课题开发过程(三)

牢骚 时隔一个多月才完成了基本的可视化系统的搭建,中间有着各种各样的原因:Vue3第一次用、Django-Ninja不熟悉等等再加上一些闲杂原因就一直拖到了现在,效率多少有点堪忧。 总之不算怎么说,也算是曲折的完成了原定计划,下面将从后端、前端的技术选择、功能介绍、成品展示等几个章节大致的讲述下。 后端 项目地址 KurongTohsaka/PCCKGVisualization 技术栈 Web 框架自然是选相对擅长且好用的 Django ,但是 Django 本身使用起来又过于繁重,不太适合开发Restful类型的接口,所以一般会搭配着 Django REST Framework (DRF) 使用。但是这一次我打算尝试一个新的 Django 扩展,它是 FastAPI 的 Django 版:Django-Ninja 。 Web 框架订好了以后,就该选数据库了。既然是存储图数据,那肯定是 Neo4j 。而网站数据就使用简单的 Sqlite 吧,主要就是省事。 功能与接口 下面的所有接口的最上级请求路径:pcc_kg_vs 功能主要分为两部分:认证和可视化。 首先是认证,包含以下基本功能: 用户认证:用户登陆、注册、登出 API 鉴权:token 验证 CSRF 验证:CSRF token 验证 下面是简易的接口标准: 登陆 先检查该用户是否已注册,若注册则从数据库中根据信息查询到用户,然后登陆该用户。若未注册则登陆失败 1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "method": "POST", "path": "/login", "params": { "username": "", "password": "" }, "return": { "successful": bool, "code": int, "token": str, //登陆令牌 'info': str } } 登出 HTTPBearer...

November 4, 2024 · 247 words · Kurong

MAC上用docker配置neo4j以及apoc扩展的过程

拉取 neo4j Docker image 在 MAC 上用的 Docker Desktop,确实很方便操作。 直接在 Image Tab 页搜 Neo4j 就有一个官方 image,我选的 5.24.1版本,pull 下来。 这里之所以要选择一个特定的版本,原因是之后要启用的 apoc 扩展需要用对应 neo4j 版本的 jar 包。 配置容器 因为用的 Docker Desktop,很多没必要的步骤都可以直接忽略。配置好转发端口、挂载位置后,就启动容器。 Neo4j 容器启动后默认直接运行,登陆 Web 管理界面配置后用户名密码后就完成了基本配置。 Docker 真 tm 好用( 配置 apoc 这里需要注意一点,neo4j 5.x 之后的版本中,apoc 本身的相关配置需要写在 neo4j/conf/apoc.conf 里,而不是 neo4j/conf/neo4j.conf 中,不然在启动服务时会报错!!! 首先是 apoc 的下载地址:Index of /doc/neo4j-apoc/ 。 下载好后,把它放进 neo4j/plugins,并改名为 apoc.jar (啥名都行,这里是方面后面改配置)。 接下来修改 neo4j/conf/neo4j.conf ,修改以下两行并取消该行注释: 1 2 dbms.security.procedures.unrestricted=apoc.* dbms.security.procedures.allowlist=apoc.coll.*,apoc.load.* 最后修改 neo4j/conf/apoc.conf : 1 2 apoc.import.file.enabled=true apoc....

October 22, 2024 · 72 words · Kurong

《计组KG》课题开发过程(二)

前言 自从上次记录已经过去了一个月,整个课题进展不大。原因一个是暑期有点摆,另一个是关系抽取确实比较繁琐。不管怎么说,来记录下吧。 NER 数据集下的模型训练 首先需要声明的是该阶段的模型不参与于最后 KG 的构建,目的仅仅是跑通模型训练、验证的过程,为后续阶段提供便利。 NER 数据集 该部分信息在完成 RE 部分后可能会发生些微变动,仅作参考,后续会做调整。 共标记5147条中文语句,实体共标注1472个。 下面是各个标签下的数量统计: Label Count TECH 388 COMP 382 STOR 170 DATA 133 INST 105 ARCH 71 IO 61 PERF 54 PROG 52 CORP 17 ALG 16 PROT 15 PER 4 GRP 4 模型选择 模型有两大类: 传统深度学习方法 CNN-CRF BiLSTM-CRF BERT 系预训练模型,输出层为 CRF 或 MLP+Softmax BERT:BERT 是一个双向 Transformer 模型,通过掩码语言模型(Masked Language Model, MLM)和下一句预测(Next Sentence Prediction, NSP)任务进行预训练 RoBERTa:RoBERTa 是对 BERT 的优化版本,移除了 NSP 任务,并采用了动态掩码策略 ALBERT:ALBERT 是 BERT 的轻量级版本,通过参数共享和嵌入参数因子化来减少模型大小 XLM-RoBERTa:XLM-RoBERTa 是针对多语言的预训练模型,基于 RoBERTa 和 XLM 的结合 这里选择的是 XLM-RoBERTa,预训练模型选择的是 FacebookAI/xlm-roberta-large-finetuned-conll03-english · Hugging Face...

September 14, 2024 · 423 words · Kurong

《计组KG》课题开发番外:关系抽取的思考过程

现状 目前课题进行到了关系抽取这一步。在看完之前的RE综述后,我决定整一个 Joint 的 NER+RE 模型。目前已经完成了 NER 的标注工作,下一步工作自然就是 RE 的标注。但是 RE 的标注要比 NER 要困难的多,一个是对于关系的定义依赖于实体类型、句子词性等多种复杂要素,二是手工标注势必工作量巨大。所以 RE 的标注有什么解决方法呢? 我决定把整个思考过程记录下来,方便复盘。 相关开源库 thunlp/OpenNRE:用于神经关系提取 (NRE) 的开源包 (github.com) SapienzaNLP/relik:检索、读取和 LinK:学术预算上的快速准确的实体链接和关系提取 (ACL 2024) (github.com) huggingface/setfit:使用句子转换器进行高效的少样本学习 (github.com) EleutherAI/lm-evaluation-harness:一种用于语言模型小样本评估的框架。 (github.com) 关系抽取的可能方案 首先能想到的两种常见方案: 使用 RE 预训练模型预标注:和 NER 的标注工作流程一致,但是中文 RE 预训练模型很难找 远程监督:使用已有的知识库或实体关系词典,对大规模文本进行远程监督标注。但是不适合当前课题的小规模数据集,而且没有现成的大量 RE 标注数据 以上两种方案中只有预训练模型的预标注还算可行,那有没有什么方法可以加速这一过程?可以看看下面的两种方法: 半监督学习:结合少量人工标注数据和大量未标注数据,通过半监督学习的方法训练模型 few-shot 小样本学习:使用少量人工标注的数据对few-shot模型进行训练,以提高模型在少样本情况下的泛化能力 这两种方案值得单独进行介绍。 半监督学习 半监督学习(Semi-Supervised Learning)是一种结合了监督学习和无监督学习的机器学习方法。它利用少量的标记数据和大量的未标记数据来训练模型,从而提高模型的泛化能力和性能。 半监督学习的样本标注依赖假设,以下是部分常见假设: 平滑性假设:如果两个数据点在高密度区域中且距离很近,那么它们的输出也应该相似 聚类假设:如果数据点形成簇,那么同一簇中的数据点应该属于同一类 流形假设:高维数据通常位于低维流形上,同一流形上的数据点具有相同的标签 常用的方法有: 一致性正则化:假设对未标记数据加入小扰动后,其分类结果不应改变 伪标签:使用已标记数据训练初始模型,然后用该模型对未标记数据进行预测,生成伪标签,再将这些伪标签数据加入训练集中进行再训练 生成式模型:利用生成模型(如GANs)从数据分布中生成样本,并将这些样本用于训练分类器 优势: 利用未标注数据,提高模型的泛化能力和性能 较低标注成本 适应性强,可以应用于多种任务 缺点: 依赖数据假设:半监督学习通常假设未标记数据和标记数据在特征空间中具有相似性,这在实际应用中并不总是成立。如果这些假设不成立,可能会导致模型性能下降 标签传播误差 数据不平衡问题 我曾经在 kaggle 比赛中使用过半监督学习中的伪标签方法,只从工程实现的角度看比较容易,但是模型性能不一定有提升。...

August 24, 2024 · 431 words · Kurong