[Java实战]性能优化qps从1万到3万
一、问题背景
事情起因是项目上springboot项目提供的tps达不到客户要求,除了增加服务器提高tps之外,作为团队的技术总监,架构师,技术扛把子,本着我不入地狱谁入地狱的原则,决心从代码上优化,让客户享受到飞一般的感觉。虽然大多数编程工作在写下第一行代码时已经完成,但本着谦虚使人进步,骄傲使人落后的原则还是一步一个脚印的把问题慢慢展开,慢慢分析。以下内容是抽丝剥茧的心路历程,请君欣赏。
二、TPS与 QPS
说到性能优化就要说到高并发,说到高并发就要说到Tps和Qps。或许是小白第一次见到这个概念,但高手都是从低手走过来的,所以请高手以及高高手们,允许小人啰嗦的叙说吧!
以下是TPS(Transactions Per Second)与QPS(Queries Per Second)的核心区别及关联解析:
2.1. 定义与组成差异
-
TPS(每秒事务数)
• 定义:单位时间内系统完成的完整事务数量,一个事务包含从客户端请求到服务端处理并返回的全流程(例如:订单支付包含下单、扣款、生成凭证等多个操作)。
• 组成:通常包括三个阶段:
◦ 客户端发起请求
◦ 服务端处理(含业务逻辑、数据库操作等)
◦ 服务端返回响应结果。 -
QPS(每秒查询数)
• 定义:单位时间内服务端响应的独立查询请求数量,通常指单个接口或操作的调用次数(例如:一次商品详情页查询)。
• 组成:仅针对单一请求的响应,不涉及多步骤业务逻辑。
2. 应用场景与关系
指标 | 适用场景 | 与事务的关联性 | 典型示例 |
---|---|---|---|
TPS | 完整业务流程(如电商下单) | 一个事务可能包含多个QPS(如支付流程涉及库存查询、支付接口调用) | 支付系统需关注TPS以评估整体业务处理能力 |
QPS | 单一接口或查询(如API调用) | 单个查询可能属于某个事务的组成部分 | 商品搜索接口需关注QPS以优化响应速度 |
关系公式:
• 当单事务仅包含一次查询时,TPS = QPS
• 当单事务包含多次查询时,QPS = TPS × 单事务内查询次数
• 系统整体吞吐量受限于性能最差的环节(如数据库瓶颈可能导致高QPS但低TPS)
3. 性能测试中的差异
-
测试目标
• TPS反映系统处理复杂业务链路的综合能力(如银行转账事务)
• QPS更关注接口层或服务的单点性能(如高并发下的缓存查询) -
瓶颈分析
• TPS低可能由事务中某个子环节(如第三方支付接口延迟)导致,需定位具体步骤
• QPS低通常与服务器资源(CPU、内存)或代码效率直接相关 -
优化策略
• 提升TPS:优化事务内耗时最长的环节(如数据库批量操作、异步处理)
• 提升QPS:减少单次查询的响应时间(如索引优化、缓存命中率)
4. 总结
• 核心区别:TPS衡量完整业务流程的吞吐量,QPS衡量单一操作的频率。
• 选择依据:需根据业务场景决定主优化方向:
• 电商大促需优先保障TPS(确保订单流程不积压)
• 搜索引擎需优先优化QPS(提升单次查询效率)
• 关联性:两者共同构成系统吞吐量的评估体系,实际应用中常需结合分析。
通过以上专业的介绍,想必各位已经知道了什么是Tps还是Qps了,如果各位还是不明白,那不能怪你们,要怪就怪小人说的太专业了。
知道了客户想要的是什么,那么接下来就好办了,结合本身提供的api服务所使用的技术架构就很容易想到NIO和BIO,因为springboot项目打成jar包后是用的内嵌的tomcat,而tomcat7之前是BIO模式,sprinboot2.x内嵌的tomcat已经是8以上了,在spirngboot自动配置中,创建tomcat实例,默认启用NIO连接器。这一不小心又说的专业了,那BIO与NIO又是什么呢?对于小白来说,满脸的黑人问号,而高手只是轻轻一笑,这小伙子又在搞什么名堂,且待我慢慢看来。
三、BIO与NIO
从TPS和QPS,再到BIO与NIO,各位看官辛苦了。虽然说是“书山有路勤为径,学海无涯苦作舟”,但是方法不对,努力全费,知道了需求,怎么去解决,那么了解BIO和NIO就是正确的解决之道了。
BIO(Blocking I/O)和NIO(New I/O 或 Non-blocking I/O)是两种常见的I/O模型,它们在处理网络请求和数据传输时有显著的区别,适用于不同的应用场景。
1. BIO(Blocking I/O)
- 定义:BIO是同步阻塞I/O模型。线程在执行I/O操作时会被阻塞,直到数据准备完成。
- 工作原理:每个连接都需要一个独立的线程来处理,线程在等待数据时无法执行其他任务。
- 优点:实现简单,代码直观。
- 缺点:线程资源消耗高,不适合高并发场景。
- 适用场景:连接数较少且稳定的场景。
2. NIO(New I/O 或 Non-blocking I/O)
- 定义:NIO是同步非阻塞I/O模型。线程在发起I/O请求后不会被阻塞,而是可以继续执行其他任务。
- 工作原理:通过
Selector
(选择器)和Channel
(通道)来管理多个连接。单个线程可以监听多个通道的I/O事件(如读、写、连接),从而实现高并发。 - 优点:支持高并发,资源利用率高。
- 缺点:编程复杂度较高,需要手动管理事件循环。
- 适用场景:连接数多且连接较短(轻操作)的场景,如聊天服务器。
3. BIO与NIO的对比
特性 | BIO | NIO |
---|---|---|
阻塞模式 | 阻塞 | 非阻塞 |
数据处理方式 | 流(Stream) | 块(Buffer) |
核心组件 | Socket/ServerSocket | Channel/Buffer/Selector |
并发能力 | 低(一线程一连接) | 高(单线程多连接) |
编程复杂度 | 简单 | 复杂 |
4. 应用领域
- BIO:适用于低并发、简单应用场景,如内部工具或原型验证。
- NIO:适用于高并发、实时通信场景,如API网关、聊天服务。
总结来说嘛,BIO和NIO各有优缺点,选择合适的I/O模型需要根据实际的业务需求和并发场景来决定。
知道了目前已经是NIO模式了,那么怎么增加性能呢,换个语言不就得了,换个c吧,c是世界上最好的语言,嗯,是个不错的选择,换go吧,go是世界上最好的语言,嗯,这个也不错。想的都对,但还是用Java,Java是世界上最好的语言。
于是在一番思索后,依旧使用Java,因为 gRPC 实现依赖了netty,所以netty应该能满足客户的需求,那接下来就是见证奇迹的时刻, “实践是检验真理的唯一标准” ,是骡子是马是时候该拉出来溜溜了。
四、Netty方案
在薅掉十几根头发后,终于写出了这个替代方案,代码如下:
package com.example.demo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import