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 提供支持
在本页
  • 0. 概述
  • 1. 服务端监听设置SO_REUSEADDR选项
  • 2. 建立并遵守应用监听端口规范
  • 3. 应用服务端口与管理端口分离
  • 4. 建立连接设置超时时间
  • 5. 使用应用层心跳对连接进行健康检查
  • 6. 连接重连需要增加退让与窗口抖动
  • 7. 服务端需要限制最大连接数
  • 8. 尽量不要依赖中心化四层负载均衡器
  • 9. 警惕大量CLOSE_WAIT
  • 10. 合理设置长连接TTL
  • 11. 通过域名访问服务需定期解析DNS
  • 12. 降低网络读写系统调用次数
  • 13. 谨慎设置TCP缓冲区大小
  • 14. 网络相关参数支持灵活配置
  • 15. 合理设置连接池大小
  • 16. 完善网络指标监控
  • 17. 总结
  • 18. 参考资料

这有帮助吗?

  1. 计算机基础
  2. 网络编程

有赞TCP网络编程最佳实践

上一页网络编程下一页计算机基础

最后更新于2年前

这有帮助吗?

原文链接:有赞TCP网络编程最佳实践

0. 概述

本文是根据有赞中间件团队多年的TCP网络编程实践经验总结而来,目的是为了避免应用因各种网络异常而出现各种非预期行为,从而造成非预期的影响,影响系统稳定性与可靠性。

本文不会涉及TCP的各个基础知识点,主要是总结一些TCP网络编程实践中可能碰到的一些问题,以及相应的经过实践验证的解决方案等。虽然本文档很多细节主要是针对于Linux系统,不过,大部分建议适合于所有系统。

本文共总结了16项建议,下面逐一进行介绍。

1. 服务端监听设置SO_REUSEADDR选项

当我们重启服务端程序的时候可能会碰到“address already in use”这样的报错信息,即地址已被使用,导致程序无法快速成功重启。老的进程关闭退出了,为什么还会报地址已被使用呢?

我们先来理解如下两点:

  • TCP连接主动关闭方存在持续2MSL的TIME_WAIT状态;

  • TCP连接由是由四元组<本地地址,本地端口,远程地址,远程端口>来确定的。

我们先简单回顾一下TCP连接关闭过程中的TIME_WAIT状态,如下所示:

(图片来源:Wikipedia)

参考:为什么 TCP 协议有 TIME_WAIT 状态

TIME_WAIT存在的意义主要有两点:

  1. 维护连接状态,使TCP连接能够可靠地关闭。如果连接主动关闭端发送的最后一条ACK丢失,连接被动关闭端会重传FIN报文。因此,主动关闭方必须维持连接状态,以支持收到重传的FIN后再次发送ACK。如果没有TIME_WAIT,并且最后一个ACK丢失,那么此时被动关闭端还会处于LAST_ACK一段时间,并等待重传;如果此时主动关闭方又立即创建新TCP连接且恰好使用了相同的四元组,连接会创建失败,会被对端重置。

  2. 等待网络中所有此连接老的重复的、走失的报文消亡,避免此类报文对新的相同四元组的TCP连接造成干扰,因为这些报文的序号可能恰好落在新连接的接收窗口内。

因为每个TCP报文最大存活时间为MSL,一个往返最大是2*MSL,所以TIME_WAIT需要等待2MSL。

当进程关闭时,进程会发起连接的主动关闭,连接最后会进入TIME_WAIT状态。当新进程bind监听端口时,就会报错,因为有对应本地端口的连接还处于TIME_WAIT状态。

实际上,只有当新的TCP连接和老的TCP连接四元组完全一致,且老的迷走的报文序号落在新连接的接收窗口内时,才会造成干扰。为了使用TIME_WAIT状态的端口,现在大部分系统的实现都做了相关改进与扩展:

  • 新连接SYN告知的初始序列号,要求一定要比TIME_WAIT状态老连接的序列号大,可以一定程度保证不会与老连接的报文序列号重叠。

  • 开启TCP timestamps扩展选项后,新连接的时间戳要求一定要比TIME_WAIT状态老连接的时间戳大,可以保证老连接的报文不会影响新连接。

