Java学习指南
  • Java 编程的逻辑
  • Java进阶
  • Java FrameWorks
  • 了解 USB Type-A,B,C 三大标准接口
  • 深入浅出DDD
  • 重构:改善既有代码的设计
  • 面试大纲
  • 云原生
    • 什么是无服务器(what is serverless)?
  • 博客
    • 深入分析Log4j 漏洞
  • 博客
    • Serverless之快速搭建Spring Boot应用
  • 博客
    • 使用 Prometheus + Grafana + Spring Boot Actuator 监控应用
  • 博客
    • 使用 Prometheus + Grafana 监控 MySQL
  • 博客
    • 使用Github Actions + Docker 部署Spring Boot应用
  • 博客
    • Redis分布式锁之Redisson的原理和实践
  • 博客
    • 数据库中的树结构应该怎样去设计
  • 学习&成长
    • 如何成为技术大牛
  • 开发工具
    • Git Commit Message Guidelines
  • 开发工具
    • git命名大全
  • 开发工具
    • Gradle vs Maven Comparison
  • 开发工具
    • Swagger2常用注解及其说明
  • 开发工具
    • 简明 VIM 练级攻略
  • 微服务
    • 十大微服务设计模式和原则
  • 微服务
    • 微服务下的身份认证和令牌管理
  • 微服务
    • 微服务坏味道之循环依赖
  • 设计模式
    • 设计模式 - JDK中的设计模式
  • 设计模式
    • 设计模式 - Java三种代理模式
  • 设计模式
    • 设计模式 - 六大设计原则
  • 设计模式
    • 设计模式 - 单例模式
  • 设计模式
    • 设计模式 - 命名模式
  • 设计模式
    • 设计模式 - 备忘录模式
  • 设计模式
    • 设计模式 - 概览
  • 设计模式
    • 设计模式 - 没用的设计模式
  • 质量&效率
    • Homebrew 替换国内镜像源
  • 质量&效率
    • 工作中如何做好技术积累
  • Java FrameWorks
    • Logback
      • 自定义 logback 日志过滤器
  • Java FrameWorks
    • Mybatis
      • MyBatis(十三) - 整合Spring
  • Java FrameWorks
    • Mybatis
      • MyBatis(十二) - 一些API
  • Java FrameWorks
    • Mybatis
      • Mybatis(一) - 概述
  • Java FrameWorks
    • Mybatis
      • Mybatis(七) - 结果集的封装与映射
  • Java FrameWorks
    • Mybatis
      • Mybatis(三) - mapper.xml及其加载机制
  • Java FrameWorks
    • Mybatis
      • Mybatis(九) - 事务
  • Java FrameWorks
    • Mybatis
      • Mybatis(二) - 全局配置文件及其加载机制
  • Java FrameWorks
    • Mybatis
      • Mybatis(五) - SqlSession执行流程
  • Java FrameWorks
    • Mybatis
      • Mybatis(八) - 缓存
  • Java FrameWorks
    • Mybatis
      • Mybatis(六) - 动态SQL的参数绑定与执行
  • Java FrameWorks
    • Mybatis
      • Mybatis(十) - 插件
  • Java FrameWorks
    • Mybatis
      • Mybatis(十一) - 日志
  • Java FrameWorks
    • Mybatis
      • Mybatis(四) - Mapper接口解析
  • Java FrameWorks
    • Netty
      • Netty 可靠性分析
  • Java FrameWorks
    • Netty
      • Netty - Netty 线程模型
  • Java FrameWorks
    • Netty
      • Netty堆外内存泄露排查盛宴
  • Java FrameWorks
    • Netty
      • Netty高级 - 高性能之道
  • Java FrameWorks
    • Shiro
      • Shiro + JWT + Spring Boot Restful 简易教程
  • Java FrameWorks
    • Shiro
      • 非常详尽的 Shiro 架构解析!
  • Java FrameWorks
    • Spring
      • Spring AOP 使用介绍,从前世到今生
  • Java FrameWorks
    • Spring
      • Spring AOP 源码解析
  • Java FrameWorks
    • Spring
      • Spring Event 实现原理
  • Java FrameWorks
    • Spring
      • Spring Events
  • Java FrameWorks
    • Spring
      • Spring IOC容器源码分析
  • Java FrameWorks
    • Spring
      • Spring Integration简介
  • Java FrameWorks
    • Spring
      • Spring MVC 框架中拦截器 Interceptor 的使用方法
  • Java FrameWorks
    • Spring
      • Spring bean 解析、注册、实例化流程源码剖析
  • Java FrameWorks
    • Spring
      • Spring validation中@NotNull、@NotEmpty、@NotBlank的区别
  • Java FrameWorks
    • Spring
      • Spring 如何解决循环依赖?
  • Java FrameWorks
    • Spring
      • Spring 异步实现原理与实战分享
  • Java FrameWorks
    • Spring
      • Spring中的“for update”问题
  • Java FrameWorks
    • Spring
      • Spring中的设计模式
  • Java FrameWorks
    • Spring
      • Spring事务失效的 8 大原因
  • Java FrameWorks
    • Spring
      • Spring事务管理详解
  • Java FrameWorks
    • Spring
      • Spring计时器StopWatch使用
  • Java FrameWorks
    • Spring
      • 详述 Spring MVC 框架中拦截器 Interceptor 的使用方法
  • Java FrameWorks
    • Spring
      • 透彻的掌握 Spring 中@transactional 的使用
  • Java
    • Java IO&NIO&AIO
      • Java IO - BIO 详解
  • Java
    • Java IO&NIO&AIO
      • Java NIO - IO多路复用详解
  • Java
    • Java IO&NIO&AIO
      • Java N(A)IO - Netty
  • Java
    • Java IO&NIO&AIO
      • Java IO - Unix IO模型
  • Java
    • Java IO&NIO&AIO
      • Java IO - 分类
  • Java
    • Java IO&NIO&AIO
      • Java NIO - 基础详解
  • Java
    • Java IO&NIO&AIO
      • Java IO - 常见类使用
  • Java
    • Java IO&NIO&AIO
      • Java AIO - 异步IO详解
  • Java
    • Java IO&NIO&AIO
      • Java IO概述
  • Java
    • Java IO&NIO&AIO
      • Java IO - 设计模式
  • Java
    • Java IO&NIO&AIO
      • Java NIO - 零拷贝实现
  • Java
    • Java JVM
      • JVM 优化经验总结
  • Java
    • Java JVM
      • JVM 内存结构
  • Java
    • Java JVM
      • JVM参数设置
  • Java
    • Java JVM
      • Java 内存模型
  • Java
    • Java JVM
      • 从实际案例聊聊Java应用的GC优化
  • Java
    • Java JVM
      • Java 垃圾回收器G1详解
  • Java
    • Java JVM
      • 垃圾回收器Shenandoah GC详解
  • Java
    • Java JVM
      • 垃圾回收器ZGC详解
  • Java
    • Java JVM
      • 垃圾回收基础
  • Java
    • Java JVM
      • 如何优化Java GC
  • Java
    • Java JVM
      • 类加载机制
  • Java
    • Java JVM
      • 类字节码详解
  • Java
    • Java 基础
      • Java hashCode() 和 equals()
  • Java
    • Java 基础
      • Java 基础 - Java native方法以及JNI实践
  • Java
    • Java 基础
      • Java serialVersionUID 有什么作用?
  • Java
    • Java 基础
      • Java 泛型的类型擦除
  • Java
    • Java 基础
      • Java 基础 - Unsafe类解析
  • Java
    • Java 基础
      • Difference Between Statement and PreparedStatement
  • Java
    • Java 基础
      • Java 基础 - SPI机制详解
  • Java
    • Java 基础
      • Java 基础 - final
  • Java
    • Java 基础
      • Java中static关键字详解
  • Java
    • Java 基础
      • 为什么说Java中只有值传递?
  • Java
    • Java 基础
      • Java 基础 - 即时编译器原理解析及实践
  • Java
    • Java 基础
      • Java 基础 - 反射
  • Java
    • Java 基础
      • Java多态的面试题
  • Java
    • Java 基础
      • Java 基础 - 异常机制详解
  • Java
    • Java 基础
      • 为什么要有抽象类?
  • Java
    • Java 基础
      • 接口的本质
  • Java
    • Java 基础
      • Java 基础 - 枚举
  • Java
    • Java 基础
      • Java 基础 - 泛型机制详解
  • Java
    • Java 基础
      • Java 基础 - 注解机制详解
  • Java
    • Java 基础
      • 为什么 String hashCode 方法选择数字31作为乘子
  • Java
    • Java 并发
      • Java 并发 - 14个Java并发容器
  • Java
    • Java 并发
      • Java 并发 - AQS
  • Java
    • Java 并发
      • Java 并发 - BlockingQueue
  • Java
    • Java 并发
      • Java 并发 - CAS
  • Java
    • Java 并发
      • Java 并发 - Condition接口
  • Java
    • Java 并发
      • Java 并发 - CopyOnWriteArrayList
  • Java
    • Java 并发
      • Java 并发 - CountDownLatch、CyclicBarrier和Phaser对比
  • Java
    • Java 并发
      • Java 并发 - Fork&Join框架
  • Java
    • Java 并发
      • Java 并发 - Java CompletableFuture 详解
  • Java
    • Java 并发
      • Java 并发 - Java 线程池
  • Java
    • Java 并发
      • Java 并发 - Lock接口
  • Java
    • Java 并发
      • Java 并发 - ReentrantLock
  • Java
    • Java 并发
      • Java 并发 - ReentrantReadWriteLock
  • Java
    • Java 并发
      • Java 并发 - Synchronized
  • Java
    • Java 并发
      • Java 并发 - ThreadLocal 内存泄漏问题
  • Java
    • Java 并发
      • Java 并发 - ThreadLocal
  • Java
    • Java 并发
      • Java 并发 - Volatile
  • Java
    • Java 并发
      • Java 并发 - 从ReentrantLock的实现看AQS的原理及应用
  • Java
    • Java 并发
      • Java 并发 - 公平锁和非公平锁
  • Java
    • Java 并发
      • Java 并发 - 内存模型
  • Java
    • Java 并发
      • Java 并发 - 原子类
  • Java
    • Java 并发
      • Java 并发 - 如何确保三个线程顺序执行?
  • Java
    • Java 并发
      • Java 并发 - 锁
  • Java
    • Java 的新特性
      • Java 10 新特性概述
  • Java
    • Java 的新特性
      • Java 11 新特性概述
  • Java
    • Java 的新特性
      • Java 12 新特性概述
  • Java
    • Java 的新特性
      • Java 13 新特性概述
  • Java
    • Java 的新特性
      • Java 14 新特性概述
  • Java
    • Java 的新特性
      • Java 15 新特性概述
  • Java
    • Java 的新特性
      • Java 8的新特性
  • Java
    • Java 的新特性
      • Java 9 新特性概述
  • Java
    • Java 调试排错
      • 调试排错 - Java Debug Interface(JDI)详解
  • Java
    • Java 调试排错
      • 调试排错 - CPU 100% 排查优化实践
  • Java
    • Java 调试排错
      • 调试排错 - Java Heap Dump分析
  • Java
    • Java 调试排错
      • 调试排错 - Java Thread Dump分析
  • Java
    • Java 调试排错
      • 调试排错 - Java动态调试技术原理
  • Java
    • Java 调试排错
      • 调试排错 - Java应用在线调试Arthas
  • Java
    • Java 调试排错
      • 调试排错 - Java问题排查:工具单
  • Java
    • Java 调试排错
      • 调试排错 - 内存溢出与内存泄漏
  • Java
    • Java 调试排错
      • 调试排错 - 在线分析GC日志的网站GCeasy
  • Java
    • Java 调试排错
      • 调试排错 - 常见的GC问题分析与解决
  • Java
    • Java 集合
      • Java 集合 - ArrayList
  • Java
    • Java 集合
      • Java 集合 - HashMap 和 ConcurrentHashMap
  • Java
    • Java 集合
      • Java 集合 - HashMap的死循环问题
  • Java
    • Java 集合
      • Java 集合 - LinkedHashSet&Map
  • Java
    • Java 集合
      • Java 集合 - LinkedList
  • Java
    • Java 集合
      • Java 集合 - PriorityQueue
  • Java
    • Java 集合
      • Java 集合 - Stack & Queue
  • Java
    • Java 集合
      • Java 集合 - TreeSet & TreeMap
  • Java
    • Java 集合
      • Java 集合 - WeakHashMap
  • Java
    • Java 集合
      • Java 集合 - 为什么HashMap的容量是2的幂次方
  • Java
    • Java 集合
      • Java 集合 - 概览
  • Java
    • Java 集合
      • Java 集合 - 高性能队列Disruptor详解
  • 分布式
    • RPC
      • ⭐️RPC - Dubbo&hsf&Spring cloud的区别
  • 分布式
    • RPC
      • ⭐️RPC - Dubbo的架构原理
  • 分布式
    • RPC
      • ⭐️RPC - HSF的原理分析
  • 分布式
    • RPC
      • ⭐️RPC - 你应该知道的RPC原理
  • 分布式
    • RPC
      • ⭐️RPC - 动态代理
  • 分布式
    • RPC
      • 深入理解 RPC 之协议篇
  • 分布式
    • RPC
      • RPC - 序列化和反序列化
  • 分布式
    • RPC
      • ⭐️RPC - 服务注册与发现
  • 分布式
    • RPC
      • RPC - 核心原理
  • 分布式
    • RPC
      • ⭐️RPC - 框架对比
  • 分布式
    • RPC
      • ⭐️RPC - 网络通信
  • 分布式
    • 分布式事务
      • 分布式事务 Seata TCC 模式深度解析
  • 分布式
    • 分布式事务
      • 分布式事务的实现原理
  • 分布式
    • 分布式事务
      • 常用的分布式事务解决方案
  • 分布式
    • 分布式事务
      • 手写实现基于消息队列的分布式事务框架
  • 分布式
    • 分布式算法
      • CAP 定理的含义
  • 分布式
    • 分布式算法
      • Paxos和Raft比较
  • 分布式
    • 分布式算法
      • 分布式一致性与共识算法
  • 分布式
    • 分布式锁
      • ⭐️分布式锁的原理及实现方式
  • 分布式
    • 搜索引擎
      • ElasticSearch与SpringBoot的集成与JPA方法的使用
  • 分布式
    • 搜索引擎
      • 全文搜索引擎 Elasticsearch 入门教程
  • 分布式
    • 搜索引擎
      • 十分钟学会使用 Elasticsearch 优雅搭建自己的搜索系统
  • 分布式
    • 搜索引擎
      • 腾讯万亿级 Elasticsearch 技术解密
  • 分布式
    • 日志系统
      • Grafana Loki 简明教程
  • 分布式
    • 日志系统
      • 分布式系统中如何优雅地追踪日志
  • 分布式
    • 日志系统
      • 如何优雅地记录操作日志?
  • 分布式
    • 日志系统
      • 日志收集组件—Flume、Logstash、Filebeat对比
  • 分布式
    • 日志系统
      • 集中式日志系统 ELK 协议栈详解
  • 分布式
    • 消息队列
      • 消息队列 - Kafka
  • 分布式
    • 消息队列
      • 消息队列 - Kafka、RabbitMQ、RocketMQ等消息中间件的对比
  • 分布式
    • 消息队列
      • 消息队列之 RabbitMQ
  • 分布式
    • 消息队列
      • 消息队列 - 使用docker-compose构建kafka集群
  • 分布式
    • 消息队列
      • 消息队列 - 分布式系统与消息的投递
  • 分布式
    • 消息队列
      • 消息队列 - 如何保证消息的可靠性传输
  • 分布式
    • 消息队列
      • 消息队列 - 如何保证消息的顺序性
  • 分布式
    • 消息队列
      • 消息队列 - 如何保证消息队列的高可用
  • 分布式
    • 消息队列
      • 消息队列 - 消息队列设计精要
  • 分布式
    • 监控系统
      • 深度剖析开源分布式监控CAT
  • 大数据
    • Flink
      • Flink架构与核心组件
  • 微服务
    • Dubbo
      • 基于dubbo的分布式应用中的统一异常处理
  • 微服务
    • Dubbo
      • Vim快捷键
  • 微服务
    • Service Mesh
      • Istio 是什么?
  • 微服务
    • Service Mesh
      • OCTO 2.0:美团基于Service Mesh的服务治理系统详解
  • 微服务
    • Service Mesh
      • Service Mesh是什么?
  • 微服务
    • Service Mesh
      • Spring Cloud向Service Mesh迁移
  • 微服务
    • Service Mesh
      • 数据挖掘算法
  • 微服务
    • Service Mesh
      • Seata Saga 模式
  • 微服务
    • Spring Cloud
      • Seata TCC 模式
  • 微服务
    • Spring Cloud
      • Spring Cloud Config
  • 微服务
    • Spring Cloud
      • Seata AT 模式
  • 微服务
    • Spring Cloud
      • Spring Cloud Gateway
  • 微服务
    • Spring Cloud
      • Spring Cloud OpenFeign 的核心原理
  • 微服务
    • Spring Cloud
      • Seata XA 模式
  • 数据库
    • Database Version Control
      • Liquibase vs. Flyway
  • 数据库
    • Database Version Control
      • Six reasons to version control your database
  • 数据库
    • MySQL
      • How Sharding Works
  • 数据库
    • MySQL
      • MySQL InnoDB中各种SQL语句加锁分析
  • 数据库
    • MySQL
      • MySQL 事务隔离级别和锁
  • 数据库
    • MySQL
      • MySQL 索引性能分析概要
  • 数据库
    • MySQL
      • MySQL 索引设计概要
  • 数据库
    • MySQL
      • MySQL出现Waiting for table metadata lock的原因以及解决方法
  • 数据库
    • MySQL
      • MySQL的Limit性能问题
  • 数据库
    • MySQL
      • MySQL索引优化explain
  • 数据库
    • MySQL
      • MySQL索引背后的数据结构及算法原理
  • 数据库
    • MySQL
      • MySQL行转列、列转行问题
  • 数据库
    • MySQL
      • 一条SQL更新语句是如何执行的?
  • 数据库
    • MySQL
      • 一条SQL查询语句是如何执行的?
  • 数据库
    • MySQL
      • 为什么 MySQL 使用 B+ 树
  • 数据库
    • MySQL
      • 为什么 MySQL 的自增主键不单调也不连续
  • 数据库
    • MySQL
      • 为什么我的MySQL会“抖”一下?
  • 数据库
    • MySQL
      • 为什么数据库不应该使用外键
  • 数据库
    • MySQL
      • 为什么数据库会丢失数据
  • 数据库
    • MySQL
      • 事务的可重复读的能力是怎么实现的?
  • 数据库
    • MySQL
      • 大众点评订单系统分库分表实践
  • 数据库
    • MySQL
      • 如何保证缓存与数据库双写时的数据一致性?
  • 数据库
    • MySQL
      • 浅谈数据库并发控制 - 锁和 MVCC
  • 数据库
    • MySQL
      • 深入浅出MySQL 中事务的实现
  • 数据库
    • MySQL
      • 浅入浅出MySQL 和 InnoDB
  • 数据库
    • PostgreSQL
      • PostgreSQL upsert功能(insert on conflict do)的用法
  • 数据库
    • Redis
      • Redis GEO & 实现原理深度分析
  • 数据库
    • Redis
      • Redis 和 I/O 多路复用
  • 数据库
    • Redis
      • Redis分布式锁
  • 数据库
    • Redis
      • Redis实现分布式锁中的“坑”
  • 数据库
    • Redis
      • Redis总结
  • 数据库
    • Redis
      • 史上最全Redis高可用技术解决方案大全
  • 数据库
    • Redis
      • Redlock:Redis分布式锁最牛逼的实现
  • 数据库
    • Redis
      • 为什么 Redis 选择单线程模型
  • 数据库
    • TiDB
      • 新一代数据库TiDB在美团的实践
  • 数据库
    • 数据仓库
      • 实时数仓在有赞的实践
  • 数据库
    • 数据库原理
      • OLTP与OLAP的关系是什么?
  • 数据库
    • 数据库原理
      • 为什么 OLAP 需要列式存储
  • 系统设计
    • DDD
      • Domain Primitive
  • 系统设计
    • DDD
      • Repository模式
  • 系统设计
    • DDD
      • 应用架构
  • 系统设计
    • DDD
      • 聊聊如何避免写流水账代码
  • 系统设计
    • DDD
      • 领域层设计规范
  • 系统设计
    • DDD
      • 从三明治到六边形
  • 系统设计
    • DDD
      • 阿里盒马领域驱动设计实践
  • 系统设计
    • DDD
      • 领域驱动设计(DDD)编码实践
  • 系统设计
    • DDD
      • 领域驱动设计在互联网业务开发中的实践
  • 系统设计
    • 基础架构
      • 容错,高可用和灾备
  • 系统设计
    • 数据聚合
      • GraphQL及元数据驱动架构在后端BFF中的实践
  • 系统设计
    • 数据聚合
      • 高效研发-闲鱼在数据聚合上的探索与实践
  • 系统设计
    • 服务安全
      • JSON Web Token 入门教程
  • 系统设计
    • 服务安全
      • 你还在用JWT做身份认证嘛?
  • 系统设计
    • 服务安全
      • 凭证(Credentials)
  • 系统设计
    • 服务安全
      • 授权(Authorization)
  • 系统设计
    • 服务安全
      • 理解OAuth2.0
  • 系统设计
    • 服务安全
      • 认证(Authentication)
  • 系统设计
    • 架构案例
      • 微信 Android 客户端架构演进之路
  • 系统设计
    • 高可用架构
      • 业务高可用的保障:异地多活架构
  • 计算机基础
    • 字符编码
      • Base64原理解析
  • 计算机基础
    • 字符编码
      • 字符编码笔记:ASCII,Unicode 和 UTF-8
  • 计算机基础
    • 操作系统
      • 为什么 CPU 访问硬盘很慢
  • 计算机基础
    • 操作系统
      • 为什么 HTTPS 需要 7 次握手以及 9 倍时延
  • 计算机基础
    • 操作系统
      • 为什么 Linux 默认页大小是 4KB
  • 计算机基础
    • 操作系统
      • 磁盘IO那些事
  • 计算机基础
    • 操作系统
      • 虚拟机的3种网络模式
  • 计算机基础
    • 服务器
      • mac终端bash、zsh、oh-my-zsh最实用教程
  • 计算机基础
    • 服务器
      • Nginx强制跳转Https
  • 计算机基础
    • 服务器
      • curl 的用法指南
  • 计算机基础
    • 网络安全
      • 如何设计一个安全的对外接口?
  • 计算机基础
    • 网络安全
      • 浅谈常见的七种加密算法及实现
  • 计算机基础
    • 网络编程
      • MQTT - The Standard for IoT Messaging
  • 计算机基础
    • 网络编程
      • 两万字长文 50+ 张趣图带你领悟网络编程的内功心法
  • 计算机基础
    • 网络编程
      • 为什么 TCP 协议有 TIME_WAIT 状态
  • 计算机基础
    • 网络编程
      • 为什么 TCP 协议有性能问题
  • 计算机基础
    • 网络编程
      • 为什么 TCP 协议有粘包问题
  • 计算机基础
    • 网络编程
      • 为什么 TCP 建立连接需要三次握手
  • 计算机基础
    • 网络编程
      • 为什么 TCP/IP 协议会拆分数据
  • 计算机基础
    • 网络编程
      • 使用 OAuth 2 和 JWT 为微服务提供安全保障
  • 计算机基础
    • 网络编程
      • 四种常见的 POST 提交数据方式
  • 计算机基础
    • 网络编程
      • 有赞TCP网络编程最佳实践
  • 计算机基础
    • 网络编程
      • 看完这篇HTTP,跟面试官扯皮就没问题了
  • 计算机基础
    • 网络编程
      • 详细解析 HTTP 与 HTTPS 的区别
  • 质量&效率
    • 快捷键
      • Idea快捷键(Mac版)
  • 质量&效率
    • 快捷键
      • Shell快捷键
  • 质量&效率
    • 快捷键
      • conduit
  • 质量&效率
    • 敏捷开发
      • Scrum的3种角色
  • 质量&效率
    • 敏捷开发
      • Scrum的4种会议
  • 质量&效率
    • 敏捷开发
      • ThoughtWorks的敏捷开发
  • 质量&效率
    • 敏捷开发
      • 敏捷开发入门教程
  • 运维&测试
    • Docker
      • Docker (容器) 的原理
  • 运维&测试
    • Docker
      • Docker Compose:链接外部容器的几种方式
  • 运维&测试
    • Docker
      • Docker 入门教程
  • 运维&测试
    • Docker
      • Docker 核心技术与实现原理
  • 运维&测试
    • Docker
      • Dockerfile 最佳实践
  • 运维&测试
    • Docker
      • Docker开启Remote API 访问 2375端口
  • 运维&测试
    • Docker
      • Watchtower - 自动更新 Docker 镜像与容器
  • 运维&测试
    • Kubernetes
      • Kubernetes 介绍
  • 运维&测试
    • Kubernetes
      • Kubernetes 在有赞的实践
  • 运维&测试
    • Kubernetes
      • Kubernetes 学习路径
  • 运维&测试
    • Kubernetes
      • Kubernetes如何改变美团的云基础设施?
  • 运维&测试
    • Kubernetes
      • Kubernetes的三种外部访问方式:NodePort、LoadBalancer 和 Ingress
  • 运维&测试
    • Kubernetes
      • 谈 Kubernetes 的架构设计与实现原理
  • 运维&测试
    • 压测
      • 全链路压测平台(Quake)在美团中的实践
  • 运维&测试
    • 测试
      • Cpress - JavaScript End to End Testing Framework
  • 运维&测试
    • 测试
      • 代码覆盖率-JaCoCo
  • 运维&测试
    • 测试
      • 浅谈代码覆盖率
  • 运维&测试
    • 测试
      • 测试中 Fakes、Mocks 以及 Stubs 概念明晰
  • Java FrameWorks
    • Spring
      • Spring AOP
        • Spring AOP中的Bean是如何被AOP代理的
  • Java FrameWorks
    • Spring
      • Spring AOP
        • Spring AOP原生动态代理和Cglib动态代理
  • Java FrameWorks
    • Spring
      • Spring AOP
        • Spring AOP实现方式(xml&注解)
  • Java FrameWorks
    • Spring
      • Spring AOP
        • Spring AOP是如何收集切面类并封装的
  • Java FrameWorks
    • Spring
      • Spring AOP
        • Spring AOP概述
  • Java FrameWorks
    • Spring
      • Spring AOP
        • Spring AOP的底层核心后置处理器
  • Java FrameWorks
    • Spring
      • Spring AOP
        • Spring AOP的延伸知识
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot - IOC(一)
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot - IOC(三)
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot - IOC(二)
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot - IOC(五)
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot - IOC(四) - 循环依赖与解决方案
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot - 启动引导
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot JarLauncher
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot Web Mvc 自动装配
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot 使用ApplicationListener监听器
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot 声明式事务
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot 嵌入式容器
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot引起的“堆外内存泄漏”排查及经验总结
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot的启动流程
  • Java FrameWorks
    • Spring
      • Spring Boot
        • Spring Boot自动化配置源码分析
  • Java FrameWorks
    • Spring
      • Spring Boot
        • 如何自定义Spring Boot Starter?
  • Java FrameWorks
    • Spring
      • Spring IOC
        • IOC - 模块装配和条件装配
  • Java FrameWorks
    • Spring
      • Spring IOC
        • IOC - 配置源(xml,注解)
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring Environment
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring ApplicationContext
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring BeanDefinition
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring BeanFactory
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring BeanFactoryPostProcessor
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring BeanPostProcessor
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring Bean的生命周期(一) - 概述
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring Bean的生命周期(三) - 实例化阶段
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring Bean的生命周期(二) - BeanDefinition
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring Bean的生命周期(五) - 销毁阶段
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring Bean的生命周期(四) - 初始化阶段
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring ComponentScan
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring Events
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring IOC 基础篇
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring IOC 总结
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring IOC 进阶篇
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring IOC容器的生命周期
  • Java FrameWorks
    • Spring
      • Spring IOC
        • Spring Resource
  • Java FrameWorks
    • Spring
      • Spring MVC
        • DispatcherServlet的初始化原理
  • Java FrameWorks
    • Spring
      • Spring MVC
        • DispatcherServlet的核心工作原理
  • Java FrameWorks
    • Spring
      • Spring MVC
        • WebMvc的架构设计与组件功能解析
  • Java FrameWorks
    • Spring
      • Spring Security
        • Spring Boot 2 + Spring Security 5 + JWT 的单页应用 Restful 解决方案
  • Java FrameWorks
    • Spring
      • Spring Security
        • Spring Security Oauth
  • Java FrameWorks
    • Spring
      • Spring Security
        • Spring Security
  • Java FrameWorks
    • Spring
      • Spring WebFlux
        • DispatcherHandler的工作原理(传统方式)
  • Java FrameWorks
    • Spring
      • Spring WebFlux
        • DispatcherHandler的工作原理(函数式端点)
  • Java FrameWorks
    • Spring
      • Spring WebFlux
        • WebFlux的自动装配
  • Java FrameWorks
    • Spring
      • Spring WebFlux
        • 快速了解响应式编程与Reactive
  • Java FrameWorks
    • Spring
      • Spring WebFlux
        • 快速使用WebFlux
  • 分布式
    • 协调服务
      • Zookeeper
        • Zookeeper - 客户端之 Curator
  • 分布式
    • 协调服务
      • Zookeeper
        • 详解分布式协调服务 ZooKeeper
  • 分布式
    • 协调服务
      • etcd
        • 高可用分布式存储 etcd 的实现原理
  • 数据库
    • Database Version Control
      • Flyway
        • Database Migrations with Flyway
  • 数据库
    • Database Version Control
      • Flyway
        • How Flyway works
  • 数据库
    • Database Version Control
      • Flyway
        • Rolling Back Migrations with Flyway
  • 数据库
    • Database Version Control
      • Flyway
        • The meaning of the concept of checksums
  • 数据库
    • Database Version Control
      • Liquibase
        • Introduction to Liquibase Rollback
  • 数据库
    • Database Version Control
      • Liquibase
        • LiquiBase中文学习指南
  • 数据库
    • Database Version Control
      • Liquibase
        • Use Liquibase to Safely Evolve Your Database Schema
  • 系统设计
    • 流量控制
      • RateLimiter
        • Guava Rate Limiter实现分析
  • 系统设计
    • 流量控制
      • Sentinel
        • Sentinel 与 Hystrix 的对比
  • 系统设计
    • 流量控制
      • Sentinel
        • Sentinel工作主流程
  • 系统设计
    • 流量控制
      • 算法
        • 分布式服务限流实战
  • 系统设计
    • 解决方案
      • 秒杀系统
        • 如何设计一个秒杀系统
  • 系统设计
    • 解决方案
      • 红包系统
        • 微信高并发资金交易系统设计方案--百亿红包背后的技术支撑
  • 计算机基础
    • 数据结构与算法
      • 其他相关
        • 什么是预排序遍历树算法(MPTT,Modified Preorder Tree Traversal)
  • 计算机基础
    • 数据结构与算法
      • 其他相关
        • 加密算法
  • 计算机基础
    • 数据结构与算法
      • 其他相关
        • 推荐系统算法
  • 计算机基础
    • 数据结构与算法
      • 其他相关
        • linkerd
  • 计算机基础
    • 数据结构与算法
      • 其他相关
        • 查找算法
  • 计算机基础
    • 数据结构与算法
      • 其他相关
        • 缓存淘汰算法中的LRU和LFU
  • 计算机基础
    • 数据结构与算法
      • 其他相关
        • 负载均衡算法
  • 计算机基础
    • 数据结构与算法
      • 分布式算法
        • 分布式算法 - Paxos算法
  • 计算机基础
    • 数据结构与算法
      • 分布式算法
        • 分布式算法 - Raft算法
  • 计算机基础
    • 数据结构与算法
      • 分布式算法
        • 分布式算法 - Snowflake算法
  • 计算机基础
    • 数据结构与算法
      • 分布式算法
        • 分布式算法 - ZAB算法
  • 计算机基础
    • 数据结构与算法
      • 分布式算法
        • 分布式算法 - 一致性Hash算法
  • 计算机基础
    • 数据结构与算法
      • 大数据处理
        • 大数据处理 - Bitmap & Bloom Filter
  • 计算机基础
    • 数据结构与算法
      • 大数据处理
        • 大数据处理 - Map & Reduce
  • 计算机基础
    • 数据结构与算法
      • 大数据处理
        • 大数据处理 - Trie树/数据库/倒排索引
  • 计算机基础
    • 数据结构与算法
      • 大数据处理
        • 大数据处理 - 分治/hash/排序
  • 计算机基础
    • 数据结构与算法
      • 大数据处理
        • 大数据处理 - 双层桶划分
  • 计算机基础
    • 数据结构与算法
      • 大数据处理
        • 大数据处理 - 外(磁盘文件)排序
  • 计算机基础
    • 数据结构与算法
      • 大数据处理
        • 大数据处理 - 布隆过滤器
  • 计算机基础
    • 数据结构与算法
      • 大数据处理
        • 大数据处理算法
  • 计算机基础
    • 数据结构与算法
      • 字符串匹配算法
        • 字符串匹配 - 文本预处理:后缀树(Suffix Tree)
  • 计算机基础
    • 数据结构与算法
      • 字符串匹配算法
        • 字符串匹配 - 模式预处理:BM 算法 (Boyer-Moore)
  • 计算机基础
    • 数据结构与算法
      • 字符串匹配算法
        • 字符串匹配 - 模式预处理:KMP 算法(Knuth-Morris-Pratt)
  • 计算机基础
    • 数据结构与算法
      • 字符串匹配算法
        • 字符串匹配 - 模式预处理:朴素算法(Naive)(暴力破解)
  • 计算机基础
    • 数据结构与算法
      • 字符串匹配算法
        • 字符串匹配
  • 计算机基础
    • 数据结构与算法
      • 常用算法
        • 分支限界算法
  • 计算机基础
    • 数据结构与算法
      • 常用算法
        • 分治算法
  • 计算机基础
    • 数据结构与算法
      • 常用算法
        • 动态规划算法
  • 计算机基础
    • 数据结构与算法
      • 常用算法
        • 回溯算法
  • 计算机基础
    • 数据结构与算法
      • 常用算法
        • 贪心算法
  • 计算机基础
    • 数据结构与算法
      • 排序算法
        • 十大排序算法
  • 计算机基础
    • 数据结构与算法
      • 排序算法
        • 图解排序算法(一)之3种简单排序(选择,冒泡,直接插入)
  • 计算机基础
    • 数据结构与算法
      • 排序算法
        • 图解排序算法(三)之堆排序
  • 计算机基础
    • 数据结构与算法
      • 排序算法
        • 图解排序算法(二)之希尔排序
  • 计算机基础
    • 数据结构与算法
      • 排序算法
        • 图解排序算法(四)之归并排序
  • 计算机基础
    • 数据结构与算法
      • 数据结构
        • 树的高度和深度
  • 计算机基础
    • 数据结构与算法
      • 数据结构
        • 红黑树深入剖析及Java实现
  • 计算机基础
    • 数据结构与算法
      • 数据结构
        • 线性结构 - Hash
  • 计算机基础
    • 数据结构与算法
      • 数据结构
        • 线性结构 - 数组、链表、栈、队列
  • 计算机基础
    • 数据结构与算法
      • 数据结构
        • 逻辑结构 - 树
  • 运维&测试
    • 测试
      • Spock
        • Groovy 简明教程
  • 运维&测试
    • 测试
      • Spock
        • Spock 官方文档
  • 运维&测试
    • 测试
      • Spock
        • Spock单元测试框架介绍以及在美团优选的实践
  • 运维&测试
    • 测试
      • TDD
        • TDD 实践 - FizzFuzzWhizz(一)
  • 运维&测试
    • 测试
      • TDD
        • TDD 实践 - FizzFuzzWhizz(三)
  • 运维&测试
    • 测试
      • TDD
        • TDD 实践 - FizzFuzzWhizz(二)
  • 运维&测试
    • 测试
      • TDD
        • 测试驱动开发(TDD)- 原理篇
  • 微服务
    • Spring Cloud
      • Spring Cloud Alibaba
        • Nacos
          • Nacos 服务注册的原理
  • 微服务
    • Spring Cloud
      • Spring Cloud Alibaba
        • Nacos
          • Nacos 配置中心原理分析
  • 微服务
    • Spring Cloud
      • Spring Cloud Alibaba
        • Seata
          • 服务调用过程
  • 微服务
    • Spring Cloud
      • Spring Cloud Alibaba
        • Seata
          • Spring Cloud Bus
  • 微服务
    • Spring Cloud
      • Spring Cloud Alibaba
        • Seata
          • Spring Cloud Consul
  • 微服务
    • Spring Cloud
      • Spring Cloud Alibaba
        • Seata
          • Spring Cloud Stream
  • 微服务
    • Spring Cloud
      • Spring Cloud Alibaba
        • Sentinel
          • Sentinel 与 Hystrix 的对比
  • 微服务
    • Spring Cloud
      • Spring Cloud Alibaba
        • Sentinel
          • Sentinel
  • 微服务
    • Spring Cloud
      • Spring Cloud Netflix
        • Hystrix
          • How Hystrix Works
  • 微服务
    • Spring Cloud
      • Spring Cloud Netflix
        • Hystrix
          • Hystrix
  • 微服务
    • Spring Cloud
      • Spring Cloud Netflix
        • Hystrix
          • Hystrix原理与实战
  • 微服务
    • Spring Cloud
      • Spring Cloud Netflix
        • Hystrix
          • Spring Cloud Hystrix基本原理
