Todos los productos
Search
Centro de documentos

Alibaba Cloud Model Studio:Prácticas recomendadas para síntesis de voz con alta concurrencia

Última actualización: Oct 19, 2026

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

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 entornoValor predeterminadoDescripción

DASHSCOPE_CONNECTION_POOL_SIZE

32

Configuración del pool de conexiones inactivas OkHttp subyacente.

DASHSCOPE_MAXIMUM_ASYNC_REQUESTS

32

Máximo de solicitudes del dispatcher asíncrono subyacente.

DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOST

32

Máximo de solicitudes por host del dispatcher asíncrono subyacente.

COSYVOICE_OBJECTPOOL_SIZE

500

Capacidad del pool del ejemplo de Java Qwen-Audio-TTS/CosyVoice, utilizada en maxTotal y maxIdle. No es un parámetro integrado del SDK.

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.

SerieServidor de Alibaba CloudTareas simultáneasCapacidad del pool de objetosConfiguració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

  1. Configure la clave de API y el endpoint, y cree un pool de objetos compartido por las tareas de la aplicación.
  2. 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 a borrowObject y, cada vez que obtenga un objeto, a updateParamAndCallback(param, callback) para configurar los parámetros y el callback de la tarea.
  3. Ejecute la síntesis. Los objetos Qwen-Audio-TTS/CosyVoice admiten call o streaming_call en Python, y call o streamingCall en Java.
  4. Tras una finalización normal, devuelva el objeto con return_synthesizer en Python o returnObject en Java. No devuelva directamente objetos incompletos o fallidos: llame al close() del objeto en Python, o descártelo con invalidateObject en Java para que la factory cierre la conexión.
  5. Cuando terminen todas las tareas, libere los recursos del pool con shutdown() en Python o con el close() del pool en Java.

Autenticación y varias claves de API

  • La clave de API se envía en el encabezado Authorization solo durante la negociación WebSocket. Los mensajes run-task posteriores 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_key antes de crear el pool. Las conexiones establecidas al inicializarlo conservan la clave de la negociación y borrow_synthesizer no tiene parámetro de clave de API. SpeechSynthesizerObjectPool es 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 updateParamAndCallback cada vez que obtenga un objeto. Este método no vuelve a autenticar una conexión existente.

Calentamiento y medición de latencia

  1. 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.
  2. El pool de Python establece conexiones durante la inicialización. Java GenericObjectPool crea 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.
  3. 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).
  4. 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.

  1. Revise si hubo problemas de red o muchos errores antes del pico, como Requests rate limit exceeded, please try again later..
  2. Controle la concurrencia durante el inicio y la recuperación. En Java, maxTotal limita el número de objetos obtenidos simultáneamente del mismo pool.
  3. Compruebe las cuotas de la cuenta. Para solicitar un aumento, siga el procedimiento de Límites de tasa.
  4. 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.