第2章 常见SDK类型与性能特征

做性能压测之前,首先得搞清楚你面对的是哪种SDK。同步还是异步?阻塞还是非阻塞?REST还是gRPC?

选错了分析方向,调优就是瞎忙活。我个人习惯一上来先给SDK分类,再针对性设计压测场景。

2.1 同步 vs 异步:最基础的分类

同步调用,说白了就是发请求后一直等。线程挂在那,直到结果回来才继续往下走。逻辑简单,但吞吐量上不去。

我记得有一次压测一个支付SDK,单机QPS卡在2000上不去。一看链路,每次调用都要等3次内部RPC全部返回。线程池被打满,后面请求全排队。

异步调用则不同。请求发出去立即返回一个Future或回调,线程可以去干别的活。等结果准备好了再通知你。

核心区别:同步占用线程等待,异步释放线程做其他事。

维度 同步 异步
线程占用 整个等待期间占用一整个线程 仅发起和回调时占用,其余时间释放
吞吐量与并发 受线程池大小限制,提高并发需扩线程 用少量线程即可支撑高并发
代码复杂度 简单,线性思维 需要处理回调、状态机、线程安全
常见SDK示例 JDBC、传统HTTP客户端 Netty、WebClient、gRPC异步Stub

2.2 阻塞 vs 非阻塞:调用方的等待策略

这两个词经常跟同步/异步混在一起,但它们说的是另一回事。阻塞强调的是调用方是否被挂起,而非阻塞则是调用方立即拿到一个状态值,不挂起。

举个例子:你发了一个请求,socket上没数据可读。如果线程在read()上停住,那就是阻塞I/O。如果read()立即返回一个“暂无数据”的错误码,那就是非阻塞。

一个小技巧:同步不一定阻塞,异步也不一定非阻塞。Java NIO的Selector是非阻塞的,但处理流程可以是同步的(轮询)。

我曾经踩过一个坑:用非阻塞轮询去等外部任务完成,while(true)里不断检查状态。CPU直接飙到100%,还耽误了其他业务。后来改成信号量+回调,问题解决。

模式 调用方行为 适用场景 性能风险
同步 + 阻塞 线程一直等,直到数据就绪 低并发、简单业务 线程浪费,池满后请求堆积
同步 + 非阻塞 轮询或状态检查,不挂起 必须及时响应的短事务 轮询开销大,易CPU打满
异步 + 非阻塞 注册回调,事件驱动 高并发I/O密集型 回调地狱,调试困难

2.3 REST vs gRPC:传输协议的差异

你想想看,两个系统之间通信,最常见的两种方案就是REST和gRPC。它们底层协议不同,性能差距很大。

REST基于HTTP/1.1,文本格式(JSON/XML)。好处是通用,浏览器都能调。但每次请求都带大量HTTP头,序列化/反序列化用JSON库,CPU开销不低。

我做过一个对比压测:相同负载下,gRPC的吞吐量是REST的3~4倍,延迟也低一半。当然,这跟消息大小有关。

gRPC基于HTTP/2,使用Protobuf二进制序列化。支持流式传输、多路复用、头部压缩。如果你做微服务之间的内部调用,gRPC几乎是首选。

注意:gRPC对HTTP/2强依赖,负载均衡器必须支持。如果你用Kubernetes,需要配合service mesh或者gRPC-LB。不然建连失败让你怀疑人生。

对比维度 REST (HTTP/1.1) gRPC (HTTP/2)
传输格式 JSON/XML(文本) Protobuf(二进制)
序列化速度 慢,反射开销大 快,代码生成,零拷贝
连接复用 每个请求可能新建TCP连接 多路复用,一个连接够用
流式支持 SSE(单向) 双向流原生支持
调优重点 连接池、Keep-Alive 流控窗口、并发流数量

2.4 并发模型:线程池、协程与事件驱动

SDK内部如何管理并发,直接决定了它在高负载下的表现。我见过太多人只关注协议,忽略了并发模型。

  • 线程池模型:每个任务分配一个线程。简单,但线程数过多导致上下文切换爆炸。压测时CPU上不去,吞吐先掉。
  • 协程/纤程模型:用户态调度,轻量级切换。我用Kotlin协程重构过一个老SDK,同样机器QPS从500升到4500。
  • 事件驱动模型:单线程或少量线程,通过状态机+回调处理请求。Netty、Reactor就是代表。内存占用极低。

我个人习惯:IO密集型的SDK,首选协程或事件驱动。CPU密集型的反而适合线程池,避免协程里做计算阻塞调度器。

调优建议:压测前一定要确认SDK的并发模型,并设置合理的线程数(或协程数)。对于事件驱动模型,连接数和事件循环线程数是核心参数。

SDK 性能特征分类图 SDK 调用模型 同步 / 异步 阻塞 / 非阻塞 REST / gRPC 同步(线程等待) 异步(回调/Future) 阻塞(挂起) 非阻塞(轮询) REST (HTTP/1.1) gRPC (HTTP/2) JDBC, Apache HttpClient Netty, WebClient 传统 Socket NIO / Selector Feign, RestTemplate gRPC stub 💡 压测前务必按此图分类定位自己的SDK模型,选择对应的指标和工具 同步+阻塞适合低并发,异步+非阻塞是高性能标配,gRPC适合内部RPC

好了,这一章把常见的SDK类型和它们对应的性能特征梳理了一遍。分类清楚了,后面压测才有的放矢。你想想看,如果你的SDK是同步阻塞,却拿异步指标的期望去压,那不是自己找坑吗?