由 GitBook 提供支持
在本页
  • 1. 背景
  • 1.1. 宕机的代价
  • 1.2. 软件可靠性
  • 1.3. Netty 的可靠性
  • 2. Netty 高可靠性之道
  • 2.1. 网络通信类故障
  • 2.2. 链路的有效性检测
  • 2.3. Reactor 线程的保护
  • 2.4. 优雅退出
  • 2.5. 内存保护
  • 2.6. 流量整形
  • 2.6.1. 全局流量整形
  • 3. 总结
  • 4. Netty 学习推荐书籍
  • 5. 参考

这有帮助吗?

  1. Java FrameWorks
  2. Netty

Netty 可靠性分析

[TOC]

1. 背景

1.1. 宕机的代价

1.1.1. 电信行业

毕马威国际 (KPMG International) 在对 46 个国家的 74 家运营商进行调查后发现,全球通信行业每年的收益流失约为 400 亿美元,占总收入的 1%-3%。导致收益流失的因素有多种,主要原因就是计费 BUG。

1.1.2. 互联网行业

美国太平洋时间 8 月 16 日下午 3 点 50 分到 3 点 55 分(北京时间 8 月 17 日 6 点 50 分到 6 点 55 分),谷歌遭遇了宕机。根据事后统计,短短的 5 分钟,谷歌损失了 54.5 万美元。也就是服务每中断一分钟,损失就达 10.8 万美元。