因此,在开启了TCP timestamps扩展选项的情况下(net.ipv4.tcp_timestamps = 1),可以放心的设置SO_REUSEADDR选项,支持程序快速重启。

注意不要与net.ipv4.tcp_tw_reuse系统参数混淆,该参数仅在客户端调用connect创建连接时才生效,可以使用TIME_WAIT状态超过1秒的端口(防止最后一个ACK丢失);而SO_REUSEADDR是在bind端口时生效,一般用于服务端监听时,可以使用本地非LISTEN状态的端口(另一个端口也必须设置SO_REUSEADDR),不仅仅是TIME_WAIT状态端口。

2. 建立并遵守应用监听端口规范

每个应用、每个通信协议要有固定统一的监听端口,便于在公司内部形成共识,降低协作成本,提升运维效率。如对于一些网络ACL控制,规范统一的端口会给运维带来极大的便利。

应用监听端口不能在net.ipv4.ip_local_port_range区间内,这个区间是操作系统用于本地端口号自动分配的(bind或connect时没有指定端口号),Linux系统默认值为[32768, 60999]。现在一个应用服务器实例(无论是VM还是K8S Pod等),本地不仅仅会包含应用进程自身,还可能会包括监控采集、sidecar代理等进程。如果选了net.ipv4.ip_local_port_range这个范围内的端口作为监听端口,你的应用进程启动前,对应的端口很可能已经被自动分配给其他进程的TCP连接,就会导致监听端口绑定失败,从而导致进程启动失败;当然,如果已经分配的端口设置了SO_REUSEADDR也不会导致你的应用监听端口绑定失败,但这些临时端口一般都不会设置SO_REUSEADDR。如果确实有需求监听net.ipv4.ip_local_port_range区间内的端口(如保留三方系统的默认端口),可以设置net.ipv4.ip_local_reserved_ports系统参数进行预留,预留的端口不会被自动分配出去;但这样会给运维增加系统的交付难度,所以,一般不建议这样做。

有赞的net.ipv4.ip_local_port_range系统值设置为[9000, 65535],并且对所有类型的应用、通信协议监听端口都进行了统一规范,监听端口都小于9000。

在对于繁忙的网络服务器,如代理服务器或负载平衡器,我们可能需要增加网络端口范围来增强它的处理能力。

在Linux上,有一个sysctl参数ip_local_port_range,可用于定义网络连接可用作其源(本地)端口的最小和最大端口的限制,同时适用于TCP和UDP连接。

查看当前系统开放端口范围(系统随机分配的端口范围),命令如下:

# cat /proc/sys/net/ipv4/ip_local_port_range 
32768 61000

如上命令显示"最小最大值",因此新连接的本地端口将介于32768和61000之间,默认情况下28232个随机端口,看起来很多,但在繁忙的流量服务器的时候可能很容易达到这个限制。可以通过如下配置对其修改:

# sysctl -w net.ipv4.ip_local_port_range="15000 64000"net.ipv4.ip_local_port_range = 15000 64000

或者直接通过使用echo直接传递值给/proc目录下的文件。

echo "15000 64000" > /proc/sys/net/ipv4/ip_local_port_range

如果想要持久生效,并且重启不会失效,请将配置保存在/etc/sysctl.conf的配置文件中。

# cat /etc/sysctl.d/net.ipv4.ip_local_port_range.confnet.ipv4.ip_local_port_range = 15000 65000

Linux下面一共有65535个端口,其中1–1023是系统保留的,1024–65535是供用户使用的。0到1024是众所周知的端口(知名端口,常用于系统服务等,例如http服务的端口号是80)。

正常情况下,所有的端口都可用,但是某些低位的端口号是系统服务保留的。

3. 应用服务端口与管理端口分离

服务端口即业务请求的处理端口,管理端口为框架或应用的管理请求处理端口(如服务注册上线、下线)。以Spring Boot为例,应用端口对应server.port,管理端口对应management.port。

应用的服务端口与管理端口分离有如下意义:

  • 避免业务请求与管理请求互相影响,如线程池等。

  • 更好地进行权限管理、ACL控制等。管理端口一般可以控制应用的核心行为,需要进行严格的权限管理、ACL控制,比如通过防火墙仅允许特定IP访问管理端口等。

还是不太明白这两者的区别。2021-07-14

