介绍如何利用DashScope Java SDK中的连接池与对象池机制,在高并发场景下高效调用Paraformer实时语音识别服务。
Paraformer 实时语音识别服务基于 WebSocket 协议,以支持流式实时通信。然而,在高并发场景下,为每个请求独立创建和销毁 WebSocket 连接会产生巨大的网络与系统资源开销,并引入显著的连接延迟。为优化性能并确保稳定性,DashScope SDK 内置了高效的资源复用机制(如连接池与对象池)。本文档将详细介绍如何利用 DashScope Java SDK 中的这些特性,在高并发场景下高效调用 Paraformer 实时语音识别服务。
关于模型介绍和选型建议请参见实时语音识别概述。
Java SDK 通过内置的连接池和自定义的对象池协同工作,实现最佳性能。
环境变量 描述 连接池大小。推荐值:峰值并发数的 2 倍以上。默认值:32。 最大异步请求数。推荐值:与 单主机最大异步请求数。推荐值:与 环境变量 描述 对象池大小。推荐值:峰值并发数的 1.5 至 2 倍。默认值:500。
以下配置基于在指定规格的阿里云服务器上仅运行 Paraformer 实时语音识别服务的测试结果。过高的并发数可能导致任务处理延迟。
其中单机并发数指的是同一时刻正在运行的 Paraformer 实时语音识别任务数,也可以理解为工作线程数。
在对 DashScope Java SDK 进行并发调用延迟等性能评估时,建议在正式测试前执行充分的预热操作。预热能够确保测量结果准确反映服务在稳定状态下的真实性能,避免因初始连接耗时导致的数据偏差。
DashScope Java SDK 通过全局单例的连接池高效管理和复用 WebSocket 连接,旨在减少频繁建连和断连的开销,提升高并发场景下的处理能力。
该机制的工作特点如下:
在以下场景中,连接池中可能没有可复用的活跃连接,导致请求需要新建连接:
为获取可靠的性能数据,在正式进行性能压测或延迟统计前,请遵循以下预热步骤:
前提条件
Java SDK 通过内置的连接池和自定义的对象池协同工作,实现最佳性能。
- 连接池:SDK 内部集成的 OkHttp3 连接池,负责管理和复用底层的 WebSocket 连接,减少网络握手开销。此功能默认开启。
- 对象池:基于
commons-pool2实现,用于维护一组已预先建立好连接的Recognition对象。从池中获取对象可消除连接建立的延迟,显著降低首包延迟。
实现步骤
1
添加依赖
2
根据项目构建工具,在依赖配置文件中添加 dashscope-sdk-java 和 commons-pool2。
3
以Maven和Gradle为例,配置如下:
4
Maven
Gradle
5
Gradle 项目保存文件后,在命令行切换到项目根目录,执行以下命令更新依赖:
6
./gradlew build --refresh-dependencies
7
Windows 系统使用:
8
gradlew build --refresh-dependencies
9
配置连接池
10
通过环境变量配置连接池关键参数:
11
DASHSCOPE_CONNECTION_POOL_SIZEDASHSCOPE_MAXIMUM_ASYNC_REQUESTSDASHSCOPE_CONNECTION_POOL_SIZE 保持一致。默认值:32。DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOSTDASHSCOPE_CONNECTION_POOL_SIZE 保持一致。默认值:32。12
配置对象池
13
通过环境变量配置对象池大小:
14
RECOGNITION_OBJECTPOOL_SIZE15
- 对象池的大小(
RECOGNITION_OBJECTPOOL_SIZE)必须小于或等于连接池的大小(DASHSCOPE_CONNECTION_POOL_SIZE)。否则,当对象池请求对象时,若连接池已满,会导致调用线程阻塞,等待可用连接。 - 对象池大小不应超过您账户的 QPS(每秒查询率)限制。
16
通过如下代码创建对象池:
17
class RecognitionObjectPool {
// 这里省略其它代码,完整示例请参见完整代码
public static GenericObjectPool<Recognition> getInstance() {
lock.lock();
if (recognitionGenericObjectPool == null) {
// 您可以在这里设置对象池的大小。或在环境变量RECOGNITION_OBJECTPOOL_SIZE中设置。
// 建议设置为服务器最大并发连接数的1.5到2倍。
int objectPoolSize = getObjectivePoolSize();
System.out.println("RECOGNITION_OBJECTPOOL_SIZE: "
+ objectPoolSize);
RecognitionObjectFactory recognitionObjectFactory =
new RecognitionObjectFactory();
GenericObjectPoolConfig<Recognition> config =
new GenericObjectPoolConfig<>();
config.setMaxTotal(objectPoolSize);
config.setMaxIdle(objectPoolSize);
config.setMinIdle(objectPoolSize);
recognitionGenericObjectPool =
new GenericObjectPool<>(recognitionObjectFactory, config);
}
lock.unlock();
return recognitionGenericObjectPool;
}
}
18
从对象池中获取 Recognition 对象
19
如果当前未归还的对象数量已超过对象池的最大容量,系统会额外创建一个新的
Recognition 对象。此类新创建的对象需要重新进行初始化并建立 WebSocket 连接,无法利用对象池的既有连接资源,因此不具备复用效果。20
recognizer = RecognitionObjectPool.getInstance().borrowObject();
21
进行语音识别
22
调用
Recognition 对象的 call 或 streamCall 方法进行语音识别。23
归还 Recognition 对象
24
语音识别任务结束后,归还
Recognition 对象,以便后续任务可以复用该对象。不要归还未完成任务或任务失败的对象。25
RecognitionObjectPool.getInstance().returnObject(recognizer);
完整代码
推荐配置
以下配置基于在指定规格的阿里云服务器上仅运行 Paraformer 实时语音识别服务的测试结果。过高的并发数可能导致任务处理延迟。
其中单机并发数指的是同一时刻正在运行的 Paraformer 实时语音识别任务数,也可以理解为工作线程数。
| 机器配置(阿里云) | 单机最大并发数 | 对象池大小 | 连接池大小 |
|---|---|---|---|
| 4核8GiB | 100 | 500 | 2000 |
| 8核16GiB | 200 | 500 | 2000 |
| 16核32GiB | 400 | 500 | 2000 |
资源管理与异常处理
-
任务成功:当语音识别任务正常完成时,必须调用
GenericObjectPool的returnObject方法将Recognition对象归还到池中,以便复用。在当前代码中,对应RecognitionObjectPool.getInstance().returnObject(recognizer)。不要归还未完成任务或任务失败的 Recognition 对象。 -
任务失败:当 SDK 内部或业务逻辑抛出异常导致任务中断时,必须执行以下两个操作:
- 主动关闭底层的 WebSocket 连接
- 从对象池中废弃该对象,防止被再次使用
- TaskFailed 报错:在服务出现 TaskFailed 报错时,不需要额外处理。
调用预热与耗时统计说明
在对 DashScope Java SDK 进行并发调用延迟等性能评估时,建议在正式测试前执行充分的预热操作。预热能够确保测量结果准确反映服务在稳定状态下的真实性能,避免因初始连接耗时导致的数据偏差。
连接复用机制
DashScope Java SDK 通过全局单例的连接池高效管理和复用 WebSocket 连接,旨在减少频繁建连和断连的开销,提升高并发场景下的处理能力。
该机制的工作特点如下:
- 按需创建:SDK 不会在服务启动时预创建 WebSocket 连接,而是在首次调用时按需建立。
- 限时复用:请求完成后,连接将在池中保留最多 60 秒以备复用。
- 若 60 秒内有新请求,将复用现有连接,避免重复握手开销。
- 若连接空闲超过 60 秒,将被自动关闭以释放资源。
预热的重要性
在以下场景中,连接池中可能没有可复用的活跃连接,导致请求需要新建连接:
- 应用刚启动,尚未发起任何调用。
- 服务空闲时间超过 60 秒,池中连接已因超时而关闭。
推荐做法
为获取可靠的性能数据,在正式进行性能压测或延迟统计前,请遵循以下预热步骤:
- 模拟正式测试的并发级别,提前发起一定数量的调用(例如,持续 1-2 分钟),以充分填充连接池。
- 确认连接池已建立并维持足够的活跃连接后,再开始正式的性能数据采集。
Java SDK 常见异常
异常 1、业务流量平稳,但是服务器 TCP 连接数持续上升
异常 1、业务流量平稳,但是服务器 TCP 连接数持续上升
出错原因:类型一:每一个 SDK 对象创建时都会申请一个连接。如果没有使用对象池,每一次任务结束后对象都被析构。此时这一个连接将进入无引用状态,需要等待 61 秒后服务端报错连接超时才会真正断开,这会导致这个连接在 61 秒内不可复用。在高并发场景下,新的任务在发现没有可复用连接时会创建新连接,会造成如下后果:
- 连接数持续上升。
- 由于连接数过多,服务器资源不足,服务器卡顿。
- 连接池被打满、新任务由于启动时需要等待可用连接而阻塞。
异常 2、任务耗时比正常调用多 60 秒
异常 2、任务耗时比正常调用多 60 秒
同"异常 1",连接池已经达到最大连接限制,新的任务需要等待无引用状态的连接 61 秒触发超时后才可以获得连接。
异常 3、服务启动时任务慢,之后慢慢恢复正常
异常 3、服务启动时任务慢,之后慢慢恢复正常
出错原因:在高并发调用时,同一个对象会复用同一个 WebSocket 连接,因此 WebSocket 连接只会在服务启动时创建。需要注意的是,任务启动阶段如果立刻开始较高并发调用,同时创建过多的 WebSocket 连接会导致阻塞。解决方法:启动服务后逐步提升并发量,或增加预热任务。
异常 4、服务端报错 Invalid action('run-task')! Please follow the protocol!
异常 4、服务端报错 Invalid action('run-task')! Please follow the protocol!
出错原因:这是由于出现了客户端报错后,服务端不知道客户端出错,连接处于任务中状态。此时连接和对象被复用并开启下一个任务,导致流程错误,下一个任务失败。解决方法:在抛出异常后主动关闭 WebSocket 连接后归还对象池。
异常 5、业务流量平稳,调用量出现异常尖刺
异常 5、业务流量平稳,调用量出现异常尖刺
出错原因:同时创建过多 WebSocket 连接导致阻塞,但业务流量持续打进来,导致任务短时间积压,并且在阻塞后所有积压任务立刻调用。这会造成调用量尖刺,并且有可能造成瞬时超过账号的并发数限制导致部分任务失败、服务器卡顿等。这种瞬间创建过多 WebSocket 的情况多发生于:
- 服务启动阶段
- 网络出现异常,大量 WebSocket 连接同时中断重连
- 某一时刻出现大量服务端报错,导致大量 WebSocket 重连。常见报错如并发数超过账号限制("Requests rate limit exceeded, please try again later.")。
- 检查网络情况。
- 排查尖刺前是否出现大量其他服务端报错。
- 提高账号并发限制。
- 调小对象池和连接池大小,通过对象池上限限制最大并发数。
- 提升服务器配置或扩充机器数。
异常 6、随着并发数提升,所有任务都变慢
异常 6、随着并发数提升,所有任务都变慢
解决方法:
- 检查是否已经达到网络带宽上限。
- 检查实际并发数是否已经过高。