2013 年,从美国东部时间 8 月 19 日下午 2 点 45 分开始,有用户率先发现了亚马逊网站出现宕机,大约在 20 多分钟后又恢复正常。此次宕机让亚马逊每分钟损失近 6.7 万美元,在宕机期间,消费者无法通过 Amazon.com、亚马逊移动端以及 Amazon.ca 等网站进行购物。

1.2. 软件可靠性

软件可靠性是指在给定时间内,特定环境下软件无错运行的概率。软件可靠性包含了以下三个要素:

  1. 规定的时间:软件可靠性只是体现在其运行阶段,所以将运行时间作为规定的时间的度量。运行时间包括软件系统运行后工作与挂起 (开启但空闲) 的累计时间。由于软件运行的环境与程序路径选取的随机性,软件的失效为随机事件,所以运行时间属于随机变量 ;

  2. 规定的环境条件: 环境条件指软件的运行环境。它涉及软件系统运行时所需的各种支持要素,如支持硬件、操作系统、其它支持软件、输入数据格式和范围以及操作规程等。不同的环境条件下软件的可靠性是不同的。具体地说,规定的环境条件主要是描述软件系统运行时计算机的配置情况以及对输入数据的要求,并假定其它一切因素都是理想的。有了明确规定的环境条件,还可以有效判断软件失效的责任在用户方还是提供方 ;

  3. 规定的功能: 软件可靠性还与规定的任务和功能有关。由于要完成的任务不同,软件的运行剖面会有所区别,则调用的子模块就不同 (即程序路径选择不同),其可靠性也就可能不同。所以要准确度量软件系统的可靠性必须首先明确它的任务和功能。

