Rumah > Java > javaTutorial > teks badan

Cara Corak Saga Menyelesaikan Isu Transaksi Teragih: Kaedah dan Contoh Dunia Nyata

Linda Hamilton
Lepaskan: 2024-10-20 20:11:02
asal
483 orang telah melayarinya

1. Memahami Masalah: Kerumitan Transaksi Teragih

How the Saga Pattern Resolves Distributed Transaction Issues: Methods and Real-World Example

Urus niaga yang diedarkan melibatkan berbilang perkhidmatan mikro, di mana setiap perkhidmatan melaksanakan sebahagian daripada transaksi. Sebagai contoh, platform e-dagang mungkin melibatkan perkhidmatan seperti pembayaran, inventori dan pengurusan pesanan. Perkhidmatan ini perlu bekerjasama untuk menyelesaikan transaksi. Namun, apa yang berlaku jika salah satu perkhidmatan ini gagal?

1.1 Senario Dunia Nyata

Bayangkan aplikasi e-dagang di mana langkah berikut berlaku semasa penempatan pesanan:

  • Langkah 1 : Potong bayaran daripada akaun pelanggan.
  • Langkah 2 : Kurangkan kiraan item dalam inventori.
  • Langkah 3 : Buat pesanan dalam sistem pengurusan pesanan.

How the Saga Pattern Resolves Distributed Transaction Issues: Methods and Real-World Example

Jika perkhidmatan inventori gagal selepas pembayaran ditolak tetapi sebelum pesanan dibuat, sistem akan berada dalam keadaan tidak konsisten. Pelanggan dikenakan bayaran, tetapi tiada pesanan dibuat.

1.2 Penyelesaian Tradisional dan Hadnya

Untuk mengendalikan kegagalan sedemikian, seseorang mungkin mempertimbangkan untuk menggunakan transaksi yang diedarkan dengan protokol komit dua fasa. Walau bagaimanapun, ini memperkenalkan beberapa isu:

  • Kependaman Tinggi : Setiap perkhidmatan mesti mengunci sumber semasa transaksi, yang membawa kepada peningkatan kependaman.
  • Ketersediaan Dikurangkan : Jika mana-mana perkhidmatan gagal, keseluruhan transaksi akan ditarik balik, mengurangkan ketersediaan keseluruhan sistem.
  • Gandingan Ketat : Perkhidmatan menjadi berganding rapat, menjadikannya lebih sukar untuk menskalakan atau mengubah suai perkhidmatan individu.

2. Bagaimana Corak Saga Menyelesaikan Masalah

Dalam sistem yang diedarkan, urus niaga selalunya merangkumi berbilang perkhidmatan mikro. Memastikan semua perkhidmatan sama ada selesai dengan jayanya atau tiada langsung adalah mencabar. Cara tradisional mengendalikan perkara ini—menggunakan urus niaga teragih dengan komitmen dua fasa—boleh menjadi masalah disebabkan isu seperti kependaman tinggi, gandingan yang ketat dan ketersediaan yang berkurangan.

How the Saga Pattern Resolves Distributed Transaction Issues: Methods and Real-World Example

Corak Saga menawarkan pendekatan yang lebih fleksibel. Daripada cuba melaksanakan urus niaga sebagai satu unit, corak Saga membahagikan urus niaga kepada langkah yang lebih kecil dan terpencil yang boleh dilakukan secara bebas. Setiap langkah ialah transaksi setempat yang mengemas kini pangkalan data dan kemudian mencetuskan langkah seterusnya. Jika langkah gagal, sistem melakukan tindakan pampasan untuk membuat asal perubahan yang dibuat oleh langkah sebelumnya, memastikan sistem boleh kembali kepada keadaan yang konsisten.

2.1 Apakah Corak Saga?