有赞线上曾经碰到过一个问题:一个Dubbo业务应用提供HTTP服务和Dubbo服务,HTTP服务端口与HTTP管理端口是同一个,该应用的一个实例因内部逻辑问题发生了死锁,造成请求阻塞超时,但这时服务注册的健康保活线程仍然正常,所以该异常服务实例还是在线的,客户端仍在发送请求到该实例。这时想将该实例进行服务注册下线操作但保留进程以便排查问题,但由于业务线程阻塞导致HTTP线程池所有线程阻塞,进而导致管理模块无线程可处理HTTP服务注册下线请求,最终无法正常下线。有赞Dubbo框架已经对应用服务端口与管理端口进行了分离,并进行了线程池隔离,避免再出现类似的问题。当然,熔断等其他机制也有助于应对个别实例异常问题,这里我们主要关注端口分离问题。

4. 建立连接设置超时时间

网络拥塞、IP不可达、握手队列满时,都可能会导致建立连接阻塞与超时,为了避免不可控的阻塞时间对应用造成难以预知的影响,建议在建立连接时设置超时时间,进行超时控制。如果没有主动进行设置,超时时间是由系统默认行为进行控制的,而系统的默认行为肯定是无法满足所有应用场景的。(注:握手队列满时,如果设置了系统参数net.ipv4tcp_abort_on_overflow,连接会立刻被重置)

我们看一下系统默认是如何控制连接建立超时时间的?

TCP三次握手的第一个SYN报文没有收到ACK,系统会自动对SYN报文进行重试,最大重试次数由系统参数net.ipv4.tcp_syn_retries控制,默认值为6。初始RTO为1s,如果一直收不到SYN ACK,依次等待1s、2s、4s、8s、16s、32s发起重传,最后一次重传等待64s后放弃,最终在127s后才会返回ETIMEOUT超时错误。

建议根据整个公司的业务场景,调整net.ipv4.tcp_syn_retries系统参数进行兜底。有赞将该参数设为3,即最大15s左右可返回超时错误。

5. 使用应用层心跳对连接进行健康检查

当TCP连接有异常时,我们需要尽快感知到,然后进行相应的异常处理与恢复。对于FIN或RST这种连接关闭、重置场景,应用层是可以快速感知到的。但是对于对端机器掉电、网线脱落、网络设备异常等造成的假连接,如果没有特殊措施,应用层很长时间都感知不到。

提到网络异常检测,大家可能首先想到的是TCP Keepalive。系统TCP Keepalive相关的三个参数为net.ipv4.tcp_keepalive_time、net.ipv4.tcp_keepalive_intvl、net.ipv4.tcp_keepalive_probes,默认值分别为7200s、75s、9,即如果7200s没有收到对端的数据,就开始发送TCP Keepalive报文,如果75s内,没有收到响应,会继续重试,直到重试9次都失败后,返回应用层错误信息。

为什么需要实现应用层的心跳检查呢?系统的TCP Keepalive满足不了需求吗?是的,系统的TCP Keepalive只能作为一个最基本的防御方案,而满足不了高稳定性、高可靠性场景的需求。原因有如下几点:

  • TCP Keepalive是扩展选项,不一定所有的设备都支持;

  • TCP Keepalive报文可能被设备特意过滤或屏蔽,如运营商设备;

  • TCP Keepalive无法检测应用层状态,如进程阻塞、死锁、TCP缓冲区满等情况;

  • TCP Keepalive容易与TCP重传控制冲突,从而导致失效。

对于TCP状态无法反应应用层状态问题,这里稍微介绍几个场景。第一个是TCP连接成功建立,不代表对端应用感知到了该连接,因为TCP三次握手是内核中完成的,虽然连接已建立完成,但对端可能根本没有Accept;**因此,一些场景仅通过TCP连接能否建立成功来判断对端应用的健康状况是不准确的,这种方案仅能探测进程是否存活。**另一个是,本地TCP写操作成功,但数据可能还在本地写缓冲区中、网络链路设备中、对端读缓冲区中,并不代表对端应用读取到了数据。