1.3. Netty 的可靠性

首先,我们要从 Netty 的主要用途来分析它的可靠性,Netty 目前的主流用法有三种:

  1. 构建 RPC 调用的基础通信组件,提供跨节点的远程服务调用能力;

  2. NIO 通信框架,用于跨节点的数据交换;

  3. 其它应用协议栈的基础通信组件,例如 HTTP 协议以及其它基于 Netty 开发的应用层协议栈。

以阿里的分布式服务框架 Dubbo 为例,Netty 是 Dubbo RPC 框架的核心。它的服务调用示例图如下:

图 1-1 Dubbo 的节点角色说明图

其中,服务提供者和服务调用者之间可以通过 Dubbo 协议进行 RPC 调用,消息的收发默认通过 Netty 完成。

通过对 Netty 主流应用场景的分析,我们发现 Netty 面临的可靠性问题大致分为三类:

  1. 传统的网络 I/O 故障,例如网络闪断、防火墙 Hang 住连接、网络超时等;

  2. NIO 特有的故障,例如 NIO 类库特有的 BUG、读写半包处理异常、Reactor 线程跑飞等等;

  3. 编解码相关的异常。

在大多数的业务应用场景中,一旦因为某些故障导致 Netty 不能正常工作,业务往往会陷入瘫痪。所以,从业务诉求来看,对 Netty 框架的可靠性要求是非常的高。作为当前业界最流行的一款 NIO 框架,Netty 在不同行业和领域都得到了广泛的应用,它的高可靠性已经得到了成百上千的生产系统检验。