Corak Saga pada asasnya ialah urutan transaksi yang lebih kecil yang dilaksanakan satu demi satu. Begini cara ia berfungsi:

  • Transaksi Tempatan : Setiap perkhidmatan yang terlibat dalam transaksi melakukan transaksi tempatannya sendiri. Contohnya, dalam sistem pemprosesan pesanan, satu perkhidmatan mungkin mengendalikan pembayaran, satu lagi inventori dan satu lagi rekod pesanan.
  • Penerbitan Acara atau Mesej : Selepas perkhidmatan menyelesaikan transaksi setempatnya, ia menerbitkan acara atau menghantar mesej yang menunjukkan kejayaan menyelesaikan langkah itu. Contohnya, selepas pembayaran diproses, perkhidmatan pembayaran mungkin menerbitkan acara "PaymentCompleted".
  • Mencetuskan Langkah Seterusnya : Perkhidmatan seterusnya dalam jujukan mendengar acara tersebut dan, selepas menerimanya, meneruskan transaksi setempatnya. Ini berterusan sehingga semua langkah dalam transaksi selesai.
  • Tindakan Pampasan : Jika mana-mana langkah gagal, tindakan pampasan akan digunakan. Tindakan ini direka untuk membalikkan perubahan yang dibuat oleh langkah sebelumnya. Sebagai contoh, jika pengurangan inventori gagal selepas pembayaran, tindakan pampasan akan mengembalikan pembayaran.

2.2 Jenis Hikayat

Terdapat dua cara utama untuk melaksanakan corak Saga: Koreografi dan Orkestrasi.

2.2.1 Saga Koreografi

Dalam Saga Koreografi, tiada penyelaras pusat. Sebaliknya, setiap perkhidmatan yang terlibat dalam Saga mendengar acara dan memutuskan masa untuk bertindak berdasarkan keputusan langkah sebelumnya. Pendekatan ini tidak berpusat dan membolehkan perkhidmatan beroperasi secara bebas. Begini cara ia berfungsi:

  • Penyelarasan Berasaskan Acara : Setiap perkhidmatan bertanggungjawab untuk mengendalikan acara yang berkaitan dengannya. Contohnya, selepas perkhidmatan pembayaran memproses pembayaran, ia mengeluarkan peristiwa "Pembayaran Selesai". Perkhidmatan inventori mendengar acara ini dan, apabila ia menerimanya, menolak kiraan item.
  • Kawalan Terpencar : Memandangkan tiada penyelaras pusat, setiap perkhidmatan mesti tahu apa yang perlu dilakukan seterusnya berdasarkan acara yang diterima. Ini memberikan sistem lebih fleksibiliti tetapi memerlukan perancangan yang teliti untuk memastikan semua perkhidmatan memahami urutan operasi yang betul.
  • Tindakan Pemberian Pampasan : Jika perkhidmatan mengesan bahawa berlaku kesilapan, ia boleh mengeluarkan peristiwa kegagalan, yang didengari oleh perkhidmatan lain untuk mencetuskan tindakan pampasan. Contohnya, jika perkhidmatan inventori tidak dapat mengemas kini inventori, ia mungkin mengeluarkan peristiwa "InventoryUpdateFailed", yang didengari oleh perkhidmatan pembayaran untuk mencetuskan bayaran balik.

Kelebihan Koreografi:

  • Gandingan Longgar : Perkhidmatan digandingkan secara longgar, yang menjadikannya lebih mudah untuk menskala dan mengubah suai perkhidmatan individu.
  • Ketahanan : Memandangkan setiap perkhidmatan bertindak secara bebas, sistem boleh menjadi lebih berdaya tahan terhadap kegagalan dalam perkhidmatan individu.

Cabaran Koreografi:

  • Kerumitan : Apabila bilangan perkhidmatan bertambah, mengurus dan memahami aliran acara boleh menjadi rumit.
  • Kurang Kawalan Pusat : Tanpa penyelaras pusat, adalah lebih sukar untuk memantau dan menyahpepijat aliran transaksi keseluruhan.

2.2.2 Saga Orkestrasi

Dalam Orkestrasi Saga, orkestra pusat mengawal aliran transaksi. Orkestra menentukan urutan langkah dan mengendalikan komunikasi antara perkhidmatan. Begini cara ia berfungsi:

  • Kawalan Berpusat : Orkestra menghantar arahan kepada setiap perkhidmatan mengikut urutan. Contohnya, orkestra mungkin mengarahkan perkhidmatan pembayaran untuk memproses pembayaran terlebih dahulu. Setelah itu selesai, ia memberitahu perkhidmatan inventori untuk mengemas kini inventori dan seterusnya.
  • Pelaksanaan Berurutan : Setiap perkhidmatan melaksanakan tugasnya hanya apabila diarahkan oleh orkestra, memastikan langkah-langkah itu berlaku dalam susunan yang betul.
  • Logik Pampasan : Orkestra juga bertanggungjawab untuk memulakan tindakan pampasan jika berlaku kesilapan. Contohnya, jika kemas kini inventori gagal, orkestra boleh memerintahkan perkhidmatan pembayaran untuk membayar balik pembayaran.

