سؤال

لقد كنت أعمل على هذا الأمر منذ بضعة أيام، وقد وجدت العديد من الحلول ولكن لم يكن أي منها بسيطًا أو خفيف الوزن بشكل لا يصدق.المشكلة هي في الأساس ما يلي:لدينا مجموعة مكونة من 10 أجهزة، كل منها يقوم بتشغيل نفس البرنامج على منصة ESB متعددة الخيوط.يمكنني التعامل مع مشكلات التزامن بين سلاسل الرسائل الموجودة على نفس الجهاز بسهولة إلى حد ما، ولكن ماذا عن التزامن على نفس البيانات على أجهزة مختلفة؟

يتلقى البرنامج بشكل أساسي طلبات لتغذية بيانات العميل من شركة إلى أخرى عبر خدمات الويب.ومع ذلك، قد يكون العميل موجودًا أو لا يكون موجودًا بعد على النظام الآخر.إذا لم يحدث ذلك، فإننا نقوم بإنشائه عبر طريقة خدمة الويب.لذلك يتطلب الأمر نوعًا من الاختبار والضبط، لكنني بحاجة إلى إشارة من نوع ما لمنع الأجهزة الأخرى من التسبب في ظروف السباق.لقد واجهت مواقف من قبل حيث تم إنشاء عميل بعيد مرتين لعميل محلي واحد، وهو أمر غير مرغوب فيه حقًا.

الحلول التي تعاملت معها من الناحية المفاهيمية هي:

  1. استخدام نظام الملفات المشترك المتسامح مع الأخطاء لإنشاء ملفات "القفل" التي سيتم التحقق منها بواسطة كل جهاز اعتمادًا على العميل

  2. استخدام جدول خاص في قاعدة البيانات الخاصة بنا، وقفل الجدول بأكمله من أجل إجراء "اختبار وتعيين" لسجل القفل.

  3. استخدام Terracotta، وهو برنامج خادم مفتوح المصدر يساعد في التوسع، ولكنه يستخدم نموذج المحور والتحدث.

  4. استخدام EHCache للنسخ المتماثل المتزامن لـ "الأقفال" الموجودة في الذاكرة.

لا أستطيع أن أتخيل أنني الشخص الوحيد الذي واجه هذا النوع من المشاكل.ظمىمئءنؤى؟هل قمت بطهي شيء ما في المنزل أو هل لديك منتج مفضل من طرف ثالث؟

هل كانت مفيدة؟

المحلول

قد ترغب في التفكير في استخدام هازلكاست الأقفال الموزعة.سوبر لايت وسهل.

java.util.concurrent.locks.Lock lock = Hazelcast.getLock ("mymonitor");
lock.lock ();
try {
// do your stuff
}finally {
   lock.unlock();
}

Hazelcast - قائمة الانتظار الموزعة، الخريطة، المجموعة، القائمة، القفل

نصائح أخرى

نحن نستخدم التيراكوتا، لذا أود التصويت لصالح ذلك.

لقد كنت أتابع Hazelcast ويبدو أنها تقنية واعدة أخرى، لكن لا يمكنني التصويت لها لأنني لم أستخدمها، ومع العلم أنها تستخدم نظامًا قائمًا على P2P عند سماعها، فأنا حقًا لا أثق بها على نطاق واسع احتياجات التحجيم.

لكنني سمعت أيضًا عن Zookeeper، الذي خرج من Yahoo، ويتحرك تحت مظلة Hadoop.إذا كنت مغامرًا وتجرب بعض التقنيات الجديدة، فهذا حقًا يحمل الكثير من الأمل نظرًا لأنه ضعيف جدًا ومتوسط، مع التركيز على التنسيق فقط.تعجبني الرؤية والوعد، على الرغم من أنها قد تكون خضراء للغاية.

يعتبر Terracotta أقرب إلى النموذج "المتدرج" - حيث تتحدث جميع تطبيقات العميل مع مصفوفة خادم Terracotta (والأهم من ذلك بالنسبة للحجم أنها لا تتحدث مع بعضها البعض).يمكن تجميع مصفوفة خادم Terracotta من حيث الحجم والتوافر (معكوسة من أجل التوفر ومخططة من أجل الحجم).

على أي حال، كما تعلم، يمنحك Terracotta القدرة على التعبير عن التزامن عبر المجموعة بنفس الطريقة التي تفعلها في JVM واحد باستخدام POJO syncronized/wait/notify أو باستخدام أي من الأوليات java.util.concurrent مثل ReentrantReadWriteLock و CyclicBarrier و AtomicLong و FutureTask وما إلى ذلك.

