قيد الاختبار حاليًا: سيتم فتح كود GitHub عند الاكتمال.

Ticketing Java / Kotlin مكتبة

GitHub Maven Central

يوفر TicketBroker واحد ثلاثة أساليب استدعاء معًا — كوروتينات Kotlin تستخدم acquire، وJava غير المتزامنة تستخدم acquireAsync (CompletableFuture)، وJava الحاجبة تستخدم acquireBlocking. أيًّا كان الأسلوب المستخدم، فهو نفس الوسيط ونفس الاتصالات.

المستودع

xml
kts

مثال

مثال أساسي

أنشئ broker واحدًا عند بدء التطبيق وشاركه. wait=0 محاولة فورية واحدة بلا queue، والحد 255 ثانية؛ lease بين 1 و250 ثانية. آخر minimum-work budget إلزامي وقد يكون صفرًا. تُقرب كسور الثانية إلى الأعلى، وتُرفض القيم خارج المجال أو budget أكبر من lease بعد التطبيع قبل الإرسال، بلا clamp.

Kotlin (coroutines):

kotlin
val broker = TicketBroker.connect("127.0.0.1:5225")
broker.waitReady(Duration.ofSeconds(5))

val ticket = broker.acquire("key", Duration.ofSeconds(5), Duration.ofSeconds(30), Duration.ofSeconds(2))
val token = ticket.token
// In the same DB transaction: verify/update token high-water and perform the business write.
ticket.release()

Java (blocking):

java
TicketBroker broker = TicketBroker.connect("127.0.0.1:5225");
broker.waitReadyBlocking(Duration.ofSeconds(5));

try (Ticket ticket = broker.acquireBlocking(
        "key", Duration.ofSeconds(5), Duration.ofSeconds(30), Duration.ofSeconds(2))) {
    long token = ticket.getToken();
    // In the same DB transaction: verify/update token high-water and perform the business write.
}

الـclose/drop التلقائي release محدود best-effort. استخدم explicit release API إذا كانت النتيجة مهمة. يجب أن يعمل token كـfencing للـDB write المحمي في transaction نفسها.

سلوك يجدر معرفته

  • إذا أمكن إرسال request byte واحد، فضياع الاستجابة النهائية ينتج Indeterminate. لا إعادة تلقائية لـA بالـowner نفسه؛ ولا يدخل caller الـcritical section.
  • Cancel قبل send هو unsent؛ وبعد possible-send يغلق session. إن parsed grant token بالتزامن، يحاول compensating exact-token release محدودًا.
  • M وكل E والاستجابة malformed/oversized/unknown هي session-fatal. يصبح acquire possible-send غير المحسوم Indeterminate.
  • B رفض capacity مؤكد ويعود فورًا. لا retry داخلي؛ يستطيع caller بدء acquire جديد بـowner جديد بعد application backoff.
  • يحاول explicit/compensating release الـexact token فقط خلال 5 ثوان مطلقة من call/enqueue. R نجاح، N absent/not current؛ غياب استجابة نهائية error لا نجاح مفترض.
  • لا يُعاد Ticket إلا مع زمن محافظ موجب يكفي work budget. العمل فوق 250 ثانية unsupported قبل الإرسال.
  • Token DB fencing إلزامي: في transaction نفسها ارفض token <= stored_high_water، وحدّث high-water ونفذ business write قبل commit/rollback ثم release.

خيارات الأمان (رمز · TLS)

جميع الخيارات اختيارية. يجب أن يطابق token قيمة client_tokens في الخادم، ولـ TLS أربعة أنماط: إيقاف / مخزن الثقة الافتراضي للنظام / CA محدد / تخطي التحقق (للاختبار فقط).

kotlin
val broker = TicketBroker.builder()
    .addrs("10.0.0.1:5225", "10.0.0.2:5225", "10.0.0.3:5225")
    .token("123")
    .tls(TlsMode.SystemRoots)
    // .tls(TlsMode.Ca("ca.crt"))
    // .tls(TlsMode.InsecureSkipVerify)
    .connect()

في Java، استخدم المصانع الساكنة TlsMode.systemRoots() / TlsMode.ca("ca.crt") / TlsMode.insecureSkipVerify().

يمكنك إنشاء إعدادات الرمز وTLS والعنقود على صفحة نشر خادم Ticketing.

خيوط Spring الافتراضية

عند تشغيل Spring MVC على الخيوط الافتراضية استخدم دوال *Blocking في Java وKotlin معًا. لا يستطيع المتحكّم غير المعلّق (non-suspend) استدعاء acquire، لذا يبقى acquireBlocking هو المسار الطبيعي في Kotlin أيضًا. فهي لا تمرّ عبر الكوروتينات وتتوقّف (park) عند الردّ فقط، فيبقى الخيط الحامل حرًّا.

kotlin
@RestController
class OrderController(private val broker: TicketBroker) {

    @PostMapping("/orders/{id}")
    fun place(@PathVariable id: String): String {
        broker.acquireBlocking("order-$id", Duration.ofSeconds(5), Duration.ofSeconds(30)).use {
            // المقطع الحرج
        }
        return "ok"
    }
}

يعمل التحرير الخلفي الخاص بـ close() (‏try-with-resources / use) افتراضيًا على منفّذ خيوط افتراضية، فلا يصطفّ خلف تجمّع ثابت الحجم. مرّر منفّذك الخاص لاستخدام منفّذ Spring.

kotlin
TicketBroker.builder()
    .addrs("127.0.0.1:5225")
    .executor(applicationTaskExecutor)
    .connect()

مع WebFlux أو متحكّمات الكوروتينات، تابع استخدام دوال suspend.