Gunakan Simple Message Queue (formerly MNS) untuk menerima notifikasi gangguan spot instans dan memicu logika shutdown yang mulus sebelum reclaim.
Ikhtisar alur kerja
Prasyarat
Buat AccessKey
Gunakan AccessKey dari Pengguna RAM dengan izin minimum yang diperlukan, bukan akun root. Lihat Buat AccessKey.
Berikan izin kepada Pengguna RAM
Berikan izin berikut kepada Pengguna RAM untuk mengonsumsi pesan dari Simple Message Queue (MNS):
Produk Cloud
Berikan izin
Simple Message Queue (SMQ, formerly MNS)
AliyunMNSFullAccess
Konfigurasikan kredensial akses dan titik akhir
Kode contoh membaca kredensial akses dan titik akhir dari variabel lingkungan.
Kredensial akses: lihat Konfigurasikan variabel lingkungan di Linux, macOS, dan Windows.
Titik akhir: lihat Konfigurasikan titik akhir.
Instal SDK SMQ
Instal SDK SMQ Java dengan menambahkan dependensi Maven. Untuk metode lainnya, lihat Instal SDK SMQ.
Prosedur
Buat Antrian Pesan Sederhana
Buat antrian untuk menerima notifikasi gangguan spot instans.
Login ke Konsol Simple Message Queue (formerly MNS). Di panel navigasi kiri, pilih .
Di bilah menu atas, pilih wilayah. Di halaman Queues, klik Create Queue.
Di panel Create Queue, konfigurasikan parameter dan klik OK.

Buat kebijakan langganan
Cloud Monitor mengirim notifikasi gangguan spot instans melalui saluran push yang ditentukan dalam kebijakan langganan.
Login ke Konsol Cloud Monitor. Di panel navigasi kiri, pilih .
Di tab Subscription Policies, klik Create Subscription Policy. Klik Next dan konfigurasikan parameter.
Hanya parameter utama yang dijelaskan di sini. Untuk parameter lainnya, lihat Kelola langganan event (disarankan).
Subscription Type: Pilih System Events.

Subscription Scope: Konfigurasikan seperti yang ditunjukkan di bawah ini.

