Panduan untuk Transaksi Di Seluruh Mikro

1. Pengenalan

Dalam artikel ini, kami akan membincangkan pilihan untuk melaksanakan transaksi merentas perkhidmatan mikro.

Kami juga akan memeriksa beberapa alternatif untuk transaksi dalam senario perkhidmatan mikro yang diedarkan.

2. Mengelakkan Transaksi Di Seluruh Mikro

Transaksi yang diedarkan adalah proses yang sangat kompleks dengan banyak bahagian bergerak yang boleh gagal. Juga, jika bahagian ini berjalan pada mesin yang berlainan atau bahkan di pusat data yang berlainan, proses melakukan transaksi boleh menjadi sangat lama dan tidak dapat dipercayai.

Ini boleh menjejaskan pengalaman pengguna dan lebar jalur sistem secara keseluruhan. Jadi salah satu kaedah terbaik untuk menyelesaikan masalah transaksi yang diedarkan adalah dengan mengelakkannya sepenuhnya.

2.1. Contoh Transaksi yang Memerlukan Senibina

Biasanya, perkhidmatan mikro direka sedemikian rupa sehingga bebas dan berguna dengan sendirinya. Ia harus dapat menyelesaikan beberapa tugas perniagaan atom.

Sekiranya kita dapat membahagikan sistem kita dalam perkhidmatan mikro seperti itu, ada kemungkinan besar kita tidak perlu melakukan transaksi di antara mereka sama sekali.

Sebagai contoh, mari kita pertimbangkan sistem pemesejan siaran antara pengguna.

Perkhidmatan mikro pengguna berkenaan dengan profil pengguna (membuat pengguna baru, mengedit data profil dll) dengan kelas domain asas berikut:

@Entity public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Basic private String name; @Basic private String surname; @Basic private Instant lastMessageTime; }

Perkhidmatan mikro mesej berkenaan dengan penyiaran. Ini merangkumi entiti Mesej dan semua yang ada di sekitarnya:

@Entity public class Message implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Basic private long userId; @Basic private String contents; @Basic private Instant messageTimestamp; }

Setiap perkhidmatan mikro mempunyai pangkalan data sendiri. Perhatikan bahawa kami tidak merujuk kepada Pengguna entiti dari entiti Mesej , kerana kelas pengguna tidak dapat diakses dari perkhidmatan mikro mesej . Kami merujuk kepada pengguna hanya dengan id.

Kini entiti Pengguna mengandungi medan LastMessageTime kerana kami ingin menunjukkan maklumat mengenai masa aktiviti pengguna terakhir di profilnya.

Namun, untuk menambahkan pesan baru kepada pengguna dan memperbarui TimeMessageTime terakhirnya , kita sekarang harus melaksanakan transaksi di seluruh microservices.

2.2. Pendekatan Alternatif Tanpa Transaksi

Kita boleh mengubah seni bina perkhidmatan mikro dan membuang medan lastMessageTime dari entiti Pengguna .

Maka kita dapat memaparkan kali ini di profil pengguna dengan mengeluarkan permintaan terpisah ke layanan mikro pesan dan mencari nilai maksimum messageTimestamp untuk semua pesan pengguna ini.

Mungkin, jika perkhidmatan mikro mesej berada di bawah beban tinggi atau bahkan turun, kami tidak akan dapat menunjukkan waktu mesej terakhir pengguna di profilnya.

Tetapi itu lebih dapat diterima daripada gagal melakukan transaksi yang diedarkan untuk menyimpan mesej hanya kerana perkhidmatan mikro pengguna tidak bertindak balas pada waktunya.

Sudah tentu ada senario yang lebih kompleks ketika kita harus melaksanakan proses perniagaan di beberapa perkhidmatan mikro, dan kita tidak ingin membiarkan ketidakkonsistenan antara perkhidmatan mikro tersebut.

3. Protokol Komitmen Dua Fasa

Protokol komit dua fasa (atau 2PC) adalah mekanisme untuk melaksanakan transaksi merentasi komponen perisian yang berbeza (pelbagai pangkalan data, antrian pesanan dll.)

3.1. Senibina 2PC

Salah satu peserta penting dalam transaksi yang diedarkan adalah penyelaras transaksi. Transaksi yang diedarkan terdiri daripada dua langkah:

  • Siapkan fasa - semasa fasa ini, semua peserta transaksi bersiap sedia dan memberitahu penyelaras bahawa mereka sudah bersedia untuk menyelesaikan transaksi
  • Commit atau Rollback phase - semasa fasa ini, perintah komit atau rollback dikeluarkan oleh koordinator transaksi kepada semua peserta

Masalahnya dengan 2PC adalah agak lambat berbanding waktu untuk operasi satu microservice.

Menyelaraskan transaksi antara perkhidmatan mikro, walaupun mereka berada di rangkaian yang sama, benar-benar dapat memperlambat sistem , jadi pendekatan ini biasanya tidak digunakan dalam senario beban tinggi.

