برنامه نویسی

ساختن یک سیستم ردیابی درایور در زمان واقعی مقیاس پذیر

ساختن یک سیستم ردیابی تحویل در زمان واقعی با استفاده از سوکت ، ردیس ، آداپتور جریان Redis ، کافکا

من به تازگی روی یک پروژه هیجان انگیز برای یک مشتری کار کردم – یک برنامه تحویل مشابه Zomato ، که در آن کاربران می توانند مکان درایور خود را به صورت زنده روی نقشه ردیابی کنند.

گربه

برنامه کامل از Flutter برای جلوی آن استفاده می کرد و هر دو Nestjs و Golang که نسخه های مختلفی از باطن را تأمین می کنند.

در حالی که من دو پیاده سازی جداگانه ایجاد کردم ، این مقاله صرفاً بر منطق اصلی ردیابی که کاملاً مستقل از زبان است ، تمرکز دارد.
اگر در مورد کد واقعی کنجکاو هستید ، همه چیز در GitHub موجود است: https://github.com/subham-maity/rtls-scale.

اما در مورد زبانهای برنامه نویسی خاص نگران نباشید-من این راهنما را طراحی کرده ام تا برای هر کسی که علاقه مند به درک معماری اساسی سیستم های ردیابی مکان در زمان واقعی باشد ، در دسترس باشد.

بگذارید شما را از طریق نحوه ساخت این نمونه اولیه ، نحوه کار و چگونگی مقیاس آن برای برنامه های دنیای واقعی قدم بزنم.

سلب مسئولیت مهم: این کد آماده تولید نیست ، زیرا یک اجرای کامل تجاری به منطق تجاری اضافی ، ملاحظات امنیتی ، بهینه سازی باتری و بسیاری از عوامل دیگر که در اینجا پوشش نخواهم داد ، نیاز دارد. من همچنین به الگوریتم های تطبیق درایور یا محاسبات از راه دور نمی پردازم-این مقاله منحصراً روی معماری سیستم ردیابی در زمان واقعی متمرکز است.

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

🔥Connect: https://www.subham.online

🔥repo: https://github.com/subham-maity/rtls-scale

twitter: https://twitter.com/thesubhammaity

🔥linkedin: https://www.linkedin.com/in/subham-xam


نمونه اولیه چگونه کار می کند

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

صفحه اصلی

اگر انتخاب کنید محرکبرنامه شروع به ارسال مکان (عرض جغرافیایی و طول جغرافیایی) هر چند ثانیه می کند.

محرک

اگر انتخاب کنید کاربر، می بینید که مکان درایور به صورت زنده روی نقشه به روز می شود.

کاربر

برای آزمایش آن ، صفحه درایور را روی تلفن و صفحه کاربری در لپ تاپ خود باز کردم. من با تلفن خود کمی قدم زدم و در لپ تاپم می توانم موقعیت خود را در زمان واقعی روی نقشه حرکت کنم. این احساس رضایت بخش بود ، مانند “هان ، این کار می کند!” اما این فقط یک نمونه اولیه بود. در یک برنامه واقعی ، شما به احراز هویت مناسب ، میان افزار و همه موارد نیاز دارید. در اینجا ، تمرکز من روی منطق اصلی بود: نحوه ارسال مکان درایور به طور مداوم و بدون هیچ گونه سکسکه.


سرور 1: راه اندازی اصلی WebSocket

بیایید با استفاده از WebSockets با ساده ترین روشی که من این کار را انجام دادم ، شروع کنیم. کد برای این در است

1. server (socket)/src/websockets/location.gateway.ts

در اینجا نحوه عملکرد آن ، گام به گام آورده شده است:

  1. راننده مکان را ارسال می کند: برنامه درایور با استفاده از WebSockets به سرور متصل می شود و یک ارسال می کند send-location هر چند ثانیه با عرض جغرافیایی و طول جغرافیایی آنها. به آن فکر کنید مانند راننده که می گوید ، “سلام سرور ، اینجا جایی است که من الان هستم!”
  2. سرور آن را پخش می کند: سرور برای این رویداد گوش می دهد و مکان را به کلیه مشتری های متصل (مانند برنامه کاربر) استفاده از receive-location رویداد این مانند سرور است که فریاد می زند ، “همه ، موقعیت جدید درایور است!”

  3. نقشه به روزرسانی کاربر: برنامه کاربر برای آن گوش می دهد receive-location رویدادها و نقطه راننده را روی نقشه حرکت می دهد. ساده و سریع

