CosyVoice 语音合成模型支持将文本转换为自然流畅的语音。
| 模型名称 | 单价 | 特点 |
|---|---|---|
| cosyvoice-v3-plus | $0.286706/万字符 | 高质量语音合成 |
| cosyvoice-v3-flash | $0.14335/万字符 | 快速语音合成 |
| cosyvoice-v2 | $0.286706/万字符 | 稳定版本 |
DASHSCOPE_API_KEYpip install dashscopebrew install portaudio && pip install pyaudiosudo apt-get install python-pyaudio python3-pyaudiosudo yum install -y portaudio portaudio-devel && pip install pyaudiopython -m pip install pyaudioSpeechSynthesizer类提供了三种调用方式:
提交单个语音合成任务,一次性获取完整结果。
import dashscope
from dashscope.audio.tts_v2 import *
# 若没有将API Key配置到环境变量中,需将your-api-key替换为自己的API Key
# dashscope.api_key = "your-api-key"
# 实例化SpeechSynthesizer
synthesizer = SpeechSynthesizer(
model="cosyvoice-v3-flash",
voice="longanyang"
)
# 调用call方法进行合成,返回二进制音频数据
audio_data = synthesizer.call("今天天气怎么样?")
# 保存音频文件
with open("output.mp3", "wb") as f:
f.write(audio_data)
print("音频已保存到 output.mp3")
提交单个语音合成任务,通过回调方式流式输出中间结果。
import dashscope
from dashscope.audio.tts_v2 import *
from datetime import datetime
def get_timestamp():
now = datetime.now()
return now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
# 定义回调接口
class Callback(ResultCallback):
def on_open(self):
print("连接建立:" + get_timestamp())
self.file = open("output.mp3", "wb")
def on_complete(self):
print("语音合成完成:" + get_timestamp())
def on_error(self, message: str):
print(f"语音合成出现异常:{message}")
def on_close(self):
print("连接关闭:" + get_timestamp())
self.file.close()
def on_event(self, message):
pass
def on_data(self, data: bytes) -> None:
print(get_timestamp() + " 二进制音频长度为:" + str(len(data)))
self.file.write(data)
callback = Callback()
# 实例化SpeechSynthesizer,传入回调接口
synthesizer = SpeechSynthesizer(
model="cosyvoice-v3-flash",
voice="longanyang",
callback=callback
)
# 调用call方法,通过回调接口实时获取合成结果
synthesizer.call("今天天气怎么样?")
# 获取首包延迟
print(f'[Metric] requestId为:{synthesizer.get_last_request_id()},首包延迟为:{synthesizer.get_first_package_delay()}毫秒')
在同一个语音合成任务中分多次提交文本,并通过回调方式实时获取合成结果。
import time
import pyaudio
import dashscope
from dashscope.audio.tts_v2 import *
from datetime import datetime
def get_timestamp():
now = datetime.now()
return now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
# 定义回调接口
class Callback(ResultCallback):
_player = None
_stream = None
def on_open(self):
print("连接建立:" + get_timestamp())
self._player = pyaudio.PyAudio()
self._stream = self._player.open(
format=pyaudio.paInt16, channels=1, rate=22050, output=True
)
def on_complete(self):
print("语音合成完成,所有合成结果已被接收:" + get_timestamp())
def on_error(self, message: str):
print(f"语音合成出现异常:{message}")
def on_close(self):
print("连接关闭:" + get_timestamp())
self._stream.stop_stream()
self._stream.close()
self._player.terminate()
def on_event(self, message):
pass
def on_data(self, data: bytes) -> None:
print(get_timestamp() + " 二进制音频长度为:" + str(len(data)))
self._stream.write(data)
callback = Callback()
test_text = [
"流式文本语音合成SDK,",
"可以将输入的文本",
"合成为语音二进制数据,",
"相比于非流式语音合成,",
"流式合成的优势在于实时性",
"更强。用户在输入文本的同时",
"可以听到接近同步的语音输出,",
"极大地提升了交互体验,",
"减少了用户等待时间。",
]
# 实例化SpeechSynthesizer
synthesizer = SpeechSynthesizer(
model="cosyvoice-v3-flash",
voice="longanyang",
format=AudioFormat.PCM_22050HZ_MONO_16BIT,
callback=callback
)
# 流式发送待合成文本
for text in test_text:
synthesizer.streaming_call(text)
time.sleep(0.1)
# 结束流式语音合成(必须调用)
synthesizer.streaming_complete()
# 获取首包延迟
print(f'[Metric] requestId为:{synthesizer.get_last_request_id()},首包延迟为:{synthesizer.get_first_package_delay()}毫秒')
通过SpeechSynthesizer类的构造方法进行设置。
| 参数 | 类型 | 必选 | 说明 |
|---|---|---|---|
| model | str | 是 | 语音合成模型 - cosyvoice-v3-flash - cosyvoice-v3-plus - cosyvoice-v2 |
| voice | str | 是 | 音色 系统音色:longanyang、longyingjing_v3等 复刻音色:通过声音复刻功能定制 |
| format | enum | 否 | 音频编码格式及采样率 默认:22.05kHz,mp3格式 支持格式:WAV、MP3、PCM、OPUS 支持采样率:8kHz、16kHz、22.05kHz、24kHz、44.1kHz、48kHz |
| volume | int | 否 | 音量,范围[0, 100],默认50 |
| speech_rate | float | 否 | 语速,范围[0.5, 2.0],默认1.0 |
| pitch_rate | float | 否 | 音高,范围[0.5, 2.0],默认1.0 |
| bit_rate | int | 否 | 音频码率(单位kbps),范围[6, 510],默认32 仅opus格式支持 通过additional_params设置 |
| word_timestamp_enabled | bool | 否 | 是否开启字级别时间戳,默认False 通过additional_params设置 |
| seed | int | 否 | 随机数种子,范围[0, 65535],默认0 |
| language_hints | list[str] | 否 | 语言提示 - zh:中文 - en:英文 - fr:法语 - de:德语 - ja:日语 - ko:韩语 - ru:俄语 |
| instruction | str | 否 | 设置指令 - 指定小语种:"你会用德语说出来。" - 指定方言:"请用广东话表达。" - 指定情感、场景等 |
| enable_aigc_tag | bool | 否 | 是否添加AIGC隐性标识,默认False 通过additional_params设置 |
| aigc_propagator | str | 否 | AIGC标识中的传播者字段 通过additional_params设置 |
| aigc_propagate_id | str | 否 | AIGC标识中的传播ID字段 通过additional_params设置 |
| callback | ResultCallback | 否 | 回调接口 |
# WAV格式
AudioFormat.WAV_22050HZ_MONO_16BIT
# MP3格式
AudioFormat.MP3_22050HZ_MONO_256KBPS
# PCM格式
AudioFormat.PCM_22050HZ_MONO_16BIT
# OPUS格式
AudioFormat.OGG_OPUS_16KHZ_MONO_32KBPS
# 设置码率
synthesizer = SpeechSynthesizer(
model="cosyvoice-v3-flash",
voice="longanyang",
format=AudioFormat.OGG_OPUS_16KHZ_MONO_16KBPS,
additional_params={"bit_rate": 32}
)
# 开启字级别时间戳
synthesizer = SpeechSynthesizer(
model="cosyvoice-v3-flash",
voice="longyingjing_v3",
callback=callback,
additional_params={'word_timestamp_enabled': True}
)
# 开启AIGC标识
synthesizer = SpeechSynthesizer(
model="cosyvoice-v3-flash",
voice="longanyang",
additional_params={
"enable_aigc_tag": True,
"aigc_propagator": "xxxx",
"aigc_propagate_id": "xxxx"
}
)
| 方法 | 参数 | 返回值 | 描述 |
|---|---|---|---|
| call(text, timeout_millis) | text:待合成文本 timeout_millis:超时时间(毫秒) |
二进制音频数据或None | 将整段文本转换为语音 未指定callback:返回音频数据 指定callback:返回None,通过回调获取 |
| streaming_call(text) | text:待合成文本片段 | 无 | 流式发送待合成文本 可多次调用,分片提交 |
| streaming_complete(complete_timeout_millis) | complete_timeout_millis:等待时间(毫秒) | 无 | 结束流式语音合成 双向流式调用时必须调用 |
| get_last_request_id() | 无 | request_id | 获取上一个任务的request_id |
| get_first_package_delay() | 无 | 首包延迟(毫秒) | 获取首包延迟 |
| get_response() | 无 | JSON数据 | 获取最后一次报文 |
| 方法 | 参数 | 描述 |
|---|---|---|
| on_open() | 无 | 连接建立时回调 |
| on_event(message) | message:服务端返回的信息 | 服务有回复时回调 |
| on_complete() | 无 | 语音合成完成时回调 |
| on_error(message) | message:异常信息 | 发生异常时回调 |
| on_data(data) | data:二进制音频数据 | 有合成音频返回时回调 |
| on_close() | 无 | 连接关闭时回调 |
import dashscope
from dashscope.audio.tts_v2 import *
import json
from datetime import datetime
def get_timestamp():
now = datetime.now()
return now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
class Callback(ResultCallback):
def on_open(self):
self.file = open("output.mp3", "wb")
print("连接建立:" + get_timestamp())
def on_complete(self):
print("语音合成完成:" + get_timestamp())
def on_error(self, message: str):
print(f"语音合成出现异常:{message}")
def on_close(self):
print("连接关闭:" + get_timestamp())
self.file.close()
def on_event(self, message):
json_data = json.loads(message)
if json_data['payload'] and json_data['payload']['output'] and json_data['payload']['output']['sentence']:
sentence = json_data['payload']['output']['sentence']
print(f'sentence: {sentence}')
words = sentence['words']
if words:
for word in words:
print(f'word: {word}')
# 示例值:word: {'text': '今', 'begin_index': 0, 'end_index': 1, 'begin_time': 80, 'end_time': 200}
def on_data(self, data: bytes) -> None:
print(get_timestamp() + " 二进制音频长度为:" + str(len(data)))
self.file.write(data)
callback = Callback()
# 实例化SpeechSynthesizer,开启字级别时间戳
synthesizer = SpeechSynthesizer(
model="cosyvoice-v3-flash",
voice="longyingjing_v3",
callback=callback,
additional_params={'word_timestamp_enabled': True}
)
# 发送待合成文本
synthesizer.call("今天天气怎么样?")
print(f'[Metric] requestId为:{synthesizer.get_last_request_id()},首包延迟为:{synthesizer.get_first_package_delay()}毫秒')
| 调用方式 | 单次限制 | 累计限制 |
|---|---|---|
| call方法 | 不超过2000字符 | - |
| streaming_call方法 | 每次不超过2000字符 | 累计不超过20万字符 |
cosyvoice-v2、cosyvoice-v3-flash和cosyvoice-v3-plus模型支持识别中小学常见的数学表达式。
cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型支持SSML功能,可用于:
A: 在回调接口的on_event方法中解析message,获取characters参数:
def on_event(self, message):
json_data = json.loads(message)
if 'payload' in json_data and 'usage' in json_data['payload']:
characters = json_data['payload']['usage']['characters']
print(f'计费字符数:{characters}')
A: 请检查:
A: 请检查:
A: 双向流式调用时,请确保调用了streaming_complete()方法。
A: 安装系统根证书:
# CentOS/RHEL
sudo yum install -y ca-certificates
sudo update-ca-trust enable
在代码中添加:
import os
os.environ["SSL_CERT_FILE"] = "/etc/ssl/certs/ca-bundle.crt"
CosyVoice 语音合成服务基于 WebSocket 协议,以支持流式实时通信。然而,在高并发场景下,为每个请求独立创建和销毁 WebSocket 连接会产生巨大的网络与系统资源开销,并引入显著的连接延迟。为优化性能并确保稳定性,DashScope SDK 内置了高效的资源复用机制(如连接池与对象池)。
Python SDK 通过 SpeechSynthesizerObjectPool 实现对象池优化,用于管理和复用 SpeechSynthesizer 对象。
对象池在初始化时会立即创建指定数量的 SpeechSynthesizer 实例并预先建立 WebSocket 连接。从池中获取对象时无需等待连接建立,可直接发起请求,有效降低首包延迟。当任务完成并将对象归还到对象池后,其 WebSocket 连接不会关闭,而是保持活跃状态等待下次任务复用。
pip install -U dashscope1. 创建并配置对象池
对象池大小需要通过 SpeechSynthesizerObjectPool 进行设置。推荐值:峰值并发数的 1.5 至 2 倍。对象池大小不应超过您账户的 QPS(每秒查询率)限制。
from dashscope.audio.tts_v2 import SpeechSynthesizerObjectPool
# 创建全局单例固定大小对象池
# 对象池在初始化时会立即创建指定数量的 SpeechSynthesizer 对象并建立 WebSocket 连接
synthesizer_object_pool = SpeechSynthesizerObjectPool(max_size=20)
2. 从对象池中获取 SpeechSynthesizer 对象
如果当前未归还的对象数量已超过对象池的最大容量,系统会额外创建一个新的 SpeechSynthesizer 对象。此类新创建的对象需要重新进行初始化并建立 WebSocket 连接,无法利用对象池的既有连接资源,因此不具备复用效果。
speech_synthesizer = synthesizer_object_pool.borrow_synthesizer(
model='cosyvoice-v3-flash',
voice='longanyang',
seed=12382,
callback=synthesizer_callback
)
3. 进行语音合成
调用 SpeechSynthesizer 对象的 call 或 streaming_call 方法进行语音合成。
4. 归还 SpeechSynthesizer 对象
语音合成任务结束后,归还 SpeechSynthesizer 对象,以便后续任务可以复用该对象。
注意:不要归还未完成任务或任务失败的对象。
synthesizer_object_pool.return_synthesizer(speech_synthesizer)
import os
import time
import threading
import dashscope
from dashscope.audio.tts_v2 import *
USE_CONNECTION_POOL = True
text_to_synthesize = [
'第一句、欢迎使用阿里巴巴语音合成服务。',
'第二句、欢迎使用阿里巴巴语音合成服务。',
'第三句、欢迎使用阿里巴巴语音合成服务。',
]
connectionPool = None
if USE_CONNECTION_POOL:
print('creating connection pool')
start_time = time.time() * 1000
connectionPool = SpeechSynthesizerObjectPool(max_size=3)
end_time = time.time() * 1000
print('connection pool created, cost: {} ms'.format(end_time - start_time))
def init_dashscope_api_key():
if 'DASHSCOPE_API_KEY' in os.environ:
dashscope.api_key = os.environ['DASHSCOPE_API_KEY']
else:
dashscope.api_key = '<your-dashscope-api-key>'
def synthesis_text_to_speech_and_play_by_streaming_mode(text, task_id):
global USE_CONNECTION_POOL, connectionPool
complete_event = threading.Event()
class Callback(ResultCallback):
def on_open(self):
self.file = open(f'result_{task_id}.mp3', 'wb')
print(f'[task_{task_id}] start')
def on_complete(self):
print(f'[task_{task_id}] speech synthesis task complete successfully.')
complete_event.set()
def on_error(self, message: str):
print(f'[task_{task_id}] speech synthesis task failed, {message}')
def on_close(self):
print(f'[task_{task_id}] finished')
def on_event(self, message):
pass
def on_data(self, data: bytes) -> None:
self.file.write(data)
synthesizer_callback = Callback()
if USE_CONNECTION_POOL:
speech_synthesizer = connectionPool.borrow_synthesizer(
model='cosyvoice-v3-flash',
voice='longanyang',
seed=12382,
callback=synthesizer_callback
)
else:
speech_synthesizer = SpeechSynthesizer(
model='cosyvoice-v3-flash',
voice='longanyang',
seed=12382,
callback=synthesizer_callback
)
try:
speech_synthesizer.call(text)
except Exception as e:
print(f'[task_{task_id}] speech synthesis task failed, {e}')
if USE_CONNECTION_POOL:
speech_synthesizer.close()
return
print('[task_{}] Synthesized text: {}'.format(task_id, text))
complete_event.wait()
print('[task_{}][Metric] requestId: {}, first package delay ms: {}'.format(
task_id,
speech_synthesizer.get_last_request_id(),
speech_synthesizer.get_first_package_delay()
))
if USE_CONNECTION_POOL:
connectionPool.return_synthesizer(speech_synthesizer)
if __name__ == '__main__':
init_dashscope_api_key()
task_thread_list = []
for task_id in range(3):
thread = threading.Thread(
target=synthesis_text_to_speech_and_play_by_streaming_mode,
args=(text_to_synthesize[task_id], task_id)
)
task_thread_list.append(thread)
for task_thread in task_thread_list:
task_thread.start()
for task_thread in task_thread_list:
task_thread.join()
if USE_CONNECTION_POOL:
connectionPool.shutdown()
任务成功:
当语音合成任务正常完成时,必须调用 connectionPool.return_synthesizer(speech_synthesizer) 将 SpeechSynthesizer 对象归还到池中,以便复用。
注意:不要归还未完成任务或任务失败的 SpeechSynthesizer 对象。
任务失败:
当 SDK 内部或业务逻辑抛出异常导致任务中断时,主动关闭底层的 WebSocket 连接:
speech_synthesizer.close()
关闭对象池:
在所有语音合成任务完成后,要通过如下方式关闭对象池:
connectionPool.shutdown()
注意:在服务出现 TaskFailed 报错时,不需要额外处理。
Java SDK 通过内置的连接池和自定义的对象池协同工作,实现最佳性能。
commons-pool2 实现,用于维护一组已预先建立好连接的 SpeechSynthesizer 对象。从池中获取对象可消除连接建立的延迟,显著降低首包延迟。1. 添加依赖
根据项目构建工具,在依赖配置文件中添加 dashscope-sdk-java 和 commons-pool2。
Maven 配置:
在 pom.xml 文件的 <dependencies> 标签内添加:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<!-- 请将 'the-latest-version' 替换为2.16.6及以上版本 -->
<version>the-latest-version</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<!-- 请将 'the-latest-version' 替换为最新版本 -->
<version>the-latest-version</version>
</dependency>
保存后使用 Maven 命令更新项目依赖:mvn clean install 或 mvn compile
Gradle 配置:
在 build.gradle 文件的 dependencies 块内添加:
dependencies {
// 请将 'the-latest-version' 替换为2.16.6及以上版本
implementation group: 'com.alibaba', name: 'dashscope-sdk-java', version: 'the-latest-version'
// 请将 'the-latest-version' 替换为最新版本
implementation group: 'org.apache.commons', name: 'commons-pool2', version: 'the-latest-version'
}
保存后执行 Gradle 命令更新项目依赖:./gradlew build --refresh-dependencies(Windows 系统使用 gradlew build --refresh-dependencies)
2. 配置连接池
通过环境变量配置连接池关键参数:
| 环境变量 | 描述 |
|---|---|
| DASHSCOPE_CONNECTION_POOL_SIZE | 连接池大小 推荐值:峰值并发数的 2 倍以上 默认值:32 |
| DASHSCOPE_MAXIMUM_ASYNC_REQUESTS | 最大异步请求数 推荐值:与 DASHSCOPE_CONNECTION_POOL_SIZE 保持一致 默认值:32 |
| DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOST | 单主机最大异步请求数 推荐值:与 DASHSCOPE_CONNECTION_POOL_SIZE 保持一致 默认值:32 |
3. 配置对象池
通过环境变量配置对象池大小:
| 环境变量 | 描述 |
|---|---|
| COSYVOICE_OBJECTPOOL_SIZE | 对象池大小 推荐值:峰值并发数的 1.5 至 2 倍 默认值:500 |
重要提示:
COSYVOICE_OBJECTPOOL_SIZE)必须小于或等于连接池的大小(DASHSCOPE_CONNECTION_POOL_SIZE)。否则,当对象池请求对象时,若连接池已满,会导致调用线程阻塞,等待可用连接。4. 创建对象池
class CosyvoiceObjectPool {
public static GenericObjectPool<SpeechSynthesizer> synthesizerPool;
public static String COSYVOICE_OBJECTPOOL_SIZE_ENV = "COSYVOICE_OBJECTPOOL_SIZE";
public static int DEFAULT_OBJECT_POOL_SIZE = 500;
private static Lock lock = new java.util.concurrent.locks.ReentrantLock();
public static int getObjectivePoolSize() {
try {
Integer n = Integer.parseInt(System.getenv(COSYVOICE_OBJECTPOOL_SIZE_ENV));
System.out.println("Using Object Pool Size In Env: "+ n);
return n;
} catch (NumberFormatException e) {
System.out.println("Using Default Object Pool Size: "+ DEFAULT_OBJECT_POOL_SIZE);
return DEFAULT_OBJECT_POOL_SIZE;
}
}
public static GenericObjectPool<SpeechSynthesizer> getInstance() {
lock.lock();
if (synthesizerPool == null) {
int objectPoolSize = getObjectivePoolSize();
SpeechSynthesizerObjectFactory speechSynthesizerObjectFactory =
new SpeechSynthesizerObjectFactory();
GenericObjectPoolConfig<SpeechSynthesizer> config =
new GenericObjectPoolConfig<>();
config.setMaxTotal(objectPoolSize);
config.setMaxIdle(objectPoolSize);
config.setMinIdle(objectPoolSize);
synthesizerPool =
new GenericObjectPool<>(speechSynthesizerObjectFactory, config);
}
lock.unlock();
return synthesizerPool;
}
}
5. 从对象池中获取对象
synthesizer = CosyvoiceObjectPool.getInstance().borrowObject();
6. 进行语音合成
调用 SpeechSynthesizer 对象的 call 或 streamingCall 方法进行语音合成。
7. 归还对象
CosyvoiceObjectPool.getInstance().returnObject(synthesizer);
以下配置基于在指定规格的阿里云服务器上仅运行 CosyVoice 语音合成服务的测试结果。过高的并发数可能导致任务处理延迟。
其中单机并发数指的是同一时刻正在运行的 CosyVoice 语音合成任务数,也可以理解为工作线程数。
| 机器配置(阿里云) | 单机最大并发数 | 对象池大小 | 连接池大小 |
|---|---|---|---|
| 4核8GiB | 100 | 500 | 2000 |
| 8核16GiB | 150 | 500 | 2000 |
| 16核32GiB | 200 | 500 | 2000 |
任务成功:
当语音合成任务正常完成时,必须调用 GenericObjectPool 的 returnObject 方法将 SpeechSynthesizer 对象归还到池中,以便复用。
CosyvoiceObjectPool.getInstance().returnObject(synthesizer);
注意:不要归还未完成任务或任务失败的 SpeechSynthesizer 对象。
任务失败:
当 SDK 内部或业务逻辑抛出异常导致任务中断时,必须执行以下两个操作:
从对象池中废弃该对象,防止被再次使用
// 关闭连接
synthesizer.getDuplexApi().close(1000, "bye");
// 在对象池中废弃出现异常的synthesizer
CosyvoiceObjectPool.getInstance().invalidateObject(synthesizer);
注意:在服务出现 TaskFailed 报错时,不需要额外处理。
出错原因:
类型一:
每一个 SDK 对象创建时都会申请一个连接。如果没有使用对象池,每一次任务结束后对象都被析构。此时这一个连接将进入无引用状态,需要等待 61s 秒后服务端报错连接超时才会真正断开,这会导致这个连接在 61 秒内不可复用。
在高并发场景下,新的任务在发现没有可复用连接时会创建新连接,会造成如下后果:
类型二:
对象池配置的 MaxIdle 小于 MaxTotal,导致在对象闲置时,超过 MaxIdle 的对象被销毁,从而造成连接泄漏。泄漏的连接需要等待 61 秒超时后断连,同类型一造成连接数持续上升。
解决方法:
同"异常 1",连接池已经达到最大连接限制,新的任务需要等待无引用状态的连接 61 秒触发超时后才可以获得连接。
出错原因:
在高并发调用时,同一个对象会复用同一个 WebSocket 连接,因此 WebSocket 连接只会在服务启动时创建。需要注意的是,任务启动阶段如果立刻开始较高并发调用,同时创建过多的 WebSocket 连接会导致阻塞。
解决方法:
启动服务后逐步提升并发量,或增加预热任务。
出错原因:
这是由于出现了客户端报错后,服务端不知道客户端出错,连接处于任务中状态。此时连接和对象被复用并开启下一个任务,导致流程错误,下一个任务失败。
解决方法:
在抛出异常后主动关闭 WebSocket 连接后归还对象池。
出错原因:
同时创建过多 WebSocket 连接导致阻塞,但业务流量持续打进来,导致任务短时间积压,并且在阻塞后所有积压任务立刻调用。这会造成调用量尖刺,并且有可能造成瞬时超过账号的并发数限制导致部分任务失败、服务器卡顿等。
这种瞬间创建过多 WebSocket 的情况多发生于:
解决方法:
解决方法: