plantuml, puml
Mermaid:VS Code 内置替代方案 Mermaid 是最佳替代方案,VS Code 的 Markdown 预览内置支持,无需扩展或服务器。直接在 Markdown 中写 ```mermaid 代码块,按 Ctrl+Shift+V 打开预览即可看到渲染结果。 功能 PlantUML Mermaid VS Code 原生支持 需要 Java + 扩展 内置 GitHub 渲染 不支持 支持 语法 更强大 更简洁 离线使用 需要 graphviz 完全离线 序列图 sequenceDiagram Alice->>Bob: Authentication Request Bob-->>Alice: Authentication Response Alice->>Bob: Another authentication Request Alice<<--Bob: Another authentication Response 类图 classDiagram class Animal { +String name } class Duck { +quack() } Animal <|-- Duck 活动图 / 流程图 flowchart TD start([start]) stop([stop]) decision{foo?} start --> decision decision -->|yes| process0[process0] decision -->|no| process1[process1] process0 --> stop process1 --> stop 状态图 stateDiagram-v2 [*] --> State1 State1 --> State2 State2 --> [*] 思维导图 mindmap root((Debian)) Ubuntu Linux Mint Kubuntu Lubuntu LMDE SteamOS Raspbian plantuml, puml UML 序列图, Sequence Diagram 用例图, Use Case Diagram 类图, Class Diagram 对象图, Object Diagram 活动图, Activity Diagram 组件图, Component Diagram 部署图, Deployment Diagram 状态图, State Diagram 时序图, Timing Diagram 非 UML 图 架构图, Archimate diagram UML 图 时序图, Sequence Diagram @startuml Alice -> Bob: Authentication Request Bob --> Alice: Authentication Response Alice -> Bob: Another authentication Request Alice <-- Bob: Another authentication Response Alice ->> Bob: async msg @enduml 类图, Class Diagram @startuml skinparam classFontColor red class Foo note left: parent class Bar Bar : String field0 Bar : String method0() Foo<|--Bar note left: child abstract class Abstract0 Foo--|>Abstract0 interface Interface0 ' 注意, 接口名和冒号之间必须用空格分隔 Interface0 : void method0() class Class0{ - private_field_0 } @enduml @startuml skinparam class { BackgroundColor Lightblue ArrowColor #0ACF97 BorderColor #d5d5d5 } skinparam stereotypeCBackgroundColor YellowGreen Class101 <|.. Class102 @enduml 活动图, Activity Diagram @startuml !theme plain start if (foo?) then (yes) :process0; else (no) :process1; endif stop @enduml 组件图, Component Diagram 别名后面可以标注颜色 修改线和箭头的颜色 文字颜色 语法 # 把组件显示成普通矩形 skinparam componentStyle rectangle # 组件间横向距离 skinparam nodesep 10 # 组件间纵向距离 skinparam ranksep 10 @startuml skinparam componentStyle rectangle skinparam nodesep 10 skinparam ranksep 10 skinparam ParticipantFontColor #A9DCDF ' comments line starts by single quote, 注释 [First component] [Another component] as Comp2 component Comp3 component [Last\ncomponent] as Comp4 [component0] as c0 #ff0000 [<color:#ff0000>component1</color>] as c1 [component2] as c2 [component3] as c3 [component4] as c4 c0 -- c1 c0-[#00ff00]-c2 c1--c3 c0--c3 c2--c4 note left of c0 note0 end note @enduml 部署图, Deployment Diagram @startuml circle 1 circle 2 circle 3 agent 4 1 -- 2 1 -- 3 1 -- 4 queue 5 4 -right- 5 5 -right-2 @enduml 状态图, State Diagram @startuml [*] --> State1 State1 --> [*] State1 : this is a string State1 : this is another string State1 -> State2 State2 --> [*] @enduml 安装 graphviz sudo apt-get install -y graphviz https://graphviz.org/download/ ...
ZooKeeper Leader Latch 与 Leader Election
ZooKeeper 节点类型 ZooKeeper 的节点(ZNode)类型是两个维度的组合: 生命周期维度: 持久(Persistent):客户端断开后节点依然存在,需要显式删除 临时(Ephemeral):与创建它的客户端 Session 绑定,Session 结束节点自动删除 命名维度: 普通:你指定什么路径就创建什么路径,路径已存在则报错 顺序(Sequential):路径作为前缀,ZooKeeper 自动追加 10 位单调递增序号 两两组合,实际有四种节点类型: 类型 说明 持久节点 最普通的节点,手动删除才消失 持久顺序节点 名称自动追加递增序号,永久存在 临时节点 Session 结束自动删除 临时顺序节点 Session 结束自动删除 + 名称追加序号 各场景适用类型: 场景 用哪种 分布式锁(抢占式) 普通临时节点,谁先创建谁得锁 分布式锁(公平排队) 顺序临时节点,序号最小的得锁 Leader 选举 顺序临时节点,序号最小的是 leader 配置中心、服务注册 普通持久节点 临时节点原理 ZooKeeper 客户端连接服务端后会建立一个 Session,并维持心跳(默认每隔 tickTime 发一次)。服务端为每个 Session 设置超时时间(sessionTimeout),超时内未收到心跳则判定 Session 过期,自动删除该 Session 创建的所有临时节点,其他 watch 了这些节点的客户端会收到删除通知。 状态 临时节点是否消失 网络抖动(短暂断开) 不消失,等待重连,Session 未过期 断开超过 sessionTimeout 消失,Session 过期 客户端主动 close() 立即消失 临时节点不能有子节点,因为它的生命周期是不确定的。 ...
软件工程师的能力与素质
技术能力 扎实的基础:数据结构、算法、操作系统、网络、数据库原理 系统设计能力:能权衡取舍,设计可扩展、可维护的系统 调试与排障:能快速定位问题,善用工具(profiler、日志、监控) 阅读代码:能快速理解陌生代码库,而不只是写代码 持续学习:技术演进快,能主动跟进新技术而不盲目追新 工程素养 写可读代码:代码是写给人看的,命名清晰,逻辑直白 测试意识:主动写测试,而不是依赖 QA 兜底 版本控制规范:清晰的 commit message,合理的分支策略 文档意识:在合适的地方写合适的文档(不是每行都注释) 安全意识:代码层面的 OWASP 意识,不把安全留给"以后再说" 协作能力 沟通清晰:能向非技术人员解释技术问题,能在 code review 中表达具体意见 估时准确:能分解任务,给出相对可靠的工时估算 主动暴露问题:遇到 blocker 早说,而不是默默卡住 思维方式 系统思维:看到局部问题时,能思考对整体的影响 务实:不追求完美设计,在"够好"和"完美"之间找到正确点 ownership:对自己负责的东西有主人翁意识,上线后持续关注 简单优先:倾向于简单方案,而不是过度设计;代码越少越好 软技能 抗压与专注:在复杂问题面前不慌,能保持有效的思考 接受反馈:对 code review 意见开放,不把技术讨论变成自我防卫 好奇心 好奇心是一个底层驱动力,而不只是加分项。软件工程本质上是一个持续解决未知问题的职业。好奇心驱动你: 追问"为什么这样设计",而不只是"怎么用" 遇到 bug 时愿意深挖根因,而不是绕过去 主动探索新技术,而不是等公司安排培训 对系统的运行机制真正感兴趣,而不只是完成任务 没有好奇心的工程师可以把当前的工作做得"够用",但很难做到深入——他们写的代码能跑,但不理解为什么。 好奇心本身还不够,它需要配上自律和落地能力。有些人什么都好奇,但浅尝辄止,或者永远在学新东西却从不完成一件事——这是好奇心失控的版本。 更完整的表述是:有边界的好奇心——对核心领域深挖,对周边领域保持开放,但不被新鲜感分散焦点。 如果只能选一条:能把复杂问题分解成可执行的小步骤,并持续交付——这是区分普通工程师和优秀工程师最核心的能力。 AI 时代的变化 AI 时代这些素质的重心发生了偏移,而不是全部推翻。 更重要了的 阅读和判断代码:AI 生成代码的速度远超人类,“写"的比重下降,“读、评估、审查"的比重上升 问题分解:指挥 AI 的前提是你能把问题拆清楚——分解不清,AI 给你的结果也是乱的 基础原理:AI 输出的代码"看起来对"但可能暗藏问题,没有基础你抓不住错误 判断力与品味:AI 永远给你一个"合理的答案”,但合理不等于正确,你需要能辨别好坏 系统思维:AI 处理局部,人负责全局,整体架构、权衡取舍仍然是人的责任 变得不那么关键的 记忆语法、API 细节——AI 比你记得更准 写样板代码的速度——AI 秒完成 从零实现标准算法——直接生成即可 新增的 AI 协作能力:知道什么时候用 AI、怎么引导、怎么验证输出 怀疑 AI 输出的习惯:不盲从,始终验证正确性、安全性、性能 更强的 ownership:AI 会产出大量代码,但责任仍然在人,“AI 写的"不是借口 好奇心在 AI 时代更重要,因为工具变化极快,好奇心驱动你持续理解 AI 的能力边界,而不是停留在一年前的用法上。 ...
LevelDB
简介 LevelDB 是由 Google 工程师 Jeff Dean 和 Sanjay Ghemawat 开发的开源 KV 存储引擎,两人同时也是 Bigtable 和 MapReduce 的主要设计者。LevelDB 可以理解为单机版的 Bigtable Tablet Server,能够处理十亿级别规模的 Key-Value 数据持久性存储。 主要特性: 数据按 Key 有序存储,支持自定义比较函数 提供 Put / Get / Delete 以及原子批量操作接口 支持 Snapshot 快照读,读操作不受并发写影响 支持 Snappy 数据压缩 写性能极高:随机写约 40 万条/秒,随机读约 6~10 万条/秒(4核机器) 写速度远大于读速度;顺序读写远快于随机读写 LevelDB 是单进程嵌入式库,不提供网络服务,适合作为其他系统的本地存储引擎。Chrome 浏览器用它存储 IndexedDB 数据,比特币/以太坊节点用它存储区块链状态。 LSM-Tree LevelDB 的核心数据结构是 LSM-Tree(Log-Structured Merge-Tree),一种针对写入密集型场景优化的存储结构。 核心思想: 将随机写转化为顺序写,以牺牲部分读性能换取极高的写入吞吐量。 写入流程 写入请求 ↓ WAL(预写日志,顺序写磁盘) ↓ MemTable(内存,有序跳表 SkipList) ↓ 满了之后 Immutable MemTable(只读,等待 flush) ↓ flush(minor compaction) L0(SSTable 文件,磁盘) ↓ compaction(major compaction) L1 → L2 → ... Ln 三大放大问题 问题 说明 写放大 数据会被反复 compaction,实际写磁盘量远大于原始数据量,通常 10x~30x 读放大 读时需要依次查 MemTable → Immutable MemTable → L0 → L1 → … 空间放大 同一 key 可能存在多个版本,直到 compaction 才合并清理 为什么写快 所有写入都是顺序 I/O(追加 WAL、批量 flush) 没有 B-Tree 那样的随机写和页分裂开销 一次写入只涉及一次磁盘顺序追加写 + 一次内存 SkipList 插入 使用 LSM-Tree 的系统 LevelDB / RocksDB — 经典实现(RocksDB 是 LevelDB 的增强版) Cassandra、HBase — 分布式存储 ClickHouse MergeTree — 列式存储引擎 静态结构 LevelDB 的数据存储在内存和磁盘两部分,主要由六个组件构成: ...
Command Dispatcher / Command Bus
Command Dispatcher(命令分发器)和 Command Bus(命令总线)是架构模式,属于应用层架构设计的范畴,不在 GoF 23 种设计模式之列。 注意:“Command Dispatcher” 是开发者社区的口语化称呼,没有权威模式书籍正式定义。对应的正式模式名是 EIP(企业集成模式)中的 Content-Based Router(见下文)。 两者不是同一个模式,Command Bus 是在 Content-Based Router 基础上增加了中间件管道的演化版本,核心差异在于中间件管道。 Command Dispatcher Command Bus 核心机制 ID → Handler 直接路由 ID → 中间件链 → Handler 中间件支持 无 有(日志、验证、事务等) 复杂度 简单 较复杂 典型场景 IoT 协议解析、游戏指令 CQRS、DDD 应用层 Command Dispatcher 核心结构: 收到消息 → 解析 ID → registry.get(id) → handler.Handle(data) 服务初始化时将每种指令的处理类注册到一个 Map。运行时收到消息后,解析出消息类型 ID,从注册表查找对应处理器并直接调用。 // Handler 接口,每种指令实现一个 type Handler interface { Handle(data []byte) error } // Dispatcher 维护 id -> handler 的注册表 type Dispatcher struct { handlers map[uint8]Handler } func NewDispatcher() *Dispatcher { return &Dispatcher{handlers: make(map[uint8]Handler)} } func (d *Dispatcher) Register(id uint8, h Handler) { d.handlers[id] = h } func (d *Dispatcher) Dispatch(id uint8, data []byte) error { h, ok := d.handlers[id] if !ok { return fmt.Errorf("unknown command id: 0x%02X", id) } return h.Handle(data) } // --- 具体指令处理类 --- type LoginHandler struct{} func (h *LoginHandler) Handle(data []byte) error { fmt.Printf("处理登录指令: %x\n", data) return nil } type LocationReportHandler struct{} func (h *LocationReportHandler) Handle(data []byte) error { fmt.Printf("处理位置上报: %x\n", data) return nil } // --- 初始化注册 --- func main() { d := NewDispatcher() d.Register(0x01, &LoginHandler{}) d.Register(0x02, &LocationReportHandler{}) // 收到一条原始消息,解析出 id=0x02 d.Dispatch(0x02, []byte{0x01, 0x02, 0x03}) } Command Bus 在 Dispatcher 的基础上增加了中间件管道,每条命令在到达 Handler 之前会依次经过所有中间件。 ...
AI Agent
AI Agent AI Agent(人工智能代理)是一个能够感知环境、自主决策并采取行动以完成目标的软件系统。 与普通程序不同,AI Agent 的核心特征是自主性:它不只是被动地响应单次输入,而是能够: 制定多步骤计划 调用外部工具(搜索、代码执行、API 调用等) 根据执行结果动态调整策略 持续循环直到目标完成 AI Agent 的定义 从学术角度,Agent 的经典定义来自 Russell & Norvig(《人工智能:一种现代方法》): Agent 是任何能够通过传感器感知环境、并通过执行器对环境采取行动的事物。 在 LLM 时代,AI Agent 的定义更具体: AI Agent 是以大语言模型为核心推理引擎,能够自主规划任务、调用工具、与外部系统交互,并通过反馈循环完成复杂目标的自治系统。 Agent 的四个核心要素 要素 说明 感知(Perception) 接收输入:多模态用户指令、工具返回结果、系统状态、历史记忆 规划(Planning) 将目标分解为可执行的子任务序列 行动(Action) 调用工具、执行代码、调用 API、操作文件等 记忆(Memory) 短期记忆(上下文窗口)+ 长期记忆(向量数据库等) 感知的来源 感知不只是用户输入那一句话,Agent 每次推理前收到的完整上下文都属于"感知": 来源 说明 多模态输入 用户的文本指令(最常见)、图片、音频、视频等 工具返回结果 搜索结果、API 响应、代码执行输出、数据库查询结果 系统状态 文件内容、环境变量、当前任务进度 历史记忆 短期记忆(上下文窗口中的对话历史)+ 长期记忆(从向量数据库检索) 这四类来源最终都会被拼装进 LLM 的上下文窗口,LLM 基于这个完整上下文做出下一步决策。因此,感知本质上是构建 LLM 输入上下文的过程。 上下文窗口:感知的硬性约束 能放入上下文的信息量取决于模型的上下文窗口大小(以 token 计): 模型 上下文窗口 GPT-4o 128K tokens Claude 3.5 Sonnet 200K tokens Gemini 1.5 Pro 1M tokens 超出窗口的内容会被截断,因此 Agent 需要主动管理上下文。常见策略如下(各框架实现不一,没有统一标准): ...
inode
inode An inode stores all the information about a regular file, directory, or other file system object, except its data and name. 每个文件都对应一个 inode, inode 存储了除文件名和文件内容之外的所有信息。 inode (发音: eye-node) 译成中文就是索引节点, 它用来存放文件和目录的基本信息, 包含时间、档名、使用者,群组,权限, 一个文件占用一个inode,同时记录此文件的数据所在的 block 号码; http://www.ruanyifeng.com/blog/2011/12/inode.html inode是什么? inode是一个重要概念, 是理解 Unix/Linux 文件系统和硬盘储存的基础。 我觉得,理解 inode, 不仅有助于提高系统操作水平, 还有助于体会 Unix 设计哲学, 即如何把底层的复杂性抽象成一个简单概念, 从而大大简化用户接口。 下面就是我的inode学习笔记,尽量保持简单。 理解inode 作者: 阮一峰 inode是什么? Inode 用于存储文件或目录的信息. 理解inode, 要从文件储存说起。 扇区 (Sector) 文件储存在硬盘上, 硬盘的最小存储单位叫做"扇区" (Sector) 。每个扇区储存 512 字节。(现在新的硬盘每个扇区有4K) 注意:硬盘的最小存储单位就是扇区了,而且硬盘本身并没有 block 的概念。 文件系统不是一个扇区一个扇区的来读数据,太慢了,所以有了 block(块)的概念,它是一个块一个块的读取的,block 才是文件存取的最小单位。 ...
archlinux packages
archlinux packages 本文逐步记录我的 Arch Linux 系统中安装的软件包,目标是了解每个包在当前系统中的用途,以及它是否是必要的依赖(能否安全删除)。 aalib: ASCII art graphic library aardvark-dns: Authoritative DNS server for A/AAAA container records abseil-cpp: Collection of C++ library code designed to augment the C++ standard library adwaita-fonts: GNOME Adwaita 字体,被 wechat 等应用依赖 ansible: IT automation tool ansible-core: Ansible core engine aom: Alliance for Open Media video codec appstream: Provides a standard for creating app stores across distributions archlinux-appstream-data: Arch Linux application database for AppStream-based software centers at-spi2-core: Protocol definitions and daemon for D-Bus at-spi audit: Userspace components of the audit framework avahi: Service Discovery for Linux using mDNS/DNS-SD (compatible with Bonjour) baloo-widgets: KDE Baloo 搜索框架集成小部件 bluez-libs: Deprecated libraries for the bluetooth protocol stack boost-libs: Boost C++ 通用库运行时动态库,详见 boost-libs botan: Crypto library written in C++ bubblewrap: 非特权沙箱工具,详见 bubblewrap btrfs-progs: Userspace utilities to manage btrfs filesystems c-ares: 异步 DNS 解析 C 库,被 curl、Node.js 等广泛使用 ca-certificates-mozilla: Mozilla’s set of trusted CA certificates cfitsio: C/Fortran library for reading and writing FITS data format files chrony: Lightweight NTP client and server conmon: OCI container runtime monitor cryptsetup: Userspace setup tool for transparent encryption of block devices using dm-crypt dav1d: AV1 cross-platform decoder focused on speed and correctness expat: C 语言实现的流式 XML 解析库(libexpat),详见 expat fftw: A library for computing the discrete Fourier transform (DFT) fzf: General-purpose command-line fuzzy finder gcr: A library for bits of crypto UI and parsing glslang: OpenGL Shading Language 编译器和验证器,详见 glslang gnutls: A library which provides a secure layer over a reliable transport layer gtk-update-icon-cache: GTK icon cache updater hwdata: Hardware identification databases iproute2: IP routing utilities libavif: Library for encoding and decoding .avif files libplist: Library to handle Apple Property List files libx11: X11 client-side library expat C 语言实现的流式 XML 解析库(libexpat),采用 SAX(事件驱动)解析模型,适合处理大型 XML 文档。当前系统被以下包依赖:antigravity(Google 的 AI 辅助 IDE 工具)、avahi(mDNS/DNS-SD 零配置网络服务)、cmake(跨平台构建系统)、dbus(进程间通信总线)、dbus-broker(D-Bus 的替代实现)、exiv2(图片元数据读写库)、fontconfig(字体配置库)、gdb(GNU 调试器)、git(版本控制工具)、mesa(OpenGL/Vulkan 图形驱动实现)、neon(HTTP/WebDAV 客户端库)、polkit(权限授权框架)、python(Python 解释器)、qt6-webengine(Qt6 网页渲染引擎)、vtk(3D 可视化工具库)、wayland(显示服务协议库)、webkit2gtk-4.1(WebKit 网页渲染引擎)、wechat-bin(微信桌面客户端)。 ...
AI agent development
AI Agent 开发的本质 开发 AI Agent 的核心是将已验证的知识和流程固化成可执行的规则和指令。 具体来说: 知识固化 - 把专家经验、最佳实践、业务规则编码成 Agent 可以遵循的指令 流程自动化 - 将重复性的决策流程转化为确定性的执行步骤 质量保障 - 通过固化的规则确保每次执行的一致性,避免人为疏忽 迭代优化 - 每次发现新问题或更好的做法,就更新这些"固化的知识",Agent 的能力随之提升 本质上是把隐性知识显性化,把经验驱动变成规则驱动。 这也意味着 Agent 的质量上限取决于你固化进去的知识质量。垃圾进,垃圾出;好的经验进,稳定的高质量输出。 为什么需要固化知识 LLM 的知识覆盖面极广,解决同一个问题可能有多条路径,但其中只有少部分是最佳实践。具体输出什么内容,很大程度上取决于提示词的质量。 把已验证的解决方案固化到 Agent 里,能让 Agent 在特定领域有更稳定、更优质的表现——相当于给 LLM 划定了一条"黄金路径",避免它在众多可能性中随机游走。 常用技术栈 编程语言 Python(主流,生态丰富) JavaScript/TypeScript(Web/Node.js Agent) Go、Java(高性能/企业级) 大语言模型与 API OpenAI GPT-4/3.5、Claude、Llama Hugging Face Transformers LangChain、LlamaIndex(Agent 框架) Web 框架与服务 FastAPI、Flask(Python) Express.js(Node.js) Django 数据存储 Redis、MongoDB、PostgreSQL、SQLite 向量数据库:Milvus、Pinecone、Weaviate 消息队列与异步任务 Celery、RabbitMQ、Kafka 容器与部署 Docker、Kubernetes 云服务:AWS、Azure、GCP 前端交互 React、Vue.js WebSocket、RESTful API 其他 Prompt 工程、工具插件系统 OAuth2、JWT(安全认证) 日志与监控:Prometheus、Grafana Python 调用模型方式 AI Agent 用 Python 开发时,可以调用本地模型或云端模型: ...
MCP (Model Context Protocol) 是什么
MCP 是什么 MCP(Model Context Protocol,模型上下文协议)是一个开源标准协议,用于将 AI 应用与外部系统连接起来。 可以把 MCP 理解为 AI 应用的 USB-C 接口。就像 USB-C 为电子设备提供了标准化的连接方式,MCP 为 AI 应用与外部系统的连接提供了统一标准。 有了 MCP,Claude、ChatGPT 等 AI 应用可以连接到: 数据源:本地文件、数据库 工具:搜索引擎、计算器、代码执行环境 工作流:自定义 prompt 模板、业务系统 MCP 解决了什么问题 问题背景 在 MCP 出现之前,每个 AI 应用如果想连接外部工具或数据源,都需要为每种组合单独开发集成代码。这导致: 重复开发:A 工具想接 GitHub,B 工具也想接 GitHub,各自写一套 维护成本高:N 个 AI 应用 × M 个外部工具 = N×M 个集成要维护 互不兼容:为 Claude 写的插件不能直接用在 ChatGPT 上 MCP 的解决方案 MCP 引入了统一的 Client-Server 架构: MCP Server:外部工具/数据源的提供方,按照 MCP 协议暴露能力 MCP Client:AI 应用(如 Claude、VS Code Copilot),按照协议调用 Server 这样只需要: ...
微服务
微服务 微服务是一种分布式系统解决方案。 微服务 (Microservices) 就是一些协同工作小而自治的服务。 2014年,Martin Fowler 与 James Lewis 共同提出了微服务的概念,定义了微服务是由以单一应用程序构成的小服务,自己拥有自己的进程与轻量化处理,服务依业务功能设计,以全自动的方式部署,与其他服务使用 HTTP API 通信。同时服务会使用最小的规模的集中管理 (例如 Docker) 能力,服务可以用不同的编程语言与数据库等组件实现 。 微服务与SOA 「面向服务的体系结构」 SOA (Service-Oriented Architecture) 听起来和微服务很像,但 SOA 早期均使用了总线模式,这种总线模式是与某种技术栈强绑定的,比如: J2EE。这导致很多企业的遗留系统很难对接,切换时间太长,成本太高,新系统稳定性的收敛也需要一些时间,最终 SOA 看起来很美,但却成为了企业级奢侈品,中小公司都望而生畏。 此外,实施SOA时会遇到很多问题,比如通信协议 (例如SOAP)的选择、第三方中间件如何选择、服务粒度如何确定等,目前也存在一些关于如何划分系统的指导性原则,但其中有很多都是错误的。SOA并没有告诉你如何划分单体应用成微服务,所以在实施SOA时会遇到很多问题。 这些问题再微服务框架中得到很好的解决,你可以认为微服务架构是SOA的一种特定方法。 分布式系统 什么是分布式系统 分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。 首先需要明确的是,只有当单个节点的处理能力无法满足日益增长的计算、存储任务的时候,且硬件的提升 (加内存、加磁盘、使用更好的CPU) 高昂到得不偿失的时候,应用程序也不能进一步优化的时候,我们才需要考虑分布式系统。因为,分布式系统要解决的问题本身就是和单机系统一样的,而由于分布式系统多节点、通过网络通信的拓扑结构,会引入很多单机系统没有的问题,为了解决这些问题又会引入更多的机制、协议,带来更多的问题。 “什么是分布式系统?这取决于看系统的角度。对于坐在键盘前使用IBM个人电脑的人来说,电脑不是一个分布式的系统。但对于在电脑主板上趴着的虫子来说,这台电脑就是一个分布式系统。” —— Leslie Lamport 复杂性 流量管理 (Traffic management) : 超时、重试、负载均衡; 安全性 (Security) : 终端用户的认证和授权; 可观察性 (Observability) : 跟踪、监控和日志。 私有数据的所有权 当多个服务直接读写数据库中同一张表时,对这些表做任何改动都需要协调这些相关服务的部署。这一点违背了服务相互独立这一原则。共享的数据存储很容易不经意间造成耦合。每个服务需要有自己的私有数据。 私有数据还能提供另一个优势: 根据服务的具体用例选择最适合的数据库技术。 每个服务都要有自己的数据服务器吗? 不一定。每个服务需要自己的数据库,但这些数据库可共置在一台共享的数据服务器上。重点在于不应让服务知道其他服务底层数据库的存在。这样即可用一台共享数据服务器先开始开发,以后只要更改配置即可将不同服务的数据库隔离起来。 然而共享的数据服务器也可能造成一些问题。首先会形成单点故障,进而导致一大批服务同时故障,这一点绝不能掉以轻心。其次很可能因为一个服务占用太多资源而无意中对其他服务造成影响。 确定服务的边界 这个问题很复杂。每个服务应该是一种能提供某些业务能力的自治单位。 服务应当弱耦合在一起,对其他服务的依赖应尽可能低。一个服务与其他服务的任何通信都应通过公开暴露的接口 (API、事件等) 实现,这些接口需要妥善设计以隐藏内部细节。 服务应具备高内聚力。密切相关的多个功能应尽量包含在同一个服务中,这样可将服务之间的干扰降至最低。 服务应包含单一的界限上下文。界限上下文 (Bounded context) 可将某一领域的内部细节,包括该领域特定的模块封装在一起。 ...
Spring Boot + PostgreSQL 持久层框架
主流持久层框架 Spring Data JPA + Hibernate Spring Boot 默认推荐,依赖 spring-boot-starter-data-jpa ORM 框架,用注解映射对象和表 自动生成 SQL,支持 JPQL / Criteria API 适合领域模型复杂的场景 MyBatis / MyBatis-Plus 半自动 ORM,SQL 写在 XML 或注解中 对 SQL 控制力强,适合复杂查询 MyBatis-Plus 提供代码生成、分页等增强功能 JOOQ 类型安全的 SQL DSL,SQL 风格写法 编译期检查 SQL,适合喜欢写 SQL 的团队 商业数据库需付费版 Spring Data JDBC 比 JPA 更轻量,无懒加载、缓存等复杂特性 SQL 更透明,适合简单 CRUD R2DBC(响应式) 非阻塞响应式驱动,配合 WebFlux 使用 依赖 spring-boot-starter-data-r2dbc 选型建议 场景 推荐 快速开发,标准 CRUD Spring Data JPA 复杂 SQL,精细控制 MyBatis-Plus 强类型 SQL,重查询业务 JOOQ 响应式架构 R2DBC 连接池 Spring Boot 默认使用 HikariCP 作为连接池。 ...
JDK 17 新特性
概述 JDK 17 是 Java 的长期支持版本(LTS),于 2021 年 9 月发布。相比 JDK 16,JDK 17 引入了多项新特性和改进,同时也包含了一些孵化特性的正式毕业版本。 正式特性(JEP) JEP 306: 恢复始终严格的浮点语义 将浮点运算恢复为始终严格模式(strict),删除了 strictfp 关键字的限制效果。在历史上,Java 曾因不同硬件平台的差异引入了扩展精度模式,随着现代硬件的普及,该差异不再存在,因此恢复了一致的浮点行为。 JEP 356: 增强型伪随机数生成器 提供新的接口和实现用于伪随机数生成器(PRNG),新增了 RandomGenerator 接口和多个算法实现,如 Xoshiro256PlusPlus、L64X128MixRandom 等。 RandomGenerator generator = RandomGeneratorFactory.of("L64X128MixRandom").create(); int randomInt = generator.nextInt(100); JEP 382: 新的 macOS 渲染管道 使用 Apple Metal API 实现 Java 2D 渲染管道,替代已废弃的 OpenGL API,提升在 macOS 上的图形渲染性能。 JEP 391: macOS/AArch64 移植 将 JDK 移植到 macOS/AArch64(Apple Silicon)平台,支持原生运行于 M1 及后续芯片的 Mac 电脑。 JEP 398: 废弃 Applet API 正式废弃 Applet API,标记为 @Deprecated(forRemoval = true)。浏览器早已不再支持 Java 插件,该 API 已无实际用途。 ...
JDK 8 新特性
JDK 8 相对于 JDK 7 引入的主要新特性: Lambda 表达式 函数式接口(Functional Interface) Stream API 方法引用(Method Reference) 默认方法(Default Method) Optional 新的日期时间 API(java.time) Nashorn JavaScript 引擎 Base64 并发增强(CompletableFuture、StampedLock、LongAdder) 重复注解(Repeating Annotations) 类型注解(Type Annotations) PermGen 移除,改用 Metaspace Lambda 表达式 Lambda 来自数学中的 λ 演算(Lambda Calculus),是函数式编程的理论基础。函数式编程的核心思想是:函数是"一等公民",可以像普通值一样被传递、赋值、返回。JDK 8 引入 Lambda,本质上是给 Java 这门面向对象语言加入了函数式编程的能力。 Lambda 表达式允许将函数(即 Lambda 表达式本身)作为参数传递,简化匿名内部类的写法。 语法结构 (参数列表) -> 方法体 (参数列表):传入的参数,无参数时写空括号 () ->:箭头操作符,读作 “goes to”,分隔参数列表和方法体 方法体:要执行的逻辑 Runnable 是一个接口(java.lang.Runnable),只有一个无参方法 void run(),因此参数列表为空 ()。 // JDK 7:匿名内部类写法 Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello"); } }; // JDK 8:Lambda 写法,箭头右边等价于 run() 的方法体 Runnable r = () -> System.out.println("Hello"); 将函数作为参数传递 JDK 8 之前,Java 无法直接传递"一段逻辑",只能把逻辑包装成匿名对象再传递。Lambda 表达式让你可以直接把逻辑作为参数传入方法,这就是"将函数作为参数传递"的含义。 ...
netstat 命令
netstat 用于查看网络连接、路由表、接口统计等信息。在较新的 Linux 发行版中,推荐使用 ss 命令替代。 常用命令 # 查看所有 TCP 监听端口及对应进程 netstat -ntlp # 过滤特定端口 netstat -tulpn | grep 9100 常用选项 选项 说明 -n 以 IP 地址代替主机名显示 -t 显示 TCP 协议的连接情况 -u 显示 UDP 协议的连接情况 -l 只显示监听状态的 Socket -p 显示使用该 Socket 的程序名称和 PID -a 显示所有 Socket(包括监听和非监听) 安装 netstat 包含在 net-tools 包中: # RHEL/CentOS yum install net-tools # Debian/Ubuntu apt install net-tools
关于本站/关于我
关于本站/关于我/About 本站主要是个人使用的读书笔记,因为早期使用了 Wordpress 所以申请了域名, 转到 Github + Markdown 后也保留了公开访问, 本人使用频率比较高的技术文档都有仔细整理和验证过,希望能帮助到有需要的同学。当然…也有大量的转载未整理的内容,在不断完善整理中… 收集综合症/转载 基本上是我接触过的知识的一个集合 大部分内容是转载的 70%+ 一般第一次接触的知识会找质量比较好的或者能解决问题的文章收集进来 有一部分原因是防止源站消失 (比如…已经消失的一个站点http://www.yining.org/2010/05/04/http-get-vs-post-and-thoughts/),还有方便查找,省掉重新 Google 的过程。 有仔细读过的会修正 typo 日常遇到问题会逐渐整理,也会补充其它引用来源 有明显整理痕迹的都是仔细读过的,比如有段落标题,有代码语法高亮。 会有合并摘抄 一篇文章可能有多个引用来源, 对同一个问题也有可能有相互矛盾的来源,会补充验证后的结果。 前期不太注意知识共享协议,最近在有意识的识别有共享协议的文章并按协议转载 没有明显的知识共享协议的文章会逐渐转移到私有仓库 Obsidian + AWS S3 转载内容的 Author 会标记为 “-” 会不定期删除本人使用频率低的内容, 比如早期接触过的 ASP.net, Wordpress, PHP… 本站点对应的 GitHub 仓库: https://github.com/wiloon/blog 转载内容有侵权可以随时联系我删除 整理过的 在转载文章的基础上整理过的 记录了一部分常用的命令 平时经常使用的工具, 命令, 脚本, 使用过程中遇到问题,解决问题的过程中会频繁修改,会补充命令的示例 一般会有语法高亮 有段落标注 可能有 N 个引用来源 以 Tag(remix) 方式标记整理过的转载内容 – 逐渐整理中,要花些时间从收集的文档里捞出来。 日常记录 少量的原创文章或原创比例比较高的文档 日常遇到的问题和解决过程 以 Tag(original) 方式标记 – 逐渐整理中,要花些时间从收集的文档里捞出来。 Author 会标记为 “w1100n” Blog 实现 Markdown GitHub GitHub Action Hugo PaperMod theme Cloudflare Pages 关于我 王越 ...
Harness Engineering 与状态锚点
什么是 Harness Engineering Harness Engineering 是一种以"脚手架优先"为核心思想的软件开发方法,尤其在 AI 辅助开发场景下越来越受到关注。 “Harness”(脚手架/支撑框架)这个词借用自工程领域,在软件中指围绕核心系统搭建的一套结构化支撑层,包括: 明确的文档约定(说明系统当前状态、设计决策) 测试框架和实验入口(可快速验证变更) 状态记录文件(捕捉项目在某个时间点的快照) 接口契约和边界定义 它的目标是让代码库对人和 AI 助手都更"可理解、可修改、可安全演进"。 状态锚点是什么 状态锚点(State Anchor) 是 Harness Engineering 的核心实践之一。 它是一个显式的文档文件(通常命名为 harness-state.md、project-state.md 等),用来在某个时间点精确记录系统的当前状态,就像 Git commit 对代码的作用,它对"上下文"做了一次快照。 典型内容 一个状态锚点文件通常包含: ## 当前状态(2026-04-09) ### 已完成 - [x] 核心数据模型定义完毕 - [x] API 接口初版上线 ### 进行中 - [ ] 用户认证模块(50%) ### 已知问题 - /api/user 接口在并发场景下偶发 500 ### 下一步 - 完成认证模块 → 接入集成测试 → 上线 v0.2 状态锚点的作用 1. 消除"现在在哪"的认知负担 每次回到一个项目,最耗时的事情不是写代码,而是重建上下文:“上次做到哪了?““这个模块稳定了吗?““还有什么坑没踩完?” ...
claude
安装 Claude Code # macOS 用 brew 安装(推荐) brew install --cask claude-code # 或安装最新版 brew install --cask claude-code@latest # 用 npm 安装 npm install -g @anthropic-ai/claude-code 安装 cc-switch(多账号切换工具) brew install --cask cc-switch 安装后从 Launchpad 或用以下命令启动: open -a cc-switch 认证冲突处理 cc-switch 会设置 ANTHROPIC_AUTH_TOKEN 环境变量,若同时存在 claude /login 的 key,启动时会出现: ⚠ Auth conflict: Both a token (ANTHROPIC_AUTH_TOKEN) and an API key (/login managed key) are set. 想用 cc-switch 管理的账号: claude /logout 想用 /login 的 key: ...
grub
grub 判断是否由 grub 引导 journalctl -b | grep -i grub https://www.gnu.org/software/grub/manual/grub/grub.html#Simple-configuration GRUB 来自 GRand Unified Bootloader 的缩写。它的功能是在启动时从 BIOS 接管掌控、加载自身、加载 Linux 内核到内存,然后再把执行权交给内核。一旦内核开始掌控,GRUB 就完成了它的任务,也就不再需要了。 GRUB 支持多种 Linux 内核,并允许用户在启动时通过菜单在其中选择。 GRUB 菜单提供了一个 “救援rescue” 内核,用于故障排除或者由于某些原因导致的常规内核不能完成启动过程。 grub.cfg 文件是 GRUB 配置文件。它由 grub2-mkconfig 程序根据用户的配置使用一组主配置文件以及 grub 默认文件而生成。/boot/grub2/grub.cfg 文件在 Linux 安装时会初次生成,安装新内核时又会重新生成。 grub.cfg 文件包括了类似 Bash 脚本的代码以及一个按照安装顺序排序的已安装内核列表。 grub.cfg 的主要配置文件都在 /etc/grub.d 目录。该目录中的每个文件都包含了最终会整合到 grub.cfg 文件中的 GRUB 代码。这些配置文件的命名模式以排序方式设计,这使得最终的 grub.cfg 文件可以按正确的顺序整合而成。每个文件都有注释表明该部分的开始和结束,这些注释也是最终的 grub.cfg 文件的一部分,从而可以看出每个部分是由哪个文件生成。 grub config file path check grub version grub2-install --version GRUB1.配置文件: /boot/grub/menu.lst GRUB2.配置文件: /boot/grub/grub.cfg,/etc/grub.d/下是生成配置文件的模板,/etc/default/grub是生成配置文件的参数 If you change this file, run ‘update-grub’ afterward to update ...
排序算法, Sorting Algorithm
常见排序算法: 冒泡排序 选择排序 插入排序 希尔排序 快速排序 归并排序 堆排序 计数排序 桶排序 基数排序 推荐学习顺序: 冒泡 → 选择 → 插入 → 快速 → 归并 → 堆 冒泡排序 Bubble Sort 相邻元素两两比较,大的往后"冒泡",每轮将最大值沉到末尾。 时间复杂度:O(n²) 空间复杂度:O(1) 稳定排序 def bubble_sort(arr): n = len(arr) for i in range(n): for j in range(n - i - 1): if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j] return arr # 测试 arr = [64, 34, 25, 12, 22, 11, 90] print("排序前:", arr) bubble_sort(arr) print("排序后:", arr) 执行过程(以 [5, 3, 1] 为例): 第1轮: j=0: 5 > 3,交换 → [3, 5, 1] j=1: 5 > 1,交换 → [3, 1, 5] ← 最大值 5 沉底 第2轮: j=0: 3 > 1,交换 → [1, 3, 5] ← 次大值 3 到位 第3轮:只剩1个,已有序 外层 i 控制轮数,每轮确定一个最大值的位置 内层 n - i - 1 让已排好的尾部不再参与比较 a, b = b, a 是 Python 交换变量的惯用写法 选择排序 Selection Sort 每轮从未排序部分找最小值,放到已排序部分的末尾。 ...