هناك الكثير من الوصفات البسيطة التي توضح استخدام هذه البدائيات في كتاب الطبخ الطين.

كمثال، سأقوم بنشر مثال ReentrantReadWriteLock (لاحظ أنه لا يوجد إصدار "Terracotta" من القفل - كل ما عليك فعله هو استخدام Java ReentrantReadWriteLock العادي)

import java.util.concurrent.locks.*;

public class Main
{
    public static final Main instance = new Main();
    private int counter = 0;
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(true);

    public void read()
    {
        while (true) {
            rwl.readLock().lock();
                try {
                System.out.println("Counter is " + counter);
            } finally {
                rwl.readLock().unlock();
            }
            try { Thread.currentThread().sleep(1000); } catch (InterruptedException ie) {  }
        }
    }

    public void write()
    {
        while (true) {
            rwl.writeLock().lock();
            try {
               counter++;
               System.out.println("Incrementing counter.  Counter is " + counter);
            } finally {
                 rwl.writeLock().unlock();
            }
            try { Thread.currentThread().sleep(3000); } catch (InterruptedException ie) {  }
        }
    }

    public static void main(String[] args)
    {
        if (args.length > 0)  {
            // args --> Writer
            instance.write();
        } else {
            // no args --> Reader
            instance.read();
        }
    }
}

أوصي باستخدام ريديسون.وهي تنفذ أكثر من 30 بنية وخدمة بيانات موزعة بما في ذلك java.util.Lock.مثال الاستخدام:

Config config = new Config();
config.addAddress("some.server.com:8291");
Redisson redisson = Redisson.create(config);

Lock lock = redisson.getLock("anyLock");
lock.lock();
try {
    ...
} finally {
   lock.unlock();
}

redisson.shutdown();

كنت أنصحك باستخدام memcached كوحدة تخزين RAM سريعة جدًا وموزعة لحفظ السجلات؛ولكن يبدو أن EHCache هو مشروع مشابه ولكنه أكثر تركيزًا على جافا.

أي منهما هو الحل الأمثل، طالما أنك متأكد من استخدام التحديثات الذرية (يدعمها memcached، ولا تعرف شيئًا عن EHCache).إنه الحل الأكثر قابلية للتطوير إلى حد بعيد.

باعتبارها نقطة بيانات ذات صلة، تستخدم Google "Chubby"، وهو نظام تخزين قفل موزع سريع يعتمد على ذاكرة الوصول العشوائي (RAM) كجذر للعديد من الأنظمة، من بينها BigTable.

لقد قمت بالكثير من العمل باستخدام Coherence، مما سمح بعدة طرق لتنفيذ القفل الموزع.كان النهج الساذج هو طلب قفل نفس الكائن المنطقي على جميع العقد المشاركة.من حيث التماسك، كان هذا بمثابة قفل مفتاح في ذاكرة التخزين المؤقت المنسوخة.لا يتم توسيع هذا الأسلوب بشكل جيد نظرًا لأن حركة مرور الشبكة تزداد خطيًا عند إضافة العقد.كانت الطريقة الأكثر ذكاءً هي استخدام ذاكرة التخزين المؤقت الموزعة، حيث تكون كل عقدة في المجموعة مسؤولة بشكل طبيعي عن جزء من مساحة المفتاح، لذا فإن قفل المفتاح في ذاكرة التخزين المؤقت هذه يتطلب دائمًا الاتصال بعقدة واحدة على الأكثر.يمكنك طرح منهجك الخاص بناءً على هذه الفكرة، أو الأفضل من ذلك، الحصول على التماسك.إنها حقًا مجموعة أدوات قابلية التوسع لأحلامك.

أود أن أضيف أن أي آلية قفل قائمة على شبكة متعددة العقد نصف لائقة يجب أن تكون متطورة بشكل معقول لتعمل بشكل صحيح في حالة حدوث أي فشل في الشبكة.

لست متأكدًا مما إذا كنت أفهم السياق بأكمله ولكن يبدو أن لديك قاعدة بيانات واحدة تدعم هذا؟لماذا لا تستفيد من قفل قاعدة البيانات:إذا كان إنشاء العميل عبارة عن إدراج واحد، فيمكن أن يكون هذا البيان وحده بمثابة قفل لأن قاعدة البيانات سترفض إدراجًا ثانيًا من شأنه أن ينتهك أحد القيود الخاصة بك (على سبيل المثال.حقيقة أن اسم العميل فريد على سبيل المثال).