Kelebihan Orkestrasi:

  • Kawalan Berpusat : Dengan pengatur tunggal, lebih mudah untuk memantau, mengurus dan menyahpepijat aliran transaksi.
  • Logik Lebih Mudah : Memandangkan orkestra mengendalikan aliran, perkhidmatan individu tidak perlu mengetahui keseluruhan urutan transaksi.

Cabaran Orkestrasi:

  • Titik Kegagalan Tunggal : Orkestra boleh menjadi hambatan atau titik kegagalan tunggal jika tidak direka untuk ketersediaan yang tinggi.
  • Gandingan Ketat kepada Orkestra : Perkhidmatan bergantung kepada orkestra, yang boleh menjadikan sistem kurang fleksibel berbanding dengan koreografi.

3. Melaksanakan Corak Saga Orkestrasi Mudah: Panduan Langkah demi Langkah

Mari kita pertimbangkan senario e-dagang dan laksanakannya menggunakan corak Saga.

Dalam senario pembelian kopi kami, setiap perkhidmatan mewakili transaksi tempatan. Perkhidmatan Kopi bertindak sebagai pengatur kisah ini, menyelaraskan perkhidmatan lain untuk menyelesaikan pembelian.

Berikut ialah pecahan cara saga itu mungkin berfungsi:

  • Pelanggan membuat pesanan : Pelanggan membuat pesanan melalui Perkhidmatan Pesanan.
  • Perkhidmatan Kopi memulakan kisah : Perkhidmatan Kopi menerima pesanan dan memulakan kisah.
  • Perkhidmatan Pesanan membuat pesanan : Perkhidmatan Pesanan membuat pesanan baharu dan mengekalkan pesanan itu.
  • Perkhidmatan Pengebilan mengira kos : Perkhidmatan Pengebilan mengira jumlah kos pesanan dan mencipta rekod pengebilan.
  • Perkhidmatan Pembayaran memproses pembayaran : Perkhidmatan Pembayaran memproses pembayaran.
  • Khidmat Kopi mengemas kini status pesanan : Setelah pembayaran berjaya, Perkhidmatan Kopi mengemas kini status pesanan kepada "selesai".

How the Saga Pattern Resolves Distributed Transaction Issues: Methods and Real-World Example

3.1 Entiti urus niaga

How the Saga Pattern Resolves Distributed Transaction Issues: Methods and Real-World Example

Dalam pelaksanaan saga saya, setiap SagaItemBuilder mewakili satu langkah dalam aliran transaksi teragih kami. ActionBuilder mentakrifkan tindakan yang akan dilakukan, termasuk tindakan utama dan tindakan rollback yang akan dilaksanakan jika ralat berlaku. ActionBuilder merangkumi tiga maklumat:

komponen : Contoh kacang di mana kaedah yang akan digunakan berada.

kaedah : Nama kaedah yang akan dipanggil.

args : Hujah-hujah yang akan dihantar kepada kaedah.

Pembina Tindakan

public class ActionBuilder {
    private Object component;
    private String method;
    private Object[] args;

    public static ActionBuilder builder() {
        return new ActionBuilder();
    }

    public ActionBuilder component(Object component) {
        this.component = component;
        return this;
    }

    public ActionBuilder method(String method) {
        this.method = method;
        return this;
    }

    public ActionBuilder args(Object... args) {
        this.args = args;
        return this;
    }

    public Object getComponent() { return component; }
    public String getMethod() { return method; }
    public Object[] getArgs() { return args; }
}
Salin selepas log masuk

