Reutilice objetos de síntesis y conexiones WebSocket mediante pools y configure concurrencia, calentamiento y manejo de errores para reducir la sobrecarga de conexión.
Alcance
Los pools conservan objetos de síntesis y conexiones WebSocket para reducir la creación de objetos, las negociaciones TCP/TLS y las actualizaciones de protocolo. Las siguientes series de modelos y SDK admiten pools de objetos:
- Qwen-Audio-TTS, CosyVoice: pools de objetos de Python y Java.
Requisitos previos
- Obtenga una clave de API y configure
DASHSCOPE_API_KEY. Utilice la clave de la región a la que llama. - Reemplace
{WorkspaceId}por el ID del espacio de trabajo de la región y configure su endpoint WebSocket.
Python SDK
Los pools de Qwen-Audio-TTS y CosyVoice requieren el SDK de Python de DashScope 1.25.2 o posterior:
pip install "dashscope>=1.25.2"
Java SDK
- Qwen-Audio-TTS, CosyVoice: SDK de Java de DashScope 2.16.6 o posterior.
Los ejemplos de Java también requieren Apache Commons Pool 2. Configure las dependencias con Maven o Gradle:
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<version>2.23.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.12.0</version>
</dependency>
</dependencies>
dependencies {
implementation 'com.alibaba:dashscope-sdk-java:2.23.2'
implementation 'org.apache.commons:commons-pool2:2.12.0'
}
Configurar concurrencia y pools de recursos
Configure los pools según el número previsto de tareas simultáneas y la capacidad del servidor. Los límites de los modelos se calculan por cuenta de Alibaba Cloud y los comparten sus usuarios RAM, espacios de trabajo y claves de API. Para los límites de frecuencia y concurrencia, consulte Límites de tasa.
- Comience con una capacidad del pool de 1.5 a 2 veces la concurrencia máxima y ajústela a la carga real.
- El pool de Python puede crear objetos adicionales cuando todos están prestados. Estos objetos necesitan conexiones nuevas al utilizarlos.
Configuraciones de Java
Defina las variables del SDK antes de iniciar el proceso de Java. Las primeras tres son configuraciones del SDK; las variables de capacidad siguientes se definen en los ejemplos:
| Variable de entorno | Valor predeterminado | Descripción |
|---|---|---|
| 32 | Configuración del pool de conexiones inactivas OkHttp subyacente. |
| 32 | Máximo de solicitudes del dispatcher asíncrono subyacente. |
| 32 | Máximo de solicitudes por host del dispatcher asíncrono subyacente. |
| 500 | Capacidad del pool del ejemplo de Java Qwen-Audio-TTS/CosyVoice, utilizada en |
Los ejemplos de Java establecen maxIdle igual a maxTotal para reducir las recreaciones tras destruir objetos inactivos. Cierre la conexión al destruir un objeto. Al llegar a maxTotal, las nuevas solicitudes de préstamo esperan un objeto disponible.
Referencia de configuración del servidor
Las siguientes configuraciones de servidores Alibaba Cloud sirven de referencia para ejecutar únicamente el servicio de síntesis indicado. La concurrencia por servidor significa tareas de síntesis simultáneas. Ajuste la configuración a la carga real.
La configuración del pool de conexiones de esta tabla es el valor de referencia para las tres variables: DASHSCOPE_CONNECTION_POOL_SIZE, DASHSCOPE_MAXIMUM_ASYNC_REQUESTS y DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOST. En estas configuraciones de referencia, establezca las tres en 2000. Configure la capacidad del pool de objetos con la variable de entorno del ejemplo correspondiente.
| Serie | Servidor de Alibaba Cloud | Tareas simultáneas | Capacidad del pool de objetos | Configuración del pool de conexiones |
|---|---|---|---|---|
CosyVoice | 4 vCPU / 8 GiB | 100 | 500 | 2000 |
CosyVoice | 8 vCPU / 16 GiB | 150 | 500 | 2000 |
CosyVoice | 16 vCPU / 32 GiB | 200 | 500 | 2000 |
Ejemplos de código
Qwen-Audio-TTS/CosyVoice
Los ejemplos usan qwen-audio-3.1-tts-flash y longanhuan_v3.1 para tres tareas simultáneas. Para CosyVoice, cambie el modelo y la voz a cosyvoice-v3-flash y longanyang. Para otros modelos y requisitos de voz, consulte Síntesis de voz en tiempo real.
Python SDK
import os
import threading
from concurrent.futures import ThreadPoolExecutor
import dashscope
from dashscope.audio.tts_v2 import SpeechSynthesizerObjectPool, ResultCallback, AudioFormat
dashscope.api_key = os.environ["DASHSCOPE_API_KEY"]
dashscope.base_websocket_api_url = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference"
pool = SpeechSynthesizerObjectPool(max_size=3)
def synthesize(index):
done = threading.Event()
errors = []
with open(f"output_{index}.mp3", "wb") as audio:
class Callback(ResultCallback):
def on_data(self, data: bytes):
audio.write(data)
def on_complete(self):
done.set()
def on_error(self, message):
errors.append(message)
done.set()
synthesizer = pool.borrow_synthesizer(
model="qwen-audio-3.1-tts-flash",
voice="longanhuan_v3.1",
format=AudioFormat.MP3_24000HZ_MONO_256KBPS,
callback=Callback(),
)
successful = False
try:
synthesizer.streaming_call("你好,欢迎使用阿里云百炼。")
synthesizer.streaming_complete(60000)
if not done.wait(60):
raise TimeoutError("Synthesis timed out")
if errors:
raise RuntimeError(errors[0])
successful = True
finally:
if successful:
pool.return_synthesizer(synthesizer)
else:
synthesizer.close()
try:
with ThreadPoolExecutor(max_workers=3) as executor:
list(executor.map(synthesize, range(3)))
finally:
pool.shutdown()
Java SDK
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
public class PooledTts {
static class Factory extends BasePooledObjectFactory<SpeechSynthesizer> {
public SpeechSynthesizer create() { return new SpeechSynthesizer(); }
public PooledObject<SpeechSynthesizer> wrap(SpeechSynthesizer value) {
return new DefaultPooledObject<>(value);
}
public void destroyObject(PooledObject<SpeechSynthesizer> value) {
value.getObject().getDuplexApi().close(1000, "done");
}
}
static void synthesize(GenericObjectPool<SpeechSynthesizer> pool, int index) throws Exception {
SpeechSynthesizer synthesizer = pool.borrowObject();
boolean successful = false;
AtomicReference<Exception> error = new AtomicReference<>();
try (FileOutputStream audio = new FileOutputStream("output_" + index + ".mp3")) {
SpeechSynthesisParam param = SpeechSynthesisParam.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.model("qwen-audio-3.1-tts-flash")
.voice("longanhuan_v3.1")
.format(SpeechSynthesisAudioFormat.MP3_24000HZ_MONO_256KBPS)
.build();
synthesizer.updateParamAndCallback(param, new ResultCallback<SpeechSynthesisResult>() {
public void onEvent(SpeechSynthesisResult result) {
ByteBuffer buffer = result.getAudioFrame();
if (buffer != null) {
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
try { audio.write(bytes); }
catch (Exception e) { error.set(e); }
}
}
public void onComplete() {}
public void onError(Exception e) { error.set(e); }
});
synthesizer.streamingCall("你好,欢迎使用阿里云百炼。");
synthesizer.streamingComplete(60000);
if (error.get() != null) { throw error.get(); }
successful = true;
} finally {
if (successful) { pool.returnObject(synthesizer); }
else { pool.invalidateObject(synthesizer); }
}
}
public static void main(String[] args) throws Exception {
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference";
GenericObjectPoolConfig<SpeechSynthesizer> config = new GenericObjectPoolConfig<>();
int poolSize = Integer.parseInt(System.getenv().getOrDefault("COSYVOICE_OBJECTPOOL_SIZE", "500"));
config.setMaxTotal(poolSize);
config.setMaxIdle(poolSize);
config.setMaxWait(Duration.ofSeconds(60));
ExecutorService executor = Executors.newFixedThreadPool(3);
try (GenericObjectPool<SpeechSynthesizer> pool = new GenericObjectPool<>(new Factory(), config)) {
java.util.List<Future<?>> futures = new java.util.ArrayList<>();
for (int i = 0; i < 3; i++) {
final int index = i;
futures.add(executor.submit(() -> {
try { synthesize(pool, index); }
catch (Exception e) { throw new RuntimeException(e); }
}));
}
for (Future<?> future : futures) { future.get(); }
} finally {
executor.shutdownNow();
}
}
}
Uso en producción
Administración de recursos
- Configure la clave de API y el endpoint, y cree un pool de objetos compartido por las tareas de la aplicación.
- Obtenga y configure un objeto. En Python, pase el modelo, la voz, el formato y el callback a
borrow_synthesizer. En el ejemplo Java de Qwen-Audio-TTS/CosyVoice, llame aborrowObjecty, cada vez que obtenga un objeto, aupdateParamAndCallback(param, callback)para configurar los parámetros y el callback de la tarea. - Ejecute la síntesis. Los objetos Qwen-Audio-TTS/CosyVoice admiten
callostreaming_callen Python, ycallostreamingCallen Java. - Tras una finalización normal, devuelva el objeto con
return_synthesizeren Python oreturnObjecten Java. No devuelva directamente objetos incompletos o fallidos: llame alclose()del objeto en Python, o descártelo coninvalidateObjecten Java para que la factory cierre la conexión. - Cuando terminen todas las tareas, libere los recursos del pool con
shutdown()en Python o con elclose()del pool en Java.
Autenticación y varias claves de API
- La clave de API se envía en el encabezado
Authorizationsolo durante la negociación WebSocket. Los mensajesrun-taskposteriores no la incluyen. Cambiar la clave en los parámetros o la configuración global no cambia la identidad de la conexión existente, por lo que la identidad, el uso de cuota o los cargos pueden atribuirse de forma distinta a la prevista. - En Python, configure
dashscope.api_keyantes de crear el pool. Las conexiones establecidas al inicializarlo conservan la clave de la negociación yborrow_synthesizerno tiene parámetro de clave de API.SpeechSynthesizerObjectPooles un singleton por proceso; separe las claves en procesos distintos. - En Java, cree un pool independiente para cada clave de API y use siempre la misma clave dentro de cada pool. Para Qwen-Audio-TTS/CosyVoice, llame a
updateParamAndCallbackcada vez que obtenga un objeto. Este método no vuelve a autenticar una conexión existente.
Calentamiento y medición de latencia
- Tras el inicio o la recuperación de la red, realice calentamiento y aumente la concurrencia gradualmente. Antes de las pruebas de rendimiento, caliente con la concurrencia objetivo, por ejemplo durante 1 a 2 minutos, y recopile mediciones cuando las conexiones se reutilicen de forma estable.
- El pool de Python establece conexiones durante la inicialización. Java
GenericObjectPoolcrea objetos bajo demanda y se conecta en la primera síntesis. Por ello, el calentamiento de las conexiones de Java requiere solicitudes reales de síntesis. - La primera llamada incluye la creación de la conexión WebSocket. Mida su latencia del primer audio y su tiempo total por separado de las llamadas que reutilizan conexiones. También puede evaluar la velocidad con el factor de tiempo real (RTF, tiempo total de síntesis / duración del audio).
- Para Qwen-Audio-TTS, CosyVoice, las conexiones se cierran tras 60 segundos de inactividad después de la tarea. Evalúe la llamada tras una inactividad prolongada como una conexión nueva.
La latencia del primer audio medida por el SDK incluye el establecimiento de la conexión y la transmisión de red, por lo que no equivale únicamente al tiempo de inferencia del modelo. Para Qwen-Audio-TTS/CosyVoice, los objetos Python ofrecen get_first_package_delay() para esa latencia y get_last_request_id() para el ID que permite correlacionar los registros de solicitudes. Los objetos Java ofrecen getLastRequestId() para el ID de solicitud.
Solución de problemas
Las conexiones TCP aumentan aunque el tráfico es estable
Crear objetos de síntesis con frecuencia sin cerrar las conexiones antiguas puede aumentar continuamente el número de conexiones y el consumo de recursos del servidor. Compruebe la devolución de objetos tras las tareas y el cierre de conexiones que ya no se usan.
En Java, si maxIdle es menor que maxTotal, los objetos devueltos por encima de la capacidad inactiva se destruyen. Si destroyObject de la factory no cierra el WebSocket, su conexión puede seguir abierta después de destruir el objeto. Establezca maxIdle igual a maxTotal para reducir la destrucción y creación repetidas, y cierre las conexiones al destruir objetos.
Las tareas esperan más o agotan el tiempo
Al llegar a maxTotal, un préstamo del pool de Java espera un objeto disponible. Revise capacidad, devoluciones y cierre de conexiones para evitar esperas adicionales por objetos.
Las tareas se ralentizan al iniciar o recuperar la red
Tras el inicio o la recuperación de la red, realice calentamiento y aumente la concurrencia gradualmente para evitar bloqueos por muchas negociaciones WebSocket simultáneas.
El servidor devuelve Invalid action('run-task')! Please follow the protocol!
Tras una interrupción por un error del cliente, el servidor puede seguir considerando activa la tarea anterior. Reutilizar la conexión para un nuevo run-task provoca un error de secuencia del protocolo. Cierre la conexión y descarte el objeto; después, establezca una conexión nueva para las tareas posteriores.
Las llamadas presentan picos aunque el tráfico es estable
Las reconexiones masivas al iniciar el servicio, tras una interrupción de red o tras muchos errores del servidor pueden acumular tareas. Cuando las conexiones se recuperan, el envío simultáneo de esas tareas puede generar un pico y activar los límites de la cuenta.
- Revise si hubo problemas de red o muchos errores antes del pico, como
Requests rate limit exceeded, please try again later.. - Controle la concurrencia durante el inicio y la recuperación. En Java,
maxTotallimita el número de objetos obtenidos simultáneamente del mismo pool. - Compruebe las cuotas de la cuenta. Para solicitar un aumento, siga el procedimiento de Límites de tasa.
- Ajuste las especificaciones de los servidores o añada servidores según la carga real.
Todas las tareas se ralentizan al aumentar la concurrencia
Revise si el ancho de banda de red está saturado o la concurrencia es demasiado alta. Ajuste la concurrencia o la capacidad del servidor a la carga.