3.2. Piawaian XA

Piawaian XA adalah spesifikasi untuk melakukan transaksi yang diedarkan 2PC di seluruh sumber sokongan. Mana-mana pelayan aplikasi yang mematuhi JTA (JBoss, GlassFish dll) menyokongnya di luar kotak.

Sumber yang mengambil bahagian dalam urus niaga yang diedarkan boleh menjadi, misalnya, dua pangkalan data dari dua perkhidmatan mikro yang berbeza.

Namun, untuk memanfaatkan mekanisme ini, sumber daya harus dikerahkan ke platform JTA tunggal. Ini tidak selalu dapat dilaksanakan untuk seni bina perkhidmatan mikro.

3.3. Draf Standard REST-AT

Standard lain yang dicadangkan adalah REST-AT yang telah mengalami beberapa pengembangan oleh RedHat tetapi masih tidak keluar dari tahap draf. Namun ia disokong oleh pelayan aplikasi WildFly di luar kotak.

Piawaian ini membolehkan menggunakan pelayan aplikasi sebagai koordinator transaksi dengan REST API tertentu untuk membuat dan bergabung dengan transaksi yang diedarkan.

Perkhidmatan web RESTful yang ingin mengambil bahagian dalam transaksi dua fasa juga harus menyokong REST API tertentu.

Sayangnya, untuk menjembatani transaksi yang diedarkan ke sumber tempatan perkhidmatan mikro, kita masih perlu menggunakan sumber ini ke satu platform JTA atau menyelesaikan tugas yang tidak remeh untuk menulis jambatan ini sendiri.

4. Konsistensi dan Pampasan Akhirnya

By far, one of the most feasible models of handling consistency across microservices is eventual consistency.

This model doesn't enforce distributed ACID transactions across microservices. Instead, it proposes to use some mechanisms of ensuring that the system would be eventually consistent at some point in the future.

4.1. A Case for Eventual Consistency

For example, suppose we need to solve the following task:

  • register a user profile
  • do some automated background check that the user can actually access the system

The second task is to ensure, for example, that this user wasn't banned from our servers for some reason.

But it could take time, and we'd like to extract it to a separate microservice. It wouldn't be reasonable to keep the user waiting for so long just to know that she was registered successfully.

One way to solve it would be with a message-driven approach including compensation. Let's consider the following architecture:

  • the user microservice tasked with registering a user profile
  • the validation microservice tasked with doing a background check
  • the messaging platform that supports persistent queues

The messaging platform could ensure that the messages sent by the microservices are persisted. Then they would be delivered at a later time if the receiver weren't currently available

4.2. Happy Scenario

In this architecture, a happy scenario would be:

  • the user microservice registers a user, saving information about her in its local database
  • the user microservice marks this user with a flag. It could signify that this user hasn't yet been validated and doesn't have access to full system functionality
  • a confirmation of registration is sent to the user with a warning that not all functionality of the system is accessible right away
  • the user microservice sends a message to the validation microservice to do the background check of a user
  • the validation microservice runs the background check and sends a message to the user microservice with the results of the check
    • if the results are positive, the user microservice unblocks the user
    • if the results are negative, the user microservice deletes the user account

After we've gone through all these steps, the system should be in a consistent state. However, for some period of time, the user entity appeared to be in an incomplete state.

The last step, when the user microservice removes the invalid account, is a compensation phase.

4.3. Failure Scenarios

Now let's consider some failure scenarios:

  • if the validation microservice is not accessible, then the messaging platform with its persistent queue functionality ensures that the validation microservice would receive this message at some later time
  • suppose the messaging platform fails, then the user microservice tries to send the message again at some later time, for example, by scheduled batch-processing of all users that were not yet validated
  • if the validation microservice receives the message, validates the user but can't send the answer back due to the messaging platform failure, the validation microservice also retries sending the message at some later time
  • if one of the messages got lost, or some other failure happened, the user microservice finds all non-validated users by scheduled batch-processing and sends requests for validation again

Walaupun beberapa pesan dikeluarkan berkali-kali, ini tidak akan mempengaruhi konsistensi data dalam pangkalan data perkhidmatan mikro.

Dengan mempertimbangkan semua senario kegagalan yang mungkin, kita dapat memastikan bahawa sistem kita akan memenuhi syarat-syarat konsistensi akhirnya. Pada masa yang sama, kami tidak perlu berurusan dengan transaksi yang diedarkan dengan harga yang mahal.

Tetapi kita harus sedar bahawa memastikan konsistensi akhirnya adalah tugas yang kompleks. Ia tidak mempunyai satu penyelesaian untuk semua kes.

5. Kesimpulan

Dalam artikel ini, kami telah membincangkan beberapa mekanisme untuk melaksanakan transaksi di seluruh perkhidmatan mikro.

Dan, kami juga telah meneroka beberapa alternatif untuk melakukan urus niaga ini.