قيد الاختبار حاليًا: سيتم فتح كود 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.