这里重点解释一下TCP KeepAlive与TCP重传的冲突问题。Linux系统通过net.ipv4.tcp_retries2参数控制TCP的超时重传次数,即影响TCP超时时间。初始RTO为TCP_RTO_MIN(200ms),RTO进行指数退让,最大RTO为TCP_RTO_MAX(2min),net.ipv4.tcp_retries2默认为15,大概924.6s超时。详细重传次数、RTO、超时时间关系,如下表所示。

重传次数
RTO(毫秒)
总超时时间

1

200

0.2 秒

0.0 分钟

2

400

0.6 秒

0.0 分钟

3

800

1.4 秒

0.0 分钟

4

1600

3.0 秒

0.1 分钟

5

3200

6.2 秒

0.1 分钟

6

6400

12.6 秒

0.2 分钟

7

12800

25.4 秒

0.4 分钟

8

25600

51.0 秒

0.9 分钟

9

51200

102.2 秒

1.7 分钟

10

102400

204.6 秒

3.4 分钟

11

120000

324.6 秒

5.4 分钟

12

120000

444.6 秒

7.4 分钟

13

120000

564.6 秒

9.4 分钟

14

120000

684.6 秒

11.4 分钟

15

120000

804.6 秒

13.4 分钟

16

120000

924.6 秒

15.4 分钟

如果TCP发送缓冲区中有数据未发送成功,TCP会进行超时重传,而不会触发TCP Keepalive。也就是说,即使应用设置了很小的TCP Keepalive参数,如time=10s、interval=10s、probes=3,在net.ipv4.tcp_retries2默认配置下,可能还是一直等到15min左右才能感知到网络异常。可能有的人不理解为什么Keepalive会被重传干扰,其实这里就是个优先级的问题。TCP最大重传次数的作用高于Keepalive参数的作用,未达到最大重传次数,不会向应用层报告网络错误信息。如果Keepalive不受重传影响,同样也会对关注重传的人造成干扰,比如为什么还没达到最大重传次数就放弃重传并关闭连接了?我们可以通过netstat -ot或ss -ot命令查看当前连接的计时器信息。

建议根据实际情况调低net.ipv4.tcp_retries2参数。RFC 1122建议对应的超时时间不低于100s,即至少为8,有赞系统该参数默认为10。

因此,想实现一个网络健壮的应用,应用层心跳必不可少。对于HTTP2、gRPC、Dubbo等协议都支持心跳,如果是基于这些协议开发的应用,可以直接使用这些协议的特性来实现应用层心跳。

实现应用层心跳需要考虑如下点:

  • 心跳间隔不能太小也不能太大。间隔太小心可能会对轻微抖动过于敏感,造成过度反应,反而会影响稳定性,同时也有一定的性能开销;间隔太大会导致异常检测延迟比较高。可以严格地定期发送心跳,也可以一段时间内没有收到对端数据才发起心跳。建议心跳间隔为5s~20s。

  • 设置连续失败阈值,避免瞬间抖动造成误判等。建议连续失败阈值为2~5。

  • 不要使用独立的TCP连接进行心跳检查,因为不同连接的网络路径、TCP缓冲区等都不同,无法真实反映业务通信连接的真实状态。

6. 连接重连需要增加退让与窗口抖动

当网络异常恢复后,大量客户端可能会同时发起TCP重连及进行应用层请求,可能会造成服务端过载、网络带宽耗尽等问题,从而导致客户端连接与请求处理失败,进而客户端触发新的重试。如果没有退让与窗口抖动机制,该状况可能会一直持续下去,很难快速收敛。

建议增加指数退让,如1s、2s、4s、8s...,同时必须限制最大退让时间(如64s),否则重试等待时间可能越来越大,同样导致无法快速收敛。同时,为了降低大量客户端同时建连并请求,也需要增加窗口抖动,窗口大小可以与退让等待时间保持一致,如: nextRetryWaitTime = backOffWaitTime + rand(0.0, 1.0) * backOffWaitTime。

在进行网络异常测试或演练时,需要把网络异常时间变量考虑进来,因为不同的时长,给应用带来的影响可能会完全不同。

7. 服务端需要限制最大连接数

一个服务端口,理论上能接收的最大TCP连接数是多少呢?TCP四元组中的服务端IP、服务端端口已经固定了,理论上的上限就是客户端可用IP数量*客户端可用端口数量。去除一些IP分类、端口保留等细节,理论上限就是2^32 * 2 ^16 = 2^48。