SagaItemBuilder

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class SagaItemBuilder {
    private ActionBuilder action;
    private Map<Class<? extends Exception>, ActionBuilder> onBehaviour;

    public static SagaItemBuilder builder() {
        return new SagaItemBuilder();
    }

    public SagaItemBuilder action(ActionBuilder action) {
        this.action = action;
        return this;
    }

    public SagaItemBuilder onBehaviour(Class<? extends Exception> exception, ActionBuilder action) {
        if (Objects.isNull(onBehaviour)) onBehaviour = new HashMap<>();
        onBehaviour.put(exception, action);
        return this;
    }

    public ActionBuilder getAction() {
        return action;
    }

    public Map<Class<? extends Exception>, ActionBuilder> getBehaviour() {
        return onBehaviour;
    }
}
Salin selepas log masuk

Senario

import java.util.ArrayList;
import java.util.List;

public class Scenarios {
    List<SagaItemBuilder> scenarios;

    public static Scenarios builder() {
        return new Scenarios();
    }

    public Scenarios scenario(SagaItemBuilder sagaItemBuilder) {
        if (scenarios == null) scenarios = new ArrayList<>();
        scenarios.add(sagaItemBuilder);
        return this;
    }

    public List<SagaItemBuilder> getScenario() {
        return scenarios;
    }
}
Salin selepas log masuk

Di bawah ialah bagaimana saya boleh melakukan transaksi pengedaran.

package com.example.demo.saga;

import com.example.demo.saga.exception.CanNotRollbackException;
import com.example.demo.saga.exception.RollBackException;
import com.example.demo.saga.pojo.ActionBuilder;
import com.example.demo.saga.pojo.SagaItemBuilder;
import com.example.demo.saga.pojo.Scenarios;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

@Component
public class DTC {

    public boolean commit(Scenarios scenarios) throws Exception {
        validate(scenarios);
        for (int i = 0; i < scenarios.getScenario().size(); i++) {
            SagaItemBuilder scenario = scenarios.getScenario().get(i);
            ActionBuilder action = scenario.getAction();
            Object bean = action.getComponent();
            String method = action.getMethod();
            Object[] args = action.getArgs();

            try {
                invoke(bean, method, args);
            } catch (Exception e) {
                rollback(scenarios, i, e);
                return false;
            }
        }
        return true;
    }

    private void rollback(Scenarios scenarios, Integer failStep, Exception currentStepFailException) {
        for (int i = failStep; i >= 0; i--) {
            SagaItemBuilder scenario = scenarios.getScenario().get(i);
            Map<Class<? extends Exception>, ActionBuilder> behaviours = scenario.getBehaviour();
            Set<Class<? extends Exception>> exceptions = behaviours.keySet();
            ActionBuilder actionWhenException = null;

            if (failStep == i) {
                for(Class<? extends Exception> exception: exceptions) {
                    if (exception.isInstance(currentStepFailException)) {
                        actionWhenException = behaviours.get(exception);
                    }
                }
                if (actionWhenException == null) actionWhenException = behaviours.get(RollBackException.class);
            } else {
                actionWhenException = behaviours.get(RollBackException.class);
            }

            Object bean = actionWhenException.getComponent();
            String method = actionWhenException.getMethod();
            Object[] args = actionWhenException.getArgs();
            try {
                invoke(bean, method, args);
            } catch (Exception e) {
                throw new CanNotRollbackException("Error in %s belong to %s. Can not rollback transaction".formatted(method, bean.getClass()));
            }
        }
    }

    private void validate(Scenarios scenarios) throws Exception {
        for (int i = 0; i < scenarios.getScenario().size(); i++) {
            SagaItemBuilder scenario = scenarios.getScenario().get(i);
            ActionBuilder action = scenario.getAction();
            if (action.getComponent() == null) throw new Exception("Missing bean in scenario");
            if (action.getMethod() == null) throw new Exception("Missing method in scenario");

            Map<Class<? extends Exception>, ActionBuilder> behaviours = scenario.getBehaviour();
            Set<Class<? extends Exception>> exceptions = behaviours.keySet();
            if (exceptions.contains(null)) throw new Exception("Exception can not be null in scenario has method %s, bean %s " .formatted(action.getMethod(), action.getComponent().getClass()));
            if (!exceptions.contains(RollBackException.class)) throw new Exception("Missing default RollBackException in scenario has method %s, bean %s " .formatted(action.getMethod(), action.getComponent().getClass()));
        }
    }