این است

برای یک مجموعه کوچک ، این مانند جذابیت است. اما پس از آن من شروع به فکر کردم – اگر صدها یا هزاران راننده وجود داشته باشد؟ آیا این هنوز هم ادامه خواهد داشت؟

آیا این مقیاس پذیر است؟ نه کاملاً

نه

اینجا جایی است که من به یک دیوار ضربه می زنم:

  • اتصالات خیلی زیاد: هر اتصال WebSocket از منابع سرور – CPU ، حافظه و غیره با هزاران درایور و کاربر استفاده می کند ، یک سرور به تنهایی نمی تواند آن را اداره کند. کند می شود یا خراب می شود.
  • داده های هدر دادن: سرور به روزرسانی هر درایور را به همه کاربران. بنابراین اگر 100 درایور وجود داشته باشد ، هر کاربر در هر چند ثانیه 100 به روزرسانی می کند ، حتی اگر آنها فقط به درایور خودشان اهمیت می دهند. این بسیاری از داده های بی فایده است که سیستم را مسدود می کند.
  • اضافه کردن سرورهای بیشتر: اگر برای به اشتراک گذاشتن بار سرورهای بیشتری اضافه می کنم ، چگونه می توانم اطمینان حاصل کنم که به روزرسانی های مناسب به کاربران مناسب می رسند؟ بدون ترفند هوشمندانه ، سردرد است. با فرض اینکه شما یک برنامه نویس باهوش هستید ، احساس راحتی کنید که هر راه حل پیچیده ای را در نظرات کنار بگذارید

حکم: این برای یک نمونه اولیه یا یک برنامه کوچک با کمتر از 100 درایور خوب است. اما برای یک برنامه تحویل بزرگ؟ هیچ فرصتی نیست – می شکند.


سرور 2: اضافه کردن Pub/Sub Redis

بنابراین ، من به یک راه بهتر نیاز داشتم. این زمانی است که من وارد کردم Pub/Sub Redisبشر Redis این فروشگاه بسیار سریع حافظه است و سیستم انتشار آن برای مقیاس بندی چیزهای زمان واقعی مناسب است. کد را در بررسی کنید

2. server (socket + redis pub-sub)/src/websockets/location.gateway.tsبشر در اینجا نحوه کار من ، گام به گام آورده شده است:

2. server (socket + redis pub-sub)/src/websockets/location.gateway.ts

  1. راننده مکان را منتشر می کند: وقتی راننده ارسال می کند send-location رویداد ، سرور مستقیماً آن را پخش نمی کند. در عوض ، مکان را به یک کانال redis به نام منتشر می کند location-updatesبشر در اینجا کد وجود دارد:
   @SubscribeMessage('send-location')
   handleLocation(client: Socket, data: { latitude: number; longitude: number }) {
     const locationData = {
       id: client.id,
       latitude: data.latitude,
       longitude: data.longitude,
     };
     this.pubSubService.publish('location-updates', JSON.stringify(locationData));
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. سرور مشترکات را به روز می کند و هدف قرار می دهد: سرور مشترک است location-updates کانال و بروزرسانی را ارسال می کند فقط برای کاربران خاص با استفاده از اتاق های WebSocket. هر راننده دارای یک اتاق (به نام شناسه آنها) است و کاربران برای ردیابی آنها به آن اتاق می پیوندند. در اینجا نحوه تنظیم آن در سازنده آورده شده است:
   constructor(private pubSubService: PubSubService) {
     this.pubSubService.subscribe('location-updates', (message) => {
       const locationData = JSON.parse(message);
       this.server.to(locationData.id).emit('receive-location', locationData);
     });
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

و هنگامی که کاربر می خواهد درایور را ردیابی کند:

   @SubscribeMessage('track-driver')
   handleTrackDriver(client: Socket, driverId: string) {
     client.join(driverId);
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. مقیاس با سرور چندگانه: Redis این کار را آسان می کند. چندین سرور Nestjs می توانند در همان مشترک شوند location-updates کانال هنگامی که مکان راننده منتشر می شود ، همه سرورها آن را می گیرند و آن را به اتاق مناسب می فرستند. بدون آشفتگی ، بدون هیاهو.

مرد

چرا این بهتر است

  • به روزرسانی های هدفمند: فقط کاربرانی که درایور خاص را ردیابی می کنند ، به روزرسانی های خود را دریافت می کنند. دیگر با داده های مورد نیاز همه ، همه را سیل نمی کنید.
  • مقیاس بندی افقی: سرورهای بیشتری را اضافه کنید ، و Redis هماهنگی را کنترل می کند. هر سرور مشتری های خود را مدیریت می کند و بار به اشتراک می گذارد.

این یک قدم بزرگ از تنظیمات اساسی است. اما من چیزی حتی بهتر پیدا کردم – خواندن نگهدار!

باف


سرور 3: آداپتور redis streams برای پیروزی

در حالی که Redis Pub/Sub خوب بود ، من به آداپتور جریان redis برای Socket.io ، و مانند Pub/Sub Ka Bada Bhai 💀 – قدرتمند و قابل اعتماد تر است. کد برای این در است

3. server (socket + redis streams adapter)/src/redis/redis.module.tsبشر

3. server (socket + redis streams adapter)/src/redis/redis-io-adapter.tsبشر

3. server (socket + redis streams adapter)/src/websockets/location.gateway.tsبشر

در اینجا نحوه عملکرد آن ، گام به گام آورده شده است:

  1. آداپتور را تنظیم کنید: من ایجاد کردم RedisIoAdapter در 3. server (socket + redis streams adapter)/src/redis/redis-io-adapter.ts برای استفاده از جریان های redis با Socket.io:
   export class RedisIoAdapter extends IoAdapter {
     private redisClient: Redis;
     constructor(app: INestApplication, redisClient: Redis) {
       super(app);
       this.redisClient = redisClient;
     }
     createIOServer(port: number, options?: ServerOptions): any {
       const server = super.createIOServer(port, options);
       server.adapter(createAdapter(this.redisClient));
       return server;
     }
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. راننده مکان را ارسال می کند: مانند گذشته – راننده ارسال می کند send-location رویداد ، و سرور آن را به اتاق خود منتشر می کند:
   @SubscribeMessage('send-location')
   handleLocation(client: Socket, data: { latitude: number; longitude: number }) {
     const locationData = {
       id: client.id,
       latitude: data.latitude,
       longitude: data.longitude,
     };
     this.server.to(client.id).emit('receive-location', locationData);
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. کاربران درایورها را ردیابی می کنند: کاربران با یک اتاق درایور می پیوندند track-driver رویداد:
   @SubscribeMessage('track-driver')
   handleTrackDriver(client: Socket, driverId: string) {
     client.join(driverId);
   }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. جادوی جریانها: آداپتور Redis Streams همه چیز دیگر را اداره می کند. این به روزرسانی ها را در تمام نمونه های سرور توزیع می کند ، تضمین می کند که هیچ پیامی از بین نرود و اتاق ها را یکپارچه نگه می دارد.

چرا این میخانه/زیر می زند

چرا

در اینجا یک مقایسه سریع وجود دارد:

نشان Pub/Sub Redis آداپتور جریان redis
قابلیت اطمینان اگر سرور پایین باشد ، به روزرسانی ها را از دست می دهد. پیام ها را ذخیره می کند ، بنابراین سرورها بعداً به دست می آیند.
مقیاس پذیری برای بارهای متوسط ​​خوب است ، اما با حجم عظیمی مبارزه می کند. از گروه های مصرف کننده برای مقیاس بزرگ استفاده می کند.
سفارش پیام سفارش همیشه تضمین نمی شود. نظم دقیق ، عالی برای ردیابی.
سهولت استفاده شما میخانه/زیر خود را مدیریت می کنید. Socket.io این کار را انجام می دهد – کم کد!
  • قابلیت اطمینان: اگر یک سرور با میخانه/Sub خراب شود ، به روزرسانی ها را از دست می دهد. با جریان ، پیام ها ذخیره می شوند ، بنابراین هیچ چیز از بین نمی رود.
  • مقیاس پذیری: جریان ها می توانند درایورها و کاربران بیشتری با گروه های مصرف کننده را که کار را تقسیم می کنند ، اداره کنند.
  • سادگی: نیازی به نوشتن Pub/Sub Logic – Socket.io آن را در پشت صحنه انجام می دهد.

این برای یک برنامه بزرگ با تعداد زیادی کاربران مناسب است. اما در مورد مقیاس گسترده چیست؟ اینجاست که کافکا وارد می شود.

دلهره


اثبات آینده با کافکا

اکنون تصور کنید که برنامه شما عظیم رشد می کند – هزاران راننده ، میلیون ها کاربر ، و می خواهید کارهای فانتزی مانند تجزیه و تحلیل یا ورود به سیستم را در کنار ردیابی انجام دهید. این زمانی است که کفکا وارد تصویر می شود. این یک سکوی پخش توزیع شده است که برای رسیدگی به تن داده های زمان واقعی ساخته شده است.

در اینجا برنامه اصلی وجود دارد:

  • درایور مکان را از طریق WebSockets ارسال می کند (send-location رویداد).
  • سرور آن را به یک موضوع کافکا سوق می دهد ، مانند driver-locationsبشر
  • یک سرویس مصرف کننده از این موضوع خوانده می شود و از طریق WebSockets به روزرسانی ها را برای کاربران ارسال می کند.

کافکا برای برنامه های کوچک بیش از حد است ، اما برای مقیاس در سطح شرکت ، این یک تغییر دهنده بازی است. من به زودی یک مجموعه Kafka را به repo github خود اضافه می کنم – چشم به چشم می خورد!


چه چیزی باید به Devs Frontend بگوید

HM

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

  • برنامه راننده:

    • به سرور WebSocket متصل شوید.
    • ارسال کردن send-location وقایع با عرض جغرافیایی و طول هر چند ثانیه.
    • در صورت لزوم ممکن است مکان شخصی راننده را روی نقشه نشان دهید.
  • برنامه کاربر:

    • به سرور WebSocket متصل شوید.
    • گوش فرا دادن receive-location رویدادها و نقشه را به روز کنید.
    • ارسال track-driver رویداد با شناسه راننده برای پیوستن به اتاق آنها.

همین است! Devs Frontend عاشق این خواهد بود که این چقدر آسان است – فقط چند واقعه ، و پس زمینه از بالابردن سنگین برخورد می کند.

هاها


مقایسه رویکردها

بیایید آن را با یک جدول تجزیه کنیم تا ببینیم این روش ها چگونه جمع می شوند:

رویکرد جوانب منفی بهترین برای
شبکه های اصلی تنظیم آسان ، برای برنامه های کوچک کار می کند. مقیاس پذیر نیست ، داده های زیادی را ارسال می کند. نمونه های اولیه ، برنامه های کوچک.
Pub/Sub Redis مقیاس بهتر ، به روزرسانی ها را هدف قرار می دهد. در صورت خراب شدن سرورها ، به روزرسانی ها را از دست می دهد. برنامه های متوسط
آداپتور جریان redis قابل اعتماد ، مقیاس پذیر ، کد کمتری. کمی مشکل برای تنظیم. برنامه های بزرگ با بسیاری از کاربران.
کفکا دارای ویژگی های عظیم و اضافی است. برای برنامه های کوچک خیلی زیاد ، نیاز به مادون قرمز دارد. برنامه های سطح شرکت.

بنابراین ، این داستان کامل است! از یک نمونه اولیه اصلی گرفته تا مقیاس گذاری برای یک برنامه تحویل واقعی ، اینگونه است که شما کار ردیابی در زمان واقعی را انجام می دهید. کد همه در GitHub است – آن را بررسی کنید.

دفعه بعد که منتظر غذای خود هستید و تماشای آن حرکت راننده را تماشا می کنید ، می دانید که در پشت صحنه چه اتفاقی می افتد.

یوم

امیدوارم که این همه چیز را پاک کند – بخواهید اگر سوالی دارید بدانید!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا