Sintesis suara CosyVoice menggunakan protokol WebSocket untuk mendukung komunikasi streaming real-time. Namun, dalam skenario konkurensi tinggi, pembuatan dan penghapusan koneksi WebSocket untuk setiap permintaan menimbulkan overhead signifikan pada jaringan dan sumber daya sistem serta latensi koneksi yang terasa. Untuk mengoptimalkan kinerja dan memastikan stabilitas, SDK DashScope menyediakan mekanisme penggunaan ulang sumber daya yang efisien—seperti connection pool dan object pool. Dokumen ini menjelaskan cara menggunakan fitur-fitur tersebut dalam SDK Python dan Java DashScope untuk memanggil CosyVoice secara efisien dalam kondisi konkurensi tinggi.
Prasyarat
Instal versi SDK DashScope yang memenuhi persyaratan. Kami merekomendasikan menginstal versi terbaru:
SDK Python: versi 1.25.2 atau lebih baru
SDK Java: versi 2.16.6 atau lebih baru
SDK Python: Optimisasi object pool
SDK Python menggunakan SpeechSynthesizerObjectPool untuk menerapkan optimisasi object pool. Pool ini mengelola dan menggunakan kembali objek SpeechSynthesizer.
Saat inisialisasi, object pool membuat sejumlah instance SpeechSynthesizer sesuai yang ditentukan dan menetapkan koneksi WebSocket sebelumnya. Saat Anda mengambil objek dari pool, tidak ada waktu yang dihabiskan untuk membuat koneksi baru. Anda dapat langsung mengirim permintaan, sehingga mengurangi latensi paket pertama. Setelah tugas selesai dan Anda mengembalikan objek ke pool, koneksi WebSocket-nya tetap terbuka dan siap digunakan kembali.
Langkah implementasi
Instal dependensi: Instal paket DashScope (
pip install -U dashscope).Buat dan konfigurasikan object pool.
Tentukan ukuran pool menggunakan
SpeechSynthesizerObjectPool. Nilai yang direkomendasikan adalah 1,5× hingga 2× konkurensi puncak Anda. Jangan atur ukuran pool melebihi batas permintaan per detik (QPS) akun Anda.Gunakan kode berikut untuk membuat object pool berukuran tetap global singleton. Pool ini membuat jumlah objek
SpeechSynthesizeryang ditentukan dan menetapkan koneksi WebSocket selama inisialisasi. Langkah ini memerlukan waktu.from dashscope.audio.tts_v2 import SpeechSynthesizerObjectPool synthesizer_object_pool = SpeechSynthesizerObjectPool(max_size=20)Ambil objek
SpeechSynthesizerdari pool.Jika jumlah objek yang sedang dipinjam melebihi ukuran maksimum pool, sistem akan membuat objek
SpeechSynthesizerbaru.Objek yang baru dibuat ini harus melakukan inisialisasi dan menetapkan koneksi WebSocket. Objek tersebut tidak dapat menggunakan kembali koneksi yang ada di pool, sehingga tidak mendapat manfaat dari pooling.
speech_synthesizer = connectionPool.borrow_synthesizer( model='cosyvoice-v3-flash', voice='longanyang', seed=12382, callback=synthesizer_callback )Lakukan sintesis suara.
Panggil metode call atau streaming_call dari objek
SpeechSynthesizeruntuk mensintesis suara.Kembalikan objek
SpeechSynthesizer.Setelah tugas sintesis suara selesai, kembalikan objek
SpeechSynthesizeragar dapat digunakan kembali oleh tugas berikutnya.Jangan mengembalikan objek dari tugas yang belum selesai atau gagal.
connectionPool.return_synthesizer(speech_synthesizer)
Kode lengkap
# !/usr/bin/env python3
# Copyright (C) Alibaba Group. All Rights Reserved.
# MIT License (https://opensource.org/licenses/MIT)
import os
import time
import threading
import dashscope
from dashscope.audio.tts_v2 import *
USE_CONNECTION_POOL = True
text_to_synthesize = [
'First sentence: Welcome to Alibaba Cloud speech synthesis.',
'Second sentence: Welcome to Alibaba Cloud speech synthesis.',
'Third sentence: Welcome to Alibaba Cloud speech synthesis.',
]
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():
'''
Setel Kunci API DashScope Anda. Informasi lebih lanjut:
https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
'''
# Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API Anda: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
if 'DASHSCOPE_API_KEY' in os.environ:
dashscope.api_key = os.environ[
'DASHSCOPE_API_KEY'] # muat Kunci API dari variabel lingkungan DASHSCOPE_API_KEY
else:
dashscope.api_key = '<your-dashscope-api-key>' # atur Kunci API secara manual
def synthesis_text_to_speech_and_play_by_streaming_mode(text, task_id):
global USE_CONNECTION_POOL, connectionPool
'''
Sintesis suara dengan teks yang diberikan dalam mode streaming, panggilan asinkron, dan putar audio hasil sintesis secara real-time.
Untuk informasi lebih lanjut, lihat https://www.alibabacloud.com/help/document_detail/2712523.html
'''
complete_event = threading.Event()
# Definisikan callback untuk menangani hasil
class Callback(ResultCallback):
def on_open(self):
# saat menggunakan object pool, on_open dipanggil setelah tugas dimulai
self.file = open(f'result_{task_id}.mp3', 'wb')
print(f'[task_{task_id}] start')
def on_complete(self):
print(f'[task_{task_id}] tugas sintesis suara berhasil diselesaikan.')
complete_event.set()
def on_error(self, message: str):
print(f'[task_{task_id}] tugas sintesis suara gagal, {message}')
def on_close(self):
# saat menggunakan object pool, on_open dipanggil setelah tugas selesai
print(f'[task_{task_id}] finished')
def on_event(self, message):
# print(f'recv speech synthsis message {message}')
pass
def on_data(self, data: bytes) -> None:
# kirim ke pemutar
# simpan audio ke file
self.file.write(data)
# Panggil callback synthesizer suara
synthesizer_callback = Callback()
# Inisialisasi synthesizer suara
# Anda dapat menyesuaikan parameter sintesis, seperti voice, format, sample_rate, atau parameter lainnya
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}] tugas sintesis suara gagal, {e}')
if USE_CONNECTION_POOL:
# tutup koneksi synthesizer secara manual jika tugas gagal saat menggunakan connection pool.
speech_synthesizer.close()
return
print('[task_{}] Teks yang disintesis: {}'.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)
# fungsi utama
if __name__ == '__main__':
# URL berikut untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti dengan: wss://dashscope.aliyuncs.com/api-ws/v1/inference
dashscope.base_websocket_api_url='wss://dashscope-intl.aliyuncs.com/api-ws/v1/inference'
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()Manajemen resource dan penanganan error
Tugas berhasil: Saat tugas sintesis suara selesai secara normal, panggil
connectionPool.return_synthesizer(speech_synthesizer)untuk mengembalikan objekSpeechSynthesizerke pool agar dapat digunakan kembali.PentingJangan mengembalikan objek
SpeechSynthesizerdari tugas yang belum selesai atau gagal.Tugas gagal: Saat terjadi exception dari SDK atau logika bisnis Anda yang mengganggu tugas, tutup koneksi WebSocket yang mendasari secara manual:
speech_synthesizer.close().Setelah semua tugas sintesis suara selesai, matikan object pool menggunakan
connectionPool.shutdown().Tidak diperlukan tindakan tambahan saat layanan mengembalikan error TaskFailed.
SDK Java: Optimisasi connection pool dan object pool
SDK Java mencapai kinerja optimal dengan menggabungkan connection pool internal dan object pool kustom.
Connection pool: SDK menggunakan connection pool bawaan OkHttp3 untuk mengelola dan menggunakan kembali koneksi WebSocket yang mendasari. Ini mengurangi overhead handshake dan diaktifkan secara default.
Object pool: Dibangun di atas
commons-pool2, pool ini mempertahankan sekelompok objekSpeechSynthesizerdengan koneksi yang telah ditetapkan sebelumnya. Mengambil objek dari pool menghilangkan latensi penyiapan koneksi dan secara signifikan mengurangi latensi paket pertama.
Langkah implementasi
Tambahkan dependensi.
Tambahkan dashscope-sdk-java dan commons-pool2 ke file konfigurasi dependensi proyek Anda, sesuai dengan tool build yang Anda gunakan.
Contoh untuk Maven dan Gradle:
Maven
Buka file
pom.xmlproyek Maven Anda.Tambahkan dependensi berikut di dalam tag
<dependencies>.
<dependency> <groupId>com.alibaba</groupId> <artifactId>dashscope-sdk-java</artifactId> <!-- Ganti 'the-latest-version' dengan versi 2.16.9 atau lebih baru. Periksa versi yang tersedia di: https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java --> <version>the-latest-version</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <!-- Ganti 'the-latest-version' dengan versi terbaru. Periksa versi yang tersedia di: https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 --> <version>the-latest-version</version> </dependency>Simpan file
pom.xml.Jalankan perintah Maven seperti
mvn clean installataumvn compileuntuk memperbarui dependensi proyek.
Gradle
Buka file
build.gradleproyek Gradle Anda.Tambahkan dependensi berikut di dalam blok
dependencies.dependencies { // Ganti 'the-latest-version' dengan versi 2.16.6 atau lebih baru. Periksa versi yang tersedia di: https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java implementation group: 'com.alibaba', name: 'dashscope-sdk-java', version: 'the-latest-version' // Ganti 'the-latest-version' dengan versi terbaru. Periksa versi yang tersedia di: https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 implementation group: 'org.apache.commons', name: 'commons-pool2', version: 'the-latest-version' }Simpan file
build.gradle.Di terminal Anda, navigasi ke direktori root proyek dan jalankan perintah Gradle berikut untuk memperbarui dependensi.
./gradlew build --refresh-dependenciesAtau, jika Anda menggunakan Windows, jalankan:
gradlew build --refresh-dependencies
Konfigurasikan connection pool.
Tentukan parameter kunci connection pool menggunakan variabel lingkungan:
Variabel lingkungan
Deskripsi
DASHSCOPE_CONNECTION_POOL_SIZE
Ukuran connection pool.
Nilai yang direkomendasikan: lebih dari 2× konkurensi puncak Anda.
Default: 32.
DASHSCOPE_MAXIMUM_ASYNC_REQUESTS
Jumlah maksimum permintaan asinkron.
Nilai yang direkomendasikan: sesuaikan dengan
DASHSCOPE_CONNECTION_POOL_SIZE.Default: 32.
DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOST
Jumlah maksimum permintaan asinkron per host.
Nilai yang direkomendasikan: sesuaikan dengan
DASHSCOPE_CONNECTION_POOL_SIZE.Default: 32.
Konfigurasikan object pool.
Tentukan ukuran object pool menggunakan variabel lingkungan:
Variabel lingkungan
Deskripsi
COSYVOICE_OBJECTPOOL_SIZE
Ukuran object pool.
Nilai yang direkomendasikan: 1,5× hingga 2× konkurensi puncak Anda.
Default: 500.
PentingUkuran object pool (
COSYVOICE_OBJECTPOOL_SIZE) harus kurang dari atau sama dengan ukuran connection pool (DASHSCOPE_CONNECTION_POOL_SIZE). Jika tidak, saat connection pool penuh ketika object pool meminta objek, thread pemanggil akan diblokir hingga koneksi tersedia.Ukuran object pool tidak boleh melebihi batas QPS (permintaan per detik) akun Anda.
Gunakan kode berikut untuk membuat object pool:
class CosyvoiceObjectPool { // 。。。Kode lain dihilangkan. Lihat contoh lengkap di bawah. public static GenericObjectPool<SpeechSynthesizer> getInstance() { lock.lock(); if (synthesizerPool == null) { // Anda dapat mengatur ukuran object pool di sini. Atau atur melalui variabel lingkungan COSYVOICE_OBJECTPOOL_SIZE. // Nilai yang direkomendasikan: 1,5× hingga 2× koneksi bersamaan maksimum server Anda. 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; } }Ambil objek
SpeechSynthesizerdari pool.Jika jumlah objek yang sedang dipinjam melebihi ukuran maksimum pool, sistem akan membuat objek
SpeechSynthesizerbaru.Objek yang baru dibuat ini harus melakukan inisialisasi dan menetapkan koneksi WebSocket. Objek tersebut tidak dapat menggunakan kembali koneksi yang ada di pool, sehingga tidak mendapat manfaat dari pooling.
synthesizer = CosyvoiceObjectPool.getInstance().borrowObject();Lakukan sintesis suara.
Anda dapat memanggil metode call atau streamingCall dari objek
SpeechSynthesizeruntuk melakukan sintesis suara.Kembalikan objek
SpeechSynthesizer.Setelah tugas sintesis suara selesai, kembalikan objek
SpeechSynthesizeragar dapat digunakan kembali oleh tugas berikutnya.Jangan mengembalikan objek dari tugas yang belum selesai atau gagal.
CosyvoiceObjectPool.getInstance().returnObject(synthesizer);
Kode lengkap
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.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;
import lombok.extern.slf4j.Slf4j;
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.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* Proyek Anda harus menyertakan paket org.apache.commons.pool2 dan DashScope.
*
* SDK DashScope versi 2.16.6 dan lebih baru dioptimalkan untuk skenario konkurensi tinggi.
* SDK DashScope versi sebelum 2.16.6 tidak direkomendasikan untuk penggunaan konkurensi tinggi.
*
*
* Sebelum melakukan panggilan konkurensi tinggi ke layanan TTS,
* konfigurasikan parameter connection pool menggunakan variabel lingkungan berikut:
*
* DASHSCOPE_MAXIMUM_ASYNC_REQUESTS
* DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOST
* DASHSCOPE_CONNECTION_POOL_SIZE
*
*/
class SpeechSynthesizerObjectFactory
extends BasePooledObjectFactory<SpeechSynthesizer> {
public SpeechSynthesizerObjectFactory() {
super();
}
@Override
public SpeechSynthesizer create() throws Exception {
return new SpeechSynthesizer();
}
@Override
public PooledObject<SpeechSynthesizer> wrap(SpeechSynthesizer obj) {
return new DefaultPooledObject<>(obj);
}
}
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("Menggunakan Ukuran Object Pool dari Env: "+ n);
return n;
} catch (NumberFormatException e) {
System.out.println("Menggunakan Ukuran Object Pool Default: "+ DEFAULT_OBJECT_POOL_SIZE);
return DEFAULT_OBJECT_POOL_SIZE;
}
}
public static GenericObjectPool<SpeechSynthesizer> getInstance() {
lock.lock();
if (synthesizerPool == null) {
// Anda dapat mengatur ukuran object pool di sini. Atau atur melalui variabel lingkungan COSYVOICE_OBJECTPOOL_SIZE.
// Nilai yang direkomendasikan: 1,5× hingga 2× koneksi bersamaan maksimum server Anda.
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;
}
}
class SynthesizeTaskWithCallback implements Runnable {
String[] textArray;
String requestId;
long timeCost;
public SynthesizeTaskWithCallback(String[] textArray) {
this.textArray = textArray;
}
@Override
public void run() {
SpeechSynthesizer synthesizer = null;
long startTime = System.currentTimeMillis();
// jika menerima onError
final boolean[] hasError = {false};
try {
class ReactCallback extends ResultCallback<SpeechSynthesisResult> {
ReactCallback() {}
@Override
public void onEvent(SpeechSynthesisResult message) {
if (message.getAudioFrame() != null) {
try {
byte[] bytesArray = message.getAudioFrame().array();
System.out.println("Audio diterima. Panjang aliran audio: " + bytesArray.length);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onComplete() {}
@Override
public void onError(Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
hasError[0] = true;
}
}
// Ganti your-dashscope-api-key dengan Kunci API Anda sendiri
String dashScopeApiKey = "your-dashscope-api-key";
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
.model("cosyvoice-v3-flash")
.voice("longanyang")
// Kunci API berbeda antara wilayah Singapura dan Beijing. Dapatkan Kunci API Anda: https://www.alibabacloud.com/help/zh/model-studio/get-api-key
// Jika Anda tidak mengatur variabel lingkungan, ganti baris di bawah dengan: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.format(SpeechSynthesisAudioFormat
.MP3_22050HZ_MONO_256KBPS) // Gunakan PCM atau MP3 untuk sintesis streaming
.apiKey(dashScopeApiKey)
.build();
try {
synthesizer = CosyvoiceObjectPool.getInstance().borrowObject();
synthesizer.updateParamAndCallback(param, new ReactCallback());
for (String text : textArray) {
synthesizer.streamingCall(text);
}
Thread.sleep(20);
synthesizer.streamingComplete(60000);
requestId = synthesizer.getLastRequestId();
} catch (Exception e) {
System.out.println("Exception e: " + e.toString());
hasError[0] = true;
}
} catch (Exception e) {
hasError[0] = true;
throw new RuntimeException(e);
}
if (synthesizer != null) {
try {
if (hasError[0] == true) {
// Jika terjadi error, tutup koneksi dan batalkan objek di pool.
synthesizer.getDuplexApi().close(1000, "bye");
CosyvoiceObjectPool.getInstance().invalidateObject(synthesizer);
} else {
// Jika tugas selesai secara normal, kembalikan objek.
CosyvoiceObjectPool.getInstance().returnObject(synthesizer);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
long endTime = System.currentTimeMillis();
timeCost = endTime - startTime;
System.out.println("[Thread " + Thread.currentThread() + "] Tugas sintesis suara selesai. Waktu yang dibutuhkan: " + timeCost + " ms, RequestId: " + requestId);
}
}
}
@Slf4j
public class SynthesizeTextToSpeechWithCallbackConcurrently {
public static void checkoutEnv(String envName, int defaultSize) {
if (System.getenv(envName) != null) {
System.out.println("[ENV CHECK]: " + envName + " "
+ System.getenv(envName));
} else {
System.out.println("[ENV CHECK]: " + envName
+ " Menggunakan Default yaitu " + defaultSize);
}
}
public static void main(String[] args)
throws InterruptedException, NoApiKeyException {
// URL berikut untuk wilayah Singapura. Jika Anda menggunakan model di wilayah Beijing, ganti dengan: wss://dashscope.aliyuncs.com/api-ws/v1/inference
Constants.baseWebsocketApiUrl = "wss://dashscope-intl.aliyuncs.com/api-ws/v1/inference";
// Periksa env connection pool
checkoutEnv("DASHSCOPE_CONNECTION_POOL_SIZE", 32);
checkoutEnv("DASHSCOPE_MAXIMUM_ASYNC_REQUESTS", 32);
checkoutEnv("DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOST", 32);
checkoutEnv(CosyvoiceObjectPool.COSYVOICE_OBJECTPOOL_SIZE_ENV, CosyvoiceObjectPool.DEFAULT_OBJECT_POOL_SIZE);
int runTimes = 3;
// Buat pool objek SpeechSynthesis
ExecutorService executorService = Executors.newFixedThreadPool(runTimes);
for (int i = 0; i < runTimes; i++) {
// Catat waktu pengiriman tugas
LocalDateTime submissionTime = LocalDateTime.now();
executorService.submit(new SynthesizeTaskWithCallback(new String[] {
"Before my bed, moonlight gleams,", "It seems like frost upon the ground.", "I lift my gaze to watch the bright moon,", "Then bow my head, thinking of home."}));
}
// Matikan ExecutorService dan tunggu semua tugas selesai
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
System.exit(0);
}
}Konfigurasi yang direkomendasikan
Konfigurasi berikut didasarkan pada pengujian di mana hanya layanan sintesis suara CosyVoice yang berjalan pada Instance ECS Alibaba Cloud dengan spesifikasi yang ditentukan. Terlalu banyak permintaan bersamaan dapat menyebabkan penundaan tugas.
Konkurensi single-machine mengacu pada jumlah tugas sintesis suara CosyVoice yang berjalan secara bersamaan pada satu mesin. Ini setara dengan jumlah thread pekerja.
Konfigurasi mesin (Alibaba Cloud) | Konkurensi Maksimum per Mesin Tunggal | Ukuran object pool | Ukuran connection pool |
4 vCPU, memori 8 GiB | 100 | 500 | 2000 |
8 vCPU, memori 16 GiB | 150 | 500 | 2000 |
16 vCPU, memori 32 GiB | 200 | 500 | 2000 |
Manajemen resource dan penanganan error
Tugas berhasil: Saat tugas sintesis suara selesai secara normal, Anda harus memanggil metode
returnObjectdariGenericObjectPoolpada objekSpeechSynthesizeruntuk mengembalikannya ke pool agar dapat digunakan kembali.Dalam kode saat ini, ini sesuai dengan
CosyvoiceObjectPool.getInstance().returnObject(synthesizer).PentingJangan mengembalikan objek
SpeechSynthesizerdari tugas yang belum selesai atau gagal.Tugas gagal: Saat terjadi exception dari SDK atau logika bisnis Anda yang mengganggu tugas, lakukan kedua tindakan berikut:
Tutup koneksi WebSocket yang mendasari secara manual.
Batalkan objek di object pool untuk mencegah penggunaan kembali.
// Dalam kode saat ini, ini sesuai dengan: // Tutup koneksi synthesizer.getDuplexApi().close(1000, "bye"); // Batalkan synthesizer yang rusak di pool CosyvoiceObjectPool.getInstance().invalidateObject(synthesizer);Tidak diperlukan tindakan tambahan saat layanan mengembalikan error TaskFailed.
Pemanasan awal dan metrik pengukuran waktu
Saat mengevaluasi metrik kinerja seperti latensi untuk panggilan bersamaan ke SDK Java DashScope, lakukan pemanasan awal sistem sebelum pengujian formal. Pemanasan awal memastikan hasil pengukuran secara akurat mencerminkan kinerja layanan yang sebenarnya dalam kondisi stabil dan menghindari kesenjangan data yang disebabkan oleh waktu penyiapan koneksi awal.
Mekanisme penggunaan ulang koneksi
SDK Java DashScope menggunakan connection pool singleton global untuk mengelola dan menggunakan kembali koneksi WebSocket secara efisien. Ini mengurangi overhead pembuatan dan pemutusan koneksi yang sering, meningkatkan throughput dalam skenario konkurensi tinggi.
Perilaku utama mekanisme ini:
Pembuatan sesuai permintaan: SDK tidak membuat koneksi WebSocket sebelumnya saat startup. Koneksi dibuat hanya saat panggilan pertama terjadi.
Penggunaan ulang berbatas waktu: Setelah permintaan selesai, koneksi tetap berada di pool hingga 60 detik untuk penggunaan ulang.
Jika permintaan baru tiba dalam waktu 60 detik, koneksi yang ada digunakan kembali. Ini menghindari overhead handshake.
Jika koneksi menganggur lebih dari 60 detik, koneksi tersebut ditutup secara otomatis untuk membebaskan sumber daya.
Mengapa pemanasan awal penting
Dalam kasus berikut, connection pool mungkin tidak berisi koneksi aktif yang dapat digunakan kembali. Permintaan kemudian perlu membuat koneksi baru:
Aplikasi Anda baru saja dimulai dan belum melakukan panggilan apa pun.
Layanan Anda menganggur lebih dari 60 detik, sehingga semua koneksi di pool telah habis waktu dan ditutup.
Dalam kasus ini, permintaan pertama atau awal memicu proses penyiapan koneksi WebSocket lengkap—termasuk handshake TCP, negosiasi TLS, dan upgrade protokol. Latensi end-to-end mereka jauh lebih tinggi daripada permintaan berikutnya yang menggunakan kembali koneksi. Waktu ekstra ini berasal dari inisialisasi koneksi jaringan—bukan dari pemrosesan layanan. Tanpa pemanasan awal, hasil pengujian kinerja akan condong karena mencakup waktu koneksi awal.
Praktik terbaik
Untuk mengumpulkan data kinerja yang andal, ikuti langkah pemanasan awal berikut sebelum pengujian stres formal atau pengukuran latensi:
Simulasikan tingkat konkurensi pengujian formal Anda. Kirim sejumlah panggilan pemanasan yang cukup—misalnya, selama 1–2 menit—untuk mengisi penuh connection pool.
Pastikan connection pool telah menetapkan dan mempertahankan cukup banyak koneksi aktif sebelum memulai pengumpulan data kinerja formal.
Pemanasan awal yang tepat membawa connection pool SDK ke kondisi penggunaan ulang yang stabil. Ini menghasilkan metrik latensi yang lebih representatif—dan mencerminkan kinerja online dunia nyata.