اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'خادم'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 9 نتائج

  1. سنشرح في هذا المقال آليات الاتصال الدائم المستمر مع الخادم وهو الاتصال الذي يُنشَأ فيه اتصال مستمر بين الخادم والعميل (غالبًا متصفح الويب) يجري فيه تبادل الطلبيات والردود آنيًا، وتنحصر آلية تنفيذ الاتصال الدائم تلك في ثلاثة أنواع: آلية الاستطلاع المفتوح Long polling وآلية مقابس الويب عبر البروتوكول WebSocket وآلية الأحداث المرسلة EventSource، سنناقش كل واحدة بالتفصيل على حدة. آلية الاستطلاع آلية الجس والاستطلاع polling هي الطريقة الأبسط للمحافظة على اتصال ثابت مع الخادم، ولا تتطلب أي بروتوكولات خاصة مثل WebSocket، أو أي أحداث من طرف الخادم، كما أنها سهلة الإنجاز وكافية في الكثير من الحالات. الاستطلاع الدوري Regular polling الطريقة الأبسط للحصول على معلومات من الخادم هي الاستطلاع الدوري، بإرسال طلبات إلى الخادم لسؤاله عن أي معلومات جديدة على فترات زمنية منتظمة (مثلًا: كل 10 ثوان)، وعند الاستجابة ينتبه الخادم إلى اتصال العميل به، ثم يرسل حزمةً من الرسائل التي تلقاها حتى اللحظة، سيعمل الأمر كما هو متوقع، لكن مع بعض الجوانب السلبية، منها: تمرَّر الرسائل بتأخير زمني قد يصل إلى 10 ثوان (الزمن الفاصل بين الطلبات). سيُغرَق الخادم بالطلبات كل 10 ثوان حتى لو غادر المستخدم الموقع أو استغرق في النوم، وهذا حمل كبير عليه من منظور الأداء. لهذا قد تكون هذه المقاربة جيدةً عندما تكون الخدمة بسيطةً، لكنها وبشكل عام تحتاج إلى التحسين. الاستطلاع المفتوح Long polling وهي طريقة أفضل للاستطلاع أو الجس من حالة الخادم، وتتميز بسهولة تنفيذها، وقدرتها على تسليم الرسائل دون تأخير. حيث: يُرسَل الطلب إلى الخادم. لا يغلق الخادم الاتصال حتى تكون لديه رسالة لتسليمها. عندما تظهر الرسالة، يستجيب الخادم للطلب بهذه الرسالة. يرسل المتصفح طلبًا جديدًا مباشرة. إن أساس هذه الطريقة هو إرسال المتصفح للطلب وإبقاء الاتصال مع الخادم قيد الانتظار، ولا يهيّئ المتصفح الاتصال من جديد حتى يتسلم الاستجابة، فإن فُقد الاتصال نتيجة خطأ ما، فسيرسل المتصفح طلبًا آخر مباشرةً. تمثل الشيفرة التالية الدالة subscribe التي ترسل الطلبات المفتوحة من جهة العميل: async function subscribe() { let response = await fetch("/subscribe"); if (response.status == 502) { // 502 خطأ تجاوز الوقت المسموح للاتصال // يحدث عند بقاء الاتصال مفتوحًا لفترة طويلة جدًا // ويغلقه الخادم لذا يجب إعادة الاتصال await subscribe(); } else if (response.status != 200) { // إظهار الخطأ showMessage(response.statusText); // إعادة الاتصال خلال ثانية await new Promise(resolve => setTimeout(resolve, 1000)); await subscribe(); } else { // الحصول على الرسالة وإظهارها let message = await response.text(); showMessage(message); // استدعاء التابع من جديد await subscribe(); } } subscribe(); يرسل التابع subscribe طلب إحضار، وينتظر الاستجابة ليعالجها عندما تصل، ثم يستدعي نفسه مجددًا. مثال اختباري لنظام محادثة بسيط إليك تطبيق محادثة اختباريًا يمكنك تنزيله وتشغيله محليًا إذا كنت على دراية باستخدام Node.JS وتثبيت الوحدات البرمجية اللازمة: ملف شيفرة العميل "browser.js": // POSTإرسال رسالة، طلب function PublishForm(form, url) { function sendMessage(message) { fetch(url, { method: 'POST', body: message }); } form.onsubmit = function() { let message = form.message.value; if (message) { form.message.value = ''; sendMessage(message); } return false; }; } // تلقي الرسائل باستخدام الاستطلاع المفتوح function SubscribePane(elem, url) { function showMessage(message) { let messageElem = document.createElement('div'); messageElem.append(message); elem.append(messageElem); } async function subscribe() { let response = await fetch(url); if (response.status == 502) { // تجاوز الوقت المسموح // يحدث عندما يبقى الاتصال مفتوحًا لفترة طويلة // لا بد من إعادة الاتصال await subscribe(); } else if (response.status != 200) { // Show Error showMessage(response.statusText); // إعادة الاتصال خلال ثانية await new Promise(resolve => setTimeout(resolve, 1000)); await subscribe(); } else { // الحصول على الرسالة let message = await response.text(); showMessage(message); await subscribe(); } } subscribe(); } ملف شيفرة الواجهة الخلفية "server.js": let http = require('http'); let url = require('url'); let querystring = require('querystring'); let static = require('node-static'); let fileServer = new static.Server('.'); let subscribers = Object.create(null); function onSubscribe(req, res) { let id = Math.random(); res.setHeader('Content-Type', 'text/plain;charset=utf-8'); res.setHeader("Cache-Control", "no-cache, must-revalidate"); subscribers[id] = res; req.on('close', function() { delete subscribers[id]; }); } function publish(message) { for (let id in subscribers) { let res = subscribers[id]; res.end(message); } subscribers = Object.create(null); } function accept(req, res) { let urlParsed = url.parse(req.url, true); // مستخدم جديد يريد النتائج if (urlParsed.pathname == '/subscribe') { onSubscribe(req, res); return; } // إرسال رسالة if (urlParsed.pathname == '/publish' && req.method == 'POST') { // POSTقبول req.setEncoding('utf8'); let message = ''; req.on('data', function(chunk) { message += chunk; }).on('end', function() { publish(message); // النشر للجميع res.end("ok"); }); return; } // البقية ثابتة fileServer.serve(req, res); } function close() { for (let id in subscribers) { let res = subscribers[id]; res.end(); } } // ----------------------------------- if (!module.parent) { http.createServer(accept).listen(8080); console.log('Server running on port 8080'); } else { exports.accept = accept; if (process.send) { process.on('message', (msg) => { if (msg === 'shutdown') { close(); } }); } process.on('SIGINT', close); } الملف الأساسي "index.html": <!DOCTYPE html> <script src="browser.js"></script> All visitors of this page will see messages of each other. <form name="publish"> <input type="text" name="message" /> <input type="submit" value="Send" /> </form> <div id="subscribe"> </div> <script> new PublishForm(document.forms.publish, 'publish'); // random url parameter to avoid any caching issues new SubscribePane(document.getElementById('subscribe'), 'subscribe?random=' + Math.random()); </script> وستظهر النتيجة بالشكل التالي: مجال الاستخدام تعمل طريقة الاستطلاع المفتوح بصورة ممتازة عندما تكون الرسائل نادرةً، لكن عندما تتكرر الرسائل بكثرة، فسيكون منحني (طلب-استجابة) المرسوم في الأعلى على هيئة أسنان المنشار، ولأن لكل رسالة طلبًا مستقلًا له ترويسات وآلية استيثاق خاصة، فمن الأفضل استخدام طرق أخرى مثل: Websocket أوServer Sent Events، التي سنشرحها في الفقرات التالية. استخدام البروتوكول WebSocket يزوّدنا هذا البروتوكول الموصوف في المعيار RFC 6455 بطريقة لتبادل البيانات بين الخادم والمتصفح عبر اتصال ثابت، حيث تمرَّر البيانات إلى كلا الطرفين على شكل حزم دون قطع الاتصال أو استخدام طلبات HTTP إضافية، وتظهر الفائدة الكبيرة لاستخدامه عند وجود خدمات تتطلب التبادل المستمر للبيانات، مثل ألعاب الشبكة وأنظمة التجارة العاملة في الزمن الحقيقي وغيرها. مثال بسيط سنحتاج إلى كائن مقبس اتصال جديد new WebSocket لفتح اتصال websocket باستخدام بروتوكول نقل خاص هو ws في العنوان: let socket = new WebSocket("ws://javascript.info"); كما يمكن استخدام البروتوكول المشفّر //:wss، فهو مشابه للبروتوكول HTTPS لكن مع websockets. ينبغي الاستماع إلى الأحداث التي تطرأ على مقبس الاتصال Socket بعد تأسيسه مباشرةً، وهي أربعة أحداث: open: عند تأسيس الاتصال. message: عند استقبال البيانات. error: عند وقوع خطأ يتعلق بالبروتوكول websocket. close: عند إغلاق الاتصال. ولإرسال أي بيانات يكفي استخدام الصيغة (socket.send(data، وإليك مثالًا: let socket = new WebSocket("wss://javascript.info/article/websocket/demo/hello"); socket.onopen = function(e) { alert("[open] Connection established"); alert("Sending to server"); socket.send("My name is John"); }; socket.onmessage = function(event) { alert(`[message] Data received from server: ${event.data}`); }; socket.onclose = function(event) { if (event.wasClean) { alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); } else { // كأن تلغى العملية أو فشل في الشبكة // 1006 قيمته في هذه الحالة عادة event.code alert('[close] Connection died'); } }; socket.onerror = function(error) { alert(`[error] ${error.message}`); }; لقد كتبنا شيفرة خادم تجريبي باستخدام Node.JS في الملف server.js لتشغيل الشيفرة في المثال السابق، وسيرد الخادم بالرسالة "Hello from server, John"، ثم ينتظر 5 ثوان ويقطع الاتصال. سترى إذًا تسلسل الأحداث open → message → close، والأمر بهذه البساطة فعليًا، لأنك تستخدم الآن البروتوكول WebSocket، لنتحدث الآن في أمور أكثر عمقًا. فتح اتصال مقبس ويب WebSocket يُنشئ الأمر (new WebSocket(url كائنًا جديدًا ويبدأ عملية الاتصال مباشرةً، سيسأل المتصفح الخادم خلال تأسيس الاتصال -عبر الترويسات- إذا كان يدعم البروتوكول Websocket، فإن كان الجواب نعم فسيستمر العمل به، وهو يختلف عن HTTP كليًا. وإليك مثالًا عن ترويسات المتصفح للطلب الذي يرسله الأمر (new WebSocket("wss://javascript.info/chat. GET /chat Host: javascript.info Origin: https://javascript.info Connection: Upgrade Upgrade: websocket Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== Sec-WebSocket-Version: 13 Origin: أصل الصفحة العميلة (https://javascript.info مثلًا)، فالكائنات WebSocket هي كائنات من أصول مختلطة بطبيعتها، حيث لا توجد أي قيود لاستخدامها، وطالما أنّ الخوادم القديمة لا تدعم هذا البروتوكول، فلن نواجه مشكلة توافق. تظهر أهمية الترويسة Origin في أنها تسمح للخادم بالاختيار إن كان سيتواصل مع الصفحة العميلة وفق هذا البروتوكول أم لا. Connection: Upgrade: يشير إلى رغبة العميل في تغيير البروتوكول. Upgrade: websocket: تدل أنّ البروتوكول المطلوب هو websocket. Sec-WebSocket-Key: مفتاح أمان عشوائي يولده المتصفح. Sec-WebSocket-Version: نسخة بروتوكول websocket، وهي حاليًا النسخة 13. ينبغي على الخادم أن يعيد الرمز 101 عندما يوافق على التحوّل إلى البروتوكول WebSocket: 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g= ستكون الترويسة Sec-WebSocket-Accept هي نفسها Sec-WebSocket-Key مسجلةً باستخدام خوارزمية خاصة، وسيستخدمها المتصفح للتأكد من توافق الاستجابة مع الطلب. بعد ذلك ستُنقل البيانات باستخدام بروتوكول WebSocket، وسنطلع على بنيتها (إطاراتها) قريبًا. الموسعات والبروتوكولات الفرعية هنالك أيضًا الترويستان Sec-WebSocket-Extensions التي تصف الموسّعات، وSec-WebSocket-Protocol التي تصف البروتوكولات الفرعية. فمثلًا: Sec-WebSocket-Extensions: deflate-frame: يعني أن المتصفح سيدعم ضغط البيانات، فالموسّع هو شيء يتعلق بنقل البيانات ووظيفة توسّع عمل البروتوكول، يرسل المتصفح الترويسة Sec-WebSocket-Extensions تلقائيًا مزودةً بقائمة الموسّعات المدعومة كلها. Sec-WebSocket-Protocol: soap, wamp: يعني أننا سننقل البيانات باستخدام أحد البروتوكولين الفرعيين SOAP أو WAMP (وهو اختصار للعبارة " WebSocket Application Messaging Protocol" أي "بروتوكول نقل الرسائل لتطبيق websocket"). تُسجل البروتوكولات الفرعية في IANA catalogue، أي أن هذه الترويسة الاختيارية تحدد صيغ البيانات التي سنستخدمها، وتُضبَط باستخدام المعامل الثاني للدالة البانية new WebSocket، فلو أردنا استخدام SOAP أو WAMP مثلًا، فسنضعهما في مصفوفة بالشكل التالي: let socket = new WebSocket("wss://javascript.info/chat", ["soap", "wamp"]); سيستجيب الخادم بقائمة من البروتوكولات والموسّعات التي يوافق على استخدامها، وإليك مثالًا: GET /chat Host: javascript.info Upgrade: websocket Connection: Upgrade Origin: https://javascript.info Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== Sec-WebSocket-Version: 13 Sec-WebSocket-Extensions: deflate-frame Sec-WebSocket-Protocol: soap, wamp وستكون الاستجابة: 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g= Sec-WebSocket-Extensions: deflate-frame Sec-WebSocket-Protocol: soap أي أن الخادم سيدعم الموسّع "deflate-frame" والبروتوكول الفرعي SOAP فقط. نقل البيانات يتكون الاتصال من إطارات "frames"، وهي أجزاء من البيانات يمكن إرسالها من كلا الطرفين وقد تكون من الأنواع التالية: "text": يحتوي بيانات نصيةً ترسلها الأطراف إلى بعضها. "binary data": يحتوي بيانات ثنائيةً تُرسلها الأطراف إلى بعضها. "ping/pong": تستخدَم للتحقق من الاتصال، ويرسلها الخادم إلى المتصفح الذي يرد عليها تلقائيًا. ستجد أيضًا "connection close" وبعض الإطارات الأخرى. ونعمل في بيئة المتصفح مع الإطارات النصية والثنائية. يمكن للتابع ()send. إرسال بيانات نصية أو ثنائية.، ويسمح استدعاء التابع (socket.send(body بأن تكون قيمة جسم الطلبbody بالتنسيق النصي أو الثنائي، بما في ذلك الكائن Blob وArrayBuffer وغيرهما، ولا حاجة لإجراء ضبط خاص بل تُرسَل البيانات كما هي. تأتي البيانات النصية على شكل "string" عندما نستقبل البيانات، ومع ذلك يمكننا الاختيار بين BlobوArrayBuffer للبيانات الثنائية.** وكذا ضبط ذلك باستخدام الخاصية socket.binaryType، التي تأخذ القيمة "blob" افتراضيًا، لذلك تأتي البيانات الثنائية ضمن الكائن Blob، الذي يمثل كائن بيانات ثنائية عالي المستوى ويتكامل مباشرةً مع المعرّفين <a>و<img> وغيرهما، ويمكن الانتقال إلى استخدام الكائن ArrayBuffer عند معالجة البيانات الثنائية والعمل مع البايتات بشكل منفصل. socket.binaryType = "arraybuffer"; socket.onmessage = (event) => { //أما أن يكون نصًا أو بيانات ثنائيةً event.data }; محدودية معدل النقل Rate limiting لنتخيل أن تطبيقنا قادر على توليد كمية كبيرة من البيانات، لكن المستخدم يعاني من اتصال بطيء بالانترنت. يمكن استدعاء التابع (socket.send(data مرات عدةً، لكن البيانات ستُخزّن مؤقتًا في الذاكرة وستُرسَل بالسرعة التي تسمح بها الشبكة فقط، عندها يمكن استخدام الخاصية socket.bufferedAmount التي تُخزّن حجم البيانات الموجودة في المخزن المؤقت في هذه اللحظة وتنتظر الظروف المناسبة لنقلها، ويمكن فحص الحجم المتبقي من البيانات للتأكد من أن المقبس Socket لا يزال متاحًا لمتابعة نقل البيانات. // افحص المقبس كل 100 ثانية وحاول إرسال البيانات // عندما يكون حجم البيانات المخزّنة التي لم تصل صفرًا setInterval(() => { if (socket.bufferedAmount == 0) { socket.send(moreData()); } }, 100); إغلاق الاتصال عندما يحاول أحد الأطراف إغلاق الاتصال (للمخدم والمتصفح الحق نفسه في ذلك)، يرسَل عادةً إطار إغلاق الاتصال "connection close" الذي يحتوي قيمةً عدديةً وسببًا نصيًا للإغلاق. إليك الصيغة اللازمة: socket.close([code], [reason]); code: رمز خاص لإغلاق اتصال WebSocket، وهو اختياري. reason: نص يصف سبب الإغلاق، وهو اختياري. يستقبل معالج الحدث close في الطرف الآخر الرمز والسبب: // الطرف الذي سيغلق الاتصال: socket.close(1000, "Work complete"); // الطرف الآخر socket.onclose = event => { // event.code === 1000 // event.reason === "Work complete" // event.wasClean === true (clean close) }; إليك القيم الأكثر شيوعًا للرموز: 1000: وهي القيمة الافتراضية، وتستخدم للإغلاق العادي (تستخدم إن لم يكن هناك رمز code). 1006: لا يمكن ضبط هذا الرمز يدويًا ويشير إلى فقد الاتصال. 1001: عند مغادرة أحد الأطراف، مثل: إغلاق الخادم أو مغادرة المتصفح للصفحة. 1009: الرسائل أضخم من أن تُعالج. 1011: خطأ في الخادم. يمكن الاطلاع على القائمة الكاملة ضمن المعيار RFC6455, §7.4.1. إنّ رموز WebSocket مشابهة نوعًا ما لرموز HTTP وتختلف في بعض النواحي، فأي رمز أقل من 1000 سيكون محجوزًا، وسيتولد خطأ عند استخدامه. // في حال فشل الاتصال socket.onclose = event => { // event.code === 1006 // event.reason === "" // event.wasClean === false (no closing frame) }; حالة الاتصال تتيح الخاصية socket.readyState إمكانية معرفة الوضع الحالي للاتصال وفقًا للقيم التي تأخذها: 0 متصل "CONNECTING": أي أن الاتصال يجري، ولم يُؤسَّس بعد. 1 مفتوح "OPEN": التواصل قائم. 2 يُغلق "CLOSING": يجري إغلاق الاتصال. 3 مُغلق "CLOSED": الاتصال مغلق. مثال تطبيق محادثة آنية لنراجع مثال تطبيق المحادثة باستخدام الواجهة البرمجية WebSocket API والوحدة البرمجية Node.js WebSocket module، سنركز على طرف العميل، وسيكون الخادم بسيطًا أيضًا. نحتاج إلى نموذج <form> لإرسال الرسائل ومعرِّف <div> لاستقبالها. <!-- message form --> <form name="publish"> <input type="text" name="message"> <input type="submit" value="Send"> </form> <!-- div with messages --> <div id="messages"></div> كما سنستخدم في جافاسكربت: فتح الاتصال. إرسال رسالة عن طريق النموذج (socket.send(message. وضع الرسالة المستقبلة ضمن div#messages. وإليك الشيفرة اللازمة: let socket = new WebSocket("wss://javascript.info/article/websocket/chat/ws"); // إرسال رسالة إلى النموذج document.forms.publish.onsubmit = function() { let outgoingMessage = this.message.value; socket.send(outgoingMessage); return false; }; // div#messagesإظهار الرسالة بعد وصولها في socket.onmessage = function(event) { let message = event.data; let messageElem = document.createElement('div'); messageElem.textContent = message; document.getElementById('messages').prepend(messageElem); } لن نتعمق بشيفرة الواجهة الخلفية، فهي خارج نطاق مقالتنا، لكننا سنستخدم هنا Node.js، ومع ذلك فالأمر ليس إلزاميًا، إذ أن للعديد من المنصات طرقها للتعامل مع WebSocket. تمثل الخطوات التالية خوارزمية عمل الواجهة الخلفية: إنشاء مجموعة من مقابس الاتصال ()clients = new Set. إضافة كل مقبس مقبول إلى المجموعة clients.add(socket)، وتهيئة مستمع message للحدث للحصول على الرسائل المتعلقة بالمقبس. عندما استقبال رسالة كرر عملية إرسالها إلى كل عميل. احذف مقابس العملاء عند قطع الاتصال (clients.delete(socket. const ws = new require('ws'); const wss = new ws.Server({noServer: true}); const clients = new Set(); http.createServer((req, res) => { // websocket سنعالج فقط اتصالات // websocket في مشروعنا الحقيقي ستوجد هنا شيفرة تعالج الطلبات بطرق أخرى غير wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect); }); function onSocketConnect(ws) { clients.add(ws); ws.on('message', function(message) { message = message.slice(0, 50); // 50سيكون الحجم الأكبر للرسالة هو for(let client of clients) { client.send(message); } }); ws.on('close', function() { clients.delete(ws); }); } إليك النتيجة: يمكن تنزيل التطبيق وتجربته محليًا على جهازك، ولا تنس تثبيت Node.js، ثم تثبيت WebSocket باستخدام الأمر npm install ws قبل تشغيل التطبيق. الأحداث المرسلة من قبل الخادم تشرح التوصيفات Server-Sent Events صنفًا مدمجًا هو EventSource، حيث يحافظ على الاتصال مع الخادم ويسمح باستقبال أحداث منه، وهو اتصال ثابت مثل WebSocket تمامًا لكن مع بعض الاختلافات بينهما: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } WebSocket EventSource ثنائي الاتجاه: إذ يمكن أن يتبادل الخادم والعميل الرسائل وحيد الاتجاه: فالخادم هو من يرسل الرسائل إلى العميل نصوص وبيانات ثنائية نصوص فقط بروتوكول websocket بروتوكول HTTP النظامي. يمثل EventSource طريقةً أقل قدرةً للاتصال مع الخادم بالموازنة مع WebSocket، إذًا لماذا نستخدمها؟ يعود السبب الرئيسي لاستخدامه في كونه أبسط، إذ لا نحتاج إلى القدرة الكبيرة لبروتوكول WebSocket في بعض التطبيقات، فتلقي بيانات من الخادم مثل رسائل محادثة أو لوائح أسعار أو أي شيء مشابه هو مجال EventSource، كما أنه يدعم إعادة الاتصال، وهو أمر لا بد من إنجازه يدويًا في WebSocket، ويستخدم كذلك بروتوكول HTTP وليس أي بروتوكول جديد. الحصول على الرسائل لا بد من إنشاء كائن جديد من الصنف EventSource للبدء باستقبال الرسائل، وذلك بتنفيذ الأمر (new EventSource(url، حيث سيتصل المتصفح بالعنوان url ويبقي الاتصال مفتوحًا ومنتظرًا الأحداث، وهنا ينبغي أن يستجيب الخادم برمز الحالة 200 وبالترويسة Content-Type: text/event-stream، وسيبقي الخادم بعدها قناة الاتصال مفتوحةً، وسيرسل الرسائل ضمنها بتنسيق خاص بالشكل التالي: data: Message 1 data: Message 2 data: Message 3 data: of two lines تأتي الرسالة بعد الكلمة :data بينما يعَد الفراغ الموجود بعد النقطتين اختياريًا. يفصَل بين الرسائل برمز السطر الجديد مضاعفًا n\n\ يمكننا عند الحاجة إلى إرسال رمز سطر جديد n\ أن نرسل مباشرةً الكلمة :data (كما في الرسالة الثالثة في المثال السابق)، لكن عمليًاP تًرسل الرسائل المركّبة مرمزةً بتنسيق JSON، وترمّز محارف الانتقال إلى سطر جديد بالشكل n\ ضمنها، فلا حاجة عندها للرسائل :data متعددة السطر. فمثلًا: data: {"user":"John","message":"First line\n Second line"} وهكذا يمكن أن نعد أن العبارة data: ستليها رسالة واحدة تمامًا. يولَّد الحدث message لكل من تلك الرسائل بالشكل التالي: let eventSource = new EventSource("/events/subscribe"); eventSource.onmessage = function(event) { console.log("New message", event.data); // سيطبع ثلاث رسائل في المثال السابق }; // or eventSource.addEventListener('message', ...) الطلبات ذات الأصل المختلط يدعم الصنف EventSource الطلبات ذات الأصل المختلط cross-origin requests كما يفعل fetch، إذ يمكننا استخدام أي عنوان URL: let source = new EventSource("https://another-site.com/events"); سيستقبل الخادم البعيد الترويسة Origin وينبغي عليه الإجابة بالترويسة Access-Control-Allow-Origin للمتابعة، ولتمرير الثبوتيات credentials لا بد من ضبط خيار إضافي هو withCredentials بالشكل التالي: let source = new EventSource("https://another-site.com/events", { withCredentials: true }); راجع فصل استخدام Fetch مع الطلبات ذات الأصول المختلطة للاطلاع على تفاصيل أكثر حول ترويسات الأصول المختلطة. إعادة الاتصال بعد إنشاء الكائن EventSource فسيتصل بالخادم مباشرةً ويعيد الاتصال في حال انقطاعه، وبالتالي لا حاجة لإجراء تدابير خاصة بهذا الأمر، وبطبيعة الحال يوجد تأخير زمني صغير لثوان معدودة افتراضيًا بين كل محاولتين لإعادة الاتصال، وهنا يمكن للخادم أن يضبط التأخير اللازم باستخدام الخاصية :retry في الاستجابة (مقدرًا بالميلي ثانية). retry: 15000 data: Hello, I set the reconnection delay to 15 seconds يمكن أن تأتي :retry مصحوبةً ببيانات أخرى أو برسالة وحيدة، وهكذا سيكون على الخادم انتظار الفترة المحددة قبل إعادة الاتصال، وقد ينتظر فترة أطول، فإذا علم المتصفح -عن طريق نظام التشغيل- أن الشبكة غير متصلة حاليًا مثلًا، فسينتظر حتى يعود الاتصال ثم يحاول ثانيةً. إذا أراد الخادم من المتصفح التوقف عن إعادة الاتصال، فعليه أن يرد برمز الحالة 204 (رمز HTTP). إذا أراد المتصفح إغلاق الاتصال، فعليه استدعاء التابع eventSource.close. let eventSource = new EventSource(...); eventSource.close(); إذا احتوت الاستجابة على ترويسة Content-Type خاطئةً فلن يعاد الاتصال، وكذلك إذا اختلف رمز حالة HTTP عن القيم 301 أو 307 أو 200 أو 204، فعندها سيُولد الحدث "error" ولن يعيد المتصفح الاتصال. معرف الرسالة الفريد Message id عندما ينقطع الاتصال جراء خلل في الشبكة، tلن يتأكد كلا الطرفين من كون الرسائل استلمَت أم لا، وحتى يُستأنَف الاتصال بشكل صحيح، فينبغي أن تحمل كل رسالة مُعرِّفًا مميزًا id مثل التالي: data: Message 1 id: 1 data: Message 2 id: 2 data: Message 3 data: of two lines id: 3 عندما يتلقى المتصفح الرسالة مع معرّفها :id فإنه: يضبط قيمة الخاصية eventSource.lastEventId على قيمة المعرِّف. يعيد الترويسة Last-Event-ID أثناء إعادة الاتصال مع قيمة المعرّف id، ليتمكن الخادم من إرسال الرسائل التالية إن وجدت. حالة الاتصال: الخاصية ReadyState يمتلك الكائن EventSource الخاصية readyState التي تحمل إحدى القيم الثلاث التالية: EventSource.CONNECTING = 0; // يتصل أو يعيد الاتصال EventSource.OPEN = 1; // متصل EventSource.CLOSED = 2; // الاتصال مغلق تكون قيمة هذه الخاصية عند إنشاء كائن جديد أو انقطاع الاتصال هي EventSource.CONNECTING دومًا (وهي تعادل 0)، ويمكن الاستعلام عن هذه الخاصية لمعرفة حالة الكائن EventSource. أنواع الأحداث يوّلِّد الكائن EventSource افتراضيًا ثلاثة أحداث: message: عند استلام رسالة، وتكون متاحةً باستخدام event.data. open: عندما يكون الاتصال مفتوحًا. error: عند عدم إمكانية تأسيس الاتصال، كأن يعيد الخادم رمز الحالة 500. فمثلًا: event: join data: Bob data: Hello event: leave data: Bob لمعالجة الأحداث السابقة لابد من استخدام مستمع الحدث addEventListener وليس onmessage: eventSource.addEventListener('join', event => { alert(`Joined ${event.data}`); }); eventSource.addEventListener('message', event => { alert(`Said: ${event.data}`); }); eventSource.addEventListener('leave', event => { alert(`Left ${event.data}`); }); مثال نموذجي كامل يرسل الخادم في هذا المثال الرسائل 1 و2 و3 ثم bye ويقطع الاتصال، بعدها يعيد المتصفح الاتصال تلقائيًا. شيفرة الخادم "server.js": let http = require('http'); let url = require('url'); let querystring = require('querystring'); let static = require('node-static'); let fileServer = new static.Server('.'); function onDigits(req, res) { res.writeHead(200, { 'Content-Type': 'text/event-stream; charset=utf-8', 'Cache-Control': 'no-cache' }); let i = 0; let timer = setInterval(write, 1000); write(); function write() { i++; if (i == 4) { res.write('event: bye\ndata: bye-bye\n\n'); clearInterval(timer); res.end(); return; } res.write('data: ' + i + '\n\n'); } } function accept(req, res) { if (req.url == '/digits') { onDigits(req, res); return; } fileServer.serve(req, res); } if (!module.parent) { http.createServer(accept).listen(8080); } else { exports.accept = accept; } شيفرة الملف "index.html": <!DOCTYPE html> <script> let eventSource; function start() { // "Start" عند ضغط الزر if (!window.EventSource) { // متصفح إنترنت إكسبلورر أو أي متصفح قديم alert("The browser doesn't support EventSource."); return; } eventSource = new EventSource('digits'); eventSource.onopen = function(e) { log("Event: open"); }; eventSource.onerror = function(e) { log("Event: error"); if (this.readyState == EventSource.CONNECTING) { log(`Reconnecting (readyState=${this.readyState})...`); } else { log("Error has occurred."); } }; eventSource.addEventListener('bye', function(e) { log("Event: bye, data: " + e.data); }); eventSource.onmessage = function(e) { log("Event: message, data: " + e.data); }; } function stop() { // when "Stop" button pressed eventSource.close(); log("eventSource.close()"); } function log(msg) { logElem.innerHTML += msg + "<br>"; document.documentElement.scrollTop = 99999999; } </script> <button onclick="start()">Start</button> Press the "Start" to begin. <div id="logElem" style="margin: 6px 0"></div> <button onclick="stop()">Stop</button> "Stop" to finish. وستظهر النتيجة بالشكل التالي: خلاصة تعرفنا في هذا الفصل على آليات إنشاء إتصال مستمر مع الخادم، فبدأ أولًا بعرض آلية الاستطلاع والجس وهي الطريقة الأبسط للمحافظة على اتصال ثابت مع الخادم، ولا تتطلب أي بروتوكولات أو أي أحداث خاصة من طرف الخادم، وهي طريقة سهلة الإنجاز وكافية في الكثير من الحالات. انتقلنا بعدها إلى شرح بروتوكول WebSocket والذي يُقدم طريقةً عصريةً لتأسيس اتصال ثابت بين المتصفح والخادم، له ميزات منها: لا توجد تقييدات للأصول المختلطة. مدعوم جيدًا من قبل المتصفحات. يمكنه إرسال نصوص وبيانات ثنائية واستقبالها. واجهته البرمجية بسيطة. التوابع المستعملة في هذا البروتوكول: socket.send(data)‎ socket.close([ code ], [reason])‎ أما الأحداث: open message error close لا تقدّم WebSocket بذاتها تقنيات لإعادة الاتصال أو الاستيثاق وغيرها من الآليات عالية المستوى، لذلك ستجد مكتبات للعميل والخادم تهتم بهذه النقاط، ويمكنك أيضًا تنفيذ شيفرتها يدويًا بنفسك. ولكي تتكامل WebSocket مع مشروع جاهز، جرت العادة أن يشغل المستخدم خادم WebSocket بالتوازي مع خادم HTTP الرئيسي ليتشاركا بقاعدة بيانات واحدة، حيث تُرسل طلبات WebSocket إلى النطاق الفرعي wss://ws.site.com الذي يقود إلى خادم WebSocket، بينما يقود العنوان https://site.com إلى خادم HTTP الرئيسي، كما توجد طرق أخرى للتكامل. عرضنا أخيرًا الآلية الثالثة للاتصال وهي عبر الكائن EventSource الذي يؤسس اتصالًا ثابتًا مع الخادم ويسمح له بإرسال الرسائل عبر قناة الاتصال، ويقدم: إعادة الاتصال التلقائي مع قيمة زمنية retry لإعادة الاتصال بعدها. معرّفات فريدة للرسائل "ids" لاستئناف الأحداث من آخر معرّف أرسِل مع الترويسة Last-Event-ID أثناء إعادة الاتصال. حالة الاتصال من خلال الخاصية readyState. تجعل هذه المزايا الكائن EventSource بديًلا قيمًا للكائن WebSocket، الذي يعمل في مستوىً أدنى ويفتقر إلى الميزات السابقة، والتي يمكن إنجازها يدويًا بالطبع. يعَد الكائن EventSource في الكثير من التطبيقات الحقيقية قادرًا كفاية على إنجاز المطلوب، وتدعمه جميع المتصفحات الحديثة، بينما لا يدعمه IE، وإليك صيغة استخدامه: let source = new EventSource(url, [credentials]); للوسيط الثاني خيار وحيد ممكن هو: { withCredentials: true }، ويسمح بإرسال ثبوتيات طلبات الأصول المختلطة. إن عوامل الأمان المتعلقة باستخدام طلبات الأصول المختلطة للكائن EventSourceمشابهة للاتصال fetch وغيره من طرق الاتصال عبر الشبكات. خاصيات الكائن EventSource: readyState: حالة الاتصال القائم EventSource.CONNECTING (=0)‎ EventSource.OPEN (=1)‎ EventSource.CLOSED (=2)‎ lastEventId: آخر معرّف استقبله المتصفح، وسيرسله أثناء إعادة الاتصال مع الترويسة Last-Event-ID. وتوابع EventSource فتتمثل بالتابع: ()close: يغلق الاتصال. أما أحداث EventSource فهي: message: عند استلام رسالة، وتكون متاحةً باستخدام event.data. open: عندما يكون الاتصال مفتوحًا. error: في حال وقع خطأ، بما في ذلك فقدان الاتصال أو الأخطاء القاتلة، يمكننا التحقق من الخاصية readyState للتأكد من وجود محاولات لإعادة الاتصال. يمكن للخادم اختيار اسم مخصص لحدث ما من خلال :event، وينبغي معالجة هذه الأحداث من خلال مستمع حدث addEventListener وليس باستخدام <on<event. رأينا من أجل تنسيق استجابة الخادم أن الخادم يرسل الرسائل مفصولةً عن بعضها باستخدامn\n\، ويمكن أن تحتوي الرسالة الحقول التالية: :data ويمثل جسم الرسالة، وتُفسَّر سلسلة من الحقول data كرسالة واحدة يفصل بين أجزائها الكتلة n\. :id تُجدِّد قيمة lastEventId وتُرسَل ضمن الترويسة Last-Event-ID عند إعادة الاتصال. :retry تحدد فترة التأخير الزمني بين كل محاولتين لإعادة الاتصال بالميلي ثانية، ولا يمكن ضبطها باستخدام JavaScript. :event اسم الحدث وينبغي أن تسبق الحقل data. يمكن أن تحتوي الرسالة على أحد الحقول السابقة أو أكثر وبأي ترتيب، لكن الحقل :id يأتي في النهاية عادةً. ترجمة -وبتصرف- للفصول Long polling وWebSocket وServer sent events من سلسلة The Modern JavaScript Tutorial. اقرأ أيضًا المقال السابق: استئناف رفع الملفات في جافاسكريبت تطويع البيانات في جافاسكربت
  2. الهواتف الذكيّة والسيارات، الحواسيب الخارقة والأجهزة المنزلية، أجهزة سطح المكتب وخواديم الشركات - كلها تستعمل نظام التشغيل لينكس، النظام الذي ستراه حولك أينما أدرت وجهك. بدأ نظام التشغيل لينكس منذ منتصف التسعينات تقريبًا وقد حقّق منذئذ قاعدة مستخدمين ضخمة تجوب العالم. وكما مستخدميه، فلينكس في كل مكان: أجهزتك المحمولة، منظّمات الحرارة، السيارات، الثلاجات، أجهزة Roku لبثّ الوسائط المتعدّدة، أجهزة التلفاز، وغيرها وغيرها. ولا ننسى أنّ أغلب شبكة الإنترنت تعمل عليه، إضافةً إلى أقوى 500 حاسوب خارق في العالم، كما والبورصة أيضًا. ولكن لو نسينا لبرهة أنّه الخيار الأمثل لتشغيل الأجهزة المكتبيّة والخواديم والأنظمة المضمّنة، فنظام التشغيل لينكس هو أكثر نظام من بين الأنظمة الموجودة - أكثرها أمانًا وثقةً ويُسرًا دون متاعب تُذكر. كتبنا لك أدناه كل ما تحتاجه لتكون عارفًا وعالمًا بمنصّة لينكس. فلو كنت مبتدئًا، فلا تقلقنّ البتة. ما هو نظام التشغيل لينكس أصلًا؟ ”وندوز“ و”آي‌أو‌إس“ و”ماك أو‌إس“ هي أنظمة تشغيل، ولينكس أيضًا نظام تشغيل. وفي هذا السياق، أتعلم أنّ أكثر منصّة رائجة في كوكبنا الأرض ”أندرويد“ تعمل بنظام التشغيل لينكس؟ نظام التشغيل (ويُختصر بِـ OS) هو البرمجية التي تُدير كلّ ما يتعلّق بالعتاد في حاسوبك المكتبي أو المحمول. لنبسّطها أكثر، نظام التشغيل يُدير الاتصالات بين البرمجيّات (Software) والعتاد (Hardware) على جهازك. ودون نظام التشغيل فلن تعمل أيًا من البرامج. يعتمد أساس نظام التشغيل لينكس على هذه الأجزاء المختلفة: محمِّل الإقلاع (Bootloader): أي البرمجية التي تُدير عمليّة إقلاع/تشغيل حاسوبك. حين يرى أغلب المستخدمين شاشة البداية التي تظهر أمامهم وتختفي بعد فترة لتفتح نظام التشغيل، تعرف أنّهم قد رأوا هذا المحمِّل. النواة (Kernel): في الواقع فَإن نظام التشغيل لينكس مكوّن من أجزاء عديدة، النواة هذه واحدة منها. كما يدلّ اسمها، فهي نواة النظام والتي تُدير المعالج والذاكرة والأجهزة الداخليّة والخارجيّة. تعد النواة المستوى الأدنى من مستويات نظام التشغيل وهي حلقة الوصل الفعليّة بين البرمجيّات والعتاد. نظام التمهيد (Init System): هذا أحد الأنظمة الفرعيّة التي ”تُمهّد“ المساحة المخصّصة للمستخدم، إضافةً إلى إدارة العفاريت. أكثر هذه الأنظمة استعمالًا هو systemd ويتصادف أنّه أيضًا أكثرها جدلًا بين المستخدمين. تقع على عاتق هذا النظام إدارةَ عمليّة الإقلاع ما إن يُسلِّم محمّل الإقلاع (مثل GRUB أي ”محمِّل الإقلاع الموحّد الأعظم“ [GRand Unified Bootloader]) عمليّة الإقلاع الأولي. العفاريت (Daemons): الخدمات التي تجري في الخلفيّة (مثل الطباعة والصوت وترتيب مهام النظام وغيرها) تُسمّى بالعفاريت، ويمكن أن تبدأ إمّا أثناء الإقلاع أو بعد الدخول إلى سطح المكتب. تعمل هذه الخدمات بمنأًى عن تحكّم المستخدم المباشر، ويمكن أن تنفّذ مختلف الوظائف والعمليات والأوامر. خادوم الرسوميّات (Graphical Server): هذا أحد الأنظمة الفرعيّة الأخرى التي تعرض أيّ رسوم ورسوميّات على الشاشة لديك. النسخة الأولية منه كانت ”خادوم X“ (أو إكس/X اختصارًا)، أما الآن فالتوجه نحو Wayland وهو أفضل من نواحٍ عدّة عن سلفه ”خادوم X“. بيئة سطح المكتب (Desktop Environment): هذه هي ما يتفاعل المستخدمين معه حقًا. توجد أعداد كبيرة من بيئات سطح المكتب ليختار المستخدم ما يُريحه منها (جنوم أو القرفة أو متّة أو بانيثون أو إنلايتمنت أو كدي أو إكسفسي أو غيرها). وفي كلّ بيئة سطح مكتب عدد من التطبيقات المثبّتة مبدئيًا (مثل مدراء الملفات وأدوات الضبط ومتصفّحات الوِب والألعاب). التطبيقات: بالطبع، لا تقدّم بيئات سطح المكتب كلّ ما تحتاج من تطبيقات. فكما نظامي وندوز وماك أو‌إس، يقدّم نظام التشغيل لينكس أيضًا آلافًا مؤلّفة من البرمجيات الموثوقة عالية الكفاءة وسهلة الوصول والتثبيت. أصبحت تحتوي أغلب توزيعات لينكس الحديثة (نتطرّق إليها أسفله) أدوات تشبه ”أسواق البرامج“ تحاول تبسيط تثبيت البرمجيات وتوحيدها في مكان واحد. فمثلًا تحتوي توزيعة أوبنتو لينكس على ”مركز برمجيات أوبونتو“ (نسخة من «برمجيّات جنوم») وهو يقدّم لك آليّة سهلة للبحث بسرعة بين آلاف التطبيقات وتثبيتها، كلّه في مكان واحد. ما هي مزايا نظام التشغيل لينكس؟ يطرح أغلب الأشخاص هذا السؤال بالذات. لماذا أُتعب نفسي وأتعلّم بيئة حاسوبيّة مختلفة تمامًا عمّا أعرف، طالما نظام التشغيل -المثبّت في أغلب الأجهزة المكتبية والمحولة والخواديم- يعمل دون مشاكل تُذكر؟ لأُجيبك على هذا السؤال سأطرح سؤالًا آخر. هل حقًا يعمل نظام التشغيل الذي تستعمله الآن ”دون مشاكل تُذكر“؟ أمّ أنّك تصارع (بل وتكافح ضدّ) المصاعب المتوالية من فيروسات وبرامج ضارّة وحالات بطء وانهيارات وإصلاحات باهظة للنظام ورسوم تراخيص ثقيلة؟ لو كنت حقًا كذلك فربّما تكون لينكس المنصّة الأمثل لاحتياجاتك! فقد تطوّر نظام التشغيل لينكس وصار واحدًا من الأنظمة البيئيّة الثِّقة للحواسيب على هذه البسيطة. هذه الثِّقة زائدًا تكلفة الصفر قرش ستكون حقًا أمثل حلّ لمنصّات أسطح المكتب. نظام تشغيل مجاني كما قلتُ تمامًا، صفر قرش… أي مجانًا. يمكنك تثبيت نظام التشغيل لينكس على الحواسيب التي تريد (مهما كان العدد) دون أن تدفع ولو قرشًا لتراخيص البرمجيّات أو الخواديم. تعال نرى تكلفة خادوم لينكس موازنةً بخادوم وندوز 2016. تكلفة Standard edition من Windows Server 2016 هي 882 دولارًا (نشتريها من مايكروسوفت مباشرةً). هذا لا يشمل ترخيص الوصول للعميل (CAL) ورُخص البرمجيات الأخرى التي سيشّغلها الخادوم (مثل قاعدة البيانات وخادوم الوِب وخادوم البريد وغيرها). فمثلًا يُكلّف ترخيص CAL لمستخدم واحد لِـ Windows Server 2016 38 دولارًا. لو اضطررت إلى إضافة 10 مستخدمين فتزيد عليها 388 أخرى فقط لترخيص برمجيات الخادوم. لو كنت على خادوم لينكس فسيكون كلّ هذا مجانيًا وسهل التثبيت. بل أنّ تثبيت خادوم وِب كامل متكامل (ويشمل خادوم قواعد بيانات) عمليّة قوامها بعض النقرات أو الأوامر (لتعرف مدى بساطة ذلك طالع ”كيفية تثبيت حزم LAMP“). ألم تقتنع بعد بالصفر قرش؟ إذًا لو أخبرتك بنظام تشغيل سيعمل دون أيّة مشكلة تُذكر مهما طال استخدامك له، ما تقول؟ استعملت شخصيًا نظام التشغيل لينكس طوال 20 سنة أو يزيد (أكان لجهاز المكتب أم لمنصّة الخادوم) ولم أواجه قط أيّ مشكلة مع برامج الفِدية أو البرامج الضارة أو الفيروسات أو غيرها. فلينكس (وبصفة عامة) أقلّ تعرضًا لهذه الأخطار والاعتداءات من غيره. وحين نتحدّث عن إعادة إقلاع الخادوم، فهي مطلوبة فقط وفقط عند تحديث النواة. ولو سمعت بأنّ خواديم لينكس تعمل لسنوات وسنوات دون إعادة إقلاعها فلا تستغرب، هذا طبيعي. لو أخذت بالتحديثات المستحسن تثبيتها دوريًا، فتأكّد من ثبات النظام واستقلاليّته، وليس هذا حبرًا على ورق، بل كلام عمليّ مُثبت. بل أنّ منهجيّة ”اضبطه مرّة وانساه كل مرّة“ (Set it and forget it) هي المتّبعة في أوساط خواديم لينكس. مفتوح المصدر كما وأنّ نظام التشغيل لينكس يُوزّع برخصة مفتوحة المصدر. وهذا ما تضمنه لك فلسفات البرمجيات مفتوحة المصدر: حريّتك في تشغيل البرنامج لأيّ سبب أردته. حريّتك في دراسة أسلوب وطريقة عمل البرنامج، كما وتغييرها لتتناسب مع رغباتك. حريّتك في إعادة توزيع نُسخ البرنامج لتُساعد جارك في الانتقال. حريّتك في توزيع نُسخ البرنامج التي عدّلتها للآخرين. إدراك هذه النقاط ضروري لتفهم طبيعة المجتمع الذي يعمل على تطوير منصّة لينكس. ولا شكّ بأنّ لينكس هو نظام ”طوّره الناس، ليستعمله الناس“. كما أنّ هذه الفلسفات هي من الأسباب الرئيسية لتفضيل الناس لينكس على غيره. الحريّة، حريّة الاستخدام، حريّة الخيار، هذا ما يسعى وراءه المرء. ما هي توزيعات نظام التشغيل لينكس؟ توجد عديد من الإصدارات المختلفة من نظام التشغيل لينكس الهادفة إلى تقديم الخيار لجميع الناس مهمًا كانوا، أكانوا مستخدمين جدد أو معمّرين. أيًا كنت فستجد ”نكهة“ من لينكس تناسب متطلباتك واحتياجاتك. هذه الإصدارات هي ما نسمّيه التوزيعات (Distribution). يمكن تنزيل كلّ توزيعة من توزيعات لينكس مجانًا (إلى حدّ ما)، وحرقها على اسطوانة (أو جهاز USB) وتثبيتها (على أيّ جهاز أردت، بل وأجهزة أيضًا). من التوزيعات الشائعة للينكس: لينكس مِنت (Linux Mint) مانجارو (Manjaro) دِبيان (Debian) أوبونتو (Ubuntu) أنتيرغوس (Antergos) سولوس (Solus) فيدورا (Fedora) إليمنتري أو‌ إس (elementary OS) أوبن سوزة (openSUSE) لكلّ توزيعة منها توجّه مختلف على ماهيّة سطح المكتب. بعضها تفضّل واجهات المستخدم الحديثة (مثل جنوم وبانيثون في ”نظام تشغيل إليمنتري“)، والأخرى تفضّل البقاء مع بيئات سطح المكتب التقليدية (مثل أوبن سوزة التي تستخدم كدي). يمكنك أيضًا مطالعة موقع Distrowatch لتعرف أفضل 100 توزيعة لينكس من جهة نظر المجتمع. ولو فكّرت للحظة بأنّ الخواديم ليس لها نصيب من هذا، فأنت مخطئ! انظر: Red Hat Enterprise Linux Ubuntu Server CentOS SUSE Enterprise Linux بعض هذه التوزيعات المخصّصة للخواديم مجّاني (مثل Ubuntu Server وCentOS)، وأخرى تطلب ثمنًا لقائها (مثل Red Hat Enterprise Linux وSUSE Enterprise Linux). هذه التي تطلب الثمن تقدّم الدعم الفنّي أيضًا. ما هي توزيعة لينكس التي تناسبني؟ إجابتك على هذه الأسئلة الثلاثة تحدّد التوزيعة الأنسب لك: مدى خبرتك بالأمور التقنية والحاسوبية. أكنت تريد واجهة مستخدم حديثة أم قياسية عادية. أكنت تريده للخادوم أو للأجهزة المكتبية. لو كانت خبرتك ومهاراتك بالحاسوب بسيطة، فالأفضل أن تتّجه نحو توزيعة تناسب المبتدئين مثل Linux Mint أو Ubuntu أو Elementary OS أو Deepin. ولو كانت مهاراتك فوق المتوسّط، فستكون التوزيعات أمثال Debian وFedora أفضل لك. أمّا لو كنت ”احترفت مِهنة الحاسوب“ وإدارة الأنظمة، فتوزيعة جنتو وأمثالها طُوّرت لك خصّيصًا. لكن، لو أردت تحدّيًا لمهاراتك الفذّة أخي الخبير، فاصنع توزيعة لينكس خاصّة بك أنت، طبعًا مستعينًا بِـ Linux From Scratch. في حال كنت تبحث عن توزيعة للخواديم فقط، فالسؤال الرابع هو لو أردت واجهة سطح مكتب أم أنّك ستُدير الخادوم عبر سطر الأوامر دون واجهة. فمثلًا Ubuntu Server لا تحتوي واجهة رسوميّة، وهذا يعطيك ميزتين. الأولى هي إزاحة ثقل تحميل الرسوميات عن الخادوم، والثانية هي تعلّم سطر أوامر لينكس تعلّمًا عميقًا. يمكنك مع ذلك تثبيت حزمة واجهة رسوميّة في Ubuntu Server بأمر بسيط مثل sudo apt-get install ubuntu-desktop. غالبًا ما يختار مدراء الأنظمة التوزيعات حسب الميزات التي تقدّمها. فهل تريد توزيعة متخصّصة للخواديم تقدّم لك كل ما تحتاجه لخادومك على طبق من فضّة؟ لو أردت فستكون CentOS فضل الخيارات. أو ربما تريد توزيعة لسطح المكتب تُضيف ما تريد من برامج وحزم فيها؟ لو كان كذلك فستقدّم لك Debian أو Ubuntu Linux ما تحتاج. ننصحك بالرجوع إلى مقالة الدليل النهائي لاختيار توزيعة لينكس لمزيد من التفاصيل حول التوزيعات واختيارها. تثبيت لينكس لسان حال أغلب الناس عن تثبيت نظام التشغيل هو ”ماذا؟ صعب! مستحيل! محال!“. بينما الواقع (صدّقته أم لا) هو أنّ تثبيت نظام التشغيل لينكس هو الأسهل من بين كلّ أنظمة التشغيل. بل أنّ أغلب نُسخ لينكس تقدّم لك ما نسمّيه ”التوزيعة الحيّة“ (Live Distribution)، أي التي تشغّل منها نظام التشغيل وهو على اسطوانة CD/DVD أو على إصبع USB (فلاش ميموري) دون أن تُعدّل أيّ بايت في القرص الصلب. هكذا تعرف بمزايا التوزيعة كاملةً دون تثبتيها حتّى، وما إن تجرّبها وترتاح لها، وتقرّر وتقول ”هذه هي توزيعة أحلامي“، تنقر مرّتين أيقونة ”ثبِّت“ وسيأخذك مُرشد التثبيت في رحلة التثبيت البسيطة (فقط!). غالبًا ما تكون رحلة مُرشد التثبيت هي هذه الخطوات (نوضّح هنا عملية تثبيت Ubuntu Linux): التحضير: التأكّد من استيفاء الجهاز لمتطلّبات التثبيت. يمكن أن يسألك هذا لو أردت تثبيت برمجيّات من الأطراف الثالثة (مثل ملحقات تشغيل MP3 ومرمزات الفيديوهات وغيرها). إعداد الاتصال اللاسلكي (لو أردته): في حال كنت تستعمل حاسوبًا محمولًا (أو جهازًا يدعم الاتصال لاسلكيًا) فقد يُطلب منك الاتصال بالشبكة لتثبيت برمجيات الأطراف الثالثة والتحديثات. تخصيص المساحة للقرص الصلب: تُتيح لك هذه الخطوة اختيار المكان الذي سيُثبّت فيه نظام التشغيل، وهل تريد تثبيت لينكس مع نظام تشغيل آخر (أي ”الإقلاع المزدوج“) أو استعمال القرص الصلب كلّه أو ترقية نسخة لينكس المثبّتة أو تثبيت لينكس عوض النسخة التي لديك. المكان: أي، مكانك على الخريطة. تخطيط لوحة المفاتيح: لغات الكتابة التي تريدها في النظام. إعداد المستخدم: أي اسم المستخدم وكلمة السر. طبعًا، يمكنك ألّا تحذف كامل القرص بما فيه (مثلما ترى في الصورة أعلاه ”تحذير“)، بل تعديل بعضه وإبقاء الآخر عبر الخيار الآخر Something else (ويفضّل طبعًا تحديد مساحة لقرص التبديل/Swap تتناسب وحجم الذاكرة). وعلى سبيل الطرفة، كثير من مستعملي لينكس يواجهون هذه المشكلة أول مرة ويمسحون جميع بياناتهم المخزنة على القرص، حتّى أنا مترجم المقال فكن حذرًا إني لك من الناصحين :-). لكن هذا ليس سببًا بأنّ لينكس نظام سيئ (وبالعامية ”قمامة“)، لكنه سببٌ ”لأقرأ كامل السؤال قبل الإجابة“ :). يمكنك أيضًا تعمية القرص لتكون آمنًا من أيّ هجمات محتملة. فقط. هذا فقط. وما إن ينتهي تثبيت النظام، أعِد التشغيل واشرع العمل! لو أردت دليلًا لتثبيت لينكس معمّقًا أكثر، فطالع ” أسهل وأأمن طريقة لتثبيت لينكس وتجربته“ أو نزّل كتيّب PDF عن تثبيت لينكس من مؤسّسة لينكس نفسها. تثبيت البرمجيات في لينكس كما كان تثبيت نظام التشغيل سهلًا، فتثبيت التطبيقات سهل أيضًا. تحتوي أغلب توزيعات لينكس على ما يمكن تسميته بسوق للبرمجيات، وهو مكان موحّد يمكنك البحث فيه عن ما تريد من برامج وتثبيتها. تعتمد Ubuntu Linux (وغيرها الكثير) على «برمجيّات جنوم»، وتستعمل Elementary OS مركز AppCenter، أمّا Deepin فَـ Deepin Software Center، و openSUS أداة AppStore، وثمّة توزيعات تعتمد على Synaptic. مهما كان الاسم فالوظيفة واحدة: مكان واحد لتبحث عن برامج لينكس فيه، وتثبّتها أيضًا. وبالطبع، فهذه البرمجيات تعتمد على الواجهة الرسومية، لو لم تكن موجودة لما عملت. فلو لديك خادوم بدون واجهة فعليك استعمال واجهة سطر الأوامر لتثبيت البرامج. ولهذا سنأخذ نظرة سريعة على أداتين مختلفين لنوضّح سهولة التثبيت من سطر الأوامر، أسهل من شرب الماء ربما. تُستعمل هاتين الأداتين في التوزيعات المبنيّة على Debian، والمبنيّة على Fedora تستعمل الأولى (توزيعات دبيان) أداة apt-get لتثبيت البرمجيات، أمّا توزيعات فيدورا تستعمل أداة yum، ولكلتا الأداتين آلية عمل متشابهة سنوضّحها باستعمال أمر apt-get. فلنقل فرضًا أنّك تريد تثبيت أداة wget (أداة مفيدة جدًا لتنزيل الملفات عبر سطر الأوامر). لتثبيت هذه الأداة باستعمال apt-get، ستُدخل هذا الأمر: sudo apt-get install wget نُضيف أمر sudo لأنّ تثبيت البرمجيات يطلب امتيازات المستخدم الفائقة (مدير الجهاز). وكما دبيان، فتثبيت البرامج في توزيعات فيدورا على خطوتين: أولا su للامتيازات تلك (أي أن تكتب الأمر su وتُدخل كلمة سرّ الجذر)، بعدها: yum install wget هذا كل ما تحتاجه لتثبيت البرامج في جهاز لينكس. أكان صعبًا ومحالًا كما تخيّلته؟ لا تقل لي أنّك متردّد بعد؟! أتذكر ”كيفيّة تثبيت حزم LAMP“ التي تكلمنا عنها؟ بأمر واحد: sudo taskel بأمر واحد تُثبّت خادوم LAMP (اختصار Linux Apache MySQL PHP أي ”حزمة لينكس وأباتشي وMySQL وPHP“) متكامل على توزيعتك كانت للخواديم أم للأجهزة المكتبية. أجل، بأمر واحد فقط. سهل صحيح؟ مصادر أخرى للاستزادة توزيعات لينكس هي حقًا الخيار الأمثل لك لو أردت منصّة آمنة ثِقة لجهازك المكتبي وخادومك على الشبكة. كن على ثقة بأنّ لينكس سيُريحك وسطح المكتب من مختلف المشاكل، ويُشغّل لك خواديمك دون عناء، ويقلّل من طلباتك بحثًا للدعم إلى أدنى درجة. إليك بعض الروابط المفيدة لتستزيد عن لينكس بمعلومات تخدمك طول فترة استعمالك له: Linux.com: كل ما تحتاج لتعرف لينكس حقّ المعرفة (أخبار ودروس وغيرها). Howtoforge: دروس عن لينكس. مشروع توثيق لينكس: أدلّة تعليميّة وأسئلة شائعة وإجابات ”كيف أستطيع كذا“. قاعدة معلومات لينكس ودروس عنه: دروس وأدلّة عديدة وتدخل في الصميم. LWN.net: أخبار عن نواة لينكس وأكثر. قسم لينكس في الأكاديمية: مقالات متنوعة عن نظام التشغيل لينكس وكيفية تثبيت العديد من الخدمات والتطبيقات. مجتمع لينكس العربي كتاب سطر أوامر لينكس كتاب دليل إدارة خوادم أوبنتو كتاب دفتر مدير ديبيان كتاب الإدارة المتقدمة لجنو/لينكس كتاب أوبنتو ببساطة (تنويه: إصدار أوبنتو الذي يشرحه الكتاب قديم ولكن يمكن أخذ الفكرة العامة عنه وعن لينكس عمومًا) ترجمة -وبتصرف- للمقال What is linux?‎ من موقع linux.com
  3. سنعمل في هذا المقال على تطوير خادم TCP متزامن يقوم بإنشاء أرقام عشوائية باستخدام حوالي 65 سطرًا من كود Go، إذ سأشرح كيفية تطوير خادم TCP متزامن، بلغة البرمجة Go، والتي تقوم بإرجاع أرقام عشوائية. إن لم تقرأ المقال السابق حول إنشاء كلمات مرور عشوائية وآمنة في Go، فننصحك بالرجوع إليه وقراءته أولًا. تعمل خوادم TCP و UDP بخدمة عملاء الشبكة في كل مكان عبر شبكات TCP / IP. لكل اتصال وارد من عميل TCP، سيقوم خادم TCP ببدء تشغيل goroutine جديد لمعالجة هذا الطلب. تستطيع إيجاد هذا المشروع concTCP.go على GitHub. التعامل مع اتصالات TCP يمكنك العثور على منطق البرنامج في دالة ()handleConnection بلغة Go، والذي يتم تنفيذه على النحو التالي: func handleConnection(c net.Conn) { fmt.Printf("Serving %s\n", c.RemoteAddr().String()) for { netData, err := bufio.NewReader(c).ReadString('\n') if err != nil { fmt.Println(err) return } temp := strings.TrimSpace(string(netData)) if temp == "STOP" { break } result := strconv.Itoa(random()) + "\n" c.Write([]byte(string(result))) } c.Close() } إذا أرسل عميل TCP سلسلة التعليمات "STOP"، فسيتم إنهاء برنامج Go أي goroutine الذي يخدم عميل TCP محدد؛ وإلا، سيرسل خادم TCP رقمًا عشوائيًا إلى عميل TCP. تضمن الحلقة for أن عميل TCP سيتم خدمته طالما يُتَطلب ذلك. تقرأ الحلقة for الموجودة في كود Go البيانات من عميل TCP سطرًا بسطر باستخدام ('bufio.NewReader(c).ReadString('\n وتُعيد إرسال البيانات باستخدام (((c.Write([]byte(string(result. التزامن تنفيذ دالة ()main، يُعطي أمرًا لخادم TCP لبدء تشغيل برنامج goroutine جديد في كل مرة يتعين عليه خدمة عميل TCP: func main() { arguments := os.Args if len(arguments) == 1 { fmt.Println("Please provide a port number!") return } PORT := ":" + arguments[1] l, err := net.Listen("tcp4", PORT) if err != nil { fmt.Println(err) return } defer l.Close() rand.Seed(time.Now().Unix()) for { c, err := l.Accept() if err != nil { fmt.Println(err) return } go handleConnection(c) } } أولاً، تتأكد ()main من أن البرنامج يحتوي على وسيطة سطر أوامر واحدة على الأقل. لاحظ أن الكود الموجود لا يتحقق مما إذا كانت وسيطة سطر الأوامر المحددة هي رقم مَنفذ TCP صالح أم لا. ومع ذلك، إذا لم تكن القيمة المحددة رقم منفذ TCP صالحًا، فسوف يفشل استدعاء ()net.Listen مع ظهور رسالة خطأ مشابهة لما يلي: $ go run concTCP.go 12a listen tcp4: lookup tcp4/12a: nodename nor servname provided, or not known $ go run concTCP.go -10 listen tcp4: address -10: invalid port يتم استخدام استدعاء ()net.Listen لإخبار برنامج Go بقبول اتصالات الشبكة وبالتالي كخادم. قيمة الإرجاع ()net.Listen هي من النوع net.Conn، والتي تنفذ واجهات io.Reader و io.Writer. تقوم الدالة ()main أيضًا باستدعاء الدالة ()rand.seed لتهيئة مُنشئ الأرقام العشوائية. أخيرًا، تُتيح حلقة for للبرنامج الحفاظ على قبول عملاء TCP الجدد باستخدام ()accept والتي سيتم معالجتها بواسطة نسخ الدالة ()handleConnection، والتي يتم تنفيذها على شكل goroutines. أول معامل للدالة ()net.Listen يُحدد المعامل الأول من الدالة ()net.Listen نوع الشبكة التي سيتم استخدامها، بينما يُحدد المعامل الثاني عنوان الخادم بالإضافة إلى رقم المَنفذ الذي سيستمع إليه الخادم. القيم الصالحة للمُعامل الأول هي: tcp, tcp4 (IPv4-only), tcp6 (IPv6-only), udp, udp4 (IPv4- only), udp6 (IPv6-only), ip, ip4 (IPv4-only), ip6 (IPv6-only), Unix (Unix sockets), Unixgram, Unixpacket فعالية خادم TCP المتزامن يتطلب concTCP.go وسيطة سطر أوامر واحدة، وهي رقم المَنفذ الذي سيستمع إليه. سيكون الناتج الذي ستحصل عليه من concTCP.go عند خدمة عملاء TCP مشابهًا لما يلي: $ go run concTCP.go 8001 Serving 127.0.0.1:62554 Serving 127.0.0.1:62556 يمكن لناتج (1)netStat التحقق من أن concTCP.go يخدم العديد من عملاء TCP أثناء الاستماع لمزيد من الاتصالات: $ netstat -anp TCP | grep 8001 tcp4 0 0 127.0.0.1.8001 127.0.0.1.62556 ESTABLISHED tcp4 0 0 127.0.0.1.62556 127.0.0.1.8001 ESTABLISHED tcp4 0 0 127.0.0.1.8001 127.0.0.1.62554 ESTABLISHED tcp4 0 0 127.0.0.1.62554 127.0.0.1.8001 ESTABLISHED tcp4 0 0 *.8001 *.* LISTEN يعلمنا السطر الأخير من ناتج الأمر السابق أن هناك عملية تستمع إلى المَنفذ 8001، مما يعني أنه لا يزال بإمكانك الاتصال بمنفذ TCP رقم 8001. يتحقق أول سطرين من وجود اتصال شبكة TCP ثابت يستخدم أرقام المنافذ 8001 و 62556. وبالمثل، يتحقق السطران الثالث والرابع من وجود اتصال TCP آخر يستخدم أرقام المنافذ 8001 و 62554. تُظهر هذه الصورة ناتج concTCP.go عند خدمة العديد من عملاء TCP: بشكل مشابه، تُظهر الصورة التالية الناتج من عميلين من TCP يتم تنفيذهما باستخدام (1)nc: يُمكنك ايجاد معلومات أكثر عن (1)nc، والتي تُدعى أيضًا (1)netcat على ويكيبيديا. الملخص لقد تعلمت للتو كيفية تطوير خادم TCP متزامن يقوم بإنشاء أرقام عشوائية باستخدام حوالي 65 سطرًا من كود Go، وهو أمر مثير للإعجاب جدًا! إذا كنت تريد أن يقوم خادم TCP بمهمة مختلفة، فقط قم بتغيير تنفيذ الدالة ()handleConnection. ترجمة وبتصرّف للمقال Build a concurrent TCP server in Go، لصاحبه Mihalis Tsoukalos.
  4. عندما تُنشئ خادم أوبونتو 18.04 لأول مرة، فهناك بعض خطوات الإعداد التي ينبغي عليك اتخاذها مبكّرًا كجزء من الإعداد الأساسي. سيزيد ذلك من أمان الخادم وسهولة استخدامه وسيمنحك أساسًا قويًا لاتخاذ إجراءات لاحقة. رغم أنه يمكنك إكمال هذه الخطوات يدويًا، إلا أنه في بعض الأحيان يكون من الأسهل برمجة العمليات عبر سكربت لتوفير الوقت وتفادي الأخطاء البشرية. يشرح هذا الدليل كيفية أتمتة الخطوات الموجودة في دليل إعداد الخادم الأولي في سكربت. ماذا يفعل السكربت؟ يعدّ هذا السكربت بديلًا للتشغيل اليدوي من خلال الطريقة الموضحة في دليل إعداد خادم أوبونتو 18.04 الأولي ودليل إعداد مفاتيح SSH على أوبونتو 18.04. تؤثر المتغيرات التالية على كيفية عمل السكربت: USERNAME: اسم حساب المستخدم العادي الذي ستُنشأ وتمنح له صلاحيات sudo. COPY_AUTHORIZED_KEYS_FROM_ROOT: تحدّد ما إذا كنت تريد نسخ أصول مفتاح SSH من الحساب الجذري root إلى حساب sudo الجديد. OTHER_PUBLIC_KEYS_TO_ADD: مجموعة من السلاسل النصية التي تمثل مفاتيح عامة أخرى تُضاف إلى الحساب sudo. يمكن استخدام هذه بالاختيار بين إضافتها أو جعلها بديلًا لنسخ المفاتيح من الحساب الجذري root. يجب عليك تحديث هذه المتغيرات حسب الحاجة قبل تشغيل السكربت. عند تنفيذ السكربت، تُنفّذ الإجراءات التالية: إنشاء حساب مستخدم عادي مع امتيازات sudo باستخدام الاسم الذي يحدده المتغير USERNAME . إعداد إطار لكلمة المرور الأولية للحساب الجديد: إذا كان الخادم معدًّا لمصادقة الهوية بكلمة المرور، فستُنقل كلمة المرور الإدارية الأصلية التي وُلِّدت لحساب root إلى حساب sudo الجديد. حينها تكون كلمة المرور للحساب الجذري مقفلة. إذا كان الخادم معدًّا لمصادقة الهوية بمفتاح SSH، فستُعيّن كلمة مرور فارغة لحساب sudo. تُعلّم كلمة مرور المستخدم sudo بعلامة "منتهي الصلاحية" مما يوجب تغييرها عند أول تسجيل للدخول. يُنسخ ملف Authorized_keys من الحساب الجذري إلى مستخدم sudo إذا عُيِّن المتغير COPY_AUTHORIZED_KEYS_FROM_ROOT على "صحيح". تُضاف أي مفاتيح معرّفة في OTHER_PUBLIC_KEYS_TO_ADD إلى ملف Authorized_keys للمستخدم sudo. يُعطّل التصديق SSH المستند إلى كلمة المرور في الحساب الجذري root. يُفعّل جدار الحماية UFW مع السماح باتصالات SSH. كيف يُستخدَم السكربت؟ يمكن تنفيذ السكربت بطريقتين: عن طريق إضافته إلى حقل بيانات مستخدم الخادم أثناء الإنشاء أو عن طريق تسجيل الدخول بحساب root وتنفيذه بعد التشغيل. عبر بيانات المستخدم عند إنشاء Droplet على DigitalOcean، يمكنك تحديد بيانات المستخدم، وهو سكربت يُنفّذ أثناء تشغيل الخادم الأولي من أجل إجراء إعدادٍ إضافي. إذا كنت تُنشئ Droplet من لوحة التحكم، فيمكنك تحديد خانة الاختيار "بيانات المستخدم" في قسم تحديد خيارات إضافية. سيظهر لك مربع نصي حيث يمكنك لصق السكربت: إذا كنت تُنشئ Droplet باستخدام واجهة برمجة تطبيقات DigitalOcean، فيمكنك تمرير السكربت باستخدام سمة user_data بدلاً من ذلك. إذا كنت تُنشئ Droplet باستخدام أداة سطر الأوامر doctl، فيمكنك تمرير السكربت باستخدام خيار ‎--user-data-file: $ doctl compute droplet create ... --user-data-file /path/to/script بغض النظر عن الطريقة التي تستخدمها لإضافة بيانات المستخدم، سيُنفّذ السكربت في أول مرة يُشغّل فيها الخادم الجديد. قد تضطر إلى الانتظار لبضع دقائق حتى تكتمل العملية، ولكن بعد ذلك، يمكنك تسجيل الدخول إلى الخادم الخاص بك عبر حساب المستخدم sudo للحصول على أي إعداد إضافي. عند تسجيل الدخول لأول مرة، سيُطلب منك تغيير كلمة المرور الخاصة بك. سينهي الخادم جلسة SSH الحالية بمجرد تقديم بيانات الاعتماد الجديدة الخاصة بك وتأكيدها. بعد ذلك، يمكنك إعادة SSH مرة أخرى مثل العادة. تنفيذ السكربت بعد التشغيل إذا كنت لا ترغب في استخدام بيانات المستخدم، يمكنك أيضًا تشغيل السكربت يدويًا عبر SSH بمجرد تشغيل الخادم. إذا نزّلت السكربت على جهاز الكمبيوتر المحلي الخاص بك، يمكنك تمرير السكربت مباشرةً إلى SSH بكتابة ما يلي: $ ssh root@servers_public_IP "bash -s" -- < /path/to/script/file ينبغي أن تكون الآن قادرًا على تسجيل الدخول باستخدام حساب sudo الخاص بك من أجل أي إعداد إضافي. إذا لم تُنزّل السكربت على جهاز الحاسوب المحلي، فابدأ بتسجيل الدخول إلى الحساب root على الخادم الخاص بك: $ ssh root@servers_public_IP بعد ذلك، نزِّل السكربت الأولي على الخادم: $ curl -L https://raw.githubusercontent.com/do-community/automated-setups/master/Ubuntu-18.04/initial_server_setup.sh -o /tmp/initial_setup.sh افحص السكربت للتأكد من تنزيله بشكل صحيح و حدِّث أي متغيرات ترغب في تغييرها: $ nano /tmp/initial_setup.sh عندما تكون راضيًا عن المعطيات، شغّل السكربت يدويًا باستخدام bash: $ bash /tmp/initial_setup.sh ينبغي أن تكون الآن قادرًا على تسجيل الدخول باستخدام الحساب ذي الصلاحيات sudo لإتمام أي إعدادٍ إضافي. محتويات السكربت يمكنك العثور على السكربت لإعداد الخادم الأولي في مخزن الإعداد التلقائي لمؤسسة DigitalOcean Community GitHub. لنسخ محتويات السكربت أو تنزيلها مباشرةً، انقر فوق الزر (Raw) أعلى النص، أو انقر هنا لعرض المحتويات الأولية مباشرة. لقد أدرجت المحتويات كاملة أيضًا هنا لتسهيل العملية: #!/bin/bash set -euo pipefail ######################## ### SCRIPT VARIABLES ### ######################## # اسم حساب المستخدم العادي الذي ستُنشأ وتمنح له صلاحيات # Name of the user to create and grant sudo privileges USERNAME=sammy # الجديد sudo إلى حساب root من الحساب الجذري SSH تحدّد ما إذا كنت تريد نسخ أصول مفتاح # Whether to copy over the root user's `authorized_keys` file to the new sudo # user. COPY_AUTHORIZED_KEYS_FROM_ROOT=true # sudo مجموعة من السلاسل النصية التي تمثل مفاتيح عامة أخرى تُضاف إلى الحساب # Additional public keys to add to the new sudo user # OTHER_PUBLIC_KEYS_TO_ADD=( # "ssh-rsa AAAAB..." # "ssh-rsa AAAAB..." # ) OTHER_PUBLIC_KEYS_TO_ADD=( ) #################### ### SCRIPT LOGIC ### #################### # ومنحه الصلاحيات sudo إنشاء حساب المستخدم # Add sudo user and grant privileges useradd --create-home --shell "/bin/bash" --groups sudo "${USERNAME}" # تحقق من توفّر الحساب الجذري على كلمة مرور # Check whether the root account has a real password set encrypted_root_pw="$(grep root /etc/shadow | cut --delimiter=: --fields=2)" if [ "${encrypted_root_pw}" != "*" ]; then # تُنقل كلمة المرور الإدارية الأصلية التي وُلِّدت لحساب الجذري إلى الحساب الجديد. حينها تُقفل كلمة المرور للحساب الجذري # Transfer auto-generated root password to user if present # and lock the root account to password-based access echo "${USERNAME}:${encrypted_root_pw}" | chpasswd --encrypted passwd --lock root else # حذف كلمة مرور غير صالحة للمستخدم في حالة استخدام المفاتيح بحيث يمكن تعيين كلمة مرور جديدة دون تقديم قيمة سابقة # Delete invalid password for user if using keys so that a new password # can be set without providing a previous value passwd --delete "${USERNAME}" fi # إنهاء صلاحيات المستخدم العادي لإجباره على تغييرها # Expire the sudo user's password immediately to force a change change --lastday 0 "${USERNAME}" # sudo للمستخدم SSH إنشاء مجلد # Create SSH directory for sudo user home_directory="$(eval echo ~${USERNAME})" mkdir --parents "${home_directory}/.ssh" # نسخ ملف المفاتيح من الحساب الجذري إذا كان ضروريًا # Copy `authorized_keys` file from root if requested if [ "${COPY_AUTHORIZED_KEYS_FROM_ROOT}" = true ]; then cp /root/.ssh/authorized_keys "${home_directory}/.ssh" fi # إضافة المفاتيح الإضافية المتوفرة # Add additional provided public keys for pub_key in "${OTHER_PUBLIC_KEYS_TO_ADD[@]}"; do echo "${pub_key}" >> "${home_directory}/.ssh/authorized_keys" done # SSH ضبط تكوينات ملكية وصلاحيات # Adjust SSH configuration ownership and permissions chmod 0700 "${home_directory}/.ssh" chmod 0600 "${home_directory}/.ssh/authorized_keys" chown --recursive "${USERNAME}":"${USERNAME}" "${home_directory}/.ssh" # إيقاف تسجيل الدخول للحساب الجذري باستعمال كلمة المرور # Disable root SSH login with password sed --in-place 's/^PermitRootLogin.*/PermitRootLogin prohibit-password/g' /etc/ssh/sshd_config if sshd -t -q; then systemctl restart sshd fi # SSH بعد إضافة استثناءات UFW تفعيل جدار الحماية # Add exception for SSH and then enable UFW firewall ufw allow OpenSSH ufw --force enable خاتمة يمكن أن يوفر لك إعداد الخادم الأولي تلقائيًا بعض الوقت ويمنحك أساسًا جيدًا لمزيد من الإعداد. إذا كانت هناك خطوات إضافية ترغب في اتخاذها، فيمكنك إما تسجيل الدخول بعد تشغيل السكربت للمتابعة يدويًا، أو إضافة الخطوات في نهاية السكربت لأتمتة العملية. ترجمة -وبتصرف- للمقال Automating Initial Server Setup with Ubuntu 18.04 لصاحبه Justin Ellingwood
  5. يهدف هذا الدليل إلى أن يكون مصدرًا ونقطة بداية لتشخيص واستكشاف مشاكل وإعدادات MySQL للتمهيد لحلها. سنتناول بعضًا من المشاكل التي يتعرّض لها كثيرون من مستخدمي MySQL ونوفّر إرشادات لكيفية تشخيصها وحلها. سيشتمل الدليل كذلك على روابط لمقالات من أكاديميّة حسوب ومن التوثيق الرسمي للاستفادة منها. كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL كيف تشخّص أخطاء المقابس Sockets في MySQL يحدُث أحيانًا أن تُصاب جداول MySQL بأعطاب سببها أخطاء أدّت إلى استحالة قراءة البيانات الموجودة بها. تؤدّي محاولة قراءة بيانات من جداول معطوبة – عادةً - إلى انهيّار الخادوم. في ما يلي بعضٌ من الأسباب الشائعة لعطب الجداول: توقّف خادوم MySQL أثناء عمليّة كتابة في جدول. التعديل، من طرف برنامج خارجي، على جدول يُعدِّل عليه الخادوم في نفس الوقت. إيقاف الخادوم دون سابق إنذار. إخفاق في عتاد الحاسوب. وجود علّة في برمجة MySQL. إذا كنت تظنّ أنّ أحد جداول قاعدة البيانات معطوب، فيجب أخذ نسخة احتيّاطيّة من مجلّد البيانات قبل تشخيص عطب الجدول أو محاولة إصلاحه.يُساعد هذا الإجراء في التقليل من خطر فقد البيانات. أول ما يجب عليك فعلُه هو إيقاف خدمة MySQL: sudo systemctl stop mysql ثم نقل جميع بيانات MySQL إلى مجلّد جديد. يوجد المجلّد المبدئي لبيانات MySQL بالنسبة للخواديم العاملة بتوزيعة أوبونتو في المسار ‎/var/lib/mysql/‎: cp -r /var/lib/mysql /var/lib/mysql_bkp تصبح جاهزًا – بعد إنشاء نسخة احتيّاطيّة – للبدء في التحقيق لمعرفة ما إذا كان الجدول معطوبًا أم لا. إذا كان الجدول يستخدم محرّك التخزين (MyISAM) فيمكن التحقّق من عطب الجدول باستخدام التعليمة CHECK TABLE من سطر أوامر MySQL: CHECK TABLE table_name; ستظهر رسالة في مُخرجات الأمر تخبرك ما إذا كان الجدول معطوبًا أم لا. إذا كان جدول MyISAM معطوبًا فيمكن عادةً إصلاحه بتنفيذ التعليمة REPAIR TABLE: REPAIR TABLE table_name; ستُظهرمخرجات التعليمة - بافتراض أنّ عمليّة الإصلاح ناجحة – رسالة تشبه التالي: +--------------------------+--------+----------+----------+ | Table | Op | Msg_type | Msg_text | +--------------------------+--------+----------+----------+ | database_name.table_name | repair | status | OK | +--------------------------+--------+----------+----------+ أما إذا لم تنجح العمليّة وبقي الجدول معطوبًا فإنّ توثيق MySQL يقترح بضعة طرق بديلة من أجل إصلاح الجداول المعطوبة. بالنسبة للجداول التي تستخدم محرّك التخزين InnoDB، فعمليّة الإصلاح مختلفة. بدأت قواعد بيانات MySQL باستخدام محرّك InnoDB - الذي يتميّز بتوفّرعمليّات التحقّق التلقائي من الأعطال وإصلاحها - مبدئيًّا منذ الإصدر 5.5. يتحقّق InnoDB من وجود أعطال في الصفحات بحساب مجموع تحقّق (Checksum) لكلّ صفحة يقرأها؛ وفي حال وجود اختلاف بين مجموعات التحقّق فإنّه يوقف خادوم MySQL تلقائيّا. نادرًا مّا توجد حاجة لإصلاح الأعطاب في الجداول التي تستخدم InnoDB، إذ أنّ InnoDB لديه آليّة يمكنها حلّ أغلب الأعطاب عند إعادة تشغيل الخادوم. على الرغم من ذلك، ينصح توثيق MySQL باستخدام طريقة "الطرح وإعادة التحميل" Dump and reload. يعني هذا إعادة الوصول إلى الجدول المعطوب باستخدام الأداة mysqldump، لإنشاء نسخة احتيّاطيّة منطقيّة من الجدول يُحتَفظ فيها ببُنيته وبياناته؛ ثم إعادة تحميل الجدول إلى قاعدة البيانات. حاول إعادة تشغيل خدمة MySQL لترى إن كنت ستتمكّن من الوصول إلى الخادوم، مع استحضار آلية عمل طريقة "الطرح وإعادة التحميل: sudo systemctl restart mysql إنْ استمرّ انهيّار الخادوم أو استحال الوصول إليه لسبب آخر، فربّما يكون من المفيد تفعيل الخيّار force_recovery ضمن عدادات InnoDB. يمكن فعلُ ذلك بسهولة عبر تحرير الملفّ mysqld.cnf: sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf أضف السّطر التالي في المقطع [mysqld] من الملفّ: . . . [mysqld] . . . innodb_force_recovery=1 احفظ الملفّ ثم أغلقه، ثم جرّب إعادة تشغيل MySQL من جديد. إذا استطعت النجاح في الوصول إلى الجدول المعطوب فاستخدم الأداة mysqldump لطرح بيانات الجدول في ملفّ جديد. يمكنك تسميّة الملفّ بما يحلو لك. في ما يلي اخترنا الاسم out.sql: mysqldump database_name table_name > out.sql الخطوة المواليّة هي حذف الجدول من قاعدة البيانات. يمكنك استخدام الصيغة التاليّة لتفادي فتح سطرأوامر MySQL من جديد: mysql -u user -p --execute="DROP TABLE database_name.table_name" أخيرًا، استعد الجدول بالاعتماد على ملفّ الطرح الذي أنشاته للتو: mysql -u user -p < out.sql فليكن في علمك أن محرّك التخزين InnoDB – في العموم – أكثر مقاومةً للأخطاء من محركّ التخزين القديم MyISAM. لا يعني هذا أنّ جداول InnoDB غير معرَّضة للأخطاء، لكن وجود ميزات التغلّب على الأخطاء تلقائيّا يجعل احتمال عطب الجداول وما ينتج عنه من انهيّارات أقلّ بكثير. ترجمة - بتصرّف - للمقال How To Fix Corrupted Tables in MySQL لصاحبه Mark Drake. اقرأ أيضًا المقال التالي: كيف تشخّص أخطاء المقابس Sockets في MySQL
  6. يهدف هذا الدليل إلى أن يكون مصدرًا ونقطة بداية لتشخيص واستكشاف مشاكل وإعدادات MySQL للتمهيد لحلها. سنتناول بعضًا من المشاكل التي يتعرّض لها كثيرون من مستخدمي MySQL ونوفّر إرشادات لكيفية تشخيصها وحلها. سيشتمل الدليل كذلك على روابط لمقالات من أكاديميّة حسوب ومن التوثيق الرسمي للاستفادة منها. كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL كيف تشخّص أخطاء المقابس Sockets في MySQL يُدير خادوم MySQL الاتّصالات القادمة إلى قاعدة البيانات اعتمادًا على ملفّ مقبس Socket، وهو نوعٌ خاصّ من الملفّات يسهّل التواصل بين عمليّات Processes مختلفة. يحمل الملفّ المقبس الخاصّ بخادوم MySQL اسمَ mysqld.sock ويوجد عادةً - بالنسبة للخواديم العاملة بتوزيعة أوبنتو – في المجلّد ‎/var/run/mysqld/‎. تُنشئ خدمة MySQL هذا الملفّ تلقائيّا. تتسبّب التعديلات على نظام التشغيل أو على إعدادات MySQL في عدم تمكّن MySQL من قراءة الملفّ المقبس، ممّا يمنع الوصول إلى قواعد البيانات على الخادوم. يظهر خطأ المقبس الشائع على النحو التالي: ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2) توجد بضعة أمور تتسبّب في الخطأ أعلاه، وخيّارات محدودة لإصلاحه. أحد الأسباب الشائعة هي توقيف خدمة MySQL أو الإخفاق في تشغيلها؛ بمعنى أنّ الخدمة لم تستطع إنشاء الملفّ المقبس ابتداءً. حاول تشغيل الخدمة بالأداة systemctl لمعرفة ما إذا كان هذا هو سبب ظهور الخطأ: sudo systemctl start mysql ثم حاول الوصول إلى سطر أوامر MySQL من جديد. تأكّد - إن استمرّ الخطأ في الظهور – من المسار الذي يبحث فيه MySQL عن الملفّ المقبس. يمكن معرفة هذا المسار من خلال ملفّ الإعداد mysqld.cnf: sudo nano /etc/mysql/mysql.conf.d/mysql.cnf ابحث عن المُعامل socket ضمن المقطع [mysqld] من الملفّ. يبدو المقطع المذكور كالتالي: . . . [mysqld] user = mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock port = 3306 . . . أغلق الملفّ ثم تأكّد من وجود الملفّ mysqld.sock بتنفيذ الأمر ls على المسار الذي يتوقّع MySQL وجودَ الملفّ فيه: ls -a /var/run/mysqld/ إذا كان الملفّ المقبس موجودًا فسيظهر في مُخرجات الأمر: . .. mysqld.pid mysqld.sock mysqld.sock.lock إذا لم يكن الملفّ موجودًا فربما يكون السبب هو أنّ MySQL يحاول إنشاءه دون أن تكون لديه الصلاحيّات المناسبة لذلك. يمكن التأكّد صحّة الصلاحيّات بتغيير ملكيّة المجلّد إلى المستخدم والمجموعة mysql: sudo chown mysql:mysql /var/run/mysqld/ ثم تأكّد بعد ذلك من أنّ المستخدم mysql لديه الصلاحيّات المناسبة على المجلّد. تصلُح الصلاحيّات 755 لأغلب الحالات: sudo chmod -R 755 /var/run/mysqld/ أخيرًا؛ أعد تشغيل MySQL لترى ما إذا كان يستطيع إنشاء الملفّ المقبس من جديد: sudo systemctl restart mysql ثم حاول الوصول إلى سطر أوامر MySQL. إن استمرّ خطأ المقبس في الظهور، فقد يشير ذلك إلى وجود مشكلة أعمق في خادوم MySQL، وفي هذه الحالة تجب مراجعة سجلّات الأخطاء بحثًا عن ما يقود إلى معرفة سبب المشكلة. ترجمة – بتصرّف – للمقال How To Troubleshoot Socket Errors in MySQL لصاحبه Mark Drake. اقرأ أيضًا كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL
  7. يهدف هذا الدليل إلى أن يكون مصدرًا ونقطة بداية لتشخيص واستكشاف مشاكل وإعدادات MySQL للتمهيد لحلها. سنتناول بعضًا من المشاكل التي يتعرّض لها كثيرون من مستخدمي MySQL ونوفّر إرشادات لكيفية تشخيصها وحلها. سيشتمل الدليل كذلك على روابط لمقالات من أكاديميّة حسوب ومن التوثيق الرسمي للاستفادة منها. كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL كيف تشخّص أخطاء المقابس Sockets في MySQL يعدّ التوقّف عن العمل، أو الإخفاق في تشغيل الخدمة بسبب نقص حجم الذاكرة، أكثرَ أسباب الانهيّارات التي تواجهها MySQL شيوعا. ستحتاج – من أجل التحقّق من الأمر – إلى مراجعة سجل أخطاء MySQL بعد الانهيار. أوّلًا، ابدأ بمحاولة تشغيل خادوم MySQL بكتابة الأمر: sudo systemctl start mysql ثم راجع سجلّات الأخطاء لمعرفة سبب انهيّار MySQL. يمكن استخدام الأمر less لمراجعة السجلّات صفحةً صفحة: sudo less /var/log/mysql/error.log رسالتا الخطأ Out of memory و mmap can’t allocate من الرسائل الشائعة التي تنمّ عن نقص في الذاكرة. في ما يلي بعضٌ من الحلول المحتملة لنقص حجم الذاكرة: تحسين إعدادات MySQL. الأداة مفتوحة المصدر MySQLtuner رائعة لهذا الغرض. يؤدّي تنفيذ سكربت MySQLtuner إلى إظهار مجموعة من التعديلات الموصى بإجرائها على ملف إعدادات MySQL‏ (mysqld.cnf). يُرجى الانتباه إلى أنّه كل ما طالت مدّة تشغيل خادوم MySQL قبل استخدام MySQLTuner كانت اقتراحات السكريبت أحسن. استخدم حاسبة MySQL هذه لتقدير حجم الذاكرة المطلوب بالنسبة لكلّ من إعداداتك الحاليّة وتلك التي يقترحها MySQLtuner. التقليل من اعتماد تطبيق الويب على MySQL في تنزيل الصفحات. إضافة نظام تخزين مؤقّت (Cache) إلى التطبيق هو الوسيلة المعتادة لهذا الأمر. من أمثلة استخدام هذه الطريقة نظامُ Joomla الذي يتضمّن وظيفة تخبئة مُضمَّنة يمكن تفعيلها، والإضافة WP Super Cache التي تضيف خاصيّة التخبئة إلى ووردبريس. الترقيّة إلى خادوم افتراضي ذي قدرات أكبر. يُنصَح بخادوم لا تقلّ ذاكرة الوصول العشوائي فيه عن 1GB مهما كانت نوعيّة استخدام قاعدة البيانات MySQL، إلّا أنّ حجم البيانات ونوعيّتها يمكن أن يؤثر كثيرًا على متطلّبات الذاكرة. يُرجى الانتباه إلى أنّ ترقيّة الخادوم، رغم أنّها من المرجّح أن تحلّ المشكلة، إلّا أنه حلّ لا يُنصَح به إلّا بعد التحقّق من الخيّارات الأخرى ومعرفة احتمالات نجاحها. تكلّف إضافة قدرات أكبر إلى الخادوم سعرًا أعلى ويجدر بك اعتماد هذا الخيّار إلّا إذا كان الحلّ الأمثل في نهاية المطاف. انتبه أيضًا إلى أنّ توثيق MySQL يتضمّن اقتراحات أخرى لتشخيص الانهيّارات ومنع حدوثها. ترجمة – بتصرّف – للمقال How To Address Crashes in MySQL لصاحبه Mark Drake. اقرأ أيضًا المقال التالي: كيف تصلح الجداول المعطوبة في MySQL
  8. يهدف هذا الدليل إلى أن يكون مصدرًا ونقطة بداية لتشخيص واستكشاف مشاكل وإعدادات MySQL للتمهيد لحلها. سنتناول بعضًا من المشاكل التي يتعرّض لها كثيرون من مستخدمي MySQL ونوفّر إرشادات لكيفية تشخيصها وحلها. سيشتمل الدليل كذلك على روابط لمقالات من أكاديميّة حسوب ومن التوثيق الرسمي للاستفادة منها. كيف تعرض سجلّات الأخطاء وتشخّص الاستعلامات في MySQL كيف تسمح بالاتّصال عن بعد بقاعدة بيانات MySQL كيف تعالج انهيار خادوم MySQL كيف تصلح الجداول المعطوبة في MySQL كيف تشخّص أخطاء المقابس Sockets في MySQL تبدأ الكثير من المواقع والتطبيقات بوضع خادوم الويب وقاعدة البيانات على نفس النظام؛ إلّا أنّ هذا الإعداد يصبح مع الوقت ثقيلًا وغير قابل للتوسيع.. من الشائع التغلّب على هذا الإشكال بفصل الوظيفتيْن بضبط قاعدة بيانات منفصلة ممّا يسمح لخادوم الوِيب وقاعدة البيانات بالنموّ على الأجهزة الخاصّة بها، كلّ حسب حاجته. يعدّ الإعداد المبدئيّ لـ MySQL الذي لا يسمح سوى بالاتّصالات القادمة من نفس الجهاز، وبالتالي يمنع الاتّصالات القادمة من نظام آخر، أحد المشاكل الشائعة التي يواجهها المستخدمون أثناء محاولة إعداد الوصول إلى MySQL عن بعد. يمكن تعديل السلوك المبدئيّ لـ MySQL بتحرير الملف mysqld.cnf: sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf انتقل إلى السطر الذي يبدأ بالتوجيه bind-address. يبدو الملفّ على النحو التالي: . . . lc-messages-dir = /usr/share/mysql skip-external-locking # # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. bind-address = 127.0.0.1 . . . يأخذ التوجيه مبدئيًّا القيمة 127.0.0.1، ممّا يعني أنّ الخادوم سيبحث عن الاتّصالات المحليّة فقط. ستحتاج لتعديل هذه القيمة لتكون عنوان IP خارجي (عنوان الجهاز الذي تريد التواصل معه). كما يمكن استخدام حرف بدل (Wildcard) لأغراض التشخيص والبحث عن الأخطاء. مثلًا: *، أو ::، أو 0.0.0.0: . . . lc-messages-dir = /usr/share/mysql skip-external-locking # # Instead of skip-networking the default is now to listen only on # localhost which is more compatible and is not less secure. bind-address = 0.0.0.0 . . . ملحوظة: لا يحتوي الملف mysqld.cnf في الإصدار 8 وما بعده من MySQL – مبدئيًّا – على التوجيه bind-address؛ لذا تجب إضافة التوجيه أسفل آخر سطر من الملفّ على النحو التالي: [mysqld] pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock datadir = /var/lib/mysql log-error = /var/log/mysql/error.log bind-address = 0.0.0.0 احفظ الملفّ بعد تعديله، ثم أغلقه وأعد تشغيل خدمة MySQL: sudo systemctl restart mysql حاول بعد ذلك الوصول إلى قاعدة البيانات من جهاز آخر كما يلي (حيث database_server_ip عنوان IP الخاص بخادوم قاعدة البيانات): mysql -u user -h database_server_ip -p إن استطعت الاتّصال بقاعدة البيانات، فهذا يؤكّد أن التوجيه bind-address كان السبب في تعذّر الاتّصال عن بعد بقاعدة البيانات. يُرجى ملاحظة أن القيمة 0.0.0.0 غير آمنة وتتيح الاتّصال بقاعدة البيانات من جميع عناوين IP. أمّا إن لم تستطع الاتّصال بقاعدة البيانات بعد ضبط إعداد bind-address، فهذا يعني أن السبب أمر آخر. قد ترغب - في الحالتين – في التعرّف على كيفيّة إعداد اتّصال بعيد أكثر أمانًا، والتي يشرحها المقال كيفية إعداد قاعدة بيانات بعيدة لتحسين أداء موقع يستخدِم MySQL. ترجمة – بتصرّف – للمقال How To Allow Remote Access to MySQ لصاحبه Mark Drake. اقرأ أيضًا المقال التالي: كيف تعالج انهيار خادوم MySQL
  9. PostgreSQL، هو نظام متطور مفتوح المصدر، كائنيّ الارتباط-Object Relational ﻹدارة قواعد البيانات، وهو نظام قابل للتوسع، بمعنى أنه يستطيع معالجة اﻷحمال المختلفة بدءًا من تطبيقات جهاز واحد إلى خدمات الويب التجارية التي تتعامل مع مستخدمين كثر في نفس الوقت. وهذا النظام Transactional أي يعامل النقل المتسلسل للبيانات -مثل تحديث قاعدة البيانات- كوحدة واحدة لضمان سلامتها، ويحقق خصائص ACID (Atomicity – Consistency – Isolation – Durability). وكذلك يدعم قسمًا كبيرًا من معايير SQL. فائدة: خصائص ACID هي أربعة خصائص يجب توافرها في تعاملات قواعد البيانات، وهي الذرية-Atomicity -أن تُنفَّذ العملية كوحدة واحدة-، والتناسق-Consistency، والعزل-Isolation، والثبات-Durability. ويوفر PostgreSQL العديد من المزايا مثل: الاستعلامات المعقدة-Complex Queries المفاتيح الأجنبية-Foreign Keys المشاهدات القابلة للتحديث-Updatable Views سلامة عمليات نقل البيانات-Transactional Integrity التحكم في التزامن متعدد الإصدارات-Multiversion Concurrency Control وذكرنا قبل قليل أنه قابل للتمدد والتوسع بواسطة مستخدميه عبر إضافة دوال-Functions جديدة، ومشغّلات-operators، وأنواع بيانات، وطرق فهرسة، ولغات إجرائية-Procedural Languages. ويقدّم PostgreSQL طرقًا عديدة لتكرار قاعدة بيانات، وسنتعلم في هذا الدليل كيفية إعداد تكرار من نوع (الرئيسي-Master/الثانوي-Slave)، وهي عملية مزامنة بين قاعدتي بيانات من خلال النسخ من قاعدة بيانات على خادم (الرئيسي) إلى قاعدة بيانات أخرى في خادم آخر (الثانوي)، وسننفذ هذه العملية على خادم يعمل بتوزيعة أوبنتو 16.04. المتطلبات أن يكون PostgreSQL 9.6 مثبتًا على خادم أوبنتو 16.04 إعداد UFW ثبّت جدار الحماية الناري غير المعقّد-Uncomplicated Firewall على خوادم أوبنتو، وهو أداة ﻹدارة جدار الحماية المعتمِد على iptables. استخدم الأمر التالي في الطرفية: # apt-get install -y ufw واﻵن، أضف PostgreSQL وخدمة SSH إلى جدار الحماية، عبر تنفيذ اﻷمر التالي: # ufw allow ssh # ufw allow postgresql فعّل جدار الحماية: # ufw enable إعداد خادم PostgreSQL الرئيسي سيمتلك الخادم الرئيسي صلاحيات القراءة والكتابة لقاعدة البيانات، وسيكون هو القادر على نقل البيانات إلى الخادم الثانوي. • افتح محررًا نصيًا وعدّل إعدادات PostgreSQL الرئيسية كما يلي: (ملاحظة: استبدل EDITOR$ بالمحرر النصي الذي تفضّله) # $EDITOR /etc/postgresql/9.6/main/postgresql.conf أزل التعليق (#) من سطر listen_addresses وأضف عنوان IP للخادم الرئيسي: listen_addresses = 'master_server_IP_address' واﻵن، أزل التعليق من سطر wal_level لتغيير قيمته: wal_level = hot_standby وأزل التعليق من السطر التالي كي تستخدم المزامنة المحلية-Local Syncing لمستوى المزامنة "Synchronization Level" synchronous_commit = local ثم أزل التعليق من السطرين التاليين، وعدلهما كما يلي، بما أننا نستخدم خادمين: max_wal_senders = 2 wal_keep_segments = 10 واﻵن احفظ الملف وأغلقه. عدّل ملف pg_hba.conf من أجل إعدادات التوثيق-Authentication، كما يلي: افتح الملف عبر هذا الأمر: # $EDITOR /etc/postgresql/9.6/main/pg_hba.conf الصق الإعدادات التالية: # Localhost host replication replica 127.0.0.1/32 md5 # PostgreSQL Master IP address host replication replica master_IP_address/32 md5 # PostgreSQL SLave IP address host replication replica slave_IP_address/32 md5 احفظ الملف وأغلقه، ثم أعد تشغيل PostgreSQL باستخدام systemctl # systemctl restart postgresql إنشاء مستخدم من أجل التكرار سننشئ مستخدم PostgreSQL من أجل عملية التكرار، فسجّل الدخول أولًا إلى حساب المستخدم المسمّىpostgres وافتح صدفة PostgreSQL، من خلال الأوامر التالية: # su - postgres $ psql أنشئ مستخدمًا جديدًا: postgres=# CREATE USER replica REPLICATION LOGIN ENCRYPTED PASSWORD 'usr_strong_pwd'; أغلق الصَّدَفة، وهكذا تنتهي إعدادات الخادم الرئيسي. إعداد الخادم الثانوي لن تكون للخادم الثانوي صلاحيات الكتابة في قاعدة البيانات، وستكون وظيفته الوحيدة هي استقبال البيانات من الخادم الرئيسي، أي ستكون له صلاحية القراءة فقط. • سنوقف أولًا خدمة PostgreSQL: # systemctl stop postgresql افتح ملف الإعدادات الرئيسية لـ PostgreSQL: # $EDITOR /etc/postgresql/9.6/main/postgresql.conf أزل التعليق من سطر listen_addresses وغيّر قيمته: listen_addresses = 'slave_IP_address' أزل التعليق من سطر wal_level وغيّره كما يلي: wal_level = hot_standby وأزل التعليق أيضًا من سطر synchronous_commit كما في الخادم الرئيسي للاستفادة من المزامنة المحلية-local syncing: synchronous_commit = local ثم أزل التعليق من السطرين التاليين وغيّر قيمهما كما يلي: max_wal_senders = 2 wal_keep_segments = 10 أزل التعليق من السطر التالي وغيّر قيمته كما يلي من أجل تفعيل hot_standby للخادم الثانوي: hot_standby = on احفظ الملف وأغلقه. نسخ البيانات من الخادم الرئيسي إلى الثانوي لكي نزامن بيانات الخادم الرئيسي مع الثانوي، فيجب أن يحل المجلد الأساسي “main” في الخادم الرئيسي محل المجلد الرئيسي في الخادم الثانوي، ونفعل هذا كما يلي: • سجل الدخول إلى مستخدم postgres: # su – postgres خذ نسخة احتياطية من مجلد البيانات الفعلي: $ cd/var/lib/postgresql/9.6/ $ mv main main_bak أنشئ مجلد أساسيًا جديدًا: $ mkdir main/ غيّر صلاحياته: $ chmod 700 main وهنا، انسخ المجلد الأساسي من الخادم الرئيسي إلى الخادم الثانوي باستخدام pg_basebackup: # pg_basebackup -h master_IP_address -U replica -D /var/lib/postgresql/9.6/main /-P –xlog وحين ينتهي النسخ، أنشئ ملف recovery.conf داخل المجلد الأساسي "main” وانسخ المحتوى التالي فيه: standby_mode = 'on' primary_conninfo = 'host=10.0.15.10 port=5432 user=replica password=usr_strong_pwd' trigger_file = '/tmp/postgresql.trigger.5432' واﻵن، احفظ الملف وأغلقه، ثم غير صلاحياته كما يلي: # chmod 600 recovery.conf شغّل خدمة PostgreSQL: # systemctl start postgresql وهنا تنتهي إعدادات الخادم الثانوي. الخلاصة لقد رأينا في هذا الدليل المبسّط كيفية ضبط تكرار Master/Slave في PostgreSQL عبر استخدام خادمين يعملان بأوبنتو. وهذه الطريقة في التكرار ما هي إﻻ إحدى طرق عديدة يوفرها نظام PostgreSQL لإدارة قواعد البيانات. ترجمة -بتصرف- لمقال PostgreSQL Replication on Ubuntu Tutorial لصاحبه Giuseppe Molica
×
×
  • أضف...