- 博客(39)
- 收藏
- 关注
原创 分布式系统中的 Gossip 协议与通信机制说明
Gossip协议是一种去中心化的分布式系统通信机制,通过随机节点选择和周期性数据交换实现状态同步。它采用全量同步和增量同步两种策略,结合Push-Pull混合模型,在保证最终一致性的同时控制网络开销。该协议具有高可用性、容错性和良好的横向扩展能力,但存在数据延迟和带宽消耗的局限性。相比ICMP协议,Gossip的应用层Ping/Pong机制承载了业务数据同步功能,而不仅是网络连通性检测。Redis Cluster等系统利用Gossip协议实现节点状态维护和故障检测。
2026-04-22 11:58:34
443
原创 Java 中 Integer 对象的缓存机制与包装类特性
摘要:Java的Integer缓存机制通过在-128到127范围内预先缓存Integer对象,优化自动装箱性能。通过Integer.valueOf()或自动装箱时,该范围内的数值会复用缓存对象,超出范围则创建新对象。开发者可通过JVM参数调整上限值,但需注意内存消耗。业务开发中易犯的错误是误用"=="比较包装类对象,应使用equals()方法。显式new Integer()会绕过缓存机制,每次创建新对象。
2026-04-07 13:24:43
405
原创 Docker 与虚拟机到底有什么本质区别?场景选择与最佳实践
本文对比了Docker容器与传统虚拟机的核心差异。虚拟机通过Hypervisor模拟完整硬件环境,需要独立的Guest OS,启动慢且资源占用大;而Docker容器直接共享宿主机内核,启动快速且资源占用小。虚拟机提供硬件级强隔离,适合多租户安全场景;Docker则轻量高效,更适合微服务部署和CI/CD流程。两者并非替代关系,现代架构通常结合使用:虚拟机划分基础资源,容器管理应用部署,兼顾隔离性与资源利用率。理解这些差异有助于根据实际需求选择合适的技术方案。
2026-04-05 19:47:28
587
原创 Docker 核心原理详解:镜像、容器、Namespace、Cgroups 与 UnionFS
摘要:Docker通过容器化技术解决了软件部署中的环境一致性问题。其核心组件包括静态模板镜像、动态运行实例容器和镜像仓库。底层依赖Linux内核的Namespace实现进程隔离、Cgroups进行资源限制,以及UnionFS实现分层文件系统。数据持久化可通过绑定挂载或数据卷实现。与传统虚拟机不同,Docker容器共享宿主机内核,具有更轻量级的资源消耗和更快的启动速度,同时通过隔离机制确保运行环境一致性。这些特性使Docker成为现代应用部署的标准方案。
2026-04-05 19:43:34
508
原创 CC-Switch 使用攻略:一键管理多款 AI CLI 工具配置
CC-Switch是一款跨平台的桌面工具,用于统一管理多个AI编程辅助工具(如Claude Code、OpenCode等)的配置。它提供图形化界面集中管理API密钥、模型选择、提示词模板和会话记录,支持一键切换配置并即时生效。工具采用原子写入技术确保配置文件安全更新,并内置MCP服务和SKILL管理功能,可自动导入现有配置。即使统一模型源,不同工具在上下文管理、代码解析等执行框架层面仍存在差异。CC-Switch支持macOS、Windows和Linux系统,可通过GitHub或包管理器安装。
2026-04-01 13:37:57
5323
原创 Redis 6.0 多线程网络 I/O 模型
Redis 6.0 引入多线程架构,主要优化网络I/O处理,同时保持核心命令执行的单线程特性。通过将耗时的网络数据读写操作分配给多个I/O线程并行执行,充分利用多核CPU能力,显著提升吞吐量。关键设计在于:1)I/O线程并行处理网络数据拷贝;2)主线程仍串行执行内存操作,避免锁竞争;3)严格的任务同步机制确保执行顺序一致性。该架构在保持Redis高性能和简单性的同时,有效突破了单核CPU处理网络I/O的瓶颈。配置时需注意多线程功能默认关闭,仅在高并发场景下按需开启。
2026-03-25 16:54:08
490
原创 基于 GitHub Actions 构建标准化 CI/CD 流水线——从手动部署到全自动化交付
本文是上一篇Spring Cloud微服务部署教程的进阶篇,重点介绍如何利用GitHub Actions构建CI/CD自动化流水线。文章首先阐述了CI/CD的核心概念,对比了传统手动部署与现代化自动化流程的差异,突出CI/CD在消除环境依赖、强制测试门禁等方面的优势。随后深入解析了GitHub Actions的底层运行机制,包括事件驱动、工作流配置和临时运行器等关键技术组件。最后通过一个Spring Boot项目的YAML配置文件示例,展示了如何实现从代码提交到自动化测试、构建和部署的全流程。全文旨在帮助开
2026-03-19 15:34:45
864
原创 树形结构的数据库存储方案:以评论系统为例
关系型数据库存储树形结构的三种方案:邻接表、闭包表和物化路径。邻接表通过parent_id字段直接存储父子关系,查询简单但递归查询效率低;闭包表额外维护节点间的所有路径关系,查询高效但写入复杂;物化路径通过路径字符串记录节点位置,适合深度固定场景。本文以评论系统为例,详细分析各方案的实现方式、优缺点及适用场景,帮助开发者根据业务需求选择最佳存储方案。
2026-03-16 10:30:00
438
原创 Java 算法实践(九):二分应用与边界控制
二分查找是一种高效的搜索算法,通过每次排除一半的搜索空间将时间复杂度降至O(logN)。但在实际应用中容易出现死循环、越界和边界错误等问题。二分查找的核心在于理解搜索区间和不变性逻辑,其本质是利用两段性而非单调性。代码实现分为闭区间和左闭右开区间两种模式,需注意循环条件和指针更新的差异。在存在重复元素的数组中,二分查找可扩展为寻找目标值的左右边界,通过调整收缩策略实现。关键在于找到目标值后继续收缩区间,最终确定边界位置。
2026-03-12 14:06:26
388
原创 Docker 网络通信中“服务名”与“容器名”的底层逻辑
在Docker环境中,容器间通信的关键在于网络配置和命名解析。纯Docker环境下,自定义网络支持通过容器名通信;而Docker Compose则提供"双保险"解析机制,同时支持服务名和容器名访问。服务名是官方推荐的选择,因其支持水平扩展和配置解耦,能自动实现负载均衡。但在特定场景下(如外部容器接入或单例中间件),固定容器名反而更实用。最佳实践取决于具体需求:集群部署优先使用服务名,本地开发或特殊接入场景可考虑容器名。理解这种命名机制差异,能帮助开发者更灵活地设计容器化架构。
2026-03-12 09:00:00
663
原创 OpenCode AI 编程助手的高级配置
OpenCode 通过 AGENTS.md 和 opencode.json 实现AI代理的精细配置。AGENTS.md 作为核心配置文件,定义AI角色、项目上下文、编码规范、操作权限和工具链,通过自动扫描或手动编写生成。系统支持多级加载机制,优先读取项目级配置,并允许全局配置覆盖。此外,opencode.json 可深度定制提示词和代理行为,支持JSON和Markdown两种配置方式,提供模型绑定、权限管理等功能。工具系统则封装了文件操作和系统命令等能力,通过权限控制确保安全执行。
2026-03-10 17:32:39
3321
原创 OpenCode AI 编程助手使用教程
OpenCode 是一款支持多平台安装的 AI 编程工具,提供 npm、brew 等多种安装方式。Windows 用户需注意 npm 全局路径配置问题,可通过设置环境变量解决。首次使用需进行初始化配置,包括工作目录设定、模型提供商选择和项目扫描。工具支持 TUI 交互界面和 CLI 命令行两种操作模式,提供代码生成、会话管理、文件操作等功能。通过指令系统可切换代理模式、管理会话历史、回滚操作等,适合集成到开发流程中。
2026-03-10 17:30:45
2100
原创 深入解析 Spring SpEL 实现机制
本文分析了Spring Expression Language (SpEL)的核心解析机制。SpEL解析分为三个阶段:解析、上下文准备和求值。重点剖析了解析阶段,该阶段通过词法分析将字符串表达式转换为Token流,再通过语法分析构建抽象语法树(AST)。具体实现由InternalSpelExpressionParser完成,先使用Tokenizer进行词法分析识别变量、操作符等元素,再通过递归下降法将Token流转换为AST。这种设计使SpEL能够高效解析动态表达式,为Spring框架的注解参数解析提供了基
2026-03-06 16:41:49
517
原创 Java 算法实践(八):贪心算法思路
本文深入解析了贪心算法与动态规划的区别及其应用场景,重点探讨了线性序列和区间覆盖两类贪心问题。通过股票买卖和跳跃游戏等经典例题,阐述了贪心算法的核心思想:将全局最优问题分解为局部最优决策,逐步累积最终解。文章详细分析了贪心策略的数学原理和实现方法,并提供了高效的Java代码实现,展示了贪心算法在时间复杂度上的优势。
2026-02-19 15:43:16
745
原创 Java 算法实践(七):动态规划
摘要:本文系统介绍了动态规划(DP)的核心思想和实现路径。首先通过斐波那契数列案例,展示了从暴力递归到记忆化搜索再到DP的优化过程,重点分析了重叠子问题和空间换时间的本质。然后详细讲解了两类典型DP问题:线性DP(如爬楼梯问题)和约束条件DP(如打家劫舍问题),深入剖析了状态定义、转移方程、初始化和边界处理等关键步骤。文章通过清晰的代码实现和复杂度分析,帮助读者掌握将递归问题转化为高效DP解法的系统性思维方法。
2026-02-19 09:27:36
1091
原创 Java 算法实践(六):回溯算法框架
回溯算法(Backtracking) 是解决 NP 完全问题(如全排列、组合、子集)的通用方法,通过决策树的深度优先遍历实现。其核心在于 做选择 → 递归 → 撤销选择 的三步操作,配合剪枝优化效率。 关键点: 决策树模型:动态构建的虚拟树,通过递归遍历所有可能性 剪枝优化:利用约束条件提前终止无效分支(如用 used[] 数组避免重复选择) 状态回退:必须撤销选择以保持路径独立性(如 track.removeLast()) 结果保存:需深拷贝当前路径(new ArrayList<>(track
2026-02-18 09:55:39
1080
原创 Java 算法实践(五):二叉树遍历与常见算法题
本文系统讲解了二叉树的三种遍历方式:递归实现的深度优先搜索(DFS)、迭代实现的显式栈模拟,以及广度优先搜索(BFS)。DFS部分详细阐述了前序、中序和后序遍历的递归思维模式与实现模板,强调递归终止条件和单层逻辑的分解。迭代部分通过栈结构模拟递归过程,提供前序和中序遍历的代码实现。最后,BFS部分介绍队列在层序遍历中的应用场景,如分层处理和最短路径问题。全文以Java代码示例为核心,辅以清晰的逻辑推演,帮助读者掌握二叉树遍历的核心算法思维。
2026-02-17 17:42:01
898
原创 Java 算法实践(四):链表核心题型
本文介绍了链表操作的两种核心方法:反转链表和虚拟头节点技巧。链表不支持随机访问,操作时需注意保存节点引用。反转链表可通过迭代或递归实现,迭代法空间更优。虚拟头节点(dummy node)能统一处理头节点和中间节点的操作,避免边界判断。以删除倒数第N个节点为例,演示了如何结合双指针和dummy node简化逻辑。这些技巧是解决复杂链表问题的基础,如回文链表、分组反转等。
2026-02-15 16:19:22
1107
原创 Java 算法实践(三):双指针与滑动窗口
双指针是一种高效的算法技巧,通过两个指针协同移动来减少无效搜索空间。主要分为两种模式: 对撞指针(左右指针):适用于有序数组和子数组问题,如两数之和、盛水容器等。通过左右指针向中间移动,根据条件调整指针位置。 快慢指针:用于原地修改数组或链表问题,如去重、检测链表环等。快指针扫描数据,慢指针维护处理后的序列。 双指针能显著优化时间复杂度,如将暴力解法从O(N²)降至O(N)。掌握指针移动条件和去重技巧是解题关键。
2026-02-14 18:59:12
611
原创 Java算法实践(二):堆与PriorityQueue实战
本文介绍了Java中PriorityQueue的原理及其在Top K问题中的应用。PriorityQueue底层是动态数组实现的小顶堆/大顶堆,具有O(log K)的插入删除效率。重点解析了求Top K大元素必须用小顶堆的反直觉设计:通过维护大小为K的小顶堆,在遍历时保留较大元素,挤出较小元素,实现O(N log K)的高效解法。文章通过LeetCode 215题示例详细演示了代码执行过程,并对比了不同解法的复杂度,指出小顶堆解法在处理海量数据时的空间优势(O(K))。最后提供了JDK API的简化写法,平
2026-02-13 19:55:55
1036
原创 Java算法实践(一):常用数据结构接口
本文介绍了Java集合框架中在算法题中最常用的数据结构:Deque和PriorityQueue。针对Deque接口,详细比较了ArrayDeque和LinkedList的实现差异,推荐使用性能更优的ArrayDeque来模拟栈和队列操作。对于PriorityQueue,分析了其基于二叉堆的特性及典型应用场景。文章提供了清晰的操作方法对比表和使用模板,并指出在算法题中应避免使用遗留的Stack类。这些核心数据结构的正确选择能显著提升代码效率和简洁性。
2026-02-12 20:28:13
1004
原创 从零部署Spring Cloud微服务系统(Kiwi-Hub)
本文介绍了在阿里云2核2G服务器上部署Kiwi-Hub微服务系统的实践。通过Docker+Nginx+Nacos架构,将系统拆分为用户、内容和短链三个微服务。针对服务器资源限制,采用Nginx替代Spring Cloud Gateway作为网关,配置Swap分区防止OOM,并使用云端托管中间件(Upstash Redis、MongoDB Atlas和CloudAMQP)减轻服务器负担。文章详细说明了服务器初始化、Swap配置、目录规划及基于Alpine镜像的分层构建策略,实现了资源受限环境下的微服务稳定部署
2026-02-03 19:31:22
1240
原创 数据结构在Java后端开发与架构设计中的实战应用
通过本文对数组、链表、栈、队列、树的剖析,可以看到,JDK 源码和中间件设计并非遥不可及,而是在特定场景下对基础数据结构的极致权衡与运用。数组 (Array)的本质是利用内存连续性带来的 CPU 缓存亲和力与随机访问速度。在通用场景下我们使用ArrayList,但在追求极致性能的滑动窗口计算中,手动实现的定长环形缓冲 (RingBuffer)能彻底消除扩容带来的 GC 压力与数据搬移开销。链表 (Linked List)的价值不在于单纯的存储,而在于拓扑结构的灵活调整。当它与哈希表结合(如。
2026-01-15 19:32:09
949
1
原创 Java 并发基石:AQS (AbstractQueuedSynchronizer)
AQS (AbstractQueuedSynchronizer) 是 Java 并发包的核心框架,通过维护共享状态(state)和CLH变体队列实现锁和同步器。其核心思想是将并发控制抽象为对state的CAS操作和线程排队管理。AQS支持独占(如ReentrantLock)和共享(如Semaphore)两种模式,通过模板方法让子类实现特定逻辑。独占模式采用单点唤醒机制,而共享模式通过级联传播实现多线程同时唤醒。内部Node节点维护线程状态(waitStatus)和前驱后继指针,确保线程安全排队和高效唤醒。A
2026-01-09 19:58:30
1442
原创 解构“工厂模式”家族
本文摘要: 《工厂模式深度解析》探讨了面向对象编程中对象创建的最佳实践。文章首先指出传统直接实例化对象带来的耦合问题,随后循序渐进地分析三种工厂模式:简单工厂模式通过集中管理对象创建实现初步解耦,但违背开闭原则;工厂方法模式引入多态工厂层级,将实例化延迟到子类;抽象工厂模式(未完整展示)则处理产品族创建问题。文章以支付系统为例,通过Java代码和UML图示详细演示了模式实现,重点比较了各模式的优缺点,为复杂系统设计提供了对象创建的有效解决方案。
2026-01-01 14:11:00
1305
原创 解构 ThreadLocal:从线程隔离机制到内存安全实践
ThreadLocal通过线程内部的ThreadLocalMap实现数据隔离,每个线程拥有独立的Entry数组存储变量。其核心设计包括:1) 使用线性探测法解决哈希冲突,通过斐波那契散列减少碰撞;2) Entry继承WeakReference实现Key的弱引用,避免内存泄漏;3) 采用先清理后扩容策略,在set操作时进行探测式清理和启发式清理,只有当有效元素超过阈值3/4时才扩容。这种机制在保证线程安全的同时,优化了内存使用和访问效率。
2025-12-27 15:25:15
1694
原创 Spring 事务原理、实现方式与失效场景分析
摘要:本文深入解析Spring事务管理机制及其实现方案。Spring事务通过AOP代理和ThreadLocal资源绑定实现数据一致性,核心流程包括代理生成、拦截器触发和事务生命周期管理。提供三种实现方案:声明式事务(推荐)、编程式事务(性能优化)和底层API手动管理(跨数据源场景)。重点分析了事务失效原因及解决方案,包括代理机制限制和ThreadLocal跨线程问题。
2025-12-26 21:03:37
1119
原创 Seata 分布式事务全解(二):浅聊 AT 模式
Seata AT模式通过改进两阶段提交(2PC)机制实现分布式事务,核心创新在于将资源锁定限制在第一阶段本地事务内。一阶段执行SQL后立即提交并释放本地锁,同时记录Undo Log;二阶段根据全局决议异步清理日志或利用Undo Log进行补偿回滚。通过全局锁机制确保写隔离,undo_log表存储数据变更前后的快照。相比传统2PC,AT模式将长周期全局锁转为短周期本地锁,大幅提升并发性能,同时通过Undo Log保证数据一致性。
2025-12-19 21:33:49
1241
原创 Seata 分布式事务全解(一):架构核心与设计原理
Seata是一款开源的分布式事务解决方案,专为微服务架构设计。其核心架构包含三个角色:事务协调者(TC)、事务管理器(TM)和资源管理器(RM)。Seata通过抽象两阶段提交模型,支持AT、TCC、Saga和XA四种事务模式,满足不同业务场景需求。TC作为服务端维护事务状态,TM和RM作为客户端嵌入应用,分别负责全局事务管理和分支资源管理。关键机制包括XID传播实现事务上下文跨服务传递,以及TC的多种存储模式支持高可用部署。Seata提供了标准化的分布式事务协作平台,在应用层实现高性能数据一致性服务。
2025-12-19 16:22:27
1085
原创 架构视角下的分布式事务(二):柔性事务与最终一致性方案
优点:生产者不需要关心消费者是否在线,发完消息即可结束。能够承受高并发冲击。相比 TCC,不需要拆分 Try/Confirm/Cancel,主要逻辑集中在消息发送和消费端。缺点:依赖异步投递,存在一定延迟。需要维护独立的消息服务、定时任务和消息表清理机制。一旦消息发出,如果下游一直失败(进入死信),上游通常无法自动回滚,只能通过“人工补偿”或生成“反向消息”来修正数据。Saga 是一种**“先提交,后补偿”的模式。它牺牲了隔离性(Isolation),换取了。
2025-12-18 20:08:50
1041
原创 架构视角下的分布式事务(一):核心理论与强一致性方案
本文系统介绍了微服务架构下的分布式事务解决方案。首先阐述了分布式事务的定义、产生背景和核心难点,分析了跨库、跨服务等典型应用场景。然后基于CAP理论,将分布式事务分为强一致性(CP)和最终一致性(AP)两大流派。详细讲解了2PC协议的实现流程及其同步阻塞、单点故障等核心缺陷,并简要提及3PC的改进思路。最后总结了不同场景下的技术选型建议,为分布式系统设计提供参考。
2025-12-18 19:53:42
1569
原创 解析 SPI 机制:从 Java 原生到 Spring 的扩展实践
摘要: SPI(Service Provider Interface)是Java实现模块解耦的核心机制,通过接口定义与实现分离,支持动态加载扩展类。Java原生SPI使用ServiceLoader加载META-INF/services/下的实现类,但存在全量加载、缺乏IOC支持等局限。Spring通过SpringFactoriesLoader改进SPI机制,采用spring.factories文件配置扩展点。Spring Boot 2.7引入AutoConfiguration.imports文件过渡,3.0
2025-12-15 19:06:05
1181
原创 MySQL、Redis、MongoDB与RabbitMQ中的事务机制
摘要 本文系统介绍了事务机制在数据库和消息队列中的应用。事务作为确保数据一致性的核心机制,遵循ACID特性(原子性、一致性、隔离性、持久性)。文章重点分析了MySQL(InnoDB)和Redis的事务实现:MySQL通过Undo Log、Redo Log、MVCC等技术完整支持ACID,提供四种隔离级别;Redis事务则采用批处理方式,保证原子性和隔离性但缺乏回滚机制。此外还对比了MongoDB、RabbitMQ等系统的事务特点,说明不同系统根据设计目标对ACID特性进行权衡。最后提供了MySQL和Redi
2025-11-24 15:24:55
719
原创 跨域:后端的应对策略
摘要:跨域问题是Web开发中常见的安全限制,源于浏览器的同源策略,当前端访问不同源的服务器时会被阻止。后端可通过CORS(跨源资源共享)解决跨域问题,主要通过在响应头添加Access-Control-Allow-Origin等字段。在Spring Boot中,可通过全局配置或注解方式实现CORS。集成Spring Security时需确保CORS配置在过滤器链之前。合理配置CORS能兼顾安全性与功能需求,让前后端安全交互。
2025-11-20 16:34:18
1485
原创 设计模式在实战中的多面性:以责任链模式为例
在实际项目中,设计模式的选用往往受到技术栈和业务复杂度的影响。特性方式一:经典链式结构(内部链接)方式二:管理器模式(外部控制)流程控制内聚在内部。外置在中。Handler 耦合强耦合(每个 Handler 需依赖下一个 Handler)。零耦合(Handler 之间完全独立)。顺序控制手动组装。依赖 Spring 的@Order注解或手动排序。拓展性新增节点需修改组装代码,不满足开闭原则。新增节点只需新建类,完全满足开闭原则。适用场景。
2025-11-15 12:50:40
958
原创 后端缓存:Caffeine、Redis、Spring Cache 与 JetCache 全解析
后端主流缓存技术对比与应用 本文系统分析了四种主流后端缓存技术。Caffeine作为高性能本地缓存,具备微秒级访问速度和灵活的回收策略;Redis作为分布式缓存支持跨服务器数据共享和大规模存储;Spring Cache提供了缓存抽象层,简化多种缓存实现的使用;JetCache则结合本地与分布式缓存的优势,实现多级缓存策略。各类技术在容量、性能、一致性等方面各有特点,开发者应根据业务场景(如数据规模、一致性要求、访问频率等)选择合适方案,必要时可采用混合缓存架构以获得最佳性能与可用性平衡。
2025-11-09 23:01:45
1262
原创 Spring Bean 生命周期深度剖析:原理、实践与问题解决
本文深入剖析了Spring框架中Bean生命周期的核心机制,包括实例化、属性赋值、初始化、使用和销毁五个阶段。重点解析了实例化阶段的构造器选择逻辑,以及通过InstantiationAwareBeanPostProcessor接口进行扩展的方法。详细对比了构造器注入、Setter方法注入和字段注入三种依赖注入方式的适用场景与特点,强调构造器注入是Spring官方推荐的方式。文章还通过流程图直观展示Bean生命周期各阶段的对应关系,为开发者深入理解Spring容器工作原理提供了系统化的指导。
2025-11-02 20:51:32
785
原创 Spring Security深度实践:从单体到微服务架构下的多方式登录与统一认证
本文系统阐述了Spring Security框架下从单体到微服务架构的多方式登录与统一认证实现。首先分析了传统认证方式的局限,如单一登录方式、难以定制逻辑等问题;随后提出了自定义解决方案,包括自定义Token、过滤器、认证提供者和处理器等核心组件;最后通过手机验证码登录的具体代码示例,展现了如何通过自定义AbstractAuthenticationToken实现灵活认证。文章强调在微服务架构下需建立统一认证中心的重要性,为复杂系统提供安全可靠的多方式认证支持。
2025-10-29 14:12:10
770
原创 JWT 身份验证:从 Cookie/Session 到双 Token + 黑名单落地
HTTP协议的无状态特性为Web应用开发带来挑战,身份验证技术应运而生,确保用户安全与系统稳定。传统方案包括Cookie与Session:Cookie存储在客户端,通过文本信息传递身份数据,但易受XSS/CSRF攻击且容量受限;Session则基于服务器存储状态,通过Session ID识别用户,但其服务器依赖性导致扩展性不足。两种方案各具优缺点,开发者需结合实际场景选择,后续章节将进一步探讨Token等现代验证方案。
2025-10-29 14:09:06
1081
1
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