دوامة: إطار عمل PHP / Go عالي الأداء



مرحبا يا هابر. اسمي أنطون تيتوف ، مدير تقنية المعلومات في Spiral Scout. اليوم أود أن أخبركم عن فيل PHP الخاص بنا. أو بالأحرى ، حول الإصدار الثاني من إطار عمل PHP / Go مفتوح المصدر بالكامل - Spiral .

Spiral هو إطار مكوّن متكامل مكوّن من قبل شركتنا لأكثر من أحد عشر عامًا ويخدم مئات المشاريع الحقيقية. تعتمد حزمة البرامج على العديد من المكتبات المفتوحة والمملوكة ، بما في ذلك RoadRunner و Cycle ORM .

يتوافق الإطار مع معظم توصيات PSR ، ويدعم MVC ويعمل بشكل أسرع 5-10 مرات من Laravel / Symfony.

إذا لم يسبق لك أن سمعت عن Spiral وتتساءل ما هو إطار عمل PHP / Go وأين ذهبت النسخة الأولى ، فمرحباً بك في القط.

حول الإطار


بدأ التطور الحلزوني في 2008/2009 كنواة محمولة للتطبيقات المستقلة. في عام 2010 ، شكلنا في النهاية شركة الاستعانة بمصادر خارجية ومنذ ذلك الحين شاركنا في تحسين مجموعتنا.

ونتيجة لذلك ، تم تحويل الإطار إلى مجموعة من المكونات المستقلة ، توحدها طبقة تكامل مشتركة.

الإعلان العام الوحيد عن الإصدار الأول حدث على Reddit في عام 2017 وأوضح لنا أنه ، من الناحية الفنية ، لم تتغلب Spiral بالفعل على المنافسين في ذلك الوقت. أخذنا في الاعتبار التعليقات التي تلقيناها وتجربة المشاريع الأكثر تعقيدًا وأكملنا الإصدار الثاني في مايو 2019.

بعد عام من التوثيق وتشغيل المشاريع الحقيقية ، نحن على استعداد لتقديم هذا التطوير للجمهور.

الفرق الرئيسي بين Spiral 2.0 من الجيل السابق للإطار ، وربما من جميع أطر PHP الموجودة الأخرى هو خادم التطبيقات المتكامل RoadRunner ، بالإضافة إلى تكييف البنية لنموذج تنفيذ طويل الأمد (وضع خفي).

الهجين رانثيم


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



يمكنك قراءة المزيد عن نموذج هجين هنا و هنا .

الخادم مسؤول عن جزء البنية التحتية بالكامل: HTTP / FastCGI ، التواصل مع وسطاء قائمة الانتظار ، GRPC ، WebSockets ، Pub / Sub ، ذاكرة التخزين المؤقت ، المقاييس ، إلخ.

لا تختلف كتابة التعليمات البرمجية لـ Spiral عمليا عن التعليمات البرمجية لأي إطار آخر. ومع ذلك ، يجب أن تكون مبادئ SOLID أكثر مسؤولية. إذا تسبب تخزين بيانات المستخدم في ذاكرة التخزين المؤقت سابقًا في ألم شديد لمراجعة الرمز فقط ، فلن يعمل هذا الرمز ببساطة بشكل صحيح.

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

كان إطلاق النار في الساق أكثر صعوبة ، فقد تقرر التخلي عن نظام الأحداث والخطافات وتحديد أولويات العمل مع مكدس المكالمة في شكل صريح.

يوفر الإطار مجموعة من الأدوات ، مثل عمليات إغلاق IoC والبرامج الوسيطة ومفسدين طبقة المجال والخدمات الثابتة.

