Window.pipedriveLeadboosterConfig = { القاعدة: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', الإصدار: 2, } ؛(الدالة () { var w = نافذة إذا كان (w.LeadBooster) { console.warn('LeadBooster موجود بالفعل') } وإلا { { w.LeadBooster = { q: [], على: دالة (ن، ح) { { هذا.q.push({ t: 'o'، n: n، n: n، h: h }) }, الزناد: الدالة (n) { هذا.q.push({ t: 't'، n: n: n }) }, } } })() سيمفوني: اتصالات الخدمات المصغرة الجزء الأول - The Codest
The Codest
  • نبذة عنا
  • الخدمات
    • تطوير البرمجيات
      • تطوير الواجهة الأمامية
      • تطوير الواجهة الخلفية
    • Staff Augmentation
      • مطورو الواجهة الأمامية
      • مطورو الواجهة الخلفية
      • مهندسو البيانات
      • مهندسو السحابة
      • مهندسو ضمان الجودة
      • أخرى
    • استشاري
      • التدقيق والاستشارات
  • الصناعات
    • التكنولوجيا المالية والمصرفية
    • E-commerce
    • أدتك
    • التكنولوجيا الصحية
    • التصنيع
    • الخدمات اللوجستية
    • السيارات
    • إنترنت الأشياء
  • القيمة مقابل
    • CEO
    • CTO
    • مدير التوصيل
  • فريقنا
  • دراسات الحالة
  • اعرف كيف
    • المدونة
    • اللقاءات
    • ندوات عبر الإنترنت
    • الموارد
الوظائف تواصل معنا
  • نبذة عنا
  • الخدمات
    • تطوير البرمجيات
      • تطوير الواجهة الأمامية
      • تطوير الواجهة الخلفية
    • Staff Augmentation
      • مطورو الواجهة الأمامية
      • مطورو الواجهة الخلفية
      • مهندسو البيانات
      • مهندسو السحابة
      • مهندسو ضمان الجودة
      • أخرى
    • استشاري
      • التدقيق والاستشارات
  • القيمة مقابل
    • CEO
    • CTO
    • مدير التوصيل
  • فريقنا
  • دراسات الحالة
  • اعرف كيف
    • المدونة
    • اللقاءات
    • ندوات عبر الإنترنت
    • الموارد
الوظائف تواصل معنا
السهم الخلفي العودة إلى الوراء
2022-06-28
تطوير البرمجيات

سيمفوني: اتصالات الخدمات المصغرة الجزء الأول

The Codest

سيباستيان لوكزاك

PHP قائد وحدة PHP

اقرأ الجزء الأول من سلسلة PHP المخصصة لاتصالات الخدمات المصغرة في إطار عمل Symfony والطريقة الأكثر شيوعًا - اتصال AMQP باستخدام RabbitMQ.

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

كيف يمكننا، من خلال إنشاء خدمات مصغرة مستقلة، أن نجعلها تتبادل المعلومات مع بعضها البعض؟

هذه المقالة هي الأولى في سلسلة من المنشورات عن اتصالات الخدمات المصغرة في سيمفوني إطار العمل ويغطي الطريقة الأكثر شيوعًا - اتصال AMQP باستخدام RabbitMQ.

الهدف

إنشاء تطبيقين مستقلين وتحقيق الاتصال بينهما باستخدام ناقل الرسائل فقط.

المفهوم

لدينا تطبيقان وهميان ومستقلان ومستقلان:
* التطبيق1:: التي ترسل إشعارات بالبريد الإلكتروني والرسائل النصية القصيرة إلى الموظفين
* التطبيق2:: مما يتيح لك إدارة عمل الموظفين وتعيين المهام لهم.

نريد إنشاء نظام حديث وبسيط يتم بموجبه إسناد العمل إلى موظف في التطبيق2 سيرسل إشعارًا إلى العميل باستخدام التطبيق1. على الرغم من المظاهر، فإن الأمر بسيط للغاية!

التحضير

لغرض هذه المقالة، سنستخدم أحدث إصدار من سيمفوني (الإصدار 6.1 وقت كتابة هذا المقال) وأحدث إصدار من PHP (8.1). في بضع خطوات بسيطة للغاية سننشئ بيئة Docker محلية عاملة مع اثنين من الخدمات المصغرة. كل ما تحتاجه هو
* جهاز كمبيوتر يعمل,
* تثبيت Docker + بيئة تركيب Docker Compose
* وتكوينه محليًا سيمفوني CLI وبعض وقت الفراغ

بيئة وقت التشغيل

سنستخدم إمكانيات Docker كأداة للمحاكاة الافتراضية للتطبيقات والحاويات. دعونا نبدأ بإنشاء شجرة دليل، وهو إطار عمل لـ تطبيقات سيمفوني، ووصف البنية التحتية لبيئاتنا باستخدام docker-compose.yml الملف.

قرص مضغوط ~
 mkdir microservices-in-symfony
 cd microservices-in-symfony
 تطبيق سيمفوني جديد1
 تطبيق سيمفوني جديد2
 المس docker-compose.yml docker-compose.yml

لقد أنشأنا دليلين لتطبيقين منفصلين من تطبيقات Symfony وأنشأنا دليلين فارغين docker-compose.yml لبدء تشغيل بيئتنا.

دعونا نضيف الأقسام التالية إلى docker-compose.yml file:

الإصدار: '3.8'

الخدمات:
التطبيق 1:
اسم الحاوية: app1
البناء: app1/.
إعادة التشغيل: عند الفشل
envfile: app1/.env
البيئة:
اسم التطبيق: app1
تيتي: صحيح
stdinopen: صحيح

التطبيق2:
اسم الحاوية: app2
بناء: التطبيق2/.
إعادة التشغيل: عند الفشل
ملف envfile: app2/.env
البيئة:
اسم التطبيق: app2
تيتي: صحيح
stdinopen: صحيح

rabbitmq:
احتواء الاسم: rabbitmq
الصورة: rabbitmq: إدارة
المنافذ
- 15672:15672
- 5672:5672
البيئة:
- RABBITMQDEFAULTUSER=المستخدم
- RABBITMQDEFAULT_PASS=كلمة المرور

المصدر الكود متاح مباشرة: thecodest-co/microservices-in-symfony/blob/main/docker-compose.yml

لكن انتظر، ماذا حدث هنا؟ بالنسبة لأولئك غير المعتادين على Docker، قد يبدو ملف التكوين أعلاه غامضًا، ولكن الغرض منه بسيط للغاية. باستخدام Docker Compose نحن نبني ثلاث "خدمات":

  • التطبيق 1: وهو عبارة عن حاوية لتطبيق Symfony الأول
  • التطبيق 2: وهو الحاوية لتطبيق سيمفوني الثاني
  • rabbitmq: صورة تطبيق RabbitMQ كطبقة وسيطة للاتصالات

للتشغيل السليم، ما زلنا بحاجة إلى ملف إرساء الملفات التي هي مصدر بناء الصور. لذا دعونا ننشئها:

المس التطبيق1/ملف الإرساء
 المس التطبيق2/ملف الإرساء

يحتوي كلا الملفين على نفس البنية بالضبط ويبدوان كما يلي:

من php:8.1

نسخ - من=composer:latest /usr/bin/composer /usr/local/bin/composer
نسخ . /التطبيق/
WORKDIR /التطبيق/

إضافة https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/lusr/local/bin/

قم بتشغيل chmod +x /usr/local/bin/in/stall-php-extensions &&&مزامنة &&
install-php-extensions amqp

تشغيل apt-get update
&&Apt-get install -y libzip-dev wget -no-install-recommends
&&أبت-get clean
&&& rm -rf -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

قم بتشغيل docker-php-ext-install zip;

CMD bash -c "cd /app && Composer install && php -a"

الكود المصدري متاح مباشرة: /thecodest-co/microservices-in-symfony/blob/main/app1/Dockerfile

يتم استخدام الملف أعلاه من قبل Docker Compose لبناء حاوية من صورة PHP 8.1 مع تثبيت Composer وامتداد AMQP. بالإضافة إلى ذلك، فإنه يقوم بتشغيل PHP intepreter في وضع الإلحاق للحفاظ على تشغيل الحاوية في الخلفية.

يجب أن يبدو الدليل وشجرة الملفات الآن كما يلي:

.
 「التطبيق 1
 │علمي ─ ملف الإرساء
 | (...) # هيكل تطبيق سيمفوني
 ̄ ̄التطبيق2
 | ملف الإرساء | ملف الإرساء
 | (...) # هيكل تطبيق سيمفوني
 │تعريف ─ docker-compose.yml

خدمة سيمفوني المصغرة الأولى

لنبدأ بـ التطبيق1 الدليل والتطبيق الأول.
في مثالنا، هو تطبيق يستمع ويستهلك الرسائل من قائمة الانتظار المرسلة من التطبيق2 كما هو موضح في المتطلبات:

تعيين وظيفة لعامل في التطبيق2 سيرسل إشعارًا إلى العميل

لنبدأ بإضافة المكتبات المطلوبة. AMQP مدعومة أصلاً لـ سيمفوني/مسنجر التمديد. سنقوم أيضًا بتثبيت مونولوج/مناجاة/مناجاة لتتبع سجلات النظام لتسهيل تحليل سلوك التطبيق.

cd app1/
 symfony composer composer reqqp amqp ampq-messenger monolog

بعد التثبيت، تمت إضافة ملف إضافي ضمن التكوين/الحزم/مسنجر.yaml. وهو ملف تهيئة لمكون Symfony Messenger ولا نحتاج إلى التكوين الكامل.
استبدله بملف YAML أدناه:

إطار العمل:
رسول:
# قم بإلغاء ربط هذا (والنقل الفاشل أدناه) لإرسال الرسائل الفاشلة إلى هذا النقل للتعامل معها لاحقًا.
# فشل_النقل: فشل

    النقل:
        # https://symfony.com/doc/current/messenger.html#transport-configuration
        الرسائل_الخارجية:
            dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
            خيارات:
                الإعداد التلقائي: خطأ
                تبادل:
                    الاسم: الرسائل
                    النوع: مباشر
                    مفتاح_النشر_التوجيه_الافتراضي: من_خارجي
                قوائم الانتظار:
                    الرسائل:
                        مفاتيح الربط: [من_خارجي]

الكود المصدري متاح مباشرة: thecodest-co/microservices-in-symfony/blob/main/app1/config/packages/messenger.yaml

يستخدم Symfony Messenger للتواصل المتزامن وغير المتزامن في تطبيقات Symfony. وهو يدعم مجموعة متنوعة من وسائل النقلأو مصادر حقيقة طبقة النقل. في مثالنا، نستخدم امتداد AMQP الذي يدعم نظام طابور انتظار الأحداث RabbitMQ.

يُعرّف التكوين أعلاه وسيلة نقل جديدة باسم الرسائل_الخارجيةالذي يشير إلى ماسنجر_ترانسبورت_دسن متغير البيئة ويحدد الاستماع المباشر على الرسائل قناة في ناقل الرسائل. في هذه المرحلة، قم أيضًا بتحرير التطبيق1/.env ملف وإضافة عنوان النقل المناسب.

"في

(...)
MESSENGERTRANSPORTDSN=amqp://user:password@rabbitmq:5672//messages
(...)
"
بعد إعداد إطار عمل التطبيق وتهيئة المكتبات، حان الوقت لتنفيذ منطق العمل. نحن نعلم أن تطبيقنا يجب أن يستجيب لتعيين مهمة إلى عامل. نحن نعلم أيضًا أن تعيين مهمة في نظام التطبيق 2 يغير حالة المهمة. لذا دعنا ننشئ نموذجًا يحاكي تغيير الحالة ونحفظه في مسارapp1/Message/StatusUpdate.php':
الصورة
 {
الدالة العامة __construct(سلسلة محمية $status){} {}

دالة عامة getStatus(): سلسلة
{
    إرجاع $this->status;
}

}

الكود المصدري متاح مباشرة: /thecodest-co/microservices-in-symfony/blob/main/app1/apprc/Message/StatusUpdate.php

ما زلنا بحاجة إلى فئة ستنفذ منطق العمل عندما تتلقى خدمتنا المصغرة الحدث أعلاه من قائمة الانتظار. لذا دعنا ننشئ فئة معالج الرسائل في app1/Handler/StatusUpdateHandler.php المسار:

الصورة
 استخدام PsrLogLogLoggerInterface;
استخدم SymfonyComponentComponentMessengerAttributeAsMessageHandler;

[معالج الرسائل]

صنف StatusUpdateHandler
{
دالة عامة __إنشاء(
LoggerInterface المحمي $logger,
) {}

الدالة العامة __invoke(StatusUpdate $statusUpdate): باطل
{
    $statusDescription = $statusUpdate->getStatus();

    $this-> مدون-> تحذير('APP1: {STATUS_UPDATE} - '.$statusDescription);

    // بقية منطق العمل، أي إرسال البريد الإلكتروني إلى المستخدم
    // $This->>emailService->email()
}

}

الكود المصدري متاح مباشرة: /thecodest-co/microservices-in-symfony/blob/main/app1/apprc/Handler/StatusUpdateHandler.php

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

خدمة سيمفوني المصغرة الثانية

سنلقي نظرة على التطبيق2 والدليل الثاني تطبيق سيمفوني. تتمثل فكرتنا في إرسال رسالة إلى قائمة الانتظار عندما يتم تعيين عامل مهمة في النظام. لذا دعنا نقوم بتهيئة سريعة لـ AMQP ونجعل خدمتنا المصغرة الثانية تبدأ في النشر تحديث الحالة الأحداث إلى ناقل الرسائل.

تثبيت المكتبات هو نفسه تماماً كما في التطبيق الأول.

قرص مضغوط ...
 cd app2/
 symfony composer composer reqqp amqp ampq-messenger monolog

دعونا نتأكد من أن التطبيق2/.env يحتوي الملف على إدخال DSN صالح ل RabbitMQ:

(...)
 MESSENGER_TRANSPORT_DSN=amqp://user:password@rabbitmq:5672//messages
 (...)

كل ما تبقى هو تكوين سيمفوني ماسنجر في التطبيق2/config/packages/messenger.yaml file:

إطار العمل:
رسول:
# قم بإلغاء تحديد هذا (والنقل الفاشل أدناه) لإرسال الرسائل الفاشلة إلى هذا النقل للتعامل معها لاحقًا.
# فشل_النقل: فشل

    النقل:
        # https://symfony.com/doc/current/messenger.html#transport-configuration
        غير متزامن
            dsn: '%env(MESSENGER_TRANSPORT_DSN)%'

    توجيه
        # توجيه الرسائل إلى وسائل النقل
        'AppMessageStatusStatusUpdate': غير متزامن

كما ترى، يشير تعريف النقل هذه المرة مباشرةً إلى غير متزامن ويحدد التوجيه في شكل إرسال تحديث الحالة رسالة إلى شبكة DSN المكوّنة. هذا هو المجال الوحيد للتكوين، كل ما تبقى هو إنشاء المنطق وطبقة التنفيذ لقائمة انتظار AMQP. لهذا سننشئ التوأم معالج تحديث الحالة StatusUpdateHandler و تحديث الحالة الفصول الدراسية في التطبيق2.

الصورة
 استخدام PsrLogLogLoggerInterface;
استخدم SymfonyComponentComponentMessengerAttributeAsMessageHandler;

[معالج الرسائل]

صنف StatusUpdateHandler
{
دالة عامة __إنشاء(
خاص للقراءة فقط LoggerInterface $logger,
) {}

الدالة العمومية __invoke(StatusUpdate $statusUpdate): باطل
{
    $statusDescription = $statusUpdate->getStatus();

    $this->مدون-> تحذير('APP2: {STATUS_UPDATE} - '.$statusDescription);

    ## منطق العمل، أي إرسال إشعار داخلي أو وضع بعض الأنظمة الأخرى في قائمة الانتظار
}

}
الصورة
 {
الدالة العامة __construct(سلسلة محمية $status){} {}

دالة عامة getStatus(): سلسلة
{
    إرجاع $this->status;
}

}


الكود المصدري متاح مباشرة: /thecodest-co/microservices-in-symfony/blob/main/app2/apprc/Message/StatusUpdate.php

أخيرًا، كل ما يجب القيام به هو إنشاء طريقة لإرسال رسالة إلى ناقل الرسائل. سنقوم بإنشاء أمر سيمفوني لهذا

الصورة
استخدام SymfonyComponentConsoleConsoleAttributeAsCommand;
استخدام SymfonyComponentConsoleCommandCommandCommand;
استخدام SymfonyComponentComponentConsoleConsoleInputInputInterface;
استخدم SymfonyComponentConsoleConsoleConputOutputOutputInterface؛
استخدام SymfonyComponentComponentMessengerMessageBusBusInterface;

[كأمر(

الاسم: "التطبيق: إرسال"

)]
صنف SendStatusCommand يمتد الأمر
{
إنشاء دالة عامة (خاص للقراءة فقط MessageBusInterface $messageBus، سلسلة $name = لاغية)
{
الأصل::إنشاء($name);
}

الدالة المحمية execute(InputInterface $input، OutputInterface $output): int
{
    $status = "تم تعيين العامل X إلى Y";

    $is-> ناقل الرسائل->dispatch(
        الرسالة: جديد StatusUpdate($status)
    );

    إرجاع الأمر::نجاح;
}

}

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

هذا كل شيء - كل ما تبقى هو تشغيل بيئة Docker Compose ونرى كيف تتصرف تطبيقاتنا.

تشغيل البيئة والاختبار

باستخدام Docker Compose، سيتم إنشاء الحاويات التي تحتوي على تطبيقينا وتشغيلها كمثيلين منفصلين، وستكون الطبقة الوسيطة الوحيدة هي أرنبمك الحاوية وتنفيذ ناقل الرسائل الخاص بنا.

من الدليل الجذر للمشروع، لنقم بتشغيل الأوامر التالية:

<cd ../ # تأكد من أنك في الدليل الرئيسي
 docker-compose up --build -d

يمكن أن يستغرق هذا الأمر بعض الوقت، حيث أنه يبني حاويتين منفصلتين مع PHP 8.1 + AMQP ويسحب صورة RabbitMQ. كن صبورًا. بعد بناء الصور يمكنك إطلاق الأمر من التطبيق2 وإرسال بعض الرسائل في قائمة انتظار.

docker exec -it app2 php bin/console app/console app:send

يمكنك القيام بذلك بقدر ما تستطيع. طالما لا يوجد المستهلك لن تتم معالجة رسائلك. بمجرد أن تقوم بتشغيل التطبيق1 واستهلك جميع الرسائل التي ستظهر على شاشتك.

docker exec -it app1 php bin/console messenger:consume -vv external_messages
الصورة

الكامل كود المصدر إلى جانب ملف README في مستودعنا العام The Codest Github

الملخص

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

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

راية التعاون

مقالات ذات صلة

تطوير البرمجيات

PHP 8.2: ما الجديد؟

الإصدار الجديد من PHP على الأبواب. ما هي التطبيقات الجديدة التي يجب أن تعرفها؟ راجع هذه المقالة لمعرفة ذلك!

The Codest
سيباستيان لوكزاك PHP قائد وحدة PHP
تطوير البرمجيات

تطوير PHP. مكون وحدة تحكم سيمفوني - نصائح وحيل

تم إنشاء هذه المقالة بهدف أن تظهر لك النصائح والحيل الأكثر فائدة واسترجاعًا حول تطوير وحدة تحكم Symfony Console.

The Codest
سيباستيان لوكزاك PHP قائد وحدة PHP

اشترك في قاعدة معارفنا وابقَ على اطلاع على آخر المستجدات في قطاع تكنولوجيا المعلومات.

    نبذة عنا

    The Codest - شركة دولية لتطوير البرمجيات لها مراكز تقنية في بولندا.

    المملكة المتحدة - المقر الرئيسي

    • المكتب 303 ب، 182-184 شارع هاي ستريت نورث E6 2JA
      لندن، إنجلترا

    بولندا - مراكز التكنولوجيا المحلية

    • مجمع مكاتب فابريتشنا المكتبي، أليجا
      بوكوجو 18، 31-564 كراكوف
    • سفارة الأدمغة، كونستروكتورسكا
      11, 02-673 02-673 وارسو، بولندا

      The Codest

    • الصفحة الرئيسية
    • نبذة عنا
    • الخدمات
    • دراسات الحالة
    • اعرف كيف
    • الوظائف
    • القاموس

      الخدمات

    • استشاري
    • تطوير البرمجيات
    • تطوير الواجهة الخلفية
    • تطوير الواجهة الأمامية
    • Staff Augmentation
    • مطورو الواجهة الخلفية
    • مهندسو السحابة
    • مهندسو البيانات
    • أخرى
    • مهندسو ضمان الجودة

      الموارد

    • حقائق وأساطير حول التعاون مع شريك خارجي لتطوير البرمجيات
    • من الولايات المتحدة الأمريكية إلى أوروبا: لماذا تقرر الشركات الأمريكية الناشئة الانتقال إلى أوروبا؟
    • مقارنة مراكز تطوير التكنولوجيا في الخارج: تك أوفشور أوروبا (بولندا)، آسيان (الفلبين)، أوراسيا (تركيا)
    • ما هي أهم التحديات التي تواجه CTOs ومديري تكنولوجيا المعلومات؟
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • شروط استخدام الموقع الإلكتروني

    جميع الحقوق محفوظة © 2025 بواسطة The Codest. جميع الحقوق محفوظة.

    arArabic
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek arArabic