<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>نوشته های babak abadkheir</title><link>https://virgool.ir/feed/@keanbabak</link><description></description><language>fa</language><pubDate>2026-11-11 16:39:04</pubDate><image><url>https://files.virgool.ir/upload/users/752670/avatar/avatar.png</url><title>babak abadkheir</title><link>https://virgool.ir/@keanbabak</link></image><item><title>ساده ترین روش نگه داری تعداد داده ها با Redis HyperLogLog</title><link>https://virgool.ir/@keanbabak/ساده-ترین-روش-نگه-داری-تعداد-داده-ها-با-redis-hyperloglog-mptbnp8e0zu5</link><description>مقدمه برای همه ی ما پیش آمده که بخواهیم تعداد رکورد های مخصوصی رو از دیتابیس بدست بیاریم . به عنوان مثال تعداد خونه هایی که در یک سال گذشته فروخته شدن . یا تعداد کلیک هایی که یک کاربر بر روی سایت ما . یا تعداد ip هایی که تا الان وارد سایت ما شدن . همه ی اینها باید یک جا ذخیره بشن . و ما می دونیم که هر query برای ما می تونه چقدر سنگین باشه. مثلا پیدا کردن تعداد همین ip ها از بین پنج میلیون رکورد می تونه چقدر زمان بر باشه و بار پردازش زیادی رو بر روی سرور ما بزاره.امروز می خواهیم راه سریعتر و راحت تری رو به شما معرفی کنیم.اگر با redis آشنا باشید می دونید که redis یک دیتابیس in-memory هستش.البته که این همش نیست. redis قابلیت های فراوانی رو در اختیار ما قرار میده. یکی از این قابلیت ها محاسبه تعداد یک set یا دسته بدون تکرار هستش. یعنی شما می تونید تعداد element های مختلف از یک دسته خاص رو داشته باشید.a = { 1, 2, 5, 6, 7 }  // cardinality is 5&#xA;b = {1, 1, 2, 2, 2, 3, 4 } // cardinality is 4                                                                                                                                      HyperLogLog  ابتدا دستور زیر رو وارد کنید تا به redis وصل بشید.redis-cliحالا می خواهیم دیتا های مورد نظر خودمون رو به صورت set ذخیره کنیم.با دستور PFADD می تونیم شروع کنیم.PFADD key value127.0.0.1:6379&gt; PFADD ips 127.1.1.1&#xA;(integer) 1&#xA;127.0.0.1:6379&gt; PFADD ips 127.5.1.33&#xA;(integer) 1&#xA;127.0.0.1:6379&gt; PFADD ips 144.22.21.10&#xA;(integer) 1&#xA;در اینجا redis برای ما set رو با اسم ips درست کرد.و مقدار مورد نظر مارو داخل set قرار داد.حالا بیایم تعداد مقدار های ذخیره شده در set رو با دستور زیر بدست بیاریم.PFCOUNT key value127.0.0.1:6379&gt; PFCOUNT ips&#xA;(integer) 3می بینیم که تعداد رو به ما بر می گردونه. حالا بیایم راستی آزمایی کنیم. اگه ‌‌ip تکراری وارد کنیم چه اتفاقی می افته؟127.0.0.1:6379&gt; PFADD ips 127.1.1.1&#xA;(integer) 0&#xA;127.0.0.1:6379&gt; PFADD ips 144.22.21.10&#xA; (integer) 0&#xA;127.0.0.1:6379&gt; PFCOUNT ips &#xA;(integer) 3بله redis مقدار های تکراری رو داخل set قرار نمیده.چقدر می تونه این کاربردی باشه برای ما رو خودمون باید تشخیص بدیم. دقت کنید که شما به خود مقدار ها دسترسی ندارید. فقط count رو می تونید بگیرید.مورد آخر اینکه redis در دستور PFCOUNT به شما این امکان رو میده که جمع set های مختلف با هم رو بدست بیارین.البته که این کار از سمت تیم توسعه redis پیشنهاد نمیشه. چون وقتی چندین تا set رو با هم محاسبه می کنیم redis مجبور میشه دوباره فرآیند set سازی رو انجام بده با داده های ورودی جدید تا مقدار های مشترک بین set هارو حذف کنه.127.0.0.1:6379&gt; PFADD ipz 127.1.1.1 &#xA;(integer) 1&#xA;127.0.0.1:6379&gt; PFCOUNT ips ipz&#xA;(integer) 3</description><category>babak abadkheir</category><author>babak abadkheir</author><pubDate>Sun, 28 Mar 2021 12:05:52 +0430</pubDate></item><item><title>managing streams in period of time with delayedDebounce job-Queue _ nodejs</title><link>https://virgool.ir/@keanbabak/managing-streams-in-period-of-time-with-delayeddebounce-job-queue-nodejs-cnqkplrooikm</link><description>مقدمهسلام بعضی از چالش ها هستن که از دور به نظر ساده میان و وقتی به اون موضوع وارد میشیم به مفاهیم  جدیدی برخورد می کنیم.سناریویی رو در نظر بگیرید که کاربر ها یک سری اطلاعات در اختیار شما قرار میدن و شما باید اونها رو به صورت دسته بندی شده در صف های مخصوص به خود همون کاربر پردازش کنید.به عنوان مثال  فرض کنیم کاربران طی یک بازه نا مشخص مختصات جغرافیایی خودشون رو به صورت استریم به سرور ما ارسال می کنن.سرور ما باید این دیتا رو به صورت پکت طور جمع کنه و یک سری پردازش رو انجام بده و یا تحویل سرویس دیگری بده. اولین چالشی من باهاش مواجه شدم به این صورت بود که تعداد مختصات در بازه زمانی مشخص نیست. یعنی کاربر ممکنه در بازه سی ثانیه چندین مختصات به سرور بده و یا اصلا نده. پس تعداد  مهم نیست!دومین چالش به این صورت بود که وقتی اولین مختصات از سمت کاربر می اومد باید تابعی به مدت سی ثانیه صبر می کرد و بعد مختصات هایی که توی آرایه بودن را یکجا بر می داشت و آرایه رو خالی می کرد.نکته ای باید در نظر گرفته می شد این بود که کدوم مختصات از سمت کاربر به عنوان مختصات اول در نظر گرفته می شد؟وقتی دیتا به صورت استریم و مداوم به سرور می رسه قابلیت تشخیص بسیار سخت می شه.سومین چالش جدا کردن دیتا های هر کاربر بود . جاب-کیو Job-Queueصف:صف ها یکی از کاربردی ترین و جذابترین و چالش برانگیز ترین مفاهیم در دنیای برنامه نویسی هستند.با صف شما می تونید به پردازش هاتون الگو بدین . می تونین دیتا هارو ذخیره کنید و در زمان مناسب اونها رو به نوبت پردازش کنید. اساس اصلی کار صف ها  به این صورت هستش که یک انتشار دهنده یا پابلیشر دیتایی رو داخل یک آرایه قرار میده. و خواننده یا کانسیومر وظیفه خواندن از صف رو به عهده داره.دقت کنید ترتیب خواندن اطلاعات عموما به  این صورته که دیتا اول که وارد صف مشه پس زودتر خوانده میشه.خب برسیم به سناریو خودمون.یکی از جالب ترین ابزار های صف در node.js بی - کیو هست.من سعی می کنم به ساده ترین شکل ممکن راه حل کردن این چالش رو در اختیار شما بزارم.اول از هر چیز ورژن نود شما باید 10 به بالا باشه.خب بعد از ساختن دایرکتوری تست  به داخل برید و دستور های زیر رو اجرا کنید.npm init -y&#xA;&#xA;touch index.js&#xA;&#xA;npm i bee-queue برای شبیه سازی استریم من از خود event emitter در node.js استفاده کردم.const EventEmitter = require(&#39;events&#39;);&#xA;const myEmitter = new EventEmitter();قدم بعدی اضافه کردن بی -کیو const Queue = require(&#39;bee-queue&#39;);    // importing bee-queue&#xA;&#xA;const queue = new Queue(&#39;&#34;q_test&#39;, { &#xA;    redis: {&#xA;        host: &#39;127.0.0.1&#39;,&#xA;        port: 6379,&#xA;        db: 0,&#xA;        options: {},&#xA;       },&#xA;   isWorker: true,&#xA;   storeJobs: true,&#xA;   activateDelayedJobs: true,&#xA;   delayedDebounce:10000,&#xA;   removeOnSuccess: true,&#xA;   removeOnFailure: false,&#xA;  });برای ساختن یک صف جدید ابتدا باید اسم صف رو به بی-کیو بدیم. در اینجا ما اسم صف رو q_test گذاشتیم.ببینید چقدر راحت می تونید صف های مختلف درست کنید. قسمت بعدی وارد کردن آپشن های bee-queue هستش. این آپشن ها برای سناریو های مختلفی استفاده مشن مثلا در مثال بالا ما از آپشن redis استفاده کردیم.چه قدرتی ! به به. یعنی می تونید صف هاتون رو روی آدرس های مختلف بسازید. من سعی می کنم یک سری آپشن ها رو به شما توضیح بدم.آپشن is_worker:طبق گفته خود بی-کیو این آپشن همیشه به صورت پیشفرض فعال هستش یعنی مشا می خواید روی صف هاتون پردازش کنید.آپشن soreJobs:به مشا این امکان رو میده که job هارو روی دیسک ذخیره کنید که اگر اتفاقی افتاد برای مثال برای سرور شما بتونید اونهارو دوباره بخونید.آپشن activateDelayedJobs:به صورت پیشفرض job ها به صورت آنی و در لحظه پردازش می شن. برای اینکه ما بتونیم این پردازش رو بندازیم عقب باید این آپشن رو فعال کنیم.آپشن delayedDebounce :مقدار زمانی که بعد از ساخته شدن یک job مورد نیاز هست تا به قسمت پردازش برسه. این مقدار بر اساس میلی ثانیه محاسبه میشه.آپشن های removeOnSuccess و removeOnFailure:همینطور که از اسم پیداست این آپشن ها برای زمانی هستن که وقتی یک job به مرحله اتمام رسید . در صورت تموم شدن پردازش چه به صورت خطا و چه به صورت کامل از صف پاک بشن یا نه.به طور کلی ما در بی-کیو می تونیم تصمیم بگیریم که یک job انجام شده یا با خطا مواجه شده.به عبارتی flow کلی در دست ماست. ما به event های یک صف دسترسی داریم . برای فهمیدن بیشتر این موضوع بهتره داک های بی-کیو رو با دقت مطالعه کنیم.خب برسیم به ادامه کار :برای شبیه سازی این موضوع من یک آبجکت درست کردم. let list = { job_1: [] , job_2:[] }&#xA;let job_ids = [ &#34;job_1&#34;, &#34;job_2&#34;];حالا نوبت emit کردن دیتا تو بازه زمانی مشخص هستش:setInterval(function(){  &#xA;    myEmitter.emit(&#34;location&#34;, {&#xA;       lat: (Math.random() * (1.000001 - 9.99999) + 9.99999).toFixed(8),&#xA;       long: (Math.random() * (1.000001 - 9.99999) + 9.99999).toFixed(8),&#xA;      job_id: job_ids[Math.floor(Math.random()*job_ids.length)]&#xA;  })&#xA;}, 1000);در اینجا من سعی کردم برای درک بهتر از دیتای دامی استفاده کنم.و برای شبیه سازی job هایی با id های مختلف ساختم.به طور کلی با یک اینتروال ساده ما می تونیم دیتا رو پوش کنیم سمت eventEmitter.خب حالا می رسیم به بخش اصلی :myEmitter.on(&#34;location&#34;, (data)=&gt;{&#xA;   list[`${data.job_id}`].push({long: data.long, lat: data.lat}) // pushing data to our list&#xA;   let time = new Date();&#xA;   time.setSeconds(time.getSeconds() + 30);&#xA;   let job = queue.createJob({long: data.long, lat: data.lat}).setId(`${data.job_id}`)&#xA;    .delayUntil(time) .save();&#xA;})در قسمت listener دیتا رو به اون ‌job_id مورد نظر پوش می کنیم. ابتدا زمان الان رو در نظر می گیریم و اون زمانی که لازم داریم تا job پردازش بشه رو بدست میاریم . من اینجا لازم داشتم هم بعد از سی ثانیه پردازش صورت بگیره. نکته قابل توجه بعدی save کردن job ساخته شده در صف هستش.نکته این قسمت وجود job_id هستش. وقتی job_id رو ست می کنیم خود به خود دیتا ها تو job ای قرار می گیرن که ما لازم داریم.سوالی که اینجا بوجود میاد اینکه دیتا داره استریم میشه و ما داریم پشت سر هم job های مشابه درست می کنیم. پس تکلیف job هایی که id یکسان دارن چی میشه؟وقتی job_id مشابه وجود داشته باشه بی-کیو می تونه جا موقعی که job فعلی پردازشش تمام نشده job جدیدی رو تو بازه delayedDebounce نسازه.حالا می رسیم به قسمت پردازش job :queue.process(function (job, done) {&#xA;  console.log(`Processing job with id of ${job.id}`);&#xA;  console.log(list[`${job.id}`])&#xA;  list[`${job.id}`] = [] // we can empty our job list here.after processing data&#xA;  return done(null, job.id);&#xA;});حالا دستور اجرا رو وارد می کنیم:node index.jsمی تونیم خروجی کار رو داخل ترمینال ببینیم.Processing job with id of job_1&#xA;[&#xA;  { long: &#39;1.80891946&#39;, lat: &#39;5.60566373&#39; },&#xA;  { long: &#39;8.98352822&#39;, lat: &#39;6.30629264&#39; },&#xA;  { long: &#39;4.16855703&#39;, lat: &#39;1.39038315&#39; },&#xA;  { long: &#39;4.04078255&#39;, lat: &#39;7.37049228&#39; },&#xA;  { long: &#39;1.89497569&#39;, lat: &#39;4.93514126&#39; },&#xA;  { long: &#39;4.25368859&#39;, lat: &#39;4.10118488&#39; },&#xA;  { long: &#39;9.37654642&#39;, lat: &#39;7.88270754&#39; },&#xA;  { long: &#39;5.01411197&#39;, lat: &#39;9.28267163&#39; },&#xA;  { long: &#39;2.04691585&#39;, lat: &#39;1.74785472&#39; },&#xA;  { long: &#39;3.73004604&#39;, lat: &#39;5.83745854&#39; },&#xA;  { long: &#39;8.53034240&#39;, lat: &#39;3.60958454&#39; },&#xA;  { long: &#39;4.24939359&#39;, lat: &#39;6.10517161&#39; }&#xA;]&#xA;Processing job with id of job_2&#xA;[ &#xA;{ long: &#39;4.17596722&#39;, lat: &#39;4.53111193&#39; },&#xA;{ long: &#39;1.14565748&#39;, lat: &#39;9.23292457&#39; },&#xA;{ long: &#39;8.87647559&#39;, lat: &#39;8.22597247&#39; },&#xA;{ long: &#39;6.28385771&#39;, lat: &#39;9.03126985&#39; },&#xA;{ long: &#39;6.24205035&#39;, lat: &#39;2.47202071&#39; },&#xA;{ long: &#39;8.83162690&#39;, lat: &#39;5.90389462&#39; },&#xA;{ long: &#39;9.60778649&#39;, lat: &#39;1.52291990&#39; },&#xA;{ long: &#39;7.51248300&#39;, lat: &#39;8.64272963&#39; },&#xA;{ long: &#39;7.10059142&#39;, lat: &#39;4.28575918&#39; },&#xA;{ long: &#39;9.98344869&#39;, lat: &#39;1.78720380&#39; },&#xA;{ long: &#39;5.35043578&#39;, lat: &#39;2.32790002&#39; },&#xA;{ long: &#39;4.90123730&#39;, lat: &#39;3.91177152&#39; },&#xA;{ long: &#39;4.20042077&#39;, lat: &#39;7.28743525&#39; },&#xA;{ long: &#39;9.54438123&#39;, lat: &#39;7.53882233&#39; },&#xA;]</description><category>babak abadkheir</category><author>babak abadkheir</author><pubDate>Sat, 27 Mar 2021 18:48:22 +0430</pubDate></item></channel></rss>