design-patterns - كيفية تنفيذ دفق النشاط في شبكة اجتماعية




stream social-networking (6)

ملخص : بالنسبة إلى حوالي مليون مستخدم نشط و 150 مليون نشاط مخزّن ، أظل بسيطًا:

  • استخدم قاعدة بيانات علائقية لتخزين الأنشطة الفريدة (سجل واحد لكل نشاط / "الشيء الذي حدث") اجعل السجلات مدمجة قدر الإمكان. البنية بحيث يمكنك الحصول بسرعة على مجموعة من الأنشطة حسب معرف النشاط أو باستخدام مجموعة من معرفات الأصدقاء مع قيود الوقت.
  • قم بنشر معرفات النشاط إلى Redis عند إنشاء سجل النشاط ، وإضافة المُعرِّف إلى قائمة "تدفق النشاط" لكل مستخدم لديه صديق / مشترك يجب أن يرى النشاط.

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

أستخدم جدول MySQL قديم بسيط للتعامل مع حوالي 15 مليون نشاط.

يبدو شيء من هذا القبيل:

id             
user_id       (int)
activity_type (tinyint)
source_id     (int)  
parent_id     (int)
parent_type   (tinyint)
time          (datetime but a smaller type like int would be better) 

يخبرني النشاط source_id بنوع النشاط ، source_id بالسجل الذي يرتبط به النشاط. لذلك إذا كان نوع النشاط يعني "مفضلة مفضلة" ، فأنا أعلم أن source_id يشير إلى معرف السجل المفضل.

تُعد parent_id / parent_type مفيدة parent_type - فهي تخبرني بما يرتبط به النشاط. إذا كان أحد الكتب مفضلاً ، فسيخبرني parent_id / parent_type أن النشاط يتعلق بكتاب (نوع) مع مفتاح أساسي محدد (id)

أنا مؤشر على (user_id, time) والاستعلام عن الأنشطة التي هي user_id IN (...friends...) AND time > some-cutoff-point . قد يكون التخلص من الهوية واختيار فهرس مجمع مختلف فكرة جيدة - لم أقم بتجربة ذلك.

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

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

  • قم بإنشاء سجل نشاط MySQL
  • لكل صديق للمستخدم الذي أنشأ النشاط ، ادفع المعرّف إلى قائمة الأنشطة الخاصة به في Redis.
  • تقليم كل قائمة إلى آخر X العناصر

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

للحصول على شرح أكثر تفصيلاً لما أتحدث عنه ، انظر مثال تويتر على موقع http://redis.io/topics/twitter-clone : http://redis.io/topics/twitter-clone

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

تحديث تموز 2014 نحن نصل إلى حوالي 700 ألف مستخدم نشط شهريًا. خلال السنتين الماضيتين ، كنت أستخدم Redis (كما هو موضح في القائمة ذات التعداد النقطي) لتخزين آخر 1000 معرّف نشاط لكل مستخدم. عادة ما يكون هناك حوالي 100 مليون سجل نشاط في النظام ولا تزال مخزنة في MySQL وما زالت بنفس التصميم. تتيح لنا هذه السجلات الحصول على ذاكرة أقل من Redis ، فهي بمثابة سجل لبيانات النشاط ، ونحن نستخدمها إذا احتاج المستخدمون إلى العودة إلى الصفحة مرة أخرى للعثور على شيء ما.

لم يكن هذا حلاً ذكيًا أو مثيرًا للاهتمام بشكل خاص ولكنه خدمني بشكل جيد.

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


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

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

http://blog.linkedin.com/2011/01/11/open-source-linkedin-kafka/

http://incubator.apache.org/kafka/index.html