Netty 是如何支持系统高可靠性的?下面,我们就从几个不同维度出发一探究竟。

2. Netty 高可靠性之道

2.1. 网络通信类故障

2.1.1. 客户端连接超时

在传统的同步阻塞编程模式下,客户端 Socket 发起网络连接,往往需要指定连接超时时间,这样做的目的主要有两个:

  1. 在同步阻塞 I/O 模型中,连接操作是同步阻塞的,如果不设置超时时间,客户端 I/O 线程可能会被长时间阻塞,这会导致系统可用 I/O 线程数的减少;

  2. 业务层需要:大多数系统都会对业务流程执行时间有限制,例如 WEB 交互类的响应时间要小于 3S。客户端设置连接超时时间是为了实现业务层的超时。

JDK 原生的 Socket 连接接口定义如下:

图 2-1 JDK Socket 连接超时接口

对于 NIO 的 SocketChannel,在非阻塞模式下,它会直接返回连接结果,如果没有连接成功,也没有发生 IO 异常,则需要将 SocketChannel 注册到 Selector 上监听连接结果。所以,异步连接的超时无法在 API 层面直接设置,而是需要通过定时器来主动监测。

下面我们首先看下 JDK NIO 类库的 SocketChannel 连接接口定义:

图 2-2 JDK NIO 类库 SocketChannel 连接接口

