اطلاعات جالبی از بازدید کنندگان وب سایت نمایشگاه کتاب تهران

هر چند بازدیدهای فیزیکی از نمایشگاه کتاب تهران بیشتر از بازدیدهای وب سایت آن است ولی مراجعه کنندگان به این وب سایت یک نمونه بسیار خوب از بازدیدکنندگان عمومی وب سایت‌ها در ایران هستند.

به همین دلیل اطلاعات به درد بخور از این بازدیدکنندگان را منشتر می‌کنم. که برخی از آن‌ها شامل اطلاعات جالبی هستند و حتی می‌توان بر اساس آن‌ها تصمیمات استراتژیک بر روی تکنولوژی‌های مختلف را اتخاذ کرد. به هر حال من سعی کردم نتایج اخلاقی هر آمار را استخراج کنم!

بازه زمانی این آمار از ۶ تا ۱۰ اردبیهشت ۱۳۹۳ می‌باشد. نمایشگاه کتاب ۱۰ اردیبهشت افتتاح شد و ۲۰م به پایان رسید.

این آمار مربوط به بازدید کنندگان منحصر به فرد است که در این بازه زمانی حدود ۳۰۴ هزار نفر بوده‌اند.

بازدید کنندگان جهانی

countries

بیشتر بازدیدکنندگان از ایران بودند ولی با وجود اینکه میهمان ویژه نمایشگاه امسال افغانستان بود بازدیدکنندگان آمریکا و اروپا و خیلی جاهای دیگر، بیشتر از افغانستان بودند 😀

نتیجه اخلاقی: استفاده از ابزارهای دورزنی انقدر مرسوم شده که حتی مراجعه به وب سایت‌های داخلی نیز بدون خاموش کردن آن‌ها صورت می‌گیرد.

بازدید کنندگان ایرانی

iran

طبق آمار، بازدیدهای اصفهان بیشتر از هر جای دیگر حتی تهران بوده است.

نتیجه اخلاقی: دو سناریو برای این موضوع وجود دارد؛ یکی اینکه در اصفهان کتاب از ارزش خاصی برخوردار است و اصفهانی‌ها ابتدا در سایت به دنبال کتاب مورد نظر خود می‌گشتند تا اگر در نمایشگاه بود به نمایشگاه مراجعه کنند. دیگری اینکه خطوط مخابراتی اصلی اینترنت از اصفهان عبور می‌کنند و تپولوژی شبکه کشور به گونه‌ای است که به نظر می‌رسد برخی از درخواست‌ها از اصفهان ارسال شده! منطقی نیست ولی از نظر فنی ممکن است.

روند بازدید در روزهای مختلف

days

روزی که بیشتر از همه بازدید داشتیم روز افتتاحیه (۱۰ اردیبهشت) بوده است و تقریبا بعد از آن روند نزولی داشته‌ایم.

نتیجه اخلاقی ندارد!

روند بازدید در ساعات مختلف

hours

بیشترین بازدید مربوط به ساعت ۱۱ صبح است. این الگو در روزهای تعطیل تغییر می‌کند.

نتیجه اخلاقی: در اوج ساعات اداری بیشترین بازدیدکننده را داشتیم. این اصلا نشانه خوبی نیست. در این ساعت کارمندان یا باید مشغول کار باشند و یا پاسخگوی ارباب رجوع.

سیستم‌های عامل

os

این یکی از جالب ترین آمارهاست. در روزهای تعطیل بعد از ویندوز ۷ بیشترین بازدیدها مربوط به اندروید بود، مخصوصا در ساعت اولیه روز. جالب اینجاست که ویندوز ۸ هنوز با اختلاف قابل توجهی از اندروید پایین‌تر است و الان بیش از یک سال از ارائه ویندوز ۸ می‌گذرد.

در انتهای لیست سیستم‌عامل‌های جالبی وجود دارند. QNX یک سیستم‌عامل Realtime است که واقعاً نمی‌دانم چطور ۳ نفر با استفاده از آن سایت را مشاهده کردند؟ البته ممکن است مربوط به سیستم‌های مانیتورینگ باشد. و از آن جالب‌تر ویندوز ۹۸ است!!! ۴ نفر با استفاده از ویندوز ۹۸ از این وب سایت بازدید کردند!

در بین سیستم‌عامل‌های موبایل یک نکته جالب دیگر ویندوز فون است که به طور واضح نشان می‌دهد که حداقل در بازار ایران کاملاً شکست خورده. حتی نتوانسته جانشین ویندوز موبایل شود!

و اما در مورد لینوکس جالب است که از مک بالاتر است و بیشتر مورد استفاده قرار گرفته. البته به قول برخی، مک هم یک توزیع فوق‌العاده زیبا از لینوکس است.

نتیجه اخلاقی:‌ البته که بعید است ویندوز (از هر نسخه‌ای) با سیستم عامل دیگری جایگزین شود. اما اوضاع مایکروسافت حداقل در ایران جالب نیست و سیستم‌عامل‌هایی که مایکروسافت برای آینده روی آن‌ها کار کرده جایگاه مناسبی ندارند.

و اما مرورگرها

browsers1

لیست بالا ۲۵ مرورگر پر استفاده در بین بازدیدکنندگان را نمایش می‌دهد. و لیست پایین جایگاه IE 5.0 را نشان می‌دهد:

browser_ie5

هر چند در بین ۵۰ مرورگر کم بازدید قرار گرفته اما ۴ بازدید کننده داشتیم که با این مرورگر سایت را مشاهده کردند. از صمیم قلب آرزو داشتم بدانم چرا و چگونه این چهار نفر با IE 5 سایت را مشاهده کردند، در کجا زندگی می‌کنند و از چه امکاناتی برخوردارند؟ آیا با مودم Dial-up به اینترنت متصل شده‌اند؟ اصلا هدفشان از اتصال به اینترنت چه بود؟ آیا می‌خواستند تست کنند که سایت ما با IE 5 هم قابل مشاهده است؟ همین سوال‌ها را از آن ۴ نفر که با ویندوز ۹۸ آمده‌اند (که احتمالا همین ۴ نفر هستند)  هم دارم.

نتیجه اخلاقی: بیشتر مراجعه کنندگان دارای امکانات HTML 5 و CSS 3 هستند و این جای خوشحالی دارد و به طور قطع استفاده از ویژگی‌های جدید HTML و CSS پذیرفته شده است.

موتورهای جستجو

searchengine

نتیجه اخلاقی:‌ گوگل اختلاف واقعاً فاحشی (حتی شاید وحشیانه) با بقیه موتورهای جستجو دارد.

و ترافیک مصرفی در ۳۰ روز گذشته

traffic

اپ‌های موبایل نمایشگاه

چند شرکت اقدام به تولید نرم‌افزارهای تحت موبایل برای نمایشگاه کتاب کردند که آمار دانلود صورت گرفته آن‌ها به شرح زیر است:

  1. نرم‌افزارهای اندرویدی جمعاً ۱۴,۰۷۶ مورد کلیک (دانلود)
  2. نرم‌افزار آی او اس ۳,۴۲۲ مورد کلیک

آمار نرم‌افزار آی او اس احتمالا دقیق نیست چون من تعداد کیک‌هایی که به سایت ایبنا برای دانلود این اپ داده بودیم را شمرده‌ام.

چند نکته

  1. در ساعات اولیه روز به خصوص روزهای تعطیل، بعد از ویندوز ۷، بیشترین بازدید توسط سیستم عامل اندروید صورت می‌گرفت
  2. با استفاده از این روش محاسبه کردم که به طور متوسط ۴.۸ درخواست در هر ثانیه بر روی سرور بوده است
  3. تعداد کانکشن‌های باز بر روی پورت ۸۰ با استفاده از دستوری که قبلاً توضیح داده بودم در ساعات شلوغی به ۸۰۰ عدد می‌رسید
  4. بیشتر مراجعات بعد از صفحه خانه به صفحه جستجوی کتاب بوده است
  5. اطلاعات کتب توسط مرکز دیگری تهیه و به ما تحویل می‌شود و متاسفانه محل بسیاری از کتاب‌ها در نمایشگاه مشخص نبود