当然,目前现实中肯定达不到理论上限的瓶颈。一个TCP socket所关联的主要资源有内存缓冲区、文件描述符等,因此,实际限制主要取决于系统内存大小与文件描述符数量限制。

服务端限制最大连接数,主要有两个目的:

  • 避免服务过载导致CPU、内存耗尽;

  • 避免文件描述符耗尽。

每个TCP连接的socket都占用一个FD,每个进程以及整个系统的FD数量都是有限制的。Linux系统下,通过ulimit -n可以查看单个用户的进程运行打开的FD最大数量,通过cat /proc/sys/fs/file-max可以查看所有进程运行打开的最大FD数量,如果不符合应用的需求,那就需要进行相应的调整。

达到FD上限会有什么影响呢?首先,肯定是无法接收新TCP连接了;其次,除了TCP连接占用的FD外,你的应用肯定还有内部场景占用或需要分配新的FD,比如日志文件发生轮转创建新日志文件时,如果日志文件创建失败,对于依赖本地存储的应用(如KV、MQ等存储型应用),就导致服务不可用了。所以,要在系统限制的基础上,根据应用的特性预留一定数量的FD,而不能把所有的FD都给客户端TCP连接使用。

有赞在线上压测时,一个应用就碰到过类似的一个问题。压测期间,压力比较高,导致磁盘IO压力增高,请求处理延迟增高,导致客户端超时。客户端发现超时关闭连接,创建新连接重试,但此时服务端由于IO阻塞带来的延迟并未能够及时回收连接关闭(CLOSE_WAIT)的socket以及FD,导致FD消耗越来越多,最终导致FD耗尽,新日志文件创建失败,而该应用又是存储类型应用,强依赖于日志落盘,最终导致服务不可用。

除了服务端限制最大连接数外,如果应用有对应的客户端SDK,最好也在客户端SDK也做一层保护。

8. 尽量不要依赖中心化四层负载均衡器

LVS是一个经典的中心化四层负载均衡解决方案,也有各种云厂商提供的类似LVS的产品,原理大多是一致的。它们的优点这里我们就不谈了。使用该类方案可能会面临如下问题:

  • 每次应用伸缩容,需要变更后端实例列表配置,运维成本高、风险高;

  • 中心化的组件可伸缩性较差,容易触达瓶颈,如网络带宽瓶颈等;

  • 中心化的组件可用性较差,一旦负载均衡器出问题,整个服务受影响;

  • 四层健康检查对后端实例异常不敏感,无法进行应用层健康检查;

  • 负载均衡器的拆分、迁移对应用影响较大,需要应用配合更新配置、发布等,使用成本较高;

  • 负载均衡器会可能丢弃一段时间内没有通信的空闲连接,给应用带来非预期的影响;

  • 客户端访问服务端需经过负载均衡器中转,可能对RT有一定影响。

建议通过分布式的动态服务注册与发现以及客户端负载均衡来替代中心化负载均衡方案,如微服务架构中的服务注册、服务发现、负载均衡等解决方案。

在不得不使用中心化负载均衡器的场景下,也需要注意以下问题:

  • 注意选择合适的负载均衡算法,避免长连接分布不均衡。比如,如果选择了轮询负载均衡算法,正常情况下各个后端实例的连接数是均衡的,但当某个实例重启后,该实例的连接断开后,客户端会发起重连,重连就大概率转移其他实例上,导致最近启动的实例连接数较少,最早启动的实例连接数较多。可以考虑最少连接数负载均衡,长连接增加TTL限制等。

  • 注意空闲超时,超时后负载均衡器可能不会给两端发送Close或Reset信号,从而导致无法通信的假连接,如果客户端与服务端双方都没有心跳、空闲超时等,假连接会一直存在,占用系统资源;应用层或TCP层的健康检查周期需要小于负载均衡器的空闲超时。

  • 注意摘除后端实例时保证平滑,如果直接移除后端实例,可能不会给两端发送Close或Reset信号,从而导致无法通信的假连接,且客户端和服务端无法及时感知到。一般先将实例权重调整为0,保证新连接不再分配到该实例,然后等待已有的连接释放,最后再完全移除后端实例。