从上面的接口定义可以看出,NIO 类库并没有现成的连接超时接口供用户直接使用,如果要在 NIO 编程中支持连接超时,往往需要 NIO 框架或者用户自己封装实现。

下面我们看下 Netty 是如何支持连接超时的,首先,在创建 NIO 客户端的时候,可以配置连接超时参数:

图 2-3 Netty 客户端创建支持设置连接超时参数

设置完连接超时之后,Netty 在发起连接的时候,会根据超时时间创建 ScheduledFuture 挂载在 Reactor 线程上,用于定时监测是否发生连接超时,相关代码如下:

图 2-4 根据连接超时创建超时监测定时任务

创建连接超时定时任务之后,会由 NioEventLoop 负责执行。如果已经连接超时,但是服务端仍然没有返回 TCP 握手应答,则关闭连接,代码如上图所示。

如果在超时期限内处理完成连接操作,则取消连接超时定时任务,相关代码如下:

图 2-5 取消连接超时定时任务

Netty 的客户端连接超时参数与其它常用的 TCP 参数一起配置,使用起来非常方便,上层用户不用关心底层的超时实现机制。这既满足了用户的个性化需求,又实现了故障的分层隔离。

2.1.2. 通信对端强制关闭连接

在客户端和服务端正常通信过程中,如果发生网络闪断、对方进程突然宕机或者其它非正常关闭链路事件时,TCP 链路就会发生异常。由于 TCP 是全双工的,通信双方都需要关闭和释放 Socket 句柄才不会发生句柄的泄漏。

在实际的 NIO 编程过程中,我们经常会发现由于句柄没有被及时关闭导致的功能和可靠性问题。究其原因总结如下:

  1. IO 的读写等操作并非仅仅集中在 Reactor 线程内部,用户上层的一些定制行为可能会导致 IO 操作的外逸,例如业务自定义心跳机制。这些定制行为加大了统一异常处理的难度,IO 操作越发散,故障发生的概率就越大;

  2. 一些异常分支没有考虑到,由于外部环境诱因导致程序进入这些分支,就会引起故障。

下面我们通过故障模拟,看 Netty 是如何处理对端链路强制关闭异常的。首先启动 Netty 服务端和客户端,TCP 链路建立成功之后,双方维持该链路,查看链路状态,结果如下:

图 2-6 Netty 服务端和客户端 TCP 链路状态正常

强制关闭客户端,模拟客户端宕机,服务端控制台打印如下异常:

图 2-7 模拟 TCP 链路故障

从堆栈信息可以判断,服务端已经监控到客户端强制关闭了连接,下面我们看下服务端是否已经释放了连接句柄,再次执行 netstat 命令,执行结果如下:

图 2-8 查看故障链路状态

从执行结果可以看出,服务端已经关闭了和客户端的 TCP 连接,句柄资源正常释放。由此可以得出结论,Netty 底层已经自动对该故障进行了处理。

下面我们一起看下 Netty 是如何感知到链路关闭异常并进行正确处理的,查看 AbstractByteBuf 的 writeBytes 方法,它负责将指定 Channel 的缓冲区数据写入到 ByteBuf 中,详细代码如下:

图 2-9 AbstractByteBuf 的 writeBytes 方法

在调用 SocketChannel 的 read 方法时发生了 IOException,代码如下:

图 2-10 读取缓冲区数据发生 IO 异常

为了保证 IO 异常被统一处理,该异常向上抛,由 AbstractNioByteChannel 进行统一异常处理,代码如下:

图 2-11 链路异常退出异常处理

为了能够对异常策略进行统一,也为了方便维护,防止处理不当导致的句柄泄漏等问题,句柄的关闭,统一调用 AbstractChannel 的 close 方法,代码如下:

图 2-12 统一的 Socket 句柄关闭接口

2.1.3. 正常的连接关闭

对于短连接协议,例如 HTTP 协议,通信双方数据交互完成之后,通常按照双方的约定由服务端关闭连接,客户端获得 TCP 连接关闭请求之后,关闭自身的 Socket 连接,双方正式断开连接。

在实际的 NIO 编程过程中,经常存在一种误区:认为只要是对方关闭连接,就会发生 IO 异常,捕获 IO 异常之后再关闭连接即可。实际上,连接的合法关闭不会发生 IO 异常,它是一种正常场景,如果遗漏了该场景的判断和处理就会导致连接句柄泄漏。

下面我们一起模拟故障,看 Netty 是如何处理的。测试场景设计如下:改造下 Netty 客户端,双发链路建立成功之后,等待 120S,客户端正常关闭链路。看服务端是否能够感知并释放句柄资源。

首先启动 Netty 客户端和服务端, 双方 TCP 链路连接正常:

图 2-13 TCP 连接状态正常

120S 之后,客户端关闭连接,进程退出,为了能够看到整个处理过程,我们在服务端的 Reactor 线程处设置断点,先不做处理,此时链路状态如下:

图 2-14 TCP 连接句柄等待释放

从上图可以看出,此时服务端并没有关闭 Socket 连接,链路处于 CLOSE_WAIT 状态,放开代码让服务端执行完,结果如下:

图 2-15 TCP 连接句柄正常释放

下面我们一起看下服务端是如何判断出客户端关闭连接的,当连接被对方合法关闭后,被关闭的 SocketChannel 会处于就绪状态,SocketChannel 的 read 操作返回值为 -1,说明连接已经被关闭,代码如下:

图 2-16 需要对读取的字节数进行判断

如果 SocketChannel 被设置为非阻塞,则它的 read 操作可能返回三个值:

  1. 大于 0,表示读取到了字节数;

  2. 等于 0,没有读取到消息,可能 TCP 处于 Keep-Alive 状态,接收到的是 TCP 握手消息;

  3. -1,连接已经被对方合法关闭。

通过调试,我们发现,NIO 类库的返回值确实为 -1:

图 2-17 链路正常关闭,返回值为 -1

得知连接关闭之后,Netty 将关闭操作位设置为 true, 关闭句柄,代码如下:

图 2-18 连接正常关闭,释放资源

2.1.4. 故障定制