$container->runScope(
    [UserContext::class => $user],
    function () use ($container) {
        dump($container->get(UserContext::class);
    }
);

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

أداء


في فئة المجموعة الكاملة من أطر عمل PHP ، تتنافس Spiral بشكل أساسي مع التجميعات القائمة على Swoole والعديد من الإطارات الصغيرة.



تتوفر المعايير الكاملة هنا و هنا .

بالنسبة لنا ، الأداء هو أحد الآثار الجانبية للعمارة المختارة. نحن على يقين من أنه مع التكوين الصحيح واستبدال PSR-7 بتجريد أخف ، يمكن زيادة الإنتاجية بنسبة 50-80 ٪ (وهذا يثبت من خلال مثال ubiquity-roadrunner ).

Swoole. Swoole RoadRunner, PHP ++. , Go. , .

RoadRunner ( ), , Windows.

Spiral Swoole, !

PSR-*


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

تتم كتابة طبقة HTTP للإطار مع مراعاة معايير PSR-7/15/17 ، ويمكنك تغيير جهاز التوجيه بأمان وتنفيذ الرسائل وما إلى ذلك.

يمكن استخدام معظم مكتبات الإطار خارج الإطار. على سبيل المثال ، يعمل RoadRunner بشكل رائع مع Symfony و Laravel ، وسيتوفر Cycle ORM في Yii3.

مكونات الخادم


بالإضافة إلى مكونات PHP ، يتضمن تجميع RoadRunner عدة مكتبات مكتوبة بلغة Golang. يمكن إدارة معظم خدمات الخادم من داخل PHP.

على وجه الخصوص ، هناك مكون قائمة انتظار يدعم العمل مع الوسطاء AMQP و Amazon SQS و Beanstalk. يمكن للمكتبة إيقاف أي عدد من قوائم الانتظار الواردة وإعادة توصيلها وتوزيعها بشكل صحيح على العديد من العمال.

خارج الصندوق ، هناك مراقبة على بروميثيوس ونقاط الفحص الصحي ، وإعادة التشغيل الساخن وحد الذاكرة. للمشاريع الموزعة ، هناك خادم وعميل GRPC. هناك مآخذ للويب ، يمكن تفويضها من تطبيق PHP وتوصيلها بناقل pub-sub (في الذاكرة أو على Redis). محركات القيمة الرئيسية قيد الاختبار حاليًا.

INFO[0154] 10.42.5.55:51990 Ok {2.28ms} /images.Service/GetFiles
INFO[0155] 10.42.3.95:50926 Ok {11.3ms} /images.Service/GetFiles
INFO[0156] 10.42.5.55:52068 Ok {3.60ms} /images.Service/GetFiles
INFO[0158] 10.42.5.55:52612 Ok {2.30ms} /images.Service/GetFiles
INFO[0166] 10.42.5.55:52892 Ok {2.23ms} /images.Service/GetFiles
INFO[0167] 10.42.3.95:49938 Ok {2.37ms} /images.Service/GetFiles
INFO[0169] 10.42.5.55:52988 Ok {2.22ms} /images.Service/GetFiles




قابلية التنقل


لا يتطلب إطار العمل PHP-FPM و NGINX. وجميع مكونات Golang لها برامج تشغيل للعمل بدون تبعيات خارجية. وبالتالي ، يمكنك استخدام قوائم الانتظار وقوائم الويب والمقاييس دون تثبيت الوسطاء أو البرامج الخارجية.

./spiral serve -v -d

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

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

دورة ORM


كما ORM خارج منطقة الجزاء يأتي دورة ORM . محرك مخطط البيانات هذا مشابه جدًا في الوظيفة للعقيدة ، ولكنه مختلف جدًا من الناحية المعمارية.

مثل Doctrine ، يمكن للدورة أن تعمل مع نماذج المجال الخالصة ، وتولد بشكل مستقل عمليات الترحيل وترتيب المفاتيح الأجنبية. يمكن وصف مخطط رسم الخرائط بواسطة كود أو جمعه من التعليقات التوضيحية. ولكن بدلاً من DQL ، يتم استخدام منشئو طلبات البحث الكلاسيكية.

//    
//        
$users = $orm->getRepository(User::class)
    ->select()
    ->where('active', true)
    ->load('orders', [
        'method' => Select::SINGLE_QUERY, // force LEFT JOIN
        'load'   => function($query) {
            $query->where('paid', true)->orderBy('timeCreated', 'DESC');
        }
    ])
    ->fetchAll();

$transaction = new Transaction($orm);

foreach($users as $user) {
    $transaction->persist($user);
}

$transaction->run();

تعمل الدورة بشكل أسرع من العقيدة في التحديدات ، ولكنها أبطأ في الإصرار. يدعم المحرك الاستعلامات المعقدة مع العديد من استراتيجيات التحميل ، وفئات الوكيل ، والتضمينات ، ويوفر المعاملات المحمولة بدلاً من EntityManager العالمي.

يمكن سماع المزيد من التفاصيل في PHP Russia 2020 .

"الميزة" الرئيسية لـ ORM هي القدرة على تغيير تعيين البيانات والروابط في وقت التشغيل. بكلمات بسيطة ، يمكنك السماح للمستخدمين بتعريف مخطط البيانات بشكل مستقل (يدعم DBAL الاستبطان والإعلان عن مخططات قاعدة البيانات).

يمكنك قراءة المزيد حول مقارنة Cycle و Eloquent و Doctrine 2 هنا .

النماذج الأولية السريعة


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

للاندماج في IDE ، هناك نظام النماذج الأولية السريعة. باستخدام الصورة النموذجية السحرية ، يمكنك الوصول بسرعة إلى النصائح في IDE.



تبحث السمة تلقائيًا عن مستودعات ORM والمكونات القياسية ويمكنها فهرسة خدماتك عن طريق التعليقات التوضيحية. تحت غطاء المحرك ، يتم استخدام طريقة __get السحرية ، والتي تضمن لك مراجعة سريعة للرمز.

ما عليك سوى تشغيل الأمر `php app.php prototype: injection -r` ، وسيزيل نظام النماذج الأولية كل السحر تلقائيًا:

namespace App\Controller;

use App\Database\Repository\UserRepository;
use Spiral\Views\ViewsInterface;

class HomeController
{
    /** @var ViewsInterface */
    private $views;

    /** @var UserRepository */
    private $users;

    /**
     * @param ViewsInterface $views
     * @param UserRepository $users
     */
    public function __construct(ViewsInterface $views, UserRepository $users)
    {
        $this->users = $users;
        $this->views = $views;
    }

    public function index()
    {
        return $this->views->render('profile', [
            'user' => $this->users->findByName('Antony')
        ]);
    }
}

تحت غطاء المحرك ، يتم استخدام PHP-Parser .

سلامة


نظرًا لأن معظم تطبيقاتنا تم تطويرها لقطاع B2B ، يجب أخذ الأمور الأمنية على محمل الجد.

ستتوفر لك مكونات التحقق من الطلبات المعقدة (طلب الفلاتر) و CSRF والتشفير (بناءً على نزع فتيل / تشفير php ). يدعم العمل مع ملفات تعريف الارتباط والجلسة مكافحة العبث وتوقيع البيانات على جانب الخادم.

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

أو جميع أنواع الرموز المميزة في نفس الوقت ، إذا كان الشرط "يربط SAML / SSO / 2FA بشكل عاجل!" :(

يتم تنفيذ إذن الوصول من خلال مكون RBAC مع بعض التحسينات التي تسمح بالتشغيل في وضع DAC و ABAC. هناك دعم للعديد من الأدوار والتعليقات التوضيحية لحماية طرق التحكم ونظام القواعد.

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

محرك القالب


إذا كنت تحب Twig فقط - يمكنك التمرير عبر هذا القسم ، فقط قم بتثبيت الامتداد واستخدام الأدوات المألوفة. :)

يأتي من خارج الصندوق محرك قالب Stemper ، أو بالأحرى ، مكتبة لإنشاء ترميز DSL الخاص بك. على وجه الخصوص ، هناك lexer كامل ، العديد من القواعد ، محلل والوصول إلى AST (على غرار محلل نيكيتا PHP-Parser).

من الممكن تحليل عدة قواعد نحوية متداخلة. لذلك ، على سبيل المثال ، يمكنك استخدام توجيهات Laravel Blade وترميز DSL الخاص بك (في شكل علامات HTML) داخل القالب نفسه. اتضح شيء مثل مكونات الويب على جانب الخادم.

نستخدم هذا المكون لوصف واجهات معقدة باستخدام بدائية وقواعد بسيطة.

<extends:admin.layout.tabs title="User Information"/>
<use:bundle path="admin/bundle"/>

<ui:tab id="info" title="Information">
  User, {{ $user->name }}
</ui:tab>

<ui:tab id="data" title="User Settings">
  <grid:table for={{ $user->settings }}>
    <grid:cell title="Key">{{ $key }}</grid:cell>
    <grid:cell title="Value">{{ $value }}</grid:cell>
  </grid:table>
</ui:tab>

وهو يدعم الهروب التلقائي مع دعم السياق (على سبيل المثال ، إخراج PHP داخل كتلة JS يحول البيانات تلقائيًا إلى JSON) ، خرائط المصدر للعمل مع الأخطاء. يتم تجميع القوالب في كود PHP المحسن ومن ثم يتم إعطاؤها مباشرة من ذاكرة التطبيق.
يمكن أن يعمل Stemper بشكل كامل مع DOM للمستند (وإن كان أبطأ إذا كنت تستخدم أدوات متخصصة).

تطوير الإطار


بالطبع ، خلال أكثر من عقد من تطوير هذه الأداة ، ارتكبنا العديد من الأخطاء. تم تصحيح بعضها لإصدار الإصدار الثاني ، بينما يتطلب الجزء الآخر مراجعة لمفهوم بعض المكونات.

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

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

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

نحن ببساطة لم نتمكن من ترجمة أشياء كثيرة من الإصدار الأول. الخطط هي استعادة حزم ODM ، لوحة الإدارة ، كتابة ملف تعريف جيد ، إلخ.

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


يمكنك تسجيل الدخول إلى مجتمع Discord الصغير الخاص بنا . قناة برقية .


يتم توزيع جميع التعليمات البرمجية بموجب ترخيص MIT وليس لديها أي قيود للاستخدام التجاري.

شكرا للانتباه. آمل أن تكون أدواتنا مفيدة في مشاريعك!

All Articles