Layanan sintesis suara CosyVoice menggunakan protokol WebSocket untuk komunikasi streaming real-time. Dalam skenario konkurensi tinggi, pembuatan dan penghapusan koneksi WebSocket untuk setiap permintaan menimbulkan overhead signifikan pada jaringan dan sumber daya sistem serta memperkenalkan latensi koneksi. Untuk mengoptimalkan performa dan memastikan stabilitas, kit pengembangan perangkat lunak (SDK) DashScope menyediakan mekanisme reuse resource, seperti connection pool dan object pool. Dokumen ini menjelaskan cara menggunakan fitur-fitur tersebut dalam SDK Python dan Java DashScope untuk memanggil layanan CosyVoice secara efisien dalam skenario konkurensi tinggi.
Untuk menggunakan model di wilayah China (Beijing), buka halaman Kunci API untuk wilayah China (Beijing)
Prasyarat
Anda telah menginstal versi SDK DashScope yang kompatibel. Kami merekomendasikan Anda menginstal versi terbaru:
Python SDK: Versi 1.25.2 atau lebih baru
Java SDK: Versi 2.16.6 atau lebih baru
Python SDK: Optimisasi object pool
Python SDK menggunakan kelas SpeechSynthesizerObjectPool untuk mengelola dan menggunakan kembali objek SpeechSynthesizer melalui optimisasi object pool.
Saat object pool diinisialisasi, SDK membuat sejumlah instance SpeechSynthesizer tertentu dan menetapkan koneksi WebSocket sebelumnya. Saat Anda mengambil objek dari pool, Anda dapat langsung mengirim permintaan tanpa menunggu koneksi dibuat. Praktik ini secara efektif mengurangi latensi paket pertama. Setelah tugas selesai dan objek dikembalikan ke pool, koneksi WebSocket-nya tetap aktif dan siap digunakan untuk tugas berikutnya.
Langkah implementasi
Instal dependensi: Instal dependensi DashScope dengan menjalankan
pip install -U dashscope.Buat dan konfigurasikan object pool.
Tentukan ukuran object pool menggunakan
SpeechSynthesizerObjectPool. Kami merekomendasikan nilai ini disetel antara 1,5 hingga 2 kali puncak konkurensi Anda. Ukuran object pool tidak boleh melebihi batas permintaan per detik (QPS) akun Anda.Gunakan kode berikut untuk membuat object pool singleton global dengan ukuran tetap. Saat object pool diinisialisasi, SDK langsung membuat jumlah objek
SpeechSynthesizeryang ditentukan dan menetapkan koneksi WebSocket. Proses ini memerlukan waktu tertentu.from dashscope.audio.tts_v2 import SpeechSynthesizerObjectPool synthesizer_object_pool = SpeechSynthesizerObjectPool(max_size=20)Ambil objek
SpeechSynthesizerdari object pool.Jika jumlah objek yang sedang digunakan melebihi kapasitas maksimum pool, sistem akan membuat objek
SpeechSynthesizerbaru.Objek baru ini harus diinisialisasi ulang dan menetapkan koneksi WebSocket baru. Objek tersebut tidak dapat menggunakan sumber daya koneksi yang ada di pool sehingga tidak mendapat manfaat dari reuse resource.
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 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.
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 = [
'Kalimat 1: Selamat datang di layanan sintesis suara Alibaba Cloud.',
'Kalimat 2: Selamat datang di layanan sintesis suara Alibaba Cloud.',
'Kalimat 3: Selamat datang di layanan sintesis suara Alibaba Cloud.',
]
connectionPool = None
if USE_CONNECTION_POOL:
print('membuat connection pool')
start_time = time.time() * 1000
connectionPool = SpeechSynthesizerObjectPool(max_size=3)
end_time = time.time() * 1000
print('connection pool dibuat, biaya: {} ms'.format(end_time - start_time))
def init_dashscope_api_key():
'''
Tetapkan Kunci API DashScope Anda. Untuk informasi selengkapnya, lihat
https://github.com/aliyun/alibabacloud-bailian-speech-demo/blob/master/PREREQUISITES.md
'''
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>' # tetapkan 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 selengkapnya, 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}] mulai')
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_close dipanggil setelah tugas selesai.
print(f'[task_{task_id}] selesai')
def on_event(self, message):
# print(f'menerima pesan sintesis suara {message}')
pass
def on_data(self, data: bytes) -> None:
# kirim ke pemutar
# simpan audio ke file
self.file.write(data)
# Panggil callback synthesizer
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:
# Jika tugas gagal saat menggunakan connection pool, tutup koneksi synthesizer secara manual.
speech_synthesizer.close()
return
print('[task_{}] Teks yang disintesis: {}'.format(task_id, text))
complete_event.wait()
print('[task_{}][Metric] requestId: {}, penundaan paket pertama dalam 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__':
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 Pengecualian
Tugas berhasil: Saat tugas sintesis suara berhasil diselesaikan, Anda harus memanggil
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: Jika tugas terganggu oleh pengecualian internal SDK atau pengecualian logika bisnis, Anda harus menutup koneksi WebSocket yang mendasari secara manual dengan memanggil
speech_synthesizer.close().Setelah semua tugas sintesis suara selesai, tutup object pool dengan memanggil
connectionPool.shutdown().Jika terjadi error `TaskFailed`, tidak diperlukan penanganan tambahan.
Java SDK: Optimisasi connection pool dan object pool
Java SDK menggunakan connection pool bawaan dan object pool kustom yang bekerja sama untuk memberikan performa optimal.
Connection pool: SDK mengintegrasikan connection pool OkHttp3 untuk mengelola dan menggunakan kembali koneksi WebSocket yang mendasari. Ini mengurangi overhead handshake jaringan. Fitur ini diaktifkan secara default.
Object pool: Diimplementasikan berdasarkan
commons-pool2, pool ini mempertahankan serangkaian objekSpeechSynthesizerdengan koneksi yang telah ditetapkan sebelumnya. Mengambil objek dari pool menghilangkan latensi setup koneksi dan secara signifikan mengurangi latensi paket pertama.
Langkah implementasi
Tambahkan dependensi.
Berdasarkan alat build proyek Anda, tambahkan `dashscope-sdk-java` dan `commons-pool2` ke file konfigurasi dependensi Anda.
Bagian berikut menunjukkan konfigurasi untuk Maven dan Gradle.
Maven
Buka file
pom.xmlproyek Maven Anda.Tambahkan informasi 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. Anda dapat menemukan nomor versi yang relevan 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. Anda dapat menemukan nomor versi yang relevan di: https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 --> <version>the-latest-version</version> </dependency>Simpan file
pom.xml.Gunakan perintah Maven, seperti
mvn clean installataumvn compile, untuk memperbarui dependensi proyek.
Gradle
Buka file
build.gradleproyek Gradle Anda.Tambahkan informasi dependensi berikut di dalam blok
dependencies.dependencies { // Ganti 'the-latest-version' dengan versi 2.16.6 atau lebih baru. Anda dapat menemukan nomor versi yang relevan 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. Anda dapat menemukan nomor versi yang relevan 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 command line, navigasi ke direktori root proyek Anda dan jalankan perintah Gradle berikut untuk memperbarui dependensi proyek.
./gradlew build --refresh-dependenciesJika Anda menggunakan Windows, jalankan perintah berikut:
gradlew build --refresh-dependencies
Konfigurasikan connection pool.
Anda dapat mengonfigurasi parameter kunci connection pool menggunakan variabel lingkungan:
Variabel lingkungan
Deskripsi
DASHSCOPE_CONNECTION_POOL_SIZE
Ukuran connection pool.
Nilai yang direkomendasikan: Minimal dua kali puncak konkurensi Anda.
Nilai default: 32.
DASHSCOPE_MAXIMUM_ASYNC_REQUESTS
Jumlah maksimum permintaan asinkron.
Nilai yang direkomendasikan: Sama dengan
DASHSCOPE_CONNECTION_POOL_SIZE.Nilai default: 32.
DASHSCOPE_MAXIMUM_ASYNC_REQUESTS_PER_HOST
Jumlah maksimum permintaan asinkron per host.
Nilai yang direkomendasikan: Sama dengan
DASHSCOPE_CONNECTION_POOL_SIZE.Nilai default: 32.
Konfigurasikan object pool.
Anda dapat mengonfigurasi ukuran object pool menggunakan variabel lingkungan:
Variabel lingkungan
Deskripsi
COSYVOICE_OBJECTPOOL_SIZE
Ukuran object pool.
Nilai yang direkomendasikan: 1,5 hingga 2 kali puncak konkurensi Anda.
Nilai default: 500.
PentingUkuran object pool (
COSYVOICE_OBJECTPOOL_SIZE) harus kurang dari atau sama dengan ukuran connection pool (DASHSCOPE_CONNECTION_POOL_SIZE). Jika tidak, ketika connection pool penuh saat object pool meminta objek, thread pemanggil akan diblokir hingga koneksi tersedia.Ukuran object pool tidak boleh melebihi batas QPS akun Anda.
Gunakan kode berikut untuk membuat object pool:
class CosyvoiceObjectPool { // ... Kode lain dihilangkan di sini. Untuk contoh lengkap, lihat kode lengkap. public static GenericObjectPool<SpeechSynthesizer> getInstance() { lock.lock(); if (synthesizerPool == null) { // Anda dapat menyetel ukuran object pool di sini atau di variabel lingkungan COSYVOICE_OBJECTPOOL_SIZE. // Setel ke 1,5 hingga 2 kali koneksi bersamaan maksimum server. 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 object pool.Jika jumlah objek yang sedang digunakan melebihi kapasitas maksimum pool, sistem akan membuat objek
SpeechSynthesizerbaru.Objek baru ini harus diinisialisasi ulang dan menetapkan koneksi WebSocket baru. Objek tersebut tidak dapat menggunakan sumber daya koneksi yang ada di pool sehingga tidak mendapat manfaat dari reuse resource.
synthesizer = CosyvoiceObjectPool.getInstance().borrowObject();Lakukan sintesis suara.
Panggil 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
package org.alibaba.bailian.example.examples;
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 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;
/**
* Anda perlu mengimpor paket org.apache.commons.pool2 dan DashScope ke dalam proyek Anda.
*
* SDK DashScope versi 2.16.6 dan lebih baru dioptimalkan untuk skenario konkurensi tinggi.
* Versi SDK DashScope sebelum 2.16.6 tidak direkomendasikan untuk digunakan dalam skenario konkurensi tinggi.
*
*
* Sebelum melakukan panggilan konkurensi tinggi ke layanan teks-ke-ucapan (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 menyetel ukuran object pool di sini atau di variabel lingkungan COSYVOICE_OBJECTPOOL_SIZE.
// Setel ke 1,5 hingga 2 kali koneksi bersamaan maksimum server.
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 onError diterima
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 stream file 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.
String dashScopeApiKey = "your-dashscope-api-key";
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
.model("cosyvoice-v3-flash")
.voice("longanyang")
.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 pengecualian, tutup koneksi dan batalkan objek di pool.
synthesizer.getDuplexApi().close(1000, "bye");
CosyvoiceObjectPool.getInstance().invalidateObject(synthesizer);
} else {
// Jika tugas selesai 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. Biaya waktu: " + 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 {
// Periksa variabel lingkungan 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 SpeechSynthesizer
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[] {
"Cahaya bulan di depan tempat tidurku,", "Apakah itu embun beku di tanah?", "Aku angkat kepalaku melihat bulan,", "Aku turunkan kepalaku dan rindu rumah."}));
}
// Matikan ExecutorService dan tunggu semua tugas selesai
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
System.exit(0);
}
}Konfigurasi yang direkomendasikan
Konfigurasi berikut didasarkan pada hasil pengujian saat menjalankan layanan sintesis suara CosyVoice di server Alibaba Cloud dengan konfigurasi yang ditentukan. Konkurensi tinggi dapat menyebabkan penundaan pemrosesan tugas.
Konkurensi per server adalah jumlah tugas sintesis suara CosyVoice yang dapat berjalan secara bersamaan. Nilai ini setara dengan jumlah thread pekerja.
Konfigurasi server (Alibaba Cloud) | Konkurensi maksimum per server | Ukuran object pool | Ukuran connection pool |
4 core 8 GiB | 100 | 500 | 2000 |
8 core 16 GiB | 150 | 500 | 2000 |
16 core 32 GiB | 200 | 500 | 2000 |
Manajemen Resource dan Penanganan Anomali
Tugas berhasil: Saat tugas sintesis suara berhasil diselesaikan, Anda harus memanggil metode `returnObject` dari `GenericObjectPool` untuk mengembalikan objek
SpeechSynthesizerke pool agar dapat digunakan kembali.Dalam contoh kode yang diberikan, hal ini dilakukan dengan memanggil
CosyvoiceObjectPool.getInstance().returnObject(synthesizer).PentingJangan mengembalikan objek
SpeechSynthesizerdari tugas yang belum selesai atau gagal.Tugas gagal: Jika tugas terganggu oleh pengecualian internal SDK atau pengecualian logika bisnis, Anda harus melakukan dua operasi berikut:
Tutup koneksi WebSocket yang mendasari.
Batalkan objek di object pool untuk mencegah penggunaannya kembali.
// Ini sesuai dengan konten berikut dalam kode saat ini // Tutup koneksi synthesizer.getDuplexApi().close(1000, "bye"); // Batalkan synthesizer bermasalah di object pool CosyvoiceObjectPool.getInstance().invalidateObject(synthesizer);Jika terjadi error `TaskFailed`, tidak diperlukan penanganan tambahan.
Pemanasan panggilan dan statistik latensi
Saat menilai metrik performa untuk SDK Java DashScope, seperti latensi panggilan konkuren, lakukan pemanasan penuh sebelum memulai pengujian formal. Pemanasan memastikan bahwa hasil pengujian secara akurat mencerminkan performa layanan dalam kondisi stabil. Hal ini mencegah bias data yang disebabkan oleh waktu koneksi awal.
Mekanisme reuse koneksi
SDK Java DashScope menggunakan connection pool singleton global untuk mengelola dan menggunakan kembali koneksi WebSocket secara efisien. Ini mengurangi overhead pembuatan dan penutupan koneksi yang sering serta meningkatkan performa dalam skenario konkurensi tinggi.
Mekanisme ini bekerja sebagai berikut:
Pembuatan sesuai permintaan: SDK tidak membuat koneksi WebSocket saat layanan dimulai. Sebaliknya, koneksi ditetapkan pada panggilan pertama.
Reuse berbatas waktu: Setelah permintaan selesai, koneksi tetap berada di pool hingga 60 detik untuk reuse.
Jika permintaan baru tiba dalam waktu 60 detik, koneksi yang ada digunakan kembali. Ini menghindari overhead handshake lainnya.
Jika koneksi menganggur lebih dari 60 detik, koneksi tersebut secara otomatis ditutup untuk melepaskan sumber daya.
Pentingnya pemanasan
Dalam skenario berikut, connection pool mungkin tidak memiliki koneksi aktif untuk digunakan kembali. Hal ini memerlukan pembuatan koneksi baru:
Aplikasi baru saja dimulai dan belum melakukan panggilan apa pun.
Layanan menganggur lebih dari 60 detik, menyebabkan koneksi di pool ditutup karena timeout.
Dalam kasus ini, beberapa permintaan pertama memicu proses koneksi WebSocket lengkap. Proses ini mencakup handshake TCP, negosiasi enkripsi Transport Layer Security (TLS), dan upgrade protokol. Latensi end-to-end untuk permintaan ini jauh lebih tinggi daripada permintaan berikutnya yang menggunakan kembali koneksi. Waktu tambahan ini disebabkan oleh inisialisasi koneksi jaringan, bukan latensi pemrosesan layanan. Oleh karena itu, tanpa pemanasan, hasil pengujian performa Anda akan bias karena mencakup overhead koneksi awal ini.
Praktik yang direkomendasikan
Untuk mendapatkan data performa yang andal, ikuti langkah pemanasan berikut sebelum memulai uji stres atau mengukur latensi:
Simulasikan tingkat konkurensi pengujian formal Anda. Lakukan sejumlah panggilan terlebih dahulu, misalnya selama 1 hingga 2 menit, untuk mengisi connection pool.
Pastikan connection pool memiliki cukup koneksi aktif. Kemudian, mulai mengumpulkan data performa formal.
Pemanasan yang tepat membawa connection pool SDK ke kondisi stabil reuse. Hal ini memungkinkan Anda mengukur metrik latensi yang lebih representatif yang mencerminkan performa sebenarnya dari layanan selama runtime stabil.