在大多数场景下,当底层网络发生故障的时候,应该由底层的 NIO 框架负责释放资源,处理异常等。上层的业务应用不需要关心底层的处理细节。但是,在一些特殊的场景下,用户可能需要感知这些异常,并针对这些异常进行定制处理,例如:

  1. 客户端的断连重连机制;

  2. 消息的缓存重发;

  3. 接口日志中详细记录故障细节;

  4. 运维相关功能,例如告警、触发邮件 / 短信等

Netty 的处理策略是发生 IO 异常,底层的资源由它负责释放,同时将异常堆栈信息以事件的形式通知给上层用户,由用户对异常进行定制。这种处理机制既保证了异常处理的安全性,也向上层提供了灵活的定制能力。

具体接口定义以及默认实现如下:

图 2-19 故障定制接口

用户可以覆盖该接口,进行个性化的异常定制。例如发起重连等。

2.2. 链路的有效性检测

当网络发生单通、连接被防火墙 Hang 住、长时间 GC 或者通信线程发生非预期异常时,会导致链路不可用且不易被及时发现。特别是异常发生在凌晨业务低谷期间,当早晨业务高峰期到来时,由于链路不可用会导致瞬间的大批量业务失败或者超时,这将对系统的可靠性产生重大的威胁。

从技术层面看,要解决链路的可靠性问题,必须周期性的对链路进行有效性检测。目前最流行和通用的做法就是心跳检测。

心跳检测机制分为三个层面:

  1. TCP 层面的心跳检测,即 TCP 的 Keep-Alive 机制,它的作用域是整个 TCP 协议栈;

  2. 协议层的心跳检测,主要存在于长连接协议中。例如 SMPP 协议;

  3. 应用层的心跳检测,它主要由各业务产品通过约定方式定时给对方发送心跳消息实现。

心跳检测的目的就是确认当前链路可用,对方活着并且能够正常接收和发送消息。

做为高可靠的 NIO 框架,Netty 也提供了心跳检测机制,下面我们一起熟悉下心跳的检测原理。

图 2-20 心跳检测机制

不同的协议,心跳检测机制也存在差异,归纳起来主要分为两类:

  1. Ping-Pong 型心跳:由通信一方定时发送 Ping 消息,对方接收到 Ping 消息之后,立即返回 Pong 应答消息给对方,属于请求 - 响应型心跳;

  2. Ping-Ping 型心跳:不区分心跳请求和应答,由通信双方按照约定定时向对方发送心跳 Ping 消息,它属于双向心跳。

心跳检测策略如下:

  1. 连续 N 次心跳检测都没有收到对方的 Pong 应答消息或者 Ping 请求消息,则认为链路已经发生逻辑失效,这被称作心跳超时;

  2. 读取和发送心跳消息的时候如何直接发生了 IO 异常,说明链路已经失效,这被称为心跳失败。

无论发生心跳超时还是心跳失败,都需要关闭链路,由客户端发起重连操作,保证链路能够恢复正常。

Netty 的心跳检测实际上是利用了链路空闲检测机制实现的,相关代码如下:

图 2-21 心跳检测的代码包路径

Netty 提供的空闲检测机制分为三种:

  1. 读空闲,链路持续时间 t 没有读取到任何消息;

  2. 写空闲,链路持续时间 t 没有发送任何消息;

  3. 读写空闲,链路持续时间 t 没有接收或者发送任何消息。

Netty 的默认读写空闲机制是发生超时异常,关闭连接,但是,我们可以定制它的超时实现机制,以便支持不同的用户场景。

WriteTimeoutHandler 的超时接口如下:

图 2-22 写超时

ReadTimeoutHandler 的超时接口如下:

图 2-23 读超时

读写空闲的接口如下:

图 2-24 读写空闲

利用 Netty 提供的链路空闲检测机制,可以非常灵活的实现协议层的心跳检测。在《Netty 权威指南》中的私有协议栈设计和开发章节,我利用 Netty 提供的自定义 Task 接口实现了另一种心跳检测机制,感兴趣的朋友可以参阅该书。

2.3. Reactor 线程的保护

Reactor 线程是 IO 操作的核心,NIO 框架的发动机,一旦出现故障,将会导致挂载在其上面的多路用复用器和多个链路无法正常工作。因此它的可靠性要求非常高。

笔者就曾经遇到过因为异常处理不当导致 Reactor 线程跑飞,大量业务请求处理失败的故障。下面我们一起看下 Netty 是如何有效提升 Reactor 线程的可靠性的。

2.3.1. 异常处理要当心

尽管 Reactor 线程主要处理 IO 操作,发生的异常通常是 IO 异常,但是,实际上在一些特殊场景下会发生非 IO 异常,如果仅仅捕获 IO 异常可能就会导致 Reactor 线程跑飞。为了防止发生这种意外,在循环体内一定要捕获 Throwable,而不是 IO 异常或者 Exception。

Netty 的相关代码如下:

图 2-25 Reactor 线程异常保护

捕获 Throwable 之后,即便发生了意外未知对异常,线程也不会跑飞,它休眠 1S,防止死循环导致的异常绕接,然后继续恢复执行。这样处理的核心理念就是:

  1. 某个消息的异常不应该导致整条链路不可用;

  2. 某条链路不可用不应该导致其它链路不可用;

  3. 某个进程不可用不应该导致其它集群节点不可用。

2.3.2. 死循环保护

通常情况下,死循环是可检测、可预防但是无法完全避免的。Reactor 线程通常处理的都是 IO 相关的操作,因此我们重点关注 IO 层面的死循环。

JDK NIO 类库最著名的就是 epoll bug 了,它会导致 Selector 空轮询,IO 线程 CPU 100%,严重影响系统的安全性和可靠性。

SUN 在 JKD1.6 update18 版本声称解决了该 BUG,但是根据业界的测试和大家的反馈,直到 JDK1.7 的早期版本,该 BUG 依然存在,并没有完全被修复。发生该 BUG 的主机资源占用图如下:

图 2-26 epoll bug CPU 空轮询

SUN 在解决该 BUG 的问题上不给力,只能从 NIO 框架层面进行问题规避,下面我们看下 Netty 是如何解决该问题的。

Netty 的解决策略:

  1. 根据该 BUG 的特征,首先侦测该 BUG 是否发生;

  2. 将问题 Selector 上注册的 Channel 转移到新建的 Selector 上;

  3. 老的问题 Selector 关闭,使用新建的 Selector 替换。

下面具体看下代码,首先检测是否发生了该 BUG:

图 2-27 epoll bug 检测

一旦检测发生该 BUG,则重建 Selector,代码如下:

图 2-28 重建 Selector

重建完成之后,替换老的 Selector,代码如下:

图 2-29 替换 Selector

大量生产系统的运行表明,Netty 的规避策略可以解决 epoll bug 导致的 IO 线程 CPU 死循环问题。

2.4. 优雅退出

Java 的优雅停机通常通过注册 JDK 的 ShutdownHook 来实现,当系统接收到退出指令后,首先标记系统处于退出状态,不再接收新的消息,然后将积压的消息处理完,最后调用资源回收接口将资源销毁,最后各线程退出执行。

通常优雅退出有个时间限制,例如 30S,如果到达执行时间仍然没有完成退出前的操作,则由监控脚本直接 kill -9 pid,强制退出。

Netty 的优雅退出功能随着版本的优化和演进也在不断的增强,下面我们一起看下 Netty5 的优雅退出。

首先看下 Reactor 线程和线程组,它们提供了优雅退出接口。EventExecutorGroup 的接口定义如下:

图 2-30 EventExecutorGroup 优雅退出

NioEventLoop 的资源释放接口实现:

图 2-31 NioEventLoop 资源释放

ChannelPipeline 的关闭接口:

图 2-32 ChannelPipeline 关闭接口

目前 Netty 向用户提供的主要接口和类库都提供了资源销毁和优雅退出的接口,用户的自定义实现类可以继承这些接口,完成用户资源的释放和优雅退出。

2.5. 内存保护

2.5.1. 缓冲区的内存泄漏保护

为了提升内存的利用率,Netty 提供了内存池和对象池。但是,基于缓存池实现以后需要对内存的申请和释放进行严格的管理,否则很容易导致内存泄漏。

如果不采用内存池技术实现,每次对象都是以方法的局部变量形式被创建,使用完成之后,只要不再继续引用它,JVM 会自动释放。但是,一旦引入内存池机制,对象的生命周期将由内存池负责管理,这通常是个全局引用,如果不显式释放 JVM 是不会回收这部分内存的。