    public String invoke(Object bean, String methodName, Object... args) throws Exception {
        try {
            Class<?>[] paramTypes = new Class[args.length];
            for (int i = 0; i < args.length; i++) {
                paramTypes[i] = parameterType(args[i]);
            }
            Method method = bean.getClass().getDeclaredMethod(methodName, paramTypes);
            Object result = method.invoke(bean, args);
            return result != null ? result.toString() : null;
        } catch (Exception e) {
            throw e;
        }
    }

    private static Class<?> parameterType (Object o) {
        if (o instanceof Integer) {
           return int.class;
        } else if (o instanceof Boolean) {
            return boolean.class;
        } else if (o instanceof Double) {
            return double.class;
        } else if (o instanceof Float) {
            return float.class;
        } else if (o instanceof Long) {
            return long.class;
        } else if (o instanceof Short) {
            return short.class;
        } else if (o instanceof Byte) {
            return byte.class;
        } else if (o instanceof Character) {
            return char.class;
        } else {
            return o.getClass();
        }
    }
}
Salin selepas log masuk

3.2 Menggunakannya

Saya mempunyai 3 perkhidmatan yang memanggil perkhidmatan luar: BillingService , OrderService , PaymentService.

Perkhidmatan Pesanan

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class OrderService {

    public String prepareOrder(String name, int number) {
        System.out.println("Prepare order for %s with order id %d ".formatted(name, number));
        return "Prepare order for %s with order id %d ".formatted(name, number);
    }

    public void Rollback_prepareOrder_NullPointException() {
        System.out.println("Rollback prepareOrder because NullPointException");
    }

    public void Rollback_prepareOrder_RollBackException() {
        System.out.println("Rollback prepareOrder because RollBackException");
    }
}
Salin selepas log masuk

Perkhidmatan Pengebilan

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class BillingService {

    public String prepareBilling(String name, int number) {
        System.out.println("Prepare billing for %s with order id %d ".formatted(name, number));
        return "Prepare billing for %s with order id %d ".formatted(name, number);
    }

    public String createBilling(String name, int number) {
        System.out.println("Create billing for %s with order id %d ".formatted(name, number));
        return "Create billing for %s with order id %d ".formatted(name, number);
    }

    public void Rollback_prepareBilling_NullPointException() {
        System.out.println("Rollback prepareBilling because NullPointException");
    }

    public void Rollback_prepareBilling_ArrayIndexOutOfBoundsException() {
        System.out.println("Rollback prepareBilling because ArrayIndexOutOfBoundsException");
    }

    public void Rollback_prepareBilling_RollBackException() {
        System.out.println("Rollback prepareBilling because RollBackException");
    }

    public void Rollback_createBilling_NullPointException() {
        System.out.println("Rollback createBilling because NullPointException");
    }

    public void Rollback_createBilling_ArrayIndexOutOfBoundsException() {
        System.out.println("Rollback createBilling because ArrayIndexOutOfBoundsException");
    }

    public void Rollback_createBilling_RollBackException() {
        System.out.println("Rollback createBilling because RollBackException");
    }
}
Salin selepas log masuk

Perkhidmatan Pembayaran

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class PaymentService {

    public String createPayment() {
        System.out.println("Create payment");
        return "Create payment";
    }

    public void Rollback_createPayment_NullPointException() {
        System.out.println("Rollback createPayment because NullPointException");
    }

    public void Rollback_createPayment_RollBackException() {
        System.out.println("Rollback createPayment because RollBackException");
    }
}
Salin selepas log masuk

Dan dalam Perkhidmatan Kopi, saya melaksanakannya seperti berikut, saya mencipta senario dan kemudian melaksanakannya.

package com.example.demo.service;

