Dalam aplikasi Spring moden, adalah perkara biasa untuk menggabungkan pelaksanaan tak segerak dengan tingkah laku transaksi. Walau bagaimanapun, menganotasi kaedah dengan @Async dan @Transactional(propagation = Propagation.REQUIRES_NEW) mungkin mengakibatkan tingkah laku yang tidak dijangka kerana Spring menguruskan tugasan dan transaksi tak segerak.
Dalam artikel ini, kami akan meneroka isu ini secara terperinci dan menunjukkan penyelesaian untuk mengendalikan kedua-dua pelaksanaan tak segerak dan pengurusan transaksi dengan betul.
Pertimbangkan coretan kod berikut:
@Async @Transactional(propagation = Propagation.REQUIRES_NEW) public void saveSomething() { // save-point one // save-point two }
Pada pandangan pertama, nampaknya semuanya berfungsi seperti yang diharapkan. Walau bagaimanapun, terdapat beberapa masalah utama dengan konfigurasi ini yang boleh membawa kepada tingkah laku yang tidak diingini.
Anotasi @Async memberitahu Spring untuk menjalankan kaedah secara tidak segerak dalam urutan yang berasingan. Ini bermakna kaedah itu tidak akan dijalankan dalam urutan asal yang memanggilnya tetapi akan dimuatkan ke utas lain dalam kumpulan benang.
Spring menggunakan proksi untuk mengurus kaedah tak segerak. Apabila anda memanggil kaedah beranotasi dengan @Async, Spring mewakilkan pelaksanaan kepada Pelaksana dalaman yang menjalankan kaedah dalam urutan yang berbeza.
Anotasi @Transactional(propagation = Propagation.REQUIRES_NEW) memastikan transaksi baharu dimulakan untuk kaedah tersebut, tanpa mengira sebarang transaksi sedia ada. Ia menggantung sebarang transaksi aktif dalam urutan panggilan dan memulakan transaksi baharu untuk kaedah tersebut.
Pengurusan urus niaga dalam Spring biasanya terikat pada urutan, bermakna konteks transaksi terikat pada urutan semasa.
Isu timbul kerana @Async menjalankan kaedah dalam urutan berbeza dan pengurusan transaksi Spring bergantung pada urutan untuk mengikat transaksi. Apabila kaedah dilaksanakan secara tidak segerak, konteks urus niaga daripada utas panggilan tidak disebarkan ke utas baharu, yang membawa kepada masalah berikut:
Untuk menyelesaikan masalah ini, anda boleh memisahkan pelaksanaan tak segerak daripada logik transaksi dengan mengendalikan transaksi dalam kaedah perkhidmatan yang berasingan. Begini cara anda boleh melakukannya:
Langkah 1: Cipta Perkhidmatan Segerak Baharu untuk Logik Transaksi
Buat perkhidmatan baharu yang mengendalikan logik transaksi. Kaedah ini akan dilaksanakan secara serentak (tanpa @Async) untuk memastikan pengurusan transaksi berfungsi seperti yang diharapkan.
Langkah 2: Panggil Kaedah Segerak Secara Asynchronous
Anda kemudiannya boleh memanggil kaedah transaksi segerak secara tak segerak menggunakan @Async. Ini memastikan bahawa logik transaksi dikendalikan dengan betul dalam urutan utama dan tingkah laku tak segerak masih dikekalkan.
Begini rupa kod yang difaktorkan semula:
@Async @Transactional(propagation = Propagation.REQUIRES_NEW) public void saveSomething() { // save-point one // save-point two }
Dalam penyelesaian yang difaktorkan semula, pelaksanaan tak segerak dicapai dengan menganotasi kaedah saveSomethingAsync() dengan @Async. Ini bermakna apabila saveSomethingAsync() dipanggil, ia akan dijalankan dalam urutan berasingan yang diuruskan oleh pelaksana tugas tak segerak Spring. Menjalankan ini dalam urutan lain membolehkan utas utama meneruskan pelaksanaannya tanpa menunggu saveSomethingAsync() selesai. Pendekatan ini berfaedah untuk senario di mana anda ingin memunggah tugasan yang sudah lama berjalan, meningkatkan daya tindak balas atau mengendalikan operasi bebas secara serentak.
Untuk tingkah laku transaksi, kaedah saveSomething() dalam TransactionalService dianotasi dengan @Transactional(propagation = Propagation.REQUIRES_NEW). Ini memastikan bahawa setiap panggilan ke saveSomething() mencipta transaksi baharu bebas daripada sebarang transaksi sedia ada dalam kaedah panggilan. Penyebaran REQUIRES_NEW memulakan transaksi baharu dan menggantung mana-mana transaksi sedia ada, membenarkan saveSomething() beroperasi dalam konteks transaksi terpencil. Ini bermakna walaupun kaedah panggilan asal mempunyai urus niaga, saveSomething() akan berfungsi dalam urus niaga berasingannya sendiri, membolehkan komit terkawal dan rollback hanya untuk operasi ini.
Dengan mengasingkan pelaksanaan tak segerak daripada logik transaksi, kami memastikan pengurusan transaksi berfungsi seperti yang diharapkan. Dalam persediaan ini, konteks transaksi kekal dikendalikan dengan betul dalam kaedah saveSomething(), manakala kaedah saveSomethingAsync() terus dilaksanakan dalam urutan yang berasingan. Pengasingan kebimbangan ini membolehkan kedua-dua faedah pemprosesan tak segerak dan pengurusan transaksi yang boleh dipercayai, membolehkan operasi data yang bebas dan selamat walaupun semasa memproses secara serentak.
Apabila Pengasingan Transaksi Adalah Kritikal: Jika anda perlu memastikan bahawa operasi tertentu dijalankan dalam transaksi berasingan (iaitu, MEMERLUKAN_BARU), pendekatan ini berfungsi dengan baik.
Operasi Asynchronous: Jika anda mempunyai tugasan bebas yang berjalan lama yang perlu dilaksanakan secara tidak segerak tetapi juga memerlukan sempadan transaksi mereka sendiri.
Jika anda memerlukan penyahgandingan yang lebih maju atau ingin mengendalikan percubaan semula, pengendalian ralat dan proses yang berjalan lama, pertimbangkan untuk memunggah tugasan ke baris gilir mesej seperti Kafka atau RabbitMQ. Dengan menggunakan baris gilir mesej, anda boleh memastikan setiap tugasan berjalan dalam konteksnya sendiri dan transaksi boleh diuruskan secara bebas.
Atas ialah kandungan terperinci Mengendalikan Pelaksanaan Asynchronous dengan Transaksi pada Musim Bunga: Perangkap Biasa dan Cara Menyelesaikannya. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!