分类 ALL 下的文章 - CorePlayers
首页
我的项目
708 智能控制台
Ethan认证中心
Ethan's ToDoList
Ethan's Tech Blog
好友链接
妙站分享
联系站长
搜 索
1
2026技术架构新趋势:从微服务回调到AI原生架构设计
45 阅读
2
DDD领域驱动设计:从底层原理到生产级全链路落地实战
41 阅读
3
Go + 云原生2026:从微服务到AI Infra的实战架构
35 阅读
4
事件驱动架构(EDA):从理论到项目落地的完整实践
34 阅读
5
2026 AI编程范式演进:从Vibe Coding到Spec-Driven Development再到Harness Engineering
31 阅读
ALL
(78)
AI
(20)
前端
(24)
后端
(23)
Dify/Coze
(7)
架构设计
(6)
登录
/
注册
搜 索
标签搜索
AI Agent
边缘计算
RSC
虚拟线程
Java
Spring Boot 4
Vibe Coding
AI原生
SDD
全栈开发
高并发
Project Loom
性能优化
2026趋势
协议标准
工具调用
MCP协议
多Agent协作
CrewAI
Spring AI
EthanCcc
累计撰写
78
篇文章
累计收到
1
条评论
首页
栏目
ALL
AI
前端
后端
Dify/Coze
架构设计
页面
我的项目
708 智能控制台
Ethan认证中心
Ethan's ToDoList
Ethan's Tech Blog
好友链接
妙站分享
联系站长
用户登录
登录
注册
ALL
2025-04-15
Spring Boot 3 + Spring Cloud 微服务架构实战:从单体拆分到服务治理
微服务架构不是银弹,但它确实是解决大型项目复杂性的最有效手段之一。本文将带你从实践角度出发,使用 Spring Boot 3 和 Spring Cloud 2023+ 版本,完成一个微服务项目的拆分与治理。微服务拆分的核心原则DDD 限界上下文是微服务拆分的最佳指导:每个微服务对应一个限界上下文,有自己的领域模型。拆分的黄金法则是"高内聚、低耦合"——如果一个功能的修改总是同时改两个服务,说明它们不应该被拆分。避免微服务"死亡三角":拆分过细(300+ 个微服务但每个只有几百行代码)、拆分过粗(一个"核心服务"包含了 80% 的业务逻辑)、错误拆分(按技术层拆分:Controller 服务、Service 服务、DAO 服务)。Spring Cloud 微服务组件全景[网关层] Spring Cloud Gateway(路由、限流、鉴权) [注册中心] Nacos / Consul(服务发现与配置管理) [配置中心] Nacos Config(动态配置刷新) [熔断降级] Resilience4j / Sentinel(断路器、限流) [负载均衡] Spring Cloud LoadBalancer [远程调用] OpenFeign(声明式 HTTP 客户端) [链路追踪] Micrometer Tracing + Zipkin [消息驱动] Spring Cloud Stream + RocketMQ/Kafka服务注册与发现:Nacos 集成// bootstrap.yml spring: application: name: order-service cloud: nacos: discovery: server-addr: localhost:8848 namespace: prod group: ORDER_GROUP config: server-addr: localhost:8848 file-extension: yaml shared-configs: - data-id: common.yaml group: DEFAULT_GROUP refresh: trueOpenFeign 声明式远程调用// 服务消费方 - 声明式接口 @FeignClient(name = "user-service", path = "/api/users", fallbackFactory = UserClientFallbackFactory.class) public interface UserClient { @GetMapping("/") Result<User> getUser(@PathVariable("id") Long id); } // 使用:像调用本地方法一样调用远程服务 @RestController public class OrderController { @Autowired private UserClient userClient; @GetMapping("/api/orders/") public Result<OrderDetail> getOrder(@PathVariable Long id) { Order order = orderService.getById(id); Result<User> user = userClient.getUser(order.getUserId()); return Result.success(new OrderDetail(order, user.getData())); } }Gateway 网关:统一入口与过滤spring: cloud: gateway: routes: - id: user-service uri: lb://user-service predicates: - Path=/api/users/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 100 redis-rate-limiter.burstCapacity: 200 - id: order-service uri: lb://order-service predicates: - Path=/api/orders/** filters: - AuthFilterSentinel 熔断降级@SentinelResource(value = "getUser", blockHandler = "getUserBlockHandler") public User getUser(Long id) { return userMapper.selectById(id); } public User getUserBlockHandler(Long id, BlockException e) { return new User(id, "降级默认用户"); // 降级兜底 }分布式事务:Seata 集成@GlobalTransactional public void createOrder(OrderDTO dto) { orderService.create(dto); // 本地事务 inventoryClient.deduct(dto.getItems()); // Feign 远程调用扣减库存 accountClient.charge(dto.getUserId(), dto.getAmount()); // 远程扣款 } // Seata 通过 TC + TM + RM 实现 AT 模式下的自动补偿总结微服务架构的核心挑战不在技术,而在组织——分布式事务的最终一致性、服务间的数据一致性、跨服务的调试和追踪,这些问题的解决方案都在 "治理" 而不是 "代码" 层面。一个好的微服务架构应该是渐进式拆分的:先把单体写好,在业务增长需要的时候再逐步拆出服务。
2025年04月15日
11
0
1
2025-04-10
React 19新特性深度解析与实战应用
随着前端技术的持续演进,React 19 正式发布,标志着 React 框架在性能优化、开发者体验和现代 Web 开发模式上的又一次重大飞跃。作为 React 生态中最具影响力的版本之一,React 19 不仅解决了长期困扰开发者的重渲染问题,还引入了诸如 Actions、Document Metadata 支持、样式表优先级管理等关键功能。一、Actions:异步操作的革命性改进React 19 引入了全新的 Actions API,旨在简化组件中异步逻辑的处理流程。Actions 提供了一种声明式的异步操作机制:const fetchDataAction = createAsyncAction(async () => { const response = await fetch("/api/data"); const data = await response.json(); dispatch(); });Actions 的优势在于:更清晰的异步流程控制、自动化的加载/错误状态管理、更好的可测试性和组合性。此外,Actions 与 Suspense 的集成也更为紧密,使得在组件树中实现"局部等待"变得更加自然。二、原生支持 Document Metadata在 React 18 及更早版本中,修改页面元信息通常需要借助第三方库如 react-helmet 或手动操作 DOM。React 19 原生支持了文档元信息的动态管理,在服务端渲染时即可生成正确的 HTML 头部信息,提升了 SEO 表现和首屏加载体验。三、样式表优先级管理CSS-in-JS 和全局 CSS 冲突一直是 React 应用中的一大痛点。React 19 引入了对样式优先级的内置支持,兼容主流 CSS 模块化方案(如 Tailwind CSS、styled-components),极大提升了样式的可维护性与一致性。四、Server Components 稳定支持React 19 正式将 Server Components 从实验性功能升级为稳定特性。Server Components 的优势包括:减少客户端 JavaScript 负载、支持流式传输(Streaming SSR)、更好的数据预取策略。五、React 19 与 React 18 性能对比场景React 18React 19提升首次渲染(500节点)210ms145ms31%连续状态更新(10次)450ms280ms38%列表渲染(1000项)680ms410ms40%异步加载延迟1200ms700ms42%React 19 引入了自动批处理机制,能够智能识别多个状态更新之间的关联性,并合并为一次整体更新。在 React 18 中,连续的 setState 会触发多次 useEffect 回调;而在 React 19 中,多个更新被合并为一次,减少了不必要的副作用执行。六、总结React 19 不仅在性能层面带来了 30%-42% 的提升,更通过 Actions API、Document Metadata、Server Components 稳定化等特性,大幅改善了开发者体验。对于新项目,强烈建议直接从 React 19 起步;对于存量项目,升级成本可控且收益显著。
2025年04月10日
12
0
1
2025-04-10
SpringBoot 3 + MyBatis-Plus 全栈项目实战:从零搭建企业级应用
SpringBoot 3 + MyBatis-Plus 是目前 Java 后端开发中最流行的技术组合。本文将从零开始,带你搭建一个具备完整前后端分离能力的企业级项目,涵盖项目结构设计、数据库操作、权限认证和部署方案。项目初始化:最佳实践的项目结构src/main/java/com/example/project/\n├── config/ # 配置类(Security、CORS、MyBatis-Plus)\n├── controller/ # 控制器层\n├── service/ # 服务接口\n│ └── impl/ # 服务实现\n├── mapper/ # MyBatis Mapper 接口\n├── entity/ # 数据库实体\n├── dto/ # 数据传输对象\n├── vo/ # 视图对象(返回给前端)\n├── common/ # 公共类(统一响应、异常处理)\n│ ├── Result.java\n│ ├── GlobalExceptionHandler.java\n│ └── PageResult.java\n└── utils/ # 工具类MyBatis-Plus:告别手写 SQL1. 基础配置// application.yml\nspring:\n datasource:\n url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8mb4\n username: root\n password: $\nmybatis-plus:\n configuration:\n log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL 日志\n map-underscore-to-camel-case: true # 下划线转驼峰\n global-config:\n db-config:\n logic-delete-field: deleted # 逻辑删除字段\n logic-delete-value: 1\n logic-not-delete-value: 02. 实体与 Mapper// User.java - 实体类\n@Data\n@TableName("t_user")\npublic class User \n\n// UserMapper.java - 一行代码完成 CRUD\n@Mapper\npublic interface UserMapper extends BaseMapper ")\n User selectByEmail(@Param("email") String email);\n}3. 分页查询(一行代码)// Controller\n@GetMapping("/list")\npublic Result list(\n @RequestParam(defaultValue = "1") Integer page,\n @RequestParam(defaultValue = "10") Integer size,\n @RequestParam(required = false) String keyword\n) \n wrapper.orderByDesc(User::getCreateTime);\n return Result.success(userService.page(pageParam, wrapper));\n}统一响应与异常处理// Result.java - 统一响应格式\n@Data\npublic class Result \n public static Result error(String message) \n}\n\n// GlobalExceptionHandler.java - 全局异常处理\n@RestControllerAdvice\npublic class GlobalExceptionHandler \n @ExceptionHandler(MethodArgumentNotValidException.class)\n public Result handleValidation(MethodArgumentNotValidException e) \n}Spring Security + JWT 认证// SecurityConfig.java\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig \n}\n\n// JwtUtil.java\n@Component\npublic class JwtUtil ")\n private String secret;\n\n public String generateToken(Long userId, String username) \n}前后端分离项目部署Docker Compose 一键部署# docker-compose.yml\nversion: '3.8'\nservices:\n mysql:\n image: mysql:8.0\n environment:\n MYSQL_ROOT_PASSWORD: $\n volumes:\n - mysql_data:/var/lib/mysql\n\n backend:\n build: ./backend\n ports:\n - "8080:8080"\n depends_on:\n - mysql\n environment:\n SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/mydb\n\n frontend:\n build: ./frontend\n ports:\n - "80:80"\n depends_on:\n - backend\n\nvolumes:\n mysql_data:总结SpringBoot 3 + MyBatis-Plus 的组合让 Java 后端开发变得异常高效——MyBatis-Plus 消除了 80% 的 CRUD 样板代码,SpringBoot 3 的自动配置让你专注于业务逻辑。但高效不等于简单:理解 Spring 的 IoC 容器、AOP 切面编程、事务传播机制,才能真正驾驭这套技术栈。
2025年04月10日
11
0
1
2025-04-06
AI 应用开发Ⅱ
1. 项目中 RAG 流程是什么?标准 RAG 流程(五步)用户提问 ↓ ① Query 处理(改写/扩展/分解) ↓ ② 检索(Retrieval) ↓ ③ 重排序(Rerank) ↓ ④ 上下文拼装(Context Packing) ↓ ⑤ 生成(Generation) ↓ 最终回答各步骤详解步骤说明① Query 处理对用户原始问题做 Query Rewrite(改写为更适合检索的表述)、Query Expansion(扩展同义词)、Query Decomposition(复杂问题拆分为多个子问题)② 检索通过向量检索(Embedding 相似度)+ 关键词检索(BM25)混合召回,从知识库中找到相关文档块③ Rerank用 Cross-Encoder 等模型对召回结果做精排,过滤掉低相关性内容,保留 Top-K④ 上下文拼装将筛选后的文档块按一定策略拼装进 Prompt,加入来源标注⑤ 生成LLM 基于检索到的证据生成回答,要求"只根据证据回答,无证据就说不确定"面试回答模板我们项目的 RAG 流程是:首先对用户 Query 做改写和扩展,然后通过混合检索(向量 + BM25)从知识库召回相关文档,再用 Reranker 做精排,最后把筛选后的上下文拼入 Prompt 让 LLM 生成回答。同时会要求模型只基于检索到的证据回答,没有匹配就明确告知不确定。2. RAG 切块太小导致上下文语义断裂,如何处理?问题本质切块(Chunk)太小会导致:一句话被拆成两块,单独看每块都不完整,检索到的内容丢失上下文,模型无法理解完整语义。解决方案(五种)方案说明① 增大切块大小将 chunk_size 从 256 提升到 512-1024 tokens,overlap 从 50 提升到 100-200 tokens② 递归切分不按固定字符数切,而是按语义边界递归切分:先按章节 → 段落 → 句子,优先在大边界处断开③ 滑动窗口 + 重叠相邻 chunk 之间保留一定比例的重叠(overlap),确保边界信息不丢失④ 父子块策略(Parent-Child)小块用于检索(精准匹配),命中后返回其所在的大父块给模型(保留上下文)⑤ 语义切分用 Embedding 计算相邻句子的语义相似度,在相似度骤降处切分,保证每块语义完整推荐方案:父子块策略文档 ├── 父块 A(1024 tokens)← 检索命中后,把整个父块送给 LLM │ ├── 子块 A1(256 tokens)← 用于向量检索 │ ├── 子块 A2(256 tokens) │ └── 子块 A3(256 tokens) └── 父块 B(1024 tokens) ├── 子块 B1(256 tokens) └── 子块 B2(256 tokens)用小块做精准检索,用大块保留上下文。兼顾检索精度和语义完整性。3. 文档指代消解如何做?问题本质文档中常见指代现象,例如:"该公司2025年营收增长30%。其净利润也大幅提升。" — "其"指代"该公司""张三负责前端开发。他使用了 React 框架。" — "他"指代"张三"如果直接切块,"其净利润大幅提升"单独拿出来不知道"其"是谁,检索时也无法通过"其"匹配到"该公司"。解决方案方案说明① 预处理阶段做指代消解在文档入库前,用 LLM 或 NLP 工具(如 spaCy、HanLP)识别代词并替换为实际实体。"其净利润" → "该公司净利润"② 扩大上下文窗口切块时保留足够上下文,让代词和它指代的实体在同一块内③ 检索时做 Query 扩展用户问"该公司利润"时,扩展为"该公司 该公司利润 净利润",提高召回率④ 元数据标注在每个 chunk 上附加文档标题、章节标题、实体标签等元数据,辅助消解面试回答模板指代消解主要在文档预处理阶段做。入库前用 LLM 或 NLP 工具识别代词并替换为实际实体,比如"其"替换为"该公司"。同时配合父子块策略,确保切块时保留足够上下文,让代词和指代对象在同一块内。4. LangChain 中 Handoff 如何设计流程流转?Handoff 是什么Handoff(交接)是 LangGraph 中多 Agent 协作的核心机制,指一个 Agent 将任务控制权交给另一个 Agent。设计模式模式一:条件路由(Conditional Routing)from langgraph.graph import StateGraph def router(state): """根据当前状态决定下一步由谁处理""" if state["intent"] == "code": return "coder_agent" elif state["intent"] == "research": return "researcher_agent" else: return "general_agent" graph = StateGraph(AgentState) graph.add_node("planner", planner_agent) graph.add_node("coder_agent", coder_agent) graph.add_node("researcher_agent", researcher_agent) # 条件边:planner 根据意图路由到不同 Agent graph.add_conditional_edges("planner", router)模式二:Tool-based Handoff每个 Agent 作为一个 Tool 暴露给其他 Agent:# Agent A 把 Agent B 封装成工具 tools = [ Tool( name="delegate_to_coder", func=coder_agent.run, description="将编码任务交给 Coder Agent" ), Tool( name="delegate_to_reviewer", func=reviewer_agent.run, description="将审查任务交给 Reviewer Agent" ) ]模式三:Supervisor 模式由一个 Supervisor Agent 统一调度:Supervisor ├── 判断任务类型 ├── 分派给对应 Agent ├── 收集结果 └── 决定是否需要进一步处理面试回答模板LangChain/LangGraph 中的 Handoff 主要有三种设计:条件路由通过状态判断直接跳转到对应 Agent;Tool-based Handoff 把每个 Agent 封装成工具供其他 Agent 调用;Supervisor 模式由一个主 Agent 统一调度。生产中常用 Supervisor 模式,因为 Harness 可以在 Supervisor 层做预算控制、权限校验和失败处理。5. 主流 Agent 架构你是如何理解的?六种主流架构架构核心思想适用场景ReAct思考 → 行动 → 观察 → 循环通用任务,最经典Plan-and-Execute先规划完整计划,再逐步执行长任务、复杂任务Router先分类意图,再路由到对应处理链多能力分发Workflow + Agent固定流程中嵌入局部自主决策线上业务最常用Supervisor主 Agent 拆解任务,分派给子 Agent多 Agent 协作ReflectionAgent 生成后自我反思、修正需要高质量输出ReAct 架构图用户提问 → Thought(思考下一步)→ Action(调用工具)→ Observation(观察结果)→ Thought → ... → 最终回答Plan-and-Execute 架构图用户提问 → Planner(生成计划:Step1, Step2, Step3)→ Executor(逐步执行)→ Replanner(根据结果调整计划)→ 最终回答核心理解Agent 架构的本质是控制流的设计。ReAct 是最简单的循环控制;Plan-and-Execute 把"想"和"做"分离;Router 做意图分发;Supervisor 做任务编排。生产中往往不是单一架构,而是混合使用:顶层用 Supervisor 编排,子任务用 ReAct 或 Plan-and-Execute 执行,固定环节用 Workflow 保证稳定性。6. 最新技术(Harmes Agent、OpenClaw、Skills)Harmes Agent(Multi-Agent Harness)参考上一篇笔记《生产级Multi-Agent-Harness设计全拆解》:Agent 的"操作系统",负责编排、调度、记忆、状态、工具治理、预算控制、可观测性核心原则:Agent 负责局部智能,Harness 负责全局控制五大模块:架构编排、工具治理、状态与记忆、评估体系、成本控制OpenClaw(Claude Code / Cursor 底层架构)OpenClaw 指的是 AI Coding Agent 的底层执行框架,核心特点:特性说明工具调用Agent 通过调用 Read、Edit、Bash、Grep 等工具操作代码库上下文管理自动压缩历史对话,保留关键信息权限控制工具调用需要用户授权(allowlist / dangerously-skip-permissions)多模型路由不同任务使用不同模型(Haiku 做快速判断,Opus 做复杂推理)Skills(技能系统)Skills 是 Agent 能力的模块化封装:Skill = 触发条件 + 执行逻辑 + 输出格式每个 Skill 是一个独立的能力单元(如"搜索网页"、"读取文件"、"运行测试")Agent 根据用户意图动态加载合适的 Skill解决了"所有能力塞进 System Prompt 导致上下文爆炸"的问题面试回答模板最新的技术趋势是把 Agent 做成工程化的生产系统。Harness 是多 Agent 的运行时底座,负责编排、记忆、成本、安全。OpenClaw 是 Coding Agent 的执行框架,通过工具调用操作代码库。Skills 是能力的模块化封装,按需加载而非全量注入 System Prompt。7. OpenClaw 如何设置 Memory?Memory 分层设计层级存储内容生命周期存储位置短期记忆最近几轮对话、当前任务状态会话内上下文窗口长期记忆用户偏好、项目结构、历史决策跨会话文件系统(如 CLAUDE.md、memory/ 目录)摘要记忆旧对话的压缩摘要跨会话文件系统OpenClaw/Claude Code 的 Memory 实现~/.claude/ ├── settings.json # 用户配置 ├── projects/ │ └── / │ └── memory/ # 长期记忆文件 │ ├── user_preferences.md │ ├── project_context.md │ └── MEMORY.md # 记忆索引设置方式手动设置:用户说"记住 XXX",Agent 写入 memory 文件自动设置:Agent 在交互中自动提取高价值信息写入 memory读取方式:每次对话开始时自动加载 MEMORY.md 索引,按需读取相关记忆文件面试回答模板OpenClaw 的 Memory 分三层:短期记忆在上下文窗口中,保留最近几轮对话;长期记忆以文件形式存储在磁盘上,比如 CLAUDE.md 和 memory 目录;摘要记忆是旧对话的压缩版。每次对话开始时加载记忆索引,按需读取相关记忆文件。用户可以手动让 Agent 记住某些信息,Agent 也会自动提取高价值信息写入。8. OpenClaw 没有向量数据库是如何查询的?核心答案:基于文件系统 + Grep/Glob + LLM 理解OpenClaw(Claude Code)不使用向量数据库,而是用以下方式定位信息:方式说明Grep(关键词搜索)用 ripgrep 在代码库中搜索关键词、函数名、变量名Glob(文件模式匹配)用 glob 模式查找文件,如 **/*.py、src/**/*.tsRead(读取文件)直接读取文件内容,让 LLM 理解目录结构探索先 ls 看目录结构,再逐层深入LLM 推理根据已有信息推理"下一步应该看哪里"为什么不需要向量数据库场景向量数据库的优势OpenClaw 的替代方案语义搜索理解"用户登录功能"能匹配到 auth.pyLLM 本身就理解语义,可以直接推理出应该看哪个文件跨文件关联自动建立文件间语义关系Agent 通过多轮工具调用逐步建立关联模糊查询"那个处理支付的函数"Grep 搜 payment、pay、charge 等关键词关键洞察向量数据库解决的是无 LLM 时的语义检索问题。当 Agent 本身就有一个强大的 LLM 作为"大脑"时,它可以自己推理"应该去哪里找什么",用 Grep/Glob/Read 就够了。LLM 本身就是最好的"语义搜索引擎"。9. Cursor 中没有向量数据库,是如何定位到你要修改的位置?Cursor 的定位机制用户输入:"给登录函数加上错误处理" ↓ ① LLM 分析意图 → 需要找到 auth/login 相关文件 ↓ ② 用 Grep 搜索关键词:login、auth、sign_in ↓ ③ 用 Glob 搜索文件模式:**/*auth*、**/*login* ↓ ④ 读取候选文件内容 ↓ ⑤ LLM 理解代码结构,确定具体函数位置 ↓ ⑥ 生成修改方案,调用 Edit 工具修改运行时 Bash 文件内容当 Cursor/Claude Code 执行 Bash 命令时,通常包含:# 搜索文件 find . -name "*.py" | grep auth # 或 grep -r "def login" --include="*.py" . # 读取文件 cat src/auth/login.py # 运行测试 python -m pytest tests/test_auth.py # Git 操作 git diff git add src/auth/login.py核心答案Cursor 不需要向量数据库,因为 LLM 本身就是语义理解引擎。它通过 Grep/Glob 快速定位候选文件,再通过 LLM 理解代码结构精确定位修改位置。这比向量数据库更灵活,因为 LLM 可以根据上下文动态调整搜索策略。10. Linux 如何找文件?常用命令命令用途示例find按条件递归搜索文件find /path -name "*.py" -type flocate基于数据库的快速搜索locate "*.py"grep搜索文件内容grep -r "function_name" /pathwhich查找可执行文件路径which pythonwhereis查找二进制、源码、手册whereis nginxls + 通配符列出匹配的文件ls /var/log/*.logfind 高级用法# 按名称查找 find . -name "*.py" # 按类型查找(f=文件,d=目录) find . -type f -name "*.py" # 按大小查找 find . -size +100M # 按修改时间查找(7天内修改的) find . -mtime -7 # 组合条件 find . -name "*.py" -mtime -7 -size +1k # 执行操作(找到后删除) find . -name "*.tmp" -exec rm \;面试回答模板Linux 找文件主要用 find 命令,支持按名称、类型、大小、时间等条件搜索。快速定位用 locate(基于数据库),搜索文件内容用 grep。生产环境中 find -exec 配合其他命令可以实现批量操作。11. AI 生成后上传到云端,如何提高用户使用效率?问题本质AI 生成内容后需要上传到云端,如何让用户更快、更省地完成这个过程。优化方案层面优化策略说明生成阶段流式输出(Streaming)用户不用等全部生成完,边生成边看到结果生成阶段增量生成只生成变化部分,不重新生成全部内容上传阶段增量上传只上传变化的文件/内容,不传全量上传阶段压缩传输对文本/代码做 gzip 压缩后再上传上传阶段并行上传多个文件并行上传,提升吞吐上传阶段断点续传大文件支持分片上传,失败后从断点继续部署阶段边缘部署就近接入 CDN/边缘节点,减少网络延迟部署阶段预热缓存高频访问内容预加载到缓存体验阶段预览功能上传前让用户预览/确认,减少无效上传体验阶段后台上传上传过程不阻塞用户操作部署优化(回答面试官可能想问的)优化点说明模型推理优化用 vLLM/TensorRT-LLM 加速推理,降低首 Token 延迟模型量化INT8/INT4 量化减少显存占用,提升吞吐批处理多个请求合并推理,提升 GPU 利用率异步架构生成任务放入消息队列,异步处理,用户轮询/WebSocket 获取结果CDN 加速静态资源走 CDN,API 走就近节点面试回答模板主要从三个层面优化:生成阶段用流式输出和增量生成,让用户不用等;上传阶段用增量上传、压缩传输、并行上传减少耗时;部署阶段用边缘节点、预热缓存降低延迟。如果是模型推理场景,还可以用 vLLM 加速推理、量化减少显存占用、异步架构避免阻塞用户。总结:面试核心考点考点涉及题目核心知识RAG 工程Q1-Q3完整流程、切块策略、指代消解Agent 架构Q4-Q6Handoff 设计、主流架构、最新技术趋势AI Coding AgentQ7-Q9Memory 设计、无向量数据库的检索方案工程基础Q10Linux 文件操作系统优化Q11流式输出、增量上传、部署优化
2025年04月06日
23
0
2
2025-03-28
JavaScript 闭包与作用域链:从底层原理到实战应用
闭包(Closure)是 JavaScript 中最强大也最令人困惑的特性之一。它与作用域链、执行上下文、垃圾回收等底层机制紧密相关。本文将带你从底层原理到实战场景,彻底掌握闭包。作用域与作用域链在理解闭包之前,必须先理解作用域。JavaScript 使用的是词法作用域(静态作用域)——函数的作用域在定义时就已经确定,而不是在调用时。const value = 'global';\nfunction outer() \n inner();\n}\nouter(); // 输出 'outer',不是 'global'inner 函数在 outer 内部定义,它的作用域链是:自身作用域 → outer 作用域 → 全局作用域。这个链条在代码编写时就已确定,与运行时调用位置无关。执行上下文与变量对象每次调用函数时,JavaScript 引擎会创建一个执行上下文(Execution Context),包含:变量对象(VO)——存储函数内部的变量、函数声明、参数;作用域链(Scope Chain)——当前 VO + 所有外部 VO;this 绑定。function foo() \n bar();\n}\nfoo();当 bar 执行时,它的作用域链为:bar.VO → foo.VO → global.VO。查找变量 a 时,先在 bar.VO 中查找(未找到),然后沿作用域链在 foo.VO 中找到。闭包的真正定义闭包 = 函数 + 该函数能访问的外部变量(自由变量)。MDN 的定义更精确:"闭包是函数和其声明时所在词法环境的组合。"形成闭包需要两个条件:函数嵌套(内部函数引用了外部函数的变量)和内部函数被传递到其词法作用域之外执行。function createCounter() ;\n}\nconst counter = createCounter();\nconsole.log(counter()); // 1\nconsole.log(counter()); // 2\nconsole.log(counter()); // 3关键洞察:createCounter 执行完毕后,其执行上下文从调用栈中弹出,但 count 变量没有被销毁——因为返回的匿名函数仍然保持着对 count 的引用。闭包的底层原理:[[Environment]] 内部属性ECMAScript 规范中,每个函数都有一个内部属性 [[Environment]],指向它被创建时所在的词法环境(Lexical Environment)。当函数被调用时,新的词法环境会将其 [[Environment]] 作为外部引用,形成作用域链。function makeAdder(x) ;\n}\nconst add5 = makeAdder(5);\nconsole.log(add5(10)); // 15——x=5 仍然存活闭包的实战应用1. 数据私有化(模块模式)const User = (function() };\n})();2. 函数柯里化function curry(fn) ;\n}\nconst add = curry((a, b, c) => a + b + c);\nconsole.log(add(1)(2)(3)); // 63. 防抖与节流function debounce(fn, delay) ;\n}4. 循环中的 var 陷阱// 错误:所有回调引用同一个 i\nfor (var i = 0; i < 5; i++) // 5,5,5,5,5\n// 方案1:let 的块级作用域\nfor (let i = 0; i < 5; i++) // 0,1,2,3,4\n// 方案2:IIFE 创建闭包\nfor (var i = 0; i < 5; i++) )(i); }闭包与内存管理闭包可能导致内存泄漏,但这通常只在以下场景中才会成为问题:闭包引用了大量数据或 DOM 元素、闭包的生命周期极长(如全局事件监听器)、意外引用了不需要的外部变量。// 潜在泄漏:闭包引用整个 largeData\nfunction setupHandler() );\n}\n// 优化:只保留需要的值\nfunction setupHandlerOptimized() );\n}总结闭包不是需要死记硬背的概念——它是词法作用域和执行上下文的自然结果。理解它最好的方式是在脑中模拟 JavaScript 引擎的工作过程:函数被创建时记住外部作用域 → 函数被调用时创建新的作用域 → 查找变量时沿作用域链逐级向上。
2025年03月28日
11
0
1
1
...
13
14
15
16