إذا لم تكن عملية "إدراج عميل" ذرية وكانت عبارة عن مجموعة من البيانات، فسأقدم (أو أستخدم) عملية INSERT أولية تنشئ بعض السجلات الأساسية البسيطة التي تحدد عميلك (مع قيود UNIQUEness اللازمة) ثم أقوم بكل ما يلي إدراجات/تحديثات أخرى في نفس المعاملة.مرة أخرى، ستهتم قاعدة البيانات بالاتساق وأي تعديلات متزامنة ستؤدي إلى فشل أحدها.

لقد قمت بإنشاء خدمة RMI بسيطة بطريقتين:القفل والإفراج.تأخذ كلتا الطريقتين مفتاحًا (يستخدم نموذج البيانات الخاص بي UUIDs كـ pk لذا كان هذا أيضًا مفتاح القفل).

يعد RMI حلاً جيدًا لهذا لأنه مركزي.لا يمكنك القيام بذلك باستخدام EJBs (خاصة في المجموعة لأنك لا تعرف الجهاز الذي ستصل إليه مكالمتك).بالإضافة إلى أنه سهل.

عملت معي.

إذا كان بإمكانك إعداد موازنة التحميل الخاصة بك بحيث يتم دائمًا تعيين طلبات عميل واحد إلى نفس الخادم، فيمكنك التعامل مع ذلك عبر المزامنة المحلية.على سبيل المثال، استخدم معرف العميل الخاص بك رقم 10 للعثور على العقد العشرة التي تريد استخدامها.

حتى إذا كنت لا ترغب في القيام بذلك في الحالة العامة، يمكن لعقدك أن توكيل بعضها البعض لهذا النوع المحدد من الطلب.

بافتراض أن المستخدمين لديك موحدون بدرجة كافية (أيإذا كان لديك الكثير منها) ولا تتوقع ظهور نقاط فعالة حيث يتم تحميل عقدة واحدة بشكل زائد، فيجب أن يظل هذا الحجم جيدًا.

قد تفكر أيضًا كاتشيونيكس للأقفال الموزعة.على عكس أي شيء آخر مذكور هنا، يدعم Cacheonix أقفال ReadWrite مع تصعيد القفل من القراءة إلى الكتابة عند الحاجة:

ReadWriteLock rwLock = Cacheonix.getInstance().getCluster().getReadWriteLock();
Lock lock = rwLock.getWriteLock();
try {
  ...
} finally {
  lock.unlock();
}

الكشف الكامل:أنا مطور Cacheonix.

نظرًا لأنك تتصل بالفعل بقاعدة بيانات، فقبل إضافة جزء آخر من البنية التحتية، قم بإلقاء نظرة عليها JdbcSemaphore, ، انها سهلة الاستخدام:

JdbcSemaphore semaphore = new JdbcSemaphore(ds, semName, maxReservations);
boolean acq = semaphore.acquire(acquire, 1, TimeUnit.MINUTES);
if (acq) {
 // do stuff
 semaphore.release();
} else {
  throw new TimeoutException();
}

إنه جزء من spf4j مكتبة.

في الماضي، كنا نستخدم "خادم قفل" محددًا على الشبكة للتعامل مع هذا الأمر.بليه.

قد يكون لدى خادم قاعدة البيانات لديك موارد مخصصة للقيام بهذا النوع من الأشياء.يحتوي MS-SQL Server على أقفال تطبيقات يمكن استخدامها من خلال sp_getapplock/sp_releaseapplock إجراءات.

لقد قمنا بتطوير مصدر مفتوح وإطار مزامنة موزع، وقد تم حاليًا تنفيذ قفل DistributedReentrantLock وDistributedReentrantReadWrite، ولكن لا يزالان في مرحلة الاختبار وإعادة البناء.في معماريتنا، يتم تقسيم مفاتيح القفل إلى مجموعات وكل عقدة مسؤولة عن عدد معين من المجموعات.لذلك، من أجل نجاح طلبات القفل، لا يوجد سوى طلب شبكة واحد فقط.نحن نستخدم أيضًا فئة AbstractQueuedSynchronizer كحالة قفل محلية، لذلك تتم معالجة جميع طلبات القفل الفاشلة محليًا، مما يقلل بشكل كبير من حركة مرور الشبكة.نحن نستخدم JGroups (http://jgroups.org) للتواصل الجماعي وهسيان للتسلسل.

لمزيد من التفاصيل، يرجى مراجعة http://code.google.com/p/vitrit/.

من فضلك أرسل لي ملاحظاتك القيمة.

كمران

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top