Push and Integration: Klik + Add Channel > Add Channel, pilih antrian yang dibuat pada Langkah 1, lalu lengkapi bidang lainnya. Lihat Kelola Saluran Push.
Simulasikan event gangguan
Gunakan Test Event Subscription untuk mensimulasikan event gangguan, karena gangguan spot instans dipicu secara pasif dan sulit direproduksi selama pengembangan.
Di tab Subscription Policies, klik Test Event Subscription.
Di panel Create Event Debugging, atur Service menjadi ECS dan Name menjadi Spot Instance Interruption Notification.
Sistem akan menghasilkan konten debugging JSON. Ganti bidang yang terkait resource dengan informasi spot instans Anda.
Ganti
Alibaba Cloud account UIDdengan UID akun Anda.Ganti
<resource-id>dan<instanceId>dengan ID spot instans Anda.Ganti
<region-id>dengan ID wilayah spot instans Anda.{ "product": "ECS", "resourceId": "acs:ecs:cn-shanghai:Alibaba Cloud account UID:instance/<resource-id>", "level": "WARN", "instanceName": "instanceName", "regionId": "<region-id>", "groupId": "0", "name": "Instance:PreemptibleInstanceInterruption", "content": { "instanceId": "<instanceId>", "instanceName": "wor***b73", "action": "delete" }, "status": "Normal" }
Klik OK. Sistem menampilkan The operation is successful., dan Cloud Monitor mengirim notifikasi uji coba ke antrian Anda.
Ambil dan tanggapi pesan
Ambil notifikasi gangguan dari antrian dan tanggapi dengan logika kustom. Kode contoh berikut menunjukkan penanganan event gangguan menggunakan program konversi gambar ke skala abu-abu:
Gunakan tugas thread untuk mensimulasikan program konversi gambar.
import com.aliyun.mns.client.CloudAccount; import com.aliyun.mns.client.CloudQueue; import com.aliyun.mns.client.MNSClient; import com.aliyun.mns.common.utils.ServiceSettings; import com.aliyun.mns.model.Message; import org.json.JSONObject; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.util.Base64; import java.util.concurrent.atomic.AtomicBoolean; /** * Prosesor gambar yang dapat diinterupsi dan mendukung konversi ke skala abu-abu. * Menggunakan variabel atomik dan flag interupsi thread untuk deteksi. * Fitur: * 1. Pemrosesan per bagian (chunked) dengan penyimpanan progres otomatis * 2. Respons langsung terhadap gangguan spot instans * 3. Pembuatan file hasil parsial setelah interupsi */ public class InterruptibleImageProcessor implements Runnable { /** * Kontrol status aman-thread menggunakan Boolean atomik */ private final AtomicBoolean running = new AtomicBoolean(true); /** * Menyimpan data gambar yang sedang diproses */ private BufferedImage processedImage; /** * Persentase progres pemrosesan (0–100) */ private int progress; /** * Titik masuk thread * **Logika interupsi**: * 1. Simpan progres saat ini setelah menangkap InterruptedException * 2. Pulihkan status interupsi thread (pertahankan semantik) */ @Override public void run() { try { convertToGrayScale(new File("input.jpg"), new File("output.jpg")); // Simulasikan penundaan post-processing Thread.sleep(5000); System.out.println("Pemrosesan gambar selesai"); } catch (InterruptedException e) { System.out.println("Pemrosesan diinterupsi. Progres tersimpan pada " + progress + "%"); saveProgress(new File("partial_output.jpg")); Thread.currentThread().interrupt(); // Pulihkan status interupsi } catch (Exception e) { System.err.println("Kesalahan pemrosesan: " + e.getMessage()); } } /** * Metode pemicu interupsi eksternal * **Mekanisme koordinasi**: * Bekerja bersama flag interupsi thread untuk deteksi ganda */ public void stop() { running.set(false); } }Metode konversi gambar ke skala abu-abu.
/** * Konversi gambar input ke skala abu-abu dan simpan * @param inputFile Objek file gambar sumber * @param outputFile Objek file output * @throws Exception Termasuk exception I/O dan interupsi * * **Algoritma**: * Konversi skala abu-abu rata-rata tertimbang sesuai persepsi luminansi manusia: * Gray = 0.30*R + 0.59*G + 0.11*B * Referensi: standar ITU-R BT.601 */ public void convertToGrayScale(File inputFile, File outputFile) throws Exception { // Baca gambar sumber BufferedImage original = ImageIO.read(inputFile); int width = original.getWidth(); int height = original.getHeight(); // Buat buffer gambar skala abu-abu processedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); // Proses per bagian untuk mendukung penyimpanan progres for (int y = 0; y < height && running.get(); y++) { // Proses piksel per piksel for (int x = 0; x < width; x++) { // Pemeriksaan interupsi pertama: flag interupsi thread if (Thread.interrupted()) { throw new InterruptedException("Pemrosesan gambar diinterupsi"); } /* Algoritma inti skala abu-abu */ int rgb = original.getRGB(x, y); // Ekstrak saluran RGB (format ARGB) // Saluran merah int r = (rgb >> 16) & 0xFF; // Saluran hijau int g = (rgb >> 8) & 0xFF; // Saluran biru int b = rgb & 0xFF; // Hitung nilai skala abu-abu (rata-rata tertimbang) int gray = (int)(0.3 * r + 0.59 * g + 0.11 * b); // Rekonstruksi RGB (nilai skala abu-abu diduplikasi di semua saluran) processedImage.setRGB(x, y, (gray << 16) | (gray << 8) | gray); // Perbarui persentase progres (perhatikan pembagian integer) progress = (y * width + x) * 100 / (width * height); } // Simpan progres otomatis setiap 50 baris (mekanisme checkpoint) if (y % 50 == 0) { saveProgress(outputFile); } } // Simpan hasil akhir ImageIO.write(processedImage, "jpg", outputFile); }Simpan progres pemrosesan gambar.
/** * Simpan progres pemrosesan ke file yang ditentukan * @param outputFile Objek file output * * **Catatan**: * 1. Gunakan kegagalan diam untuk menghindari mengganggu proses penyimpanan * 2. Hasilkan file sementara bernama partial_output.jpg */ private void saveProgress(File outputFile) { try { // Gunakan nama file sementara untuk menghindari menimpa file akhir ImageIO.write(processedImage, "jpg", new File("partial_output.jpg")); } catch (Exception e) { System.err.println("Penyimpanan otomatis gagal: " + e.getMessage()); } }Uji penanganan respons.
Uji prosesor gambar untuk mendeteksi dan menanggapi gangguan spot instans saat berjalan.
/** * Metode utama (untuk pengujian) * **Skenario uji**: * 1. Mulai thread pemrosesan * 2. Ambil pesan notifikasi gangguan * 3. Tunggu hingga thread berhenti */ public static void main(String[] args) throws InterruptedException { // Inisialisasi klien MNS CloudAccount account = new CloudAccount( ServiceSettings.getMNSAccessKeyId(), ServiceSettings.getMNSAccessKeySecret(), ServiceSettings.getMNSAccountEndpoint()); MNSClient client = account.getMNSClient(); // Periksa apakah pesan cocok dengan event gangguan spot instans boolean isMatch = false; // Mulai prosesor gambar InterruptibleImageProcessor processor = new InterruptibleImageProcessor(); Thread processThread = new Thread(processor); processThread.start(); try{ // Ambil pesan dari antrian CloudQueue queue = client.getQueueRef("spot-interruption"); Message popMsg = queue.popMessage(); if (popMsg != null){ // Isi pesan secara default dienkripsi Base64 System.out.println("isi pesan: " + popMsg.getMessageBodyAsRawString()); // Dekode Base64 byte[] decodedBytes = Base64.getDecoder().decode(popMsg.getMessageBodyAsRawString()); String decodedString = new String(decodedBytes); System.out.println("konten pesan: " + decodedString); // Parsing string JSON JSONObject json = new JSONObject(decodedString); // Dapatkan nama event dari bidang "name" String name = json.getString("name"); isMatch = "Instance:PreemptibleInstanceInterruption".equals(name); // Tangani event gangguan spot instans if(isMatch){ System.out.println("Spot instans akan diinterupsi dan direclaim"); // Hentikan prosesor gambar processor.stop(); processThread.interrupt(); System.out.println("Program dihentikan"); processThread.join(); // Hapus pesan queue.deleteMessage(popMsg.getReceiptHandle()); } } }catch (Exception e){ System.out.println("Terjadi exception yang tidak dikenal!"); e.printStackTrace(); } client.close(); }
Kode contoh lengkap:
import com.aliyun.mns.client.CloudAccount; import com.aliyun.mns.client.CloudQueue; import com.aliyun.mns.client.MNSClient; import com.aliyun.mns.common.utils.ServiceSettings; import com.aliyun.mns.model.Message; import org.json.JSONObject; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.util.Base64; import java.util.concurrent.atomic.AtomicBoolean; /** * Prosesor gambar yang dapat diinterupsi dan mendukung konversi ke skala abu-abu. * Menggunakan variabel atomik dan flag interupsi thread untuk deteksi. * Fitur: * 1. Pemrosesan per bagian (chunked) dengan penyimpanan progres otomatis * 2. Respons langsung terhadap gangguan spot instans * 3. Pembuatan file hasil parsial setelah interupsi */ public class InterruptibleImageProcessor implements Runnable { /** * Kontrol status aman-thread menggunakan Boolean atomik */ private final AtomicBoolean running = new AtomicBoolean(true); /** * Menyimpan data gambar yang sedang diproses */ private BufferedImage processedImage; /** * Persentase progres pemrosesan (0–100) */ private int progress; /** * Konversi gambar input ke skala abu-abu dan simpan * @param inputFile Objek file gambar sumber * @param outputFile Objek file output * @throws Exception Termasuk exception I/O dan interupsi * * **Algoritma**: * Konversi skala abu-abu rata-rata tertimbang sesuai persepsi luminansi manusia: * Gray = 0.30*R + 0.59*G + 0.11*B * Referensi: standar ITU-R BT.601 */ public void convertToGrayScale(File inputFile, File outputFile) throws Exception { // Baca gambar sumber BufferedImage original = ImageIO.read(inputFile); int width = original.getWidth(); int height = original.getHeight(); // Buat buffer gambar skala abu-abu processedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); // Proses per bagian untuk mendukung penyimpanan progres for (int y = 0; y < height && running.get(); y++) { // Proses piksel per piksel for (int x = 0; x < width; x++) { // Pemeriksaan interupsi pertama: flag interupsi thread if (Thread.interrupted()) { throw new InterruptedException("Pemrosesan gambar diinterupsi"); } /* Algoritma inti skala abu-abu */ int rgb = original.getRGB(x, y); // Ekstrak saluran RGB (format ARGB) // Saluran merah int r = (rgb >> 16) & 0xFF; // Saluran hijau int g = (rgb >> 8) & 0xFF; // Saluran biru int b = rgb & 0xFF; // Hitung nilai skala abu-abu (rata-rata tertimbang) int gray = (int)(0.3 * r + 0.59 * g + 0.11 * b); // Rekonstruksi RGB (nilai skala abu-abu diduplikasi di semua saluran) processedImage.setRGB(x, y, (gray << 16) | (gray << 8) | gray); // Perbarui persentase progres (perhatikan pembagian integer) progress = (y * width + x) * 100 / (width * height); } // Simpan progres otomatis setiap 50 baris (mekanisme checkpoint) if (y % 50 == 0) { saveProgress(outputFile); } } // Simpan hasil akhir ImageIO.write(processedImage, "jpg", outputFile); } /** * Simpan progres pemrosesan ke file yang ditentukan * @param outputFile Objek file output * * **Catatan**: * 1. Gunakan kegagalan diam untuk menghindari mengganggu proses penyimpanan * 2. Hasilkan file sementara bernama partial_output.jpg */ private void saveProgress(File outputFile) { try { // Gunakan nama file sementara untuk menghindari menimpa file akhir ImageIO.write(processedImage, "jpg", new File("partial_output.jpg")); } catch (Exception e) { System.err.println("Penyimpanan otomatis gagal: " + e.getMessage()); } } /** * Titik masuk thread * **Logika interupsi**: * 1. Simpan progres saat ini setelah menangkap InterruptedException * 2. Pulihkan status interupsi thread (pertahankan semantik) */ @Override public void run() { try { convertToGrayScale(new File("/Users/shaoberlin/Desktop/idea_workspace/aliyun/src/main/resources/input.jpg"), new File("output.jpg")); // Simulasikan penundaan post-processing Thread.sleep(5000); System.out.println("Pemrosesan gambar selesai"); } catch (InterruptedException e) { System.out.println("Pemrosesan diinterupsi. Progres tersimpan pada " + progress + "%"); saveProgress(new File("partial_output.jpg")); Thread.currentThread().interrupt(); // Pulihkan status interupsi } catch (Exception e) { System.err.println("Kesalahan pemrosesan: " + e.getMessage()); } } /** * Metode pemicu interupsi eksternal * **Mekanisme koordinasi**: * Bekerja bersama flag interupsi thread untuk deteksi ganda */ public void stop() { running.set(false); } /** * Metode utama (untuk pengujian) * **Skenario uji**: * 1. Mulai thread pemrosesan * 2. Picu interupsi setelah 5000 ms * 3. Tunggu hingga thread berhenti */ public static void main(String[] args) throws InterruptedException { // Inisialisasi klien MNS CloudAccount account = new CloudAccount( ServiceSettings.getMNSAccessKeyId(), ServiceSettings.getMNSAccessKeySecret(), ServiceSettings.getMNSAccountEndpoint()); MNSClient client = account.getMNSClient(); // Periksa apakah pesan cocok dengan event gangguan spot instans boolean isMatch = false; // Mulai prosesor gambar InterruptibleImageProcessor processor = new InterruptibleImageProcessor(); Thread processThread = new Thread(processor); processThread.start(); try{ // Ambil pesan dari antrian CloudQueue queue = client.getQueueRef("spot-interruption"); Message popMsg = queue.popMessage(); if (popMsg != null){ // Isi pesan secara default dienkripsi Base64 System.out.println("isi pesan: " + popMsg.getMessageBodyAsRawString()); // Dekode Base64 byte[] decodedBytes = Base64.getDecoder().decode(popMsg.getMessageBodyAsRawString()); String decodedString = new String(decodedBytes); System.out.println("konten pesan: " + decodedString); // Parsing string JSON JSONObject json = new JSONObject(decodedString); // Dapatkan nama event dari bidang "name" String name = json.getString("name"); isMatch = "Instance:PreemptibleInstanceInterruption".equals(name); // Tangani event gangguan spot instans if(isMatch){ System.out.println("Spot instans akan diinterupsi dan direclaim"); // Hentikan prosesor gambar processor.stop(); processThread.interrupt(); System.out.println("Program dihentikan"); processThread.join(); // Hapus pesan queue.deleteMessage(popMsg.getReceiptHandle()); } } }catch (Exception e){ System.out.println("Terjadi exception yang tidak dikenal!"); e.printStackTrace(); } client.close(); } }
Untuk membuat snapshot, lihat CreateSnapshot.
Untuk membuat gambar kustom, lihat CreateImage.
Referensi
Jika spot instans Anda menyimpan data atau konfigurasi penting, lihat Pertahankan dan pulihkan data untuk spot instans guna mencegah kehilangan data.