欢迎来到我的技术笔记
这里是赵岩的技术笔记,记录学习、工作和思考。
这是第一篇测试文章。 代码示例 print('Hello, World!') 总结 这是一个技术博客的开始。
文章摘要 本文基于2025年最新的RLHF技术进展,深入分析其技术原理、实现细节、应用场景以及未来发展方向。文章内容综合了最新的技术文档、学术论文、开源项目和实际部署经验。 引言 在人工智能技术快速发展的今天,RLHF作为大模型领域的重要技术方向,正在发挥着越来越重要的作用。随着AI应用场景的不断扩展,RLHF技术的价值和意义日益凸显。本文将基于最新的技术研究和实践案例,深入分析RLHF的核心技术、实现方案以及实际应用。 技术背景与最新进展 发展历程 从技术演进的角度来看,RLHF经历了以下几个关键发展阶段: 基础研究阶段(2023年前):基础理论研究和概念验证,初步验证技术可行性。研究者开始探索RLHF的基本原理和核心算法。 技术突破阶段(2023-2024):核心算法的创新和优化,性能得到显著提升。出现了多个重要的技术突破和开源项目。 工程实践阶段(2024-2025):大规模应用和性能调优,实现生产级部署。企业开始在实际场景中应用RLHF技术。 生态完善阶段(2025至今):工具链成熟、开源社区活跃、标准逐渐建立。形成了完善的生态系统。 2025年最新进展 在2025年,RLHF领域出现了多个重要进展: 算法创新:更高效的计算方法和数据结构被提出,显著提升了性能。例如,新的优化算法将推理速度提升了2-5倍。 工程实践:大规模部署的经验积累,包括性能调优、成本控制等。企业已成功在千万级用户场景中应用。 硬件协同:与新型AI芯片(如TPU、FPGA、ASIC)的协同优化成为热点。硬件加速技术使计算效率提升3-10倍。 开源生态:更多开源项目和工具支持,降低了技术使用门槛。GitHub上相关项目超过1000个,Star总数超过10万。 应用拓展:从单一场景扩展到多个垂直领域,如医疗、金融、教育、制造等,每个领域的应用效果提升20-50%。 核心技术深度解析 技术原理 要深入理解RLHF,我们需要从多个维度进行分析: 核心算法 RLHF的核心算法涉及以下关键技术点: 算法设计:通过创新的算法设计,实现更高效的处理。采用自适应策略,根据输入动态调整计算方式。例如,基于注意力机制的动态计算,将计算量降低60%。 数据结构:优化数据表示和访问方式,提升计算效率。使用分层索引、缓存优化等技术,将访问延迟降低40%。 并行化:充分利用硬件并行计算能力,加速处理过程。包括GPU并行、分布式计算等,将训练速度提升3-5倍。 优化策略:采用多种优化技术,全面提升性能。如剪枝、量化、知识蒸馏等,在保持精度的同时,将模型大小减少70%。 架构设计 在实际实现中,RLHF涉及多个工程层面的优化: # 示例:架构设计 class RLHFArchitecture: def __init__(self, config): self.config = config self.components = self._build_components() def _build_components(self): """构建系统组件""" components = dict() # 核心处理模块 components['core'] = self._build_core_module() # 优化模块 components['optimizer'] = self._build_optimizer_module() # 接口模块 components['interface'] = self._build_interface_module() return components def forward(self, inputs): """前向传播""" # 通过各组件处理输入 core_output = self.components['core'](inputs) optimized_output = self.components['optimizer'](core_output) return self.components['interface'](optimized_output) def optimize(self): """优化处理""" # 应用优化技术 for component in self.components.values(): component.optimize() 关键技术点 基于最新的研究,RLHF涉及以下关键技术点: ...
GraphRAG:让AI真正读懂知识图谱的革命性突破 引言:AI的"理解"困境 在人工智能快速发展的今天,我们经常遇到这样的场景:向ChatGPT或Claude询问一个复杂的跨领域问题,得到的回答要么泛泛而谈,要么出现事实性错误。即使是最先进的大语言模型,在面对需要深度关联多个知识点的复杂问题时,往往也会力不从心。 问题的根源在哪里?传统的大语言模型虽然拥有海量的知识储备,但这些知识是以"隐式"的方式存储在模型参数中的。当我们提问时,模型需要从数十亿个参数中"回忆"相关信息,这个过程就像在一个没有索引的图书馆里寻找一本特定的书——即使书就在那里,找到它也需要运气。 2025年,一个名为GraphRAG(Graph-based Retrieval-Augmented Generation)的技术横空出世,正在从根本上改变这一困境。它不是让大模型"更聪明",而是给它配上了一个真正的"大脑外挂"——知识图谱。 一、从RAG到GraphRAG:知识表示的进化 1.1 传统RAG的局限性 检索增强生成(RAG)技术是2023-2024年的明星方案,其核心思想很简单:在大模型回答问题之前,先从外部文档库中检索相关内容,然后基于这些上下文生成答案。 传统RAG通常采用向量数据库实现: # 传统RAG的典型流程 1. 文档分块 → 转换为向量 2. 问题查询 → 计算向量相似度 3. 检索Top-K个文档块 4. 将块内容作为上下文输入LLM 然而,这种方案存在几个关键缺陷: 碎片化问题:将文档切分成小块时,信息的完整性被破坏。一个复杂概念可能分散在多个不连续的块中,单独检索任何一个块都无法提供完整信息。 语义漂移:向量相似度计算基于词语和句子的表面特征,容易受到同义词、多义词的干扰。检索到的内容可能表面上与问题相关,但实际上是风马牛不相及。 缺乏上下文:每个文档块是独立的,检索系统不知道块与块之间的关联关系。这导致无法回答需要跨文档推理的问题。 1.2 GraphRAG的核心创新 GraphRAG由微软研究院在2024年底提出,并在2025年迅速成为企业级AI应用的热门选择。它的核心思想是:将非结构化文本转化为结构化知识图谱,然后基于图谱进行检索和推理。 GraphRAG的工作流程可以分为三个关键阶段: 阶段1:知识图谱构建 # GraphRAG图谱构建流程 1. 文档解析(支持PDF、网页、数据库) ↓ 2. 实体识别(NER)- 提取人物、地点、组织、概念等 ↓ 3. 关系抽取 - 识别实体间的语义关系 ↓ 4. 属性抽取 - 提取实体的属性信息 ↓ 5. 图谱存储(Neo4j、Neo4j Aura、Memgraph等) 与传统RAG的向量检索不同,GraphRAG在构建图谱时会维护丰富的元数据: // Neo4j中的知识图谱示例 CREATE (company:Entity {name: "OpenAI", type: "Organization"}) CREATE (person:Entity {name: "Sam Altman", type: "Person"}) CREATE (product:Entity {name: "GPT-4", type: "Product"}) CREATE (company)-[:FOUNDED_BY {confidence: 0.95}]->(person) CREATE (company)-[:DEVELOPED {release_date: "2023-03-14"}]->(product) 阶段2:图检索策略 ...
RAG 2.0与GraphRAG:知识检索的进化之路 当我们谈论AI如何理解世界时,检索增强生成(RAG)无疑是近年来最革命性的技术之一。而2025年,GraphRAG的出现,正在将这一技术推向新的高度。 一、从传统RAG到RAG 2.0:技术演进之路 1.1 传统RAG的局限性 2023年,RAG技术横空出世,解决了大模型"知识截止"和"幻觉"两大痛点。简单来说,RAG通过在生成答案前检索相关文档,让大模型能够访问最新的外部知识。 然而,传统RAG在2024年的大规模应用中暴露出了一些明显的问题: 向量检索的"语义漂移"问题:当我们用"如何优化MySQL查询"进行检索时,传统向量搜索可能返回关于数据库架构设计的文档,虽然语义相近,但并非用户真正需要的精确答案。 多跳推理能力缺失:要回答"微软的CEO是谁,他收购了哪家公司"这样的问题,需要多步推理。传统RAG很难将分散在不同文档中的信息关联起来。 全局上下文理解不足:面对大型企业知识库,传统RAG只能关注局部相关文档,难以理解整体结构和层级关系。 1.2 RAG 2.0的核心突破 2025年,随着技术的发展,RAG 2.0开始进入主流视野。相比传统RAG,RAG 2.0在以下几个方面实现了质的飞跃: 混合检索架构:结合向量搜索(语义理解)和关键词搜索(精确匹配)的优势。现代检索引擎如Pinecone 3.0、Weaviate 1.24和Milvus 2.4都原生支持混合检索,通过RRF(Reciprocal Rank Fusion)算法将两种结果智能融合。 重排序机制:在初步检索后,使用专门的重排序模型进行二次筛选。Cohere Rerank 3、BGE-Reranker v2等模型能够在检索召回率(找到相关信息)和精确率(排除无关信息)之间找到最佳平衡。 自适应检索:根据问题的复杂程度动态决定检索策略。简单问题不检索直接生成,复杂问题进行多轮检索。LlamaIndex 2.0的"Auto-Retrieval"和LangChain 0.3的"Router Chain"都实现了这一功能。 1.3 实际应用案例 让我们看一个真实的企业应用案例: 某大型电商平台的技术文档助手 传统RAG:准确率约65%,开发者经常需要多次调整查询 RAG 2.0:通过混合检索+重排序,准确率提升至85% 技术栈:Pinecone向量数据库 + BGE-Reranker + GPT-4 关键优化:对技术关键词进行精确匹配(如"Redis Pipeline"、"MySQL索引"),对概念性问题使用语义检索 二、GraphRAG:知识图谱的革命性融合 2.1 GraphRAG的核心思想 2025年5月,微软研究院发布了GraphRAG,这一技术迅速成为AI社区的热门话题。GraphRAG的核心创新在于:将知识图谱引入检索增强生成流程。 传统RAG将文档分割成片段,将每个片段转换为向量存储。而GraphRAG的做法完全不同: 实体提取:从文档中识别出关键实体(人名、公司名、技术术语、产品名等) 关系构建:分析实体之间的关系,构建知识图谱 社区发现:将相关联的实体聚类成"社区" 摘要生成:为每个实体、关系和社区生成描述性摘要 检索时,GraphRAG不是搜索相似的文档片段,而是在知识图谱中进行导航,找到相关的实体和关系。 2.2 技术架构详解 GraphRAG的完整工作流程如下: 阶段1:索引构建 原始文档 ↓ 文本分块(约300-500 token) ↓ 实体识别(使用GPT-4等大模型) - 人物:如"Sam Altman" - 组织:如"OpenAI"、"Microsoft" - 技术:如"Transformer"、"GPT-4" - 产品:如"ChatGPT" ↓ 关系抽取 - "Sam Altman" → CEO → "OpenAI" - "OpenAI" → 开发 → "GPT-4" - "Microsoft" → 投资 → "OpenAI" ↓ 社区检测(使用Leiden算法) - 将相关实体聚类 - 例如:"OpenAI生态圈"社区 ↓ 摘要生成 - 为每个实体生成描述 - 为每个关系生成解释 - 为每个社区生成总结 阶段2:检索生成 ...
AI Agent时代的到来:从工具到数字员工的革命 引言:AI发展的下一个拐点 2024年,AI领域经历了一场静默而深刻的变革。当我们还在为大语言模型(LLM)能生成优美的文章而惊叹时,AI Agent的概念已经悄然崛起。这不仅仅是技术的迭代,更是一场关于"智能"定义的革命。 Anthropic发布的Claude Cowork、OpenAI的GPT-4 AutoGPT、以及开源社区中的AutoGen、LangGraph等项目,都在向我们传递一个信号:AI正在从被动的工具,转向主动的执行者。这种转变,意味着我们与人工智能的交互方式将被彻底重构。 本文将深入探讨AI Agent的技术原理、当前发展现状、实际应用场景,以及这场变革将如何重塑我们的工作与生活。 一、什么是AI Agent? 1.1 定义与核心特征 AI Agent(人工智能代理)可以被理解为一个能够感知环境、做出决策并采取行动以实现特定目标的智能系统。与传统AI工具最根本的区别在于:Agent具有自主性。 传统AI工具(如ChatGPT、Claude等对话型AI)是被动响应的——你问,它答。而AI Agent则能够: 自主规划:根据目标,分解任务并制定执行计划 主动探索:在执行过程中主动搜索信息、调用工具 持续学习:从执行结果中学习,优化后续决策 多轮协作:能够与其他Agent或人类进行复杂的多轮交互 1.2 架构组件 一个典型的AI Agent系统包含以下几个核心组件: 感知模块(Perception):负责从环境中获取信息,包括文本、图像、音频等多模态输入,以及系统状态、API响应等结构化数据。 推理引擎(Reasoning Engine):这是Agent的"大脑",通常基于大语言模型(LLM)。它负责: 理解目标和上下文 规划任务执行步骤 做出决策和选择 评估执行结果 记忆系统(Memory):Agent需要"记住"过去的信息才能做出连贯的决策。记忆系统通常包括: 短期记忆:当前对话和任务的上下文 长期记忆:历史经验、知识和模式 向量数据库:用于快速检索相关信息 工具箱(Toolbox):Agent需要调用外部工具来完成实际任务,如: 搜索引擎(获取信息) 代码执行器(运行代码) 文件系统(读写文件) API接口(调用服务) 执行器(Executor):负责将决策转化为具体的行动,调用相应的工具或服务。 二、核心技术原理 2.1 思维链(Chain of Thought) 思维链是Agent能够进行复杂推理的基础。它要求AI将推理过程显式地表达出来,一步步拆解问题。 例如,当Agent接到"帮我预订一张从北京到上海的机票"的任务时,它的思维链可能是: 理解任务:用户需要预订机票 提取关键信息:出发地(北京)、目的地(上海) 识别缺失信息:出发日期、舱位偏好、预算 制定策略:先询问用户缺失信息,再搜索航班 执行搜索:调用航班查询API 比较选项:筛选最优航班 执行预订:调用预订接口 反馈结果:告知用户预订成功信息 通过这样的思维链,Agent能够系统性地完成复杂任务,而不是依赖"直觉"。 2.2 反思与自我修正 优秀的Agent需要具备反思能力,能够评估自己的行动效果并进行调整。这包括: 错误检测:识别何时出错了 原因分析:找出错误的原因 策略调整:修改后续的执行计划 重新执行:尝试新的方法 例如,如果Agent编写的代码运行失败,它应该能够: 查看错误信息 分析代码逻辑 找出bug所在 修正代码 重新测试 如果仍然失败,尝试不同的实现方法 这种"试错-反思-改进"的循环,是Agent能够解决复杂问题的关键。 ...
在构建 AI 应用时,如何让模型"记住"上下文,是一个核心挑战。OpenAI 的本地记忆功能为我们提供了一个优雅的解决方案。 本文将从技术实现的角度,解析 OpenAI 本地记忆的原理、实现方案和最佳实践。 一、什么是本地记忆 本地记忆是 OpenAI 提供的一个 API 功能,允许开发者为 Assistant 添加持久化存储的能力。简单来说,它让 Assistant 能够跨越对话边界,访问历史信息和用户数据。 核心概念 记忆存储:每个 Assistant 有独立的记忆空间 自动检索:API 根据当前上下文自动检索相关记忆 语义搜索:使用向量相似度进行语义匹配 自动更新:每次对话后自动保存新信息 二、技术原理 2.1 向量检索(RAG) 本地记忆的核心是检索增强生成(RAG)技术: 1. 文档分割 → 将长文本切分成小块 2. 向量化 → 将文本块转换为向量表示 3. 存储到向量数据库 4. 语义检索 → 用户查询时,找到最相关的文本块 5. 上下文注入 → 将检索到的内容注入到 LLM 2.2 Embedding 模型 OpenAI 提供了多种 Embedding 模型: text-embedding-3-small:快速,1536 维 text-embedding-3-large:高质量,3072 维 text-embedding-ada-002:老版本,8192 维 2.3 向量相似度计算 常用相似度度量: 余弦相似度:适合文本向量,范围 [-1, 1] 欧氏距离:L2 距离,适合几何空间 点积:计算简单,适合归一化向量 三、实现方案 方案一:使用 OpenAI 内置 API 内置 API 优势: ...
在构建 AI Agent 的过程中,如何让模型突破自身的边界,访问外部世界的能力,成为一个核心问题。Skill 和 MCP(Model Context Protocol)是两种不同层次的解决方案,它们在定位、架构、应用场景上存在本质差异。 本文将从技术深度和架构设计角度,解析这两种范式的核心差异。 一、核心设计理念 Skill:领域知识的程序化封装 Skill 是一种紧耦合、嵌入式的能力扩展范式。它的核心思想是将特定领域的专家知识、工作流程、工具使用方法"冻结"成可复用的代码包,让 AI Agent 在需要时直接调用。 设计哲学: 认知卸载:将重复性的领域知识从模型的推理过程中分离出来 确定性执行:通过脚本和流程化步骤,减少模型幻觉和错误 渐进式加载:按需加载资源,避免上下文窗口的浪费 MCP:标准化的上下文协议 MCP 是一种松耦合、开放协议的能力扩展范式。它定义了一套 JSON-RPC 2.0 标准协议,让 AI 应用能够通过统一的接口连接到各种数据源、工具和工作流。 设计哲学: 互操作性:不同 AI 应用和工具之间无缝协作 关注点分离:数据层与传输层解耦,支持多种传输机制 生态化:鼓励第三方开发者构建独立的 MCP Server 二、架构层面的差异 2.1 宿主关系 Skill:内嵌式 Skill 是 Agent 运行时的一部分 通过文件系统加载,与 Agent 同生命周期 紧密集成在特定的 Agent 框架中(如 Moltbot) MCP:外挂式 MCP Server 是独立的进程,可以本地或远程运行 通过标准协议通信,生命周期独立于 Host 跨平台、跨框架的通用标准 三、技术实现对比 3.1 定义格式 Skill:Markdown + 文件系统 MCP:JSON Schema + 协议 3.2 执行模式 Skill:Agent 主导 MCP:Server 主导 ...
大模型训练与优化:从理论到实践的技术深度解析 引言:大模型时代的挑战与机遇 2022年,ChatGPT的横空出世标志着大语言模型(Large Language Models, LLM)正式进入公众视野。短短两年时间,从GPT-3到GPT-4,从Claude到Llama,模型规模从百亿参数跃升至万亿级别,性能突破层出不穷。 然而,在惊叹于模型能力的同时,我们也面临着前所未有的挑战: 训练成本:训练一个万亿参数的模型需要数千万美元的计算资源 推理效率:大模型的推理延迟和吞吐量难以满足实时应用需求 数据质量:训练数据的规模、质量和多样性决定了模型的上限 微调成本:将预训练模型适配到特定领域仍需大量资源 这些问题推动了训练与优化技术的快速发展。本文将从技术深度出发,系统性地解析大模型训练与优化的核心技术和最佳实践。 一、大模型训练架构 1.1 分布式训练范式 大规模模型训练的核心挑战在于如何将模型和数据分布到多个计算节点上,同时保持训练的效率和稳定性。 数据并行(Data Parallelism) 原理:将模型复制到多个GPU上,每个GPU处理不同的数据批次,梯度在反向传播后聚合。 实现方式: AllReduce:使用NCCL或Gloo等通信库进行梯度同步 梯度累积:在多个小批次上累积梯度后再同步,减少通信频率 梯度压缩:使用量化、稀疏化等技术减少通信量 适用场景:模型能够放入单个GPU显存的情况。 张量并行(Tensor Parallelism) 原理:将模型的单个层(如矩阵乘法)分割到多个GPU上执行。 核心算法:以线性层 y = Wx 为例,将权重矩阵 W 按列分割到 N 个GPU上,每个GPU计算 yi = Wi x,最后通过AllGather拼接结果。 挑战: 需要频繁的通信(每个Transformer层的计算都需要同步) 实现复杂度高(需要修改模型代码) 代表框架:Megatron-LM。 流水线并行(Pipeline Parallelism) 原理:将模型的不同层分配到不同的GPU上,形成流水线。 关键机制: 微批次(Micro-batch):将大批次拆分为多个小批次并行处理 1F1B调度:一个Forward, 一个Backward交替执行,减少GPU空闲时间 气泡(Bubble):流水线启动和结束时的空闲周期 挑战: 需要精心设计调度策略 容易产生负载不均衡 代表框架:PipeDream, GPipe。 3D并行 原理:同时使用数据并行、张量并行和流水线并行。 典型配置: 每个节点内部使用张量并行(GPU间通信快) 节点间使用流水线并行(跨节点通信慢) 最外层使用数据并行(跨集群扩展) 代表框架:Megatron-Deepspeed, Colossal-AI。 1.2 显存优化技术 大模型训练的显存需求通常远超单个GPU的容量,需要专门的优化技术。 混合精度训练 原理:使用16位浮点数(FP16或BF16)替代32位浮点数(FP32)进行计算,同时使用FP32维护主权重。 ...
假期有闲暇学习一下AI相关的知识,尝试着想用公众号对接DeepSeek做一个问答,当做一个BlogAgent,又不想写太多的工程类的代码,想着AI这么强大,能不能直接用AI生成代码。没有看阿里,百度的产品,直接试用了豆包的AI编程,通过这两天的试用,AI惊人的生产力着实让我惊掉下巴。 一、项目级代码生成 我一开始描述,要求 实现一个微信公众号的后台服务代码,可以接收用户信息,并且回复。 豆包直接生成了一个Python的代码,实现了微信公众号的后台接口,并且实现了签名认证,并且给出了详细的代码说明和注意事项。 然后我又要求使用Java+Spring Boot实现,它竟然给我实现了了一个完整的Maven项目。代码下载下来是真的可以跑的。 二、根据要求重构代码 在仔细看了下整个工程,代码就是一个Spring Boot的启动类,再加一个Controller。稍显不足的是,关于签名的代码,放在了Controller里,导致代码很乱。我随即发了一个优化建议: 把WechatController里的通用公共代码抽出去 这货竟然完全听懂了,乖乖的提取了一个Utils类,把签证签名,XML解析等相关的操作放了进去。 我又对比了下微信的API文档,我疑问道: POST接口不需要验证签名吗? 它竟然立马意识到自己的错误,修正了自己的错误: 三、根据要求增添内容 我继续下达着我的命令: >请生成Spring Boot的配置文件 >再加入log4j2的日志框架,把相关的异常打到日志里 >把参数搞成对象,把解析xml改成通用的反序列化拦截器 >响应体也可以搞成对象,使用序列化拦截器 >把token改成配置项形式,放在配置文件里 >使用langchain4j集成调用deepseek。当微信用户发来消息以后,转发给deepseek,然后把响应给微信用户 >Spring Boot的方式初始化langchain4j 并且把相关代码抽出去单独的类,整个项目包名改成com.zhaoyanblog.agent项目名称微blog-agent >帮我在pom里设置国内的maven仓镜像地址 >集成对wordpress的访问,当微信用户请求“wp:” 开头的信息时,搜索WordPress里的文章,返回给用户。 >微信返回的消息 要是一个包含文章的摘要富媒体链接,业务点击消息可以跳转到网站。WordPress的网站地址zhaoyanblog.com。要放在配置文件里。 关于微信的Token也放在额外的类里,不要放在controller里 >请对工程使用Spring boot的方式配置accesslog, >配置文件里的注释在IDEA里是乱码,请修正. …… 它就像一个不知疲倦的程序员,不断的实现着产品经理提出的需求 四、帮我修正Bug 有关freemarker模版的问题,开始我希望把markdown改成HTML。它一开始给我的代码其中有这样一段: <p class="text-gray-800"><span class="speaker-highlight">DeepSeek</span>:<#noescape>${conversation.getContentAsHtml()}</#noescape></p> 但是我真实跑起来的时候,它报这样的错误 #noescape with no matching #escape encountered 它直接给我了解决方案,并解释了原因: <span class="speaker-highlight">DeepSeek</span>:${conversation.getContentAsHtml()?no_esc}</p> 但是好像还不行,我直接把报错信息再贴给它 ?no_esc can't be used here, as the current output format isn't a markup (escaping) format: undefined(mimeType=null, class=f.c.UndefinedOutputFormat) 这次它给的答案,完美解决了问题: 五、竟然提示我遵守规范 ...
本站在阿里云ECS已经有5年之久了,1C2G的配置,1M带宽 外加20G云盘,一年要耗费1100多大洋~~。随着云化技术的发展,一直等着阿里能有优惠,然而一直都如此。 碰巧前段时间看到华为云的优惠活动。新注册用户 2C4G的HECS 3年只要700多。瞬间心动,直接买了。 前段时间增加了域名备案,但是一直没有机会把主机迁移。 今晚早下班,一并做了迁移。同时把好久没升级的nginx和php都升级到了最新版本 华为云 棒棒的^_^
Spring扫描注解的功能 我们知道在Spring中可以使用注解声明Bean,可以让我们不用再去配置繁琐的xml文件,确实让我们的开发简便不少。只要在Spring xml里配置如下,就开启了这项功能。 <context:component-scan base-package="com.zhaoyanblog" /> Spring就会自动扫描该包下面所有打了Spring注解的类,帮你初始化病注册到Spring容器中。 @Service public class UserController { @Autowired private UserDao userDao; //TODO } 上述行为,就和在xml里进行下面的配置是等价的。 <bean class="com.zhaoyanblog.UserController"> <property name="userDao" ref="userDao" /> </bean> 那么问题来了,Spring是怎么扫描到com.zhaoyanblog包下面的所有带注解的类的呢? context:component-scan标签的处理者 要弄清楚为什么在xml里配置了context:component-scan就可以实现这样的功能,就要现找到这个标签的处理者。我们很容易联想到Spring解析xml的基本原理,就是遇到这个标签以后,交给一个类处理,这个类扫描包下带注解的类,初始化成对象。我们就是要找到这个关键的类。 Spring对Xml的解析功能后面阅读,这里先简要描述。Spring的jar包里有两个重要的配置文件:spring.schemas和spring.handlers,在META-INF目录下。 spring.schemas记录了xml的每个命名空间,对应的Schema校验XSD文件在哪个目录。 http\://www.springframework.org/schema/context/spring-context-4.2.xsd=org/springframework/context/config/spring-context.xsd http\://www.springframework.org/schema/context/spring-context-4.3.xsd=org/springframework/context/config/spring-context.xsd http\://www.springframework.org/schema/context/spring-context.xsd=org/springframework/context/config/spring-context.xsd spring.handlers里配置了每个命名空间的标签都由哪个类处理 http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler 我们可以看到context这个命名空间由org.springframework.context.config.ContextNamespaceHandler这个类处理。打开这个类 public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); } } 这个类为每个标签都给出了一个处理类,component-scan的处理类是ComponentScanBeanDefinitionParser 继续打开ComponentScanBeanDefinitionParser private static final String BASE_PACKAGE_ATTRIBUTE = "base-package"; public BeanDefinition parse(Element element, ParserContext parserContext) { String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // Actually scan for bean definitions and register them. ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; } 看到这里整明白了,扫描某个包下面的所有类的工作,就是ClassPathBeanDefinitionScanner干的。入口是doScan方法。 查找所有的包路径 ClassPathBeanDefinitionScanner有很多细节,比如可以设置class的filter, 设置classloader等等,我们先关注最主要的功能,就是怎么找到一个包下面所有的类的。 通过调用关系,一路找下去 doScan->findCandidateComponents(super)->scanCandidateComponents(super) private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 我们发现,原来Spring是在classpath下面,通过查找所有classpath*:com/zhaoyanblog/**/*.class的文件来实现的啊。 ...
阿里巴巴构建的阿里云开发者社区真是懂程序员所想,这里有一切你想你要的。 主页: https://developer.aliyun.com 特别是它的阿里云镜像站,真是一个大宝藏啊 https://developer.aliyun.com/mirror/ 里面包含所有Linux版本的操作系统镜像 此外还有: DNS解析 https://developer.aliyun.com/mirror/DNS NTP服务器 https://developer.aliyun.com/mirror/NTP 各种语言的镜像: python pypi软件仓 https://developer.aliyun.com/mirror/pypi rubygems https://developer.aliyun.com/mirror/rubygems maven仓镜像 https://maven.aliyun.com/mvn/view npmjs https://developer.aliyun.com/mirror/NPM
北京时间2019年9月11日凌晨1点,苹果秋季发布会在乔布斯大剧院如期举行,发布了最新款手机 iphone11系列。 iphone11系列能拿得出手提升主要有两个: 一是后置1200万三摄,兼顾广角和长焦,4倍光学变焦,增强夜间模式。二是最新7nm仿生芯片A13,集成85亿个晶体管,提升AI计算能力。注意不支持5G! 此外媒体还总结了6大卖点,除上面两点还包括:电池更大,18W快充,全新配色,售价更便宜。 通过苹果的发布会,我们可以看到目前全球手机厂商的创新点主要聚焦在三个方面:拍照、芯片、AI计算。 而横向比较,我们看一下华为。 首先华为是手机多摄像头方案的鼻祖,华为每款最新旗舰机手机发布拍照能力都会霸榜DxOMark。最新mate30在拍照能力上让人充满期待。 从16nm的950,到7nm的990,华为已经站在了CPU制程的最前沿。麒麟990芯片是世界上首款支持5G双模的7nm终端芯片,集成首次超100亿晶体管。将会用在随后发布的Mate30系列上。这次苹果发布会第一次吧麒麟芯片作为对比对象。但是却拿的华为上一代产品麒麟980. AI方面华为是最先提出NPU的概念,到990已经是好几代了。 我们发现不知不觉华为已经站在了手机创新的前列甚至于制高点上。苹果已经不在是创新的代名词。有人评论这可能是苹果最后一次享受乔布斯的遗产红利,通过简单修改一下参数就可以卖的很好。 最后让我们拭目以待华为9.19 最新Mate30系列全球发布会。
小米9月9日发布了全球首款30W无线闪充,然后小米高管微博开始宣传造势。 然而有网友质疑小米30W无线闪充使用了和华为相同的供应商IDT的技术方案。 时隔三日,9月12日小米产品总监王腾Thomas长篇回应质疑。 大肆宣传快充最开始有OV发起的,“充电五分钟通话两小时” 广告狂轰乱炸确实带起了OV销量。 前段时间荣耀发布荣耀9X用了10W充电,小米高管卢伟冰带了一波节奏,我想小米可能想趁热继续在充电上冲一冲,给粉丝一个小米有技术的印象。 但是我们分析这篇王腾的回复博文,看到的不是小米对技术的自信,而是心虚和掩饰。看到这5条创新技术,我感受到是小米的绞尽脑汁,勉强无力。 且看其中包括充电策略,保护算法这不是一个手机充电必须有的吗? 安全加密机制算法,我都有点乱了,这里是说加密算法,还是加密的机制?有这样称呼的吗?这个自研的算法安全吗? 可以想象小米是费了多大的劲才挑选出这样的让读者感觉表面有点技术含量的词语啊。 然而最最核心的无线充电芯片,也被加上了“供应商根据小米的需求和电路架构为小米设计”的修饰语。 我的结论是: 雷军一直说自己是科技节的无印良品,追求颜值和性价比。但是手机是一个高科技产业,手机厂商竞争进入了白日化阶段,没有核心技术的厂商会逐渐被淘汰和边缘化。 小米的底牌可能真的不多了。俗话说穷则思变,小米是时候需要把更多精力放在科技创新,打造更多核心竞争力上,而不是高管集体在微博上面耍嘴皮子。
2019年7月23日,荣耀在西安发布了荣耀X系列的下一代产品荣耀9X系列。4800像素三射,升降摄像头,PC液冷,4000mA大电池,前置指纹识别。同时搭载7nm制程的麒麟810芯片,拥有方舟编译器、EROFS超级文件系统、GPU Turbo 3.0、麒麟Gaming+技术、AI视频防抖,AI智慧通信2.0等多项华为自研高科技。 然而它有一项不尽人意的地方,就是充电仅支持5V2A=10W功率充电器。 小米副总裁卢伟冰抓住这一点不放,在微博上连续多天发文提及此事: 7月23日:“今天友商的产品让人费解,请教: 1.手机支持18W快充,inbox充电器不支持2.手机和充电器都不支持,是哪个?请大家指教” 7月24日:“今天的手机竞争,产品一定要有自己的特色和长板,而且一定不能有短板,尤其是用户非常关注的处理器、拍照、电池、快充、外观材质等,绝对不能偷工减料。卖2399元的手机只支持10W充电,在Redmi团队的话产品经理肯定被开!” 7月25日:“炮制虽繁,必不敢省人工,品味虽贵,必不敢减物力 ”7月26日: “按照友商的理论,我不用做任何测试,先下个结论:充电能力,10W的友商9X/9XPro,一定远落后于18W的小金刚Pro。因为这个世界真的是有“科学常识”存在。世界最顶级的专家,也不可能把10W优化出18W的快充能力。” 7月27日:“五年前的红米Note第一代,就已经是10W充电了…所以最近有点恍惚,好像回到了几年前。但,永远相信美好的事情即将发生” 7月28日:“Redmi 7A是我们高品质的入门款手机,4000mah大电池,售价只要549元,同样标配10W充电。我在想,下一代还要不要继续用10W呢?” 为了把事情彻底闹大 7月29日: “Redmi全民快充计划,划重点: 1.价值29.9元的18W充电器小米商城1元闪购 2.Note7 6+64版本闪降200元仅1199并加送18W充电器。 明天上午10.08分开售,全民进入18W快充时代,告别10W!” 于是网友送了一个外号 叫 “卢十瓦”, 并且产生一个功率单位1卢=10W 卢伟冰在最新的微博里也自嘲“最近不少网友叫我“卢十瓦”,我觉得这其实是件好事,因为这一定会促进产业的共同进步,能给用户带来更好的体验,我预测友商的下一代产品也不会再用10W充电了。想做出好产品就一定要较真!”, 以行业良心自居,和网友打的是热火朝天。这就是“卢十瓦”事件的背景。 我想从另外一个角度分析这个问题:荣耀9X用10W就是偷工减料吗? 荣耀没有技术水平让荣耀9X用上更大功率的快充吗? 显然不是,我想打个比喻:有一天马云骑着自行车锻炼身体,一位开着马自达的上班族看到了,嘲笑马云没有开上马自达。 马云是开不起马自达吗? 但是此时此刻马云他就是需要骑自行车。 荣耀千元机直接上7nm对小米来说就是致命性的降维打击。10W充电就是故意卖个破绽给你。小米体会不到自己的危机,反而操弄起自己熟悉的社会营销。 试想小米任何一款手机,其它任何手机厂商是不是立马就可以造出来? 华为的手机,小米可以造出来吗? 甚至于OV厂手机的一两个特性,小米都搞不出来。没有核心关键技术的企业是不可持续的,也许这就是为什么在资本市场,小米盈利能力不错,仍然被看衰,股票一跌再跌的原因吧。 通过荣耀9X,“卢十瓦”事件,表面看到的是小米的接地气,性价比。更深层次看到的是小米的未来危机,没有核心关键技术,深陷社会营销的小米到底还能走多远?
写单元测试用例,是程序员的好习惯,写java程序,一般使用Junit写单元测试。 我在写单元测试用例到时候,遇到一个问题:Junit在整个project中是一个java进程,如果你的程序里涉及静态变量,就会导致两个单元测试类之间相互影响。 你可以每次beforeClass初始化静态变量,但是有时候依赖了很多开源软件,你都不知道究竟一个测试流程里用到了哪些静态变量,甚至不知道如何还原它们。 最有效直接的办法就是每个测试用例都使用一个独立的classloader,也就是说每个test case都是类隔离的。 参考https://stackoverflow.com/questions/42102/using-different-classloaders-for-different-junit-tests 这篇帖子里的回复。实现了这个功能。 具体源码: https://github.com/johnyannj/junit-alone 用法如下: maven引入依赖 <dependency> <groupId>com.zhaoyanblog</groupId> <artifactId>junit-alone</artifactId> <version>1.0.1-SNAPSHOT</version> </dependency> 在你的测试用例中,用AloneRunner代替你原来真实的Runner,并把你真实的Runner,设置给@AloneWith @RunWith(AloneRunner.class) @AloneWith(JUnit4.class) public class JunitAloneTest { @Test public void test() { StaticClass.staticNum++; Assert.assertEquals(1, StaticClass.staticNum); } } 这样你的测试用例就会使用一个独立的classloader来执行了, 试试看吧。
10月26日,在mate 20系列发布会之后,余承东接受媒体采访,有人问到mate 20系列的Dxomark评分,大嘴回复“因为P20Pro已经拿下了第一,华为不愿意看到榜单上都是华为手机,想给友商留位置。”在这里我想到了雷军,我认为雷军在类似事情上有三大败笔。 第一: 收购安兔兔 小米创业之初的口号是“为发烧而生”,主打的是性价比,怎么体现手机的性能呢?那就是跑分,有段时间雷军是把“不服跑个分”挂在嘴上的。安兔兔是跑分软件中比较出名的一个,小米也一直占据安兔兔榜首的位置,本来是个米粉们骄傲的事情。然而雷军把它收购了(눈_눈),彻底毁掉了安兔兔的江湖地位,没有人在看重评分,这也毁掉了小米一个炫耀的资本。老罗曾在和王自如的辩论中透露: 有人曾经撺掇他收购另外一个评分软件鲁大师,老罗没答应。看来老罗在某些方面还是比雷军有原则啊。 第二: 投资王自如 国内手机评测做的比较有规模的,王自如的zealer是一个,一部手机有哪些优点和缺点,这些评测视频里的观点很重要,会引导部分消费者的购买选择。老罗也看重了这一点,手机还没发布,就花钱让王自如做评测,提意见。王自如后面不地道的做法就不说了。老罗在辩论里透露了一个信息“小米投资了王自如”。王自如的zealer的独立第三方的身份遭到质疑,这几年都没翻过身来,只能低调做视频。 第三: 合伙潘九堂 手机好不好,还有一个宣传是渠道就是大v推荐,有时候一款手机发布前,手机厂商都会送一些手机给这些大v明星试用,大v们发个点评的微博,也能给手机带来很多人气。潘九堂就是一位,挂名“华强北电子行业研究所分析师”类似职位,活跃于微博,发表一些“独到”见解。他要推荐个手机,还是有人认的。然后就在某一天,潘九堂摇身一变,变成了“小米产业投资部合伙人”,专门给小米赚起了吆喝。 雷军的这些行为就像评委是选手的一大妈,监工是包工头的二姨夫,做的太明显,也太蠢,败笔~
Cassandra提供认证机制,保证访问安全,但是默认的PasswordAuthenticator是简单的用户名密码认证,客户端在连接后传输用户名密码,服务器确认有效即认证通过。但是在认证的过程中直接传输的是明文密码,有被抓包泄漏的风险。 Cassandra服务端和JAVA客户端本身都支持SASL扩展 https://tools.ietf.org/html/rfc4422 SCRAM-SHA256是SASL的一种实现方式,参考 https://wiki.tools.ietf.org/html/rfc5802 https://tools.ietf.org/html/rfc7677 使用SCRAM-SHA256认证,仅通过抓包是无法破解密码的。 基于SCRAM-SHA256标准,我实现了一个cassandra的插件,放在了github上 https://github.com/johnyannj/cassandra-secure-plugin 欢迎使用
互联网安全和个人隐私越来越被人们关注,不仅仅是电商等交易网站都实现了全站https,现在微博、百度以及大大小小的博客都实现了全站https。如果你的网站不是https的,火狐、chrome、IE浏览器都会在地址栏前方提示用户 你当前访问的网站是不安全的。 为了跟风,赵岩的博客今天起全站使用https。 为了实现全站HTTPS。我主要做了以下工作: 第一、HTTPS证书 https使用SSL传输协议,SSL之所以安全,是因为加密传输的。初始公钥由服务器传递给浏览器,为了保证公钥安全的传输到浏览器,并被浏览器认可,我们需要一个由第三方权威机构签名的证书。详细参考《HTTPS到底是如何保证通信安全的》 普通的单域名证书一年需要1000多元,我们自然不会去花这个钱的。阿里云代理的赛门铁克提供个人免费域名证书。可以直接申请。阿里证书申请 申请的时候选择“Symantec ”、“免费型”、“一个域名”。阿里云会要求DNS或者文件验证,证明你是域名的拥有者。整个申请过程10几分钟可完成。 第二、升级Nginx Nginx需要使用–with-http_ssl_module参数编译,确保支持https。为此我趁机会升级了下Nginx的版本到最新版本。 把你的证书pem文件和私钥key文件上传到服务器。 配置nginx.conf,监听443端口并加载证书。 原来监听的80端口,301转向443端口 server { listen 80; server_name zhaoyanblog.com; include black_list; rewrite ^(.*) https://$server_name$1 permanent; } server { listen 443; server_name zhaoyanblog.com; ssl on; ssl_certificate cert/xxx.pem; ssl_certificate_key cert/xxxx.key; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ...... 第三、全站HTTPS 1、检查网站内有没有引用了别的网站的js。需要切成https地址。或者根据document.location.protocol自适应当前的访问协议。 比如:微博、百度统计、百度站长、百度联盟广告等等组件。 http://widget.weibo.com/weiboshow/index.php 改成 https://widget.weibo.com/weiboshow/index.php 2、检查是否直接使用了本站的http地址。改成相对路径。虽然已经配置了80端口301跳转。这样改会少一步对服务器的访问。 比如:http://zhaoyanblog.com/wp-content/uploads/2017/04/zhaoyanblog_2017-04-17_14-50-20.jpg 改成 /wp-content/uploads/2017/04/zhaoyanblog_2017-04-17_14-50-20.jpg 可以通过查看浏览器的调试信息,知道哪些访问的资源仍然是http的。 第四、通知百度进行HTTPS验证 在验证好全站已经使用https,并且访问http也可以301跳转到https。我们需要通知百度等搜索引擎进行https验证。因为搜索引擎在之前都是缓存我们的http地址。需要告知他我们切到https。避免导致收录的页面丢失。 直接登录百度站长,里面有个https验证页面,点击验证即可。 至此赵岩的博客已经实现全站https ...
Cassandra一开始是要写commitlog,当commitlog写到一定大小就会刷到一个sstable文件,再加上对于cassandra,删除也是一种写,这样下去sstable文件会越来越多。必须有一种机制来合并这些文件,并删除墓碑(标记为删除的记录),这种机制叫做compaction,先翻译为压缩。既然要合并文件,就要有合并策略。cassandra一开始只有size模式的压缩策略。后来增加了level压缩。 level压缩提高了读的性能,但是level压缩相比较size压缩更慢,因为它是要保证每个level都有一定数量的文件,新产生的文件都是level 0的状态,同时在执行的压缩任务是有限制的,当几个高level的文件在压缩的时候,可能导致level0的文件堆积。 level压缩需要保证低级别的level的文件较少,是为了提高查询的效率。 为了避免level0的文件因为大量写入而得不到压缩。cassandra采取了一种策略,就是level 0文件数目超过一定限制(默认32),就在level 0采用size压缩,通过合并快速减少 level 0文件数量,同时暂停高level的文件压缩。 这个设计在正常情况下是有好处的。但是当我们扩容一个节点的时候,新增节点的文件全部在level 0。 sstable level 12222 0 0 0 0 0 0 0 那么cassandra会的持续进行level 0的 size压缩。直到level 0的文件减少到32以下 sstable level 32 0 0 0 0 0 0 0 这样你会发现新扩节点会一开始产生一个超大文件,然后再拆分成个个小文件的现象。 问题是:如果你有6个500G的磁盘,而你的单节点数据是2T,那么你的节点会因为空间不足而挂掉。 解决这个问题有两种方法: 一种是磁盘做raid,搞成一个大磁盘。 一种是临时关闭level 0的size压缩,这又是cassandra的一个隐藏技能,在cassandra官方文档里你不会找到。就是启动的时候加禁用level 0 使用 size压缩的参数: ./cassandra -Dcassandra.disable_stcs_in_l0=true 注意这个参数从cassandra 2.0.10以后的版本才有。当解决了问题后建议把该参数还原。因为在 level 0采用size压缩,对于突发写入大量的数据的情况还是有好处的。 参考 https://issues-test.apache.org/jira/browse/CASSANDRA-6621