有赞线上环境曾多次碰到过LVS引起的相关问题,也正在研发分布式的四层代理。

9. 警惕大量CLOSE_WAIT

先介绍曾经碰到的一个问题。线上环境告警提示有服务器发生较高的TCP重传,经抓包分析重传包都是FIN包,且目标IP已不存在。查看连接状态发现大量CLOSE_WAIT状态连接。该问题并不是一直持续,时有时无。经过对应用日志与应用代码分析,发现某个场景应用读取到EOF时,未关闭本地socket。进一步分析,原因是客户端应用是K8S部署的,发布后,旧实例下线,作为客户端发起主动关闭连接,并且旧实例的IP很快会被回收;服务端未关闭的socket,在几分钟后GC时(Go语言应用)才会进行socket回收关闭操作,但此时,客户端IP已不存在,因此,最后一个FIN报文不断重传,一直到超过最大重传次数,从而问题恢复。等到再次有客户端应用发布时,又会出现。该问题对于没有GC机制的编程语言开发的应用,可能会造成更严重的后果,socket不断泄露,导致FD耗尽、内存耗尽等问题。

因此,一定要警惕大量CLOSE_WAIT状态连接的出现,这种情况出现时,首先要排除一些相关代码。同时,开发过程中,一定要注意正确关闭socket,通过一些语言特性进行兜底处理,如Go语言的defer,Java语言的try...catch...finally,C++语言的RAII机制等。

10. 合理设置长连接TTL

长连接减少了像短连接频繁建立连接的开销,包括三次握手开销、慢启动开销等。但也有一定的弊端:长连接的持续时间过长,可能会导致一些负载均衡问题,以及其他一些长时间难以收敛的问题。比如LVS场景,随着后端应用实例的重启,对于一些负载均衡算法(如轮询),会导致最新启动的实例连接数最少,最早启动的实例连接数最多。对于一些客户端负载均衡方案,当只需要连接后端集群中的一个节点时,长连接也会出现类似的问题,比如类似Etcd watch的场景。有赞内部有很多使用Etcd的场景,早期运维每次变更Etcd集群的时候都特别谨慎,避免连接的不均衡。

有赞中间件团队规定任何应用的TCP长连接TTL不能超过2小时。当然,这已经是一个很保守的时长了,建议根据应用场景,合理设置TTL。

11. 通过域名访问服务需定期解析DNS

DNS是一种服务发现机制,应用通过配置DNS访问其他服务,本意是为了解决其他服务实例IP变动带来的影响,但如果处理不当还是会有问题。通过域名访问其他服务时,需要定时更新域名解析,如果解析有更新,则需要重新建立连接,避免后端实例迁移(IP有变化)时导致难以收敛。千万不要只在应用启动的时候进行一次域名解析,这种情况在DNS变更后想实现快速收敛,只能重启或发布所有相关应用了。一些语言内置了DNS相关的实现,需要注意对应的一些参数以及行为是否符合预期。

另外,某些应用提供了获取最新集群成员列表的接口,如Etcd、Redis,这样即使客户端启动的时候只进行一次域名解析,只要定期从服务端同步服务集群的成员列表也能支持服务端集群成员的动态变化。

12. 降低网络读写系统调用次数

当我们调用read/write系统函数从socket读写数据时,每次调用都至少进行两次用户态与内核态的上下文切换,成本比较高。针对该问题,一般有两种优化思路:

  • 使用读写缓冲区;读数据时,先一次性从socket读入缓冲区,然后再按需要分次从缓冲区读取;写数据时,先分次写入缓冲区,缓冲区满时或所有写操作完成时,一次性写入socket。

  • 当不方便将数据合并到连续内存时,使用readv/writev一次性读取/写入多段内存数据。

对于批量写操作还有一个优点,就是可以避免Nagle算法带来的延迟(一般也不建议开启Nagle算法)。假如当前写缓冲区中没有数据,我们先通过write写4个字节,这时TCP协议栈将其发送出去,然后再通过write写96个字节,这时,由于前面发送了一个报文,还没有收到ACK,并且当前可发送数据未达到MSS,Nagle算法不允许继续发送报文,必须等到前一个报文的ACK回来才能继续发送数据,大大降低了吞吐量并且提高了延迟。如果接收端开启了延迟ACK,影响更大。