با تشکر از stat7.com برای آمارگیری، که سرویس خوبی دادند فقط گاهی اوقات از دسترس خارج می‌شد و در کل کمی نیاز به بهبود در زمینه سرعت پاسخگوی سرورها و احتمالا پهنای باند دارد.

بهینه‌سازی جستجو در MongoDB

در ساعات اوج، هر جستجو در کتب نمایشگاه تا حتی چند ثانیه طول می‌کشید. مشکل از Regular Expression بود.

من برای جستجو در مثلا عنوان کتاب از کوئری زیر استفاده می‌کردم:


{ "title": /متن جستجو/i }

چون Regular Expression یک زبان کامل است امکان استفاده از اندیس‌ها در این حالت وجود ندارد و یک کوئری ساده روی چند صد هزار رکورد ممکن است تا ۱ ثانیه طول بکشد.

البته این کوئری قابلیت استفاده از اندیس‌ها را دارد:


{ "title": /^متن جستجو/i }

ولی فقط عناوینی را بر می‌گردانند که با متن جستجو شروع شده باشند. معمولا ما در جستجو‌ها دنبال چنین حالتی نیستیم و فقط می‌خواهیم عبارت مورد نظرمان در هر جای متن که بود پیدا شود. پس این حالت هم پاسخگو نیست.

یک راه دیگر می‌ماند و آن استفاده از کلمه‌های کلیدی است. به این ترتیب که ابتدا همه رکوردهای موجود را بررسی کرده و از هر فیلد یک سری کلمه کلیدی استخراج کرد و آن‌ها را برای آن رکورد ذخیره کرد.

من اسکریپت زیر را برای اینکار نوشتم


function getKeywords(field) {
    if (field) {
        return field.toString().toLowerCase()
            .replace(/,|،|\(|\)|\[|\]|\"|\'/g, " ")
            .match(/[^ ]+/g);
    }
}

db.books.find().forEach(function(doc){
    doc.keywords = {};
    doc.keywords.title = getKeywords(doc.title)
    doc.keywords.subject = getKeywords(doc.subject)
    doc.keywords.author = getKeywords(doc.author)
    doc.keywords.translator = getKeywords(doc.translator)
    doc.keywords.publisher = getKeywords(doc.publisher)
    doc.keywords.distributor = getKeywords(doc.distributor)

    db.books.save(doc);
});

db.books.ensureIndex({"keywords.title":1})
db.books.ensureIndex({"keywords.subject":1})
db.books.ensureIndex({"keywords.author":1})
db.books.ensureIndex({"keywords.translator":1})
db.books.ensureIndex({"keywords.publisher":1})
db.books.ensureIndex({"keywords.distributor":1})

با اجرای آن از هر فیلد یک آرایه استخراج می‌شود که به صورت جداگانه ذخیره کردم و در در آخر هم برای هر یک از این آرایه‌ها اندیس‌گذاری کردم.

حالا مثلاً کتابهایی که در عنوان آن‌ها عبارت جاوا وجود داشته باشد با کوئری زیر برگردانده می‌شوند


{ "keywords.title": "java" }

اما زمان اجرای آن تقریبا برابر صفر است! بی نهایت سریعتر از Regular Expression عمل می‌کند. فقط لازم است که در هنگام جستجو عبارت مورد نظر کاربر را به درستی تقسیم کرد تا به نتیجه مورد نظرش برسد.

مثلاً اگر کاربر عبارت «شعر فارسی» را جستجو کند نباید همین عبارت عیناً در کوئری قرار گیرد بلکه باید به دو عبارت «شعر» و «فارسی» تقسیم شده و با دستور زیر جستجو شود


{ "keywords.subject": { $all: ["شعر", "فارسی"] } }

فقط در این حالت ترتیب مهم نیست و هر عبارتی که در آن «شعر» و «فارسی» باشد جزو نتایج جستجو است.

نکته اینکه اگر می‌خواستم تنها روی یک فیلد چنین امکانی داشته باشم خود Mongo امکانش را از طریق اندیس متنی می‌داد.

باید به این نکته هم توجه کرد که تعداد مراجعات به وب سایت نمایشگاه کتاب انقدر زیاد است که حتی سیستم آمارگیری که استفاده می‌کردم به مشکل خورد! به هر حال حالا سرعت جستجو نشان دهنده قدرت واقعی تکنلوژی استفاده شده است.

حالا لود سیستم که به ۶ هم رسیده بود به حالت اول بازگشت ولی مقدار رم مصرفی بیشتر شده که طبیعی است:

systemload 2014-04-26 19:29:59

لطفاً امتحان کنید و نظر بدید: http://www.tibf.ir/fa/book

وب سایت جدید نمایشگاه کتاب، نزدیک به انتشار

وب سایت جدید نمایشگاه بین‌المللی کتاب تهران در حال آماده‌شدن است. با همان تکنولوژی که قبلاً در موردش گفته بودم. هم اکنون نسخه آزمایشی این وب سایت به همراه یک وب سایت دیگر (با همان تکنولوژی) بر روی یک سرور لینوکس مجازی تک پردازنده‌ای ۳۲ بیتی با ۱ گیگ رم قرار گرفته. سرعت همین وب سایت‌های موقتی بسیار بیشتر از نسخه ویندوزی است که بر روی یک سرور ۶ هسته‌ای ۶۴ بیتی با ۶ گیگ رم قرار دارد!

به زودی نسخه نهایی آماده شده و در دسترس عموم قرار می‌گیرد. اگر فرصت بیشتری داده می‌شد علاقه داشتم که روی واسط کاربری (UI) و تجربه کاربری (UX) آن بیشتر کار کنم تا ظاهر کار هم در کنار باطنش زیباتر باشد.

همه برنامه‌نویس‌ها می‌دانند که چه لذتی دارد وقتی نرم‌افزاری که تولید کرده‌اند سرویس مهمی را ارائه می‌دهد و یا پاسخگوی نیاز تعداد زیادی از کاربران است.

سرعت فضایی Scala و Play Framework و Mongo

so_happy_small

از شعف در پوست خود نمی‌گنجم!

برای پروژه اخیر اسکالا یک سرور مجازی بر روی یک سرور مجازی دیگر راه انداختم. چون سرور اصلی مجازی بود VMWare جواب نداد و از VirtualBox و مد Binary Translation که کندترین حالت مجازی سازی است استفاده کردم. ۱ گیگابایت رم و ۴۰ گیگابایت هارد به آن اختصاص دادم. یک سرور ۳۲ بیتی اوبتو ۱۲.۰۴ روی آن نصب کردم. بعد جاوا، MongoDB، Play Framework و بقیه. و خلاصه پروژه را رویش بالا آوردم.

یک سرور Git هم برای خودکار سازی انتشار برنامه بر روی سرور نصب کردم و با یک اسکریپت ساده برنامه را بر روی سرور Publish می‌کنم.

سرعت اجرا و پاسخگویی برنامه واقعاً عجیب است! یک لحظه شک کردم که شاید دارم با نسخه روی سیستم خودم کار می‌کنم. به یکی از بچه‌ها هم زنگ زدم و خواستم سایت را چک کند. او هم از سرعت بالای سایت تعجب کرد. همه چیز عالی و با سرعت و دقت کار می‌کند در حالی که فقط حدود ۱۰۰ مگابایت از رم سرور مصرف شده بود با فقط یک CPU  تک هسته‌ای ۳۲ بیتی که دو مرتبه مجازی سازی شده! 

رکوردهای یکی از جداول را زیادتر کردم تا به تعداد بیشتر از ۲۶۰،۰۰۰ رکورد رسید و کلی اطلاعات دیگر به سیستم اضافه شد. بعد از چند ساعت کار کردن با سرور، رم مصرفی کل سیستم (به جز cache) به ۴۴۰ مگابایت رسید ولی باز هم باورد کردنی نیست. یعنی خود لینوکس، جاوا، برنامه‌ها، بانک اطلاعات و خلاصه همه چیز، فقط ۴۰۰ مگابایت!!!

Screenshot from 2014-03-06 00:42:36

البته قبلاً با ۱ میلیون رکورد روی سیستم خودم (که آن هم لینوکس است) تست کرده بودم و باز هم سرعت باور نکردنی بود! ولی فکر نمی‌کردم با چنین سرور ضعیفی چنین پاسخی بگیرم.

همین تعداد رکورد را در SQL Server دارم. در حالتی که سر سرور خلوت است فقط ۸۰۰ مگابایت توسط SQL Server به تنهایی مصرف می‌شود! همیشه مجبور بودم یک سرور ویندوزی با حداقل ۴ گیگابایت رم تهیه کنم و برای SQL Server حتما از نسخه Standard یا Enterprise (نسخه‌های دزدی) استفاده کنم تا بتواند بیشتر از ۱ گیگابایت رم را استفاده کند و سرعت خوبی داشته باشد. خود ویندوز و دات نت و بقیه برنامه‌ها هم که بقیه رم را مصرف می‌کردند و تقریبا چیزی باقی نمی‌ماند.

Screenshot from 2014-03-06 00:50:07

متاسفانه بخاطر مسائل امنیتی تا کامل شدن کار فعلاً نمی‌توانم لینک وب سایت را منتشر کنم ولی به زودی این کار را خواهم کرد.

پیشنهاد می‌کنم اگر تجربه‌ی کار با لینوکس را ندارید حتماً در اولین فرصت شروع کنید.

پردازش توزیع شده و غیر همزمان تصویر در MongoDB با Scala

MongoDB فایل‌های بزرگتر از ۱۶ مگابایت را به صورت تکه تکه شده ذخیره می‌کند. به هر کدام از این تکه‌ها یک Chunk می‌گویند. قوانینی که برای ذخیره و بازیابی فایل در MongoDB وضع شده GridFS نام دارد که توسط درایورهای MongoDB پیاده‌سازی می‌شود.

وقتی می‌خواهیم یک فایل را از MongoDB بخوانیم، فایل به صورت Chunkهایی از آرایه‌های بایت ارائه می‌شوند (Chunks of Arrays of Bytes). مثلاً یک فایل ۱۰۰ مگا بایتی در ۱۰۰ قطعه ۱ کیلوبایتی ارائه می‌شود.

چرا سازندگان روانی MongoDB این قانون را وضع کردند؟ MongoDB یک بانک اطلاعات است که از اول برای توزیع فیزیکی طراحی شده و ممکن است لازم باشد یک فایل بزرگ در بین ماشین‌های مختلف توزیع شود. و توزیع یک فایل ۱۰۰ مگابایتی سخت از توزیع ۱۰۰ قطعه یک کلیو بایتی است.

اگر هم نخواهیم فایل را در Mongo ذخیره کنیم و فقط مسیر فایل را در بانک ذخیره کنیم آن وقت اگر بخواهیم برنامه‌ را به صورت توزیع شده روی چند سرور اجرا کنیم مشکلات زیادی در Sync کردن فولدری که فایل‌ها را در آن‌ها نگهداری می‌کنیم خواهیم داشت. توجه کنید که ممکن است Sync دو طرفه باشد. اگر هم بخواهیم یک فولدر مرکزی Share شده برای فایل‌ها بگذاریم که دیگر سیستممان توزیع شده نیست!

تا اینجا خیلی مسئله پیچیده‌ای نبود. نکته مهم اینجاست که در برنامه‌نویسی غیر همزمان در هنگام خواندن یک فایل از Mongo ما نمی‌توانیم صبر کنیم تا تمام قطعات برسند و بعد آنها را به هم بچسبانیم و ارائه دهیم یا حرکتی روی آن‌ها بزنیم. بلکه با رسیدن هر قطعه به صورت Event-driven به ما اطلاع داده می‌شود و باید یک جوری قطعه‌ها را به هم بچسبانیم یا (از آن بهتر) روی همان قطعه کوچک کار کنیم و همان را به مصرف کننده پاس دهیم.

خوشبختانه در چارچوب Play امکانی فراهم شده که اجازه می‌دهد همانطور که MongoDB قطعه، قطعه فایل‌ها را به دست ما می‌رساند ما هم آنها را دست به دست به دست Browser کاربر نهایی برسانیم. کل ماجرا با جزئیاتش پیچیده‌ است ولی با کمک ویژگی‌های Scala واقعاً به راحتی انجام می‌شود. مفهومی وجود دارد به نام Iteratee (درکش کمی مشکل است ولی واقعاً کاربردی است) که با کمک آن این‌ قبیل کارها راحتتر انجام می‌شود.

اما یک مشکل دیگر وجود دارد؛ مثلاً فرض کنید یک فایل تصویری در MongoDB داریم که می‌خواهیم در لحظه (بعد از درخواست Browser) سایز آن را تغییر دهیم (از آن Thumbnail بسازیم). قبلاً اکثر ما بارها در #C آن را انجام دادیم و کتابخانه‌های مکفی برای اینکار وجود دارند. فایل تصویر را می‌خوانیم، (در قالب Stream یا آرایه‌ای از بایت) به کتابخانه Image Resizer محبوبمان می‌سپاریم و تصویر Resize شده را تحویل می‌گیریم بعد آن را با HTTP به سمت Browser می‌فرستیم. جداً کار سختی نیست. مثل شکل زیر:

پردازش خطی

ولی من نمی‌خواهم یک فایل کامل را به یک کتابخانه بدهم تا آن را تبدیل به یک تصویر کوچکتر کند. همانطور که گفتم نمی‌توانم (نباید) صبر کنم تا فایل کامل شود و بعد به سراغ Resize کردن بروم. بلکه وقتی اولین قطعه از فایل (که ممکن است مثلاً ۱ کیلوبایت باشد) به دستم برسد باید شروع به محاسبات تغییر اندازه کنم و منتظر قطعه بعدی نباشم و تا همینجای کار را برای مصرف کننده ارسال کنم. به این ترتیب هم حافظه کمتری مصرف می‌کنم هم در زمان ترافیک بالا خیلی خیلی کمتر از منابع سیستم استفاده خواهم کرد. ولی این واقعاً پیچیده است؛ در مایه‌های فیلم‌های عملی تخیلی! مثل شکل زیر:

پردازش قطعه قطعه

نمی‌دانم الگورتیمی وجود دارد که بتواند به صورت تکه تکه (نه از نظر تصویر بلکه از نظر محتوی فایل) یک فایل تصویری را Resize کند؟ اصلاً نمی‌دانم فرمت‌های تصویری مرسوم (Jpeg، Gif و …) قابلیت پردازش به این شکل را دارند یا نه؟

ولی سعی کردم؛ در گروه ReactiveMongo یک پست ارسال کردم وتقاضای کمک کردم. اگر این کار ممکن باشد تاثیر به شدت زیادی در Preformance وب سایت‌هایی که با این روش ساخته می‌شوند خواهند داشت و پاسخگویی سرور به تعداد بالای کاربر به شدت افزایش پیدا می‌کند.

هرچند پردازشی که روی تصویر انجام می‌شود ساده است ولی توزیع پذیری و کارایی بالای این روش قابل تعمیم است و قابل تصور نیست که چه کارهایی می‌توان با آن انجام داد.

برای حسن ختام، این یک قطعه کد بر روی چارچوب Play و زبان Scala است که یک فایل Zip را در همان زمان تولید، خرد خرد به کلاینت ارسال می‌کند (صبر نمی‌کند که اول فایل ایجاد شود و در آخر آن را ارسال کند).

تا قبل از مهاجرت به Scala صحبت در مورد این روش پرداش اطلاعات واقعاً یک کار بسیار سخت یا نشدنی به نظر می‌رسید ولی الان حتی اگر این کارها به راحتی قابل انجام نباشد حداقل قابل لمس شده.

توضیح: تصاویر این پست توسط نرم‌افزار LibreOffice Draw و با بی دقتی بسیار اما به راحتی ساخته شده است. لطفاً اگر از Microsoft Visio استفاده می‌کنید و نمی‌توانید آن‌ را بخرید (کپی رایت را رعایت نمی‌کنید)، از LibreOffice Draw استفاده کنید.