import com.example.demo.saga.DTC;
import com.example.demo.saga.exception.RollBackException;
import com.example.demo.saga.pojo.ActionBuilder;
import com.example.demo.saga.pojo.SagaItemBuilder;
import com.example.demo.saga.pojo.Scenarios;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CoffeeService {

    @Autowired
    private OrderService orderService;

    @Autowired
    private BillingService billingService;

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private DTC dtc;

    public String test() throws Exception {
        Scenarios scenarios = Scenarios.builder()
                .scenario(
                        SagaItemBuilder.builder()
                                .action(ActionBuilder.builder().component(orderService).method("prepareOrder").args("tuanh.net", 123))
                                .onBehaviour(NullPointerException.class, ActionBuilder.builder().component(orderService).method("Rollback_prepareOrder_NullPointException").args())
                                .onBehaviour(RollBackException.class, ActionBuilder.builder().component(orderService).method("Rollback_prepareOrder_RollBackException").args())
                ).scenario(
                        SagaItemBuilder.builder()
                                .action(ActionBuilder.builder().component(billingService).method("prepareBilling").args("tuanh.net", 123))
                                .onBehaviour(NullPointerException.class, ActionBuilder.builder().component(billingService).method("Rollback_prepareBilling_NullPointException").args())
                                .onBehaviour(RollBackException.class, ActionBuilder.builder().component(billingService).method("Rollback_prepareBilling_RollBackException").args())
                ).scenario(
                         SagaItemBuilder.builder()
                                .action(ActionBuilder.builder().component(billingService).method("createBilling").args("tuanh.net", 123))
                                .onBehaviour(NullPointerException.class, ActionBuilder.builder().component(billingService).method("Rollback_createBilling_ArrayIndexOutOfBoundsException").args())
                                .onBehaviour(RollBackException.class, ActionBuilder.builder().component(billingService).method("Rollback_createBilling_RollBackException").args())
                ).scenario(
                        SagaItemBuilder.builder()
                                .action(ActionBuilder.builder().component(paymentService).method("createPayment").args())
                                .onBehaviour(NullPointerException.class, ActionBuilder.builder().component(paymentService).method("Rollback_createPayment_NullPointException").args())
                                .onBehaviour(RollBackException.class, ActionBuilder.builder().component(paymentService).method("Rollback_createPayment_RollBackException").args())
                );
        dtc.commit(scenarios);
        return "ok";
    }
}
Salin selepas log masuk

3.3 Keputusan

Apabila saya membuat pengecualian dalam membuat pengebilan.

public String createBilling(String name, int number) {
    throw new NullPointerException();
}
Salin selepas log masuk

Keputusan

2024-08-24T14:21:45.445+07:00 INFO 19736 --- [demo] [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2024-08-24T14:21:45.450+07:00 INFO 19736 --- [demo] [main] com.example.demo.DemoApplication : Started DemoApplication in 1.052 seconds (process running for 1.498)
2024-08-24T14:21:47.756+07:00 INFO 19736 --- [demo] [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-08-24T14:21:47.756+07:00 INFO 19736 --- [demo] [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2024-08-24T14:21:47.757+07:00 INFO 19736 --- [demo] [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
Prepare order for tuanh.net with order id 123 
Prepare billing for tuanh.net with order id 123 
Rollback createBilling because RollBackException
Rollback prepareBilling because RollBackException
Rollback prepareOrder because RollBackException
Salin selepas log masuk

Lihat Repositori GitHub saya

4. Kesimpulan

Ringkasnya, corak Saga menyediakan penyelesaian yang mantap untuk mengurus urus niaga yang diedarkan dengan memecahkannya kepada langkah yang lebih kecil dan boleh diurus. Pilihan antara Koreografi dan Orkestrasi bergantung pada keperluan khusus dan seni bina sistem anda. Koreografi menawarkan gandingan dan daya tahan yang longgar, manakala Orkestrasi menyediakan kawalan terpusat dan pemantauan yang lebih mudah. Dengan mereka bentuk sistem anda dengan teliti dengan corak Saga, anda boleh mencapai ketekalan, ketersediaan dan fleksibiliti dalam seni bina perkhidmatan mikro teragih anda.

Jangan ragu untuk mengulas di bawah jika anda mempunyai sebarang soalan atau memerlukan penjelasan lanjut tentang melaksanakan corak Saga dalam sistem anda!

Baca siaran lebih lanjut di : Bagaimana Corak Saga Menyelesaikan Isu Transaksi Teragih: Kaedah dan Contoh Dunia Nyata

Atas ialah kandungan terperinci Cara Corak Saga Menyelesaikan Isu Transaksi Teragih: Kaedah dan Contoh Dunia Nyata. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!