因此,应该尽量批量读写网络数据,以提升性能。

13. 谨慎设置TCP缓冲区大小

一般来说我们不需要更改TCP默认缓冲区大小,如果我们确实有需求设置,也需要谨慎考虑与评估。

TCP缓冲区大小设置为多少合适呢?我们知道,TCP 的传输速度,受制于发送窗口与接收窗口大小,以及网络传输能力。其中,两个窗口由缓冲区大小决定,如果缓冲区大小与网络传输能力匹配,那么缓冲区的利用率就是最高的。

带宽时延积(缩写为 BDP,Bandwidth-delay Product)是用来描述网络传输能力的。如最大带宽是 100MB/s、网络时延是 10ms 时,客户端到服务端之间的网络一共可以存放 100MB/s * 0.01s = 1MB 的字节,这个 1MB 是带宽与时延的乘积,也就是带宽时延积。这 1MB 字节存在于飞行中的 TCP 报文,它们就在网络线路、路由器等网络设备上。如果飞行报文超过了 1MB,就一定会让网络过载,最终导致丢包。

由于发送缓冲区决定了发送窗口的上限,而发送窗口又决定了已发送但未确认的飞行报文的上限,因此,发送缓冲区不能超过带宽时延积,因为超出的部分没有办法用于有效的网络传输,且飞行字节大于带宽时延积还会导致丢包,从而触发网络拥塞避免;而且,缓冲区也不能小于带宽时延积,否则无法发挥出高速网络的价值。

总结而言:缓冲区太小,会降低TCP吞吐量,无法高效利用网络带宽,导致通信延迟升高;缓冲区太大,会导致TCP连接内存占用高以及受限于带宽时延积的瓶颈,从而造成内存浪费。如果缓冲区过小,如2K,还可能会导致快速重传无法生效,因为未确认的报文可能最多只有2个,不会出现3个重复的ACK。

Linux系统是可以根据系统状态自动调节缓冲区大小的,相关参数由net.ipv4.tcp_wmem和net.ipv4.tcp_rmem控制,参数是一个3元组,即最大值、初始默认值、最大值。但如果在 socket 上直接设置 SOSNDBUF 或者 SORCVBUF,这样会关闭缓冲区的系统动态调整功能,这样操作前务必要进行充分的评估。

因此,除非非常明确自己的需求,以及进行充分的评估与验证,否则,不要轻易设置TCP缓冲区大小。

14. 网络相关参数支持灵活配置

当应用可能有多种部署环境、部署场景时,需要根据使用场景、网络环境等因素,调整合适的网络相关参数。LAN和WAN的网络状况差别很大,会涉及到诸多参数的调整。

比如对于有赞的服务代理组件Tether,既有数据中心内的sidecar部署场景,又有跨公网的网关部署场景,这时就需要按需调整对应的参数,否则难以适应不同的网络环境。如连接超时、读写超时、健康检查超时、健康检查失败阈值等都应该支持灵活配置。

15. 合理设置连接池大小

对于不同类型的协议,连接池的设计也不同。我们将协议是否支持连接多路复用划分为两类:非多路复用协议和多路复用协议。非多路复用协议,一个连接发送请求后,必须等待响应返回后,该连接才能发送新的请求,如HTTP1.1、Redis等;多路复用协议,支持同一个连接同时发送多个请求,如HTTP2、gRPC、Dubbo等。

我们先看一下非多路复用协议如何设置连接池大小。连接池涉及到的参数一般有:最小连接数、最大连接数、最大空闲时间、连接获取超时时间、连接获取超时重试次数等。应用与连接池主要交互逻辑如下所示:

我们主要讨论最小连接数和最大连接数。之所以不是固定连接数,是因为流量有高峰、有低谷;固定连接数太小,流量高峰期容易导致请求等待时间过长;固定连接数太大,流量低谷期容易造成资源浪费。因此,最小连接数对应的就是流量低谷期连接数多少为合适,最大连接数对应的就是流量高峰期连接数多少为合适,也就是连接数与流量大小是相关的。除了流量大小,还需要考虑请求RT,即每个请求占用连接的时间。所需要的连接数其实就是请求并发数,这里我们可以利用著名的利特尔法则(Little's law)来计算,L=λW,在该场景即:并发数 = 请求QPS * 请求RT。比如流量低谷期请求QPS为100,请求RT为0.05s,则并发数为5,所需连接数为5;流量高峰期请求QPS为500,请求RT为0.1s,则并发数为50,所需连接数为50。这类问题其实与排队论相关,不过我们这里不做过多讨论,如果有更复杂的需求场景,可以参考更多排队论相关资料。

接下来我们继续看一下多路复用协议如何设置连接池大小。连接池涉及到的参数一般有:最小连接数、最大连接数、单连接并发请求数高水位、单连接并发请求数低水位。当单连接并发请求数高于高水位时,如果连接池未达到最大连接数,进行连接池扩容,创建连接;当单连接并发请求数低于低水位时,如果连接池未达到最小连接数,进行连接池缩容,释放连接(释放过程需要做到平滑)。由于每个请求不独占连接,请求是可以选择任意连接的,所以这里也面临负载均衡的问题,需要尽可能的确保每个连接上的处理中的请求数接近平均值。一般使用最少请求数负载均衡,但最少请求数负载均衡时间复杂度可能比较高,最简单的实现需要扫描整个连接池。我们可以使用其近似的优化实现,随机选择两个连接,选择Pending请求数少的连接;为了更加近似最少请求,可以选择3个、5个,甚至更多个连接,取其中Pending请求数最少的连接。

16. 完善网络指标监控

需要对各个关键网络指标进行监控与告警,包括但不限于:

  • TCP连接建立失败数

  • TCP报文重传率

  • TCP各个状态连接数(尤其是ESTABLISHED、TIME_WAIT、CLOSE_WAIT)

  • TCP主动关闭连接数

  • TCP被动关闭连接数

  • 连接健康检查失败数

  • 系统及进程FD使用数

  • 连接池大小

如果能尽早发现这些指标的异常,那么就可以尽快发现问题,从而降低问题影响面。

17. 总结

本文根据有赞TCP网络编程实践经验总结了16项建议,希望能够在TCP网络编程方面帮助大家提升应用的健壮性、可靠性,减少线上问题与故障。

18. 参考资料

  • Transmission Control Protocol,https://en.wikipedia.org/wiki/TransmissionControlProtocol。

  • RFC 793: TRANSMISSION CONTROL PROTOCOL,https://datatracker.ietf.org/doc/html/rfc793。

  • RFC 1122: Requirements for Internet Hosts -- Communication Layers,https://datatracker.ietf.org/doc/html/rfc1122。

  • RFC 1323: TCP Extensions for High Performance,https://datatracker.ietf.org/doc/html/rfc1323。

  • RFC 1337: TIME-WAIT Assassination Hazards in TCP,https://datatracker.ietf.org/doc/html/rfc1337。

  • RFC 6191: Reducing the TIME-WAIT State Using TCP Timestamps,https://datatracker.ietf.org/doc/html/rfc6191

  • Sprint Boot: Monitoring and management over HTTP,https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/production-ready-monitoring.html。

  • Linux IP Sysctl,https://www.kernel.org/doc/html/latest/networking/ip-sysctl.html。

  • HTTP2 Ping,https://datatracker.ietf.org/doc/html/rfc7540#section-6.7。

  • gRPC Keepalive,https://github.com/grpc/grpc/blob/master/doc/keepalive.md。

  • Dubbo Protocol,https://dubbo.apache.org/zh/docs/v2.7/dev/implementation/#%E5%8D%8F%E8%AE%AE%E5%A4%B4%E7%BA%A6%E5%AE%9A。

  • Nagle's algorithm,https://en.wikipedia.org/wiki/Nagle%27s_algorithm。

  • TCP delayed acknowledgment,https://en.wikipedia.org/wiki/TCPdelayedacknowledgment。

  • Bandwidth-delay product,https://en.wikipedia.org/wiki/Bandwidth-delay_product。

  • The anatomy of Connection Pooling,https://vladmihalcea.com/the-anatomy-of-connection-pooling/。

  • Little's law,https://en.wikipedia.org/wiki/Little%27s_law。

  • Queueing theory,https://en.wikipedia.org/wiki/Queueing_theory。

2021-07-15-62gi11