对于 Netty 的用户而言,使用者的技术水平差异很大,一些对 JVM 内存模型和内存泄漏机制不了解的用户,可能只记得申请内存,忘记主动释放内存,特别是 JAVA 程序员。

为了防止因为用户遗漏导致内存泄漏,Netty 在 Pipe line 的尾 Handler 中自动对内存进行释放,相关代码如下:

图 2-33 TailHandler 的内存回收操作

对于内存池,实际就是将缓冲区重新放到内存池中循环使用,代码如下:

图 2-34 PooledByteBuf 的内存回收操作

2.5.2. 缓冲区内存溢出保护

做过协议栈的读者都知道,当我们对消息进行解码的时候,需要创建缓冲区。缓冲区的创建方式通常有两种:

  1. 容量预分配,在实际读写过程中如果不够再扩展;

  2. 根据协议消息长度创建缓冲区。

在实际的商用环境中,如果遇到畸形码流攻击、协议消息编码异常、消息丢包等问题时,可能会解析到一个超长的长度字段。笔者曾经遇到过类似问题,报文长度字段值竟然是 2G 多,由于代码的一个分支没有对长度上限做有效保护,结果导致内存溢出。系统重启后几秒内再次内存溢出,幸好及时定位出问题根因,险些酿成严重的事故。

Netty 提供了编解码框架,因此对于解码缓冲区的上限保护就显得非常重要。下面,我们看下 Netty 是如何对缓冲区进行上限保护的:

首先,在内存分配的时候指定缓冲区长度上限:

图 2-35 缓冲区分配器可以指定缓冲区最大长度

其次,在对缓冲区进行写入操作的时候,如果缓冲区容量不足需要扩展,首先对最大容量进行判断,如果扩展后的容量超过上限,则拒绝扩展:

图 2-35 缓冲区扩展上限保护

最后,在解码的时候,对消息长度进行判断,如果超过最大容量上限,则抛出解码异常,拒绝分配内存:

图 2-36 超出容量上限的半包解码,失败

图 2-37 抛出 TooLongFrameException 异常

2.6. 流量整形

大多数的商用系统都有多个网元或者部件组成,例如参与短信互动,会涉及到手机、基站、短信中心、短信网关、SP/CP 等网元。不同网元或者部件的处理性能不同。为了防止因为浪涌业务或者下游网元性能低导致下游网元被压垮,有时候需要系统提供流量整形功能。

流量整形的原理示意图如下:

图2-38 流量整形原理图

作为高性能的NIO 框架,Netty 的流量整形有两个作用:

  1. 防止由于上下游网元性能不均衡导致下游网元被压垮,业务流程中断;

  2. 防止由于通信模块接收消息过快,后端业务线程处理不及时导致的“撑死”问题。

下面我们就具体学习下 Netty 的流量整形功能。

2.6.1. 全局流量整形

全局流量整形的作用范围是进程级的,无论你创建了多少个 Channel,它的作用域针对所有的 Channel。

用户可以通过参数设置:报文的接收速率、报文的发送速率、整形周期。相关的接口如下所示:

图 2-39 全局流量整形参数设置

Netty 流量整形的原理是:对每次读取到的 ByteBuf 可写字节数进行计算,获取当前的报文流量,然后与流量整形阈值对比。如果已经达到或者超过了阈值。则计算等待时间 delay,将当前的 ByteBuf 放到定时任务 Task 中缓存,由定时任务线程池在延迟 delay 之后继续处理该 ByteBuf。相关代码如下:

图 2-40 动态计算当前流量

如果达到整形阈值,则对新接收的 ByteBuf 进行缓存,放入线程池的消息队列中,稍后处理,代码如下:

图 2-41 缓存当前的 ByteBuf

定时任务的延时时间根据检测周期 T 和流量整形阈值计算得来,代码如下:

图 2-42 计算缓存等待周期

需要指出的是,流量整形的阈值 limit 越大,流量整形的精度越高,流量整形功能是可靠性的一种保障,它无法做到 100% 的精确。这个跟后端的编解码以及缓冲区的处理策略相关,此处不再赘述。感兴趣的朋友可以思考下,Netty 为什么不做到 100% 的精确。

流量整形与流控的最大区别在于流控会拒绝消息,流量整形不拒绝和丢弃消息,无论接收量多大,它总能以近似恒定的速度下发消息,跟变压器的原理和功能类似。

2.6.2. 单条链路流量整形

除了全局流量整形,Netty 也支持但链路的流量整形,相关的接口定义如下:

图 2-43 单链路流量整形

单链路流量整形与全局流量整形的最大区别就是它以单个链路为作用域,可以对不同的链路设置不同的整形策略。

它的实现原理与全局流量整形类似,我们不再赘述。值得说明的是,Netty 支持用户自定义流量整形策略,通过继承 AbstractTrafficShapingHandler 的 doAccounting 方法可以定制整形策略。相关接口定义如下:

图 2-44 定制流量整形策略

3. 总结

尽管 Netty 在架构可靠性上面已经做了很多精细化的设计,以及基于防御式编程对系统进行了大量可靠性保护。但是,系统的可靠性是个持续投入和改进的过程,不可能在一个版本中一蹴而就,可靠性工作任重而道远。

从业务的角度看,不同的行业、应用场景对可靠性的要求也是不同的,例如电信行业的可靠性要求是 5 个 9,对于铁路等特殊行业,可靠性要求更高,达到 6 个 9。对于企业的一些边缘 IT 系统,可靠性要求会低些。

可靠性是一种投资,对于企业而言,追求极端可靠性对研发成本是个沉重的包袱,但是相反,如果不重视系统的可靠性,一旦不幸遭遇网上事故,损失往往也是惊人的。

对于架构师和设计师,如何权衡架构的可靠性和其它特性的关系,是一个很大的挑战。通过研究和学习 Netty 的可靠性设计,也许能够给大家带来一些启示。

4. Netty 学习推荐书籍

目前市面上介绍 netty 的文章很多,如果读者希望系统性的学习 Netty,推荐两本书:

  1. 《Netty in Action》

  2. 《Netty 权威指南》

5. 参考

上一页Netty下一页Java FrameWorks

最后更新于2年前

这有帮助吗?

2021-02-23-zxwQNc
2021-02-23-WQVajC
2021-02-23-jQlytd
2021-02-23-z53iAi
2021-02-23-7RYGkh
2021-02-23-vk8FOQ
2021-02-23-NmjxGg
2021-02-23-yJoyzA
2021-02-23-By4HPF
2021-02-23-s2peEk
2021-02-23-219fkZ
2021-02-23-4uaeEA
2021-02-23-ev0Mf2
2021-02-23-S55Jfd
2021-02-23-LMdx98
2021-02-23-D44moi
2021-02-23-b6WABn
2021-02-23-xUFHc5
2021-02-23-h0q3Qx
2021-02-23-AZUKt9
2021-02-23-x8Iv63
2021-02-23-LHF9Ix
2021-02-23-7caVaJ
2021-02-23-1Kua06
2021-02-23-yyTxAI
2021-02-23-QIIOkz
2021-02-23-kBzgIH
2021-02-23-HgiVNZ
2021-02-23-bcTqJT
2021-02-23-tCJLdv
2021-02-23-90lV1P
2021-02-23-ZhEcam
2021-02-23-fL8ZT8
2021-02-23-OGlLdZ
2021-02-23-bSwlwv
2021-02-23-ejYK3B
2021-02-23-5fUi6C
2021-02-23-nfHhwl
2021-02-23-ebVoQV

下面我们一起看下流量整形 (traffic shaping) 的定义:流量整形(Traffic Shaping)是一种主动调整流量输出速率的措施。一个典型应用是基于下游的TP 指标来控制本地流量的输出。流量整形与流量监管的主要区别在于,流量整形对流量监管中需要丢弃的进行缓存——通常是将它们放入缓冲区或内,也称流量整形(Traffic Shaping,简称TS)。当令牌桶有足够的令牌时,再均匀的向外发送这些被的报文。流量整形与流量监管的另一区别是,整形可能会增加延迟,而监管几乎不引入额外的延迟。

2021-02-23-mu4tvI
2021-02-23-soKLTB
2021-02-23-hHXiv8
2021-02-23-k0vZIi
2021-02-23-vkScft
2021-02-23-JnE2Kb
2021-02-23-EQ0nlX

网络结点
报文
队列
缓存
Netty 系列之 Netty 可靠性分析