بدلاً من أن تتجول بنفسك ، يمكنك البحث عن خدمة تابعة لجهة خارجية يتم استخدامها عبر واجهة برمجة التطبيقات. لقد بدأت واحد يسمى Collabinate ( http://www.collabinate.com ) الذي يحتوي على خلفية قاعدة بيانات الرسم البياني وبعض خوارزميات متطورة إلى حد ما للتعامل مع كميات كبيرة من البيانات بطريقة متزامنة عالية الأداء. على الرغم من عدم توفر نطاق الوظائف الذي يقول Facebook أو Twitter ، إلا أنه أكثر من كافٍ لمعظم حالات الاستخدام حيث تحتاج إلى بناء تدفقات الأنشطة أو الخلاصات الاجتماعية أو وظائف تدوين المصغر في تطبيق ما.


هذا هو تنفيذ دفق النشاط ، باستخدام mysql. هناك ثلاث فئات: النشاط ، ActivityFeed ، المشترك.

يمثل النشاط إدخال نشاط ، ويبدو جدوله كما يلي:

id
subject_id
object_id
type
verb
data
time

Subject_id هو معرف الكائن الذي يقوم بتنفيذ الإجراء ، object_id معرف الكائن الذي يتلقى الإجراء. type verb يصف الإجراء نفسه (على سبيل المثال ، إذا كان المستخدم يضيف تعليقًا إلى مقالة ، فسيكون "تعليق" و "تم إنشاؤه" على التوالي) ، تحتوي البيانات على بيانات إضافية لتجنب الصلات (على سبيل المثال ، يمكن أن تحتوي على اسم الموضوع واللقب ، وعنوان المقالة وعنوان URL ، وجسم التعليق وما إلى ذلك).

ينتمي كل نشاط إلى واحد أو أكثر من ActivityFeeds ، وترتبط بجدول يبدو كالتالي:

feed_name
activity_id

في طلبي لدي خلاصة واحدة لكل مستخدم وخلاصة واحدة لكل عنصر (عادةً ما تكون المقالات في المدونات) ، ولكن يمكن أن يكون كل ما تريد.

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

كل مشترك ينتمي إلى واحد أو أكثر من ActivityFeeds ، وكما هو موضح أعلاه ، فهي مرتبطة بجدول ارتباط من هذا النوع:

feed_name
subscriber_id
reason

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

لاسترداد نشاط مشترك ، أقوم بإجراء ارتباط بسيط بالجداول الثلاثة. الانضمام سريع لأنني اخترت بعض الأنشطة بفضل حالة WHERE التي تبدو الآن - time > some hours . أتجنب الصلات الأخرى بفضل حقل البيانات في جدول النشاط.

مزيد من التوضيح في مجال reason . إذا أردت ، على سبيل المثال ، تصفية إجراءات إشعارات البريد الإلكتروني للمستخدم ، وقام المستخدم بوضع إشارة مرجعية على مشاركة مدونة (وبالتالي فهو يشترك في خلاصة النشر مع "الإشارة المرجعية") ، لا أريد أن يتلقى المستخدم إشعارات البريد الإلكتروني حول الإجراءات على هذا العنصر ، في حين أنه إذا قام بالتعليق على المشاركة (وبالتالي فإنه يشترك في تغذية النشر مع "تعليق" سبب) أريد أن يتم إبلاغه عندما يضيف المستخدمون الآخرون تعليقات على نفس المشاركة. يساعدني مجال العقل في هذا التمييز (قمت بتطبيقه من خلال فئة ActivityFilter) ، إلى جانب تفضيلات الإشعارات للمستخدم.


أعتقد أنه يمكن العثور على تفسير حول كيفية عمل نظام الإشعارات على مواقع الويب الكبيرة في سؤال تجاوز سعة المكدس ، كيف تقوم مواقع الشبكات الاجتماعية باحتساب تحديثات الأصدقاء؟ في إجابة جيريمي وول . يقترح استخدام رسالة Qeue وهو يشير إلى برنامجين مفتوحين المصدر ينفذهما :

  1. RabbitMQ
  2. Apache QPid

انظر أيضا السؤال ما هي أفضل طريقة لتنفيذ تيار النشاط الاجتماعي؟


كما توضح Tilendor في إجابة Jon Skeet ، تحتوي التدفقات على أسلوب CopyTo منذ. NET 4.

var fileStream = File.Create("C:\\Path\\To\\File");
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
myOtherObject.InputStream.CopyTo(fileStream);
fileStream.Close();

أو using صيغة الجملة:

using (var fileStream = File.Create("C:\\Path\\To\\File"))
{
    myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
    myOtherObject.InputStream.CopyTo(fileStream);
}




design-patterns stream social-networking