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

لوئیجی مونتانز
لوئیجی مونتانز

معرفی

از طریق WebSockets و EventSource ، HTML5 توسعه دهندگان را قادر می سازد تا برنامه های وب بسازند که در زمان واقعی با یک سرور ارتباط برقرار کنند. کنگره جریان (موجود در فروشگاه وب کروم ) به‌روزرسانی‌های مستقیم درباره عملکرد کنگره ایالات متحده ارائه می‌کند. به‌روزرسانی‌های طبقه‌بندی مجلس و سنا، به‌روزرسانی‌های اخبار مرتبط، توییت‌های اعضای کنگره و دیگر به‌روزرسانی‌های رسانه‌های اجتماعی را پخش می‌کند. برنامه قرار است در تمام طول روز باز بماند زیرا تجارت کنگره را به تصویر می کشد.

شروع با WebSockets

مشخصات WebSockets نسبت به آنچه که فعال می کند بسیار مورد توجه قرار گرفته است: یک سوکت TCP پایدار و دو جهته بین مرورگر و سرور. هیچ فرمت داده ای بر سوکت TCP تحمیل نشده است. توسعه دهنده برای تعریف یک پروتکل پیام آزاد است. در عمل، انتقال اشیا JSON به عنوان رشته راحت تر است. کد جاوا اسکریپت سمت کلاینت برای گوش دادن به به روز رسانی های زنده تمیز و ساده است:

var liveSocket = new WebSocket("ws://streamcongress.com:8080/live");

liveSocket.onmessage = function (payload) {
  addToStream(JSON.parse(payload.data).reverse());
};

در حالی که پشتیبانی مرورگر برای WebSockets ساده است، پشتیبانی از سمت سرور هنوز در مرحله شکل گیری است. Socket.IO در Node.js یکی از بالغ ترین و قوی ترین راه حل های سمت سرور را ارائه می دهد. یک سرور رویداد محور مانند Node.js مناسب برای WebSockets است. برای پیاده سازی های جایگزین، توسعه دهندگان پایتون می توانند از Twisted و Tornado استفاده کنند، در حالی که توسعه دهندگان Ruby دارای EventMachine هستند.

معرفی کرامپ

Cramp یک چارچوب وب ناهمزمان Ruby است که در بالای EventMachine اجرا می شود. این توسط Pratik Naik ، یکی از اعضای تیم اصلی Ruby on Rails نوشته شده است. Cramp با ارائه یک زبان خاص دامنه (DSL) برای برنامه های وب بلادرنگ، یک انتخاب ایده آل برای توسعه دهندگان وب Ruby است. کسانی که با کنترلرهای نوشتن در Ruby on Rails آشنا هستند، سبک کرامپ را می شناسند:

require "rubygems"
require "bundler"
Bundler.require
require 'cramp'
require 'http_router'
require 'active_support/json'
require 'thin'

Cramp::Websocket.backend = :thin

class LiveSocket < Cramp::Websocket
periodic_timer :check_activities, :every => 15

def check_activities
    @latest_activity ||= nil
    new_activities = find_activities_since(@latest_activity)
    @latest_activity = new_activities.first unless new_activities.empty?
    render new_activities.to_json
end
end

routes = HttpRouter.new do
add('/live').to(LiveSocket)
end
run routes

از آنجایی که کرامپ در بالای EventMachine غیر مسدود کننده قرار دارد، چندین ملاحظات وجود دارد که باید در نظر داشته باشید:

  • از درایورهای پایگاه داده غیر مسدود کننده مانند MySQLPlus و em-mongo باید استفاده شود.
  • باید از وب سرورهای رویداد محور استفاده شود. پشتیبانی برای Thin و Rainbows ساخته شده است.
  • برنامه Cramp باید جدا از برنامه اصلی Rails که کنگره استریم را قدرت می‌دهد اجرا شود، مجدداً راه‌اندازی و نظارت شود.

محدودیت های فعلی

WebSockets در 8 دسامبر 2010 با انتشار یک آسیب پذیری امنیتی دچار شکست شد. هم فایرفاکس و هم اپرا پشتیبانی مرورگر را از WebSockets حذف کردند. در حالی که هیچ پلی پری خالص جاوا اسکریپت وجود ندارد، یک فلش بازگشتی وجود دارد که به طور گسترده مورد استفاده قرار گرفته است. با این حال، تکیه بر فلش دور از ایده آل است. اگرچه کروم و سافاری همچنان از WebSocket ها پشتیبانی می کنند، مشخص شد که برای پشتیبانی از تمام مرورگرهای مدرن بدون تکیه بر Flash، WebSocket ها باید جایگزین شوند.

بازگشت به رای گیری AJAX

تصمیم گرفته شد که از WebSockets دور شویم و به نظرسنجی AJAX "مدرسه قدیمی" برگردیم. در حالی که از منظر ورودی/خروجی دیسک و شبکه بسیار کارآمدتر بود، نظرسنجی AJAX اجرای فنی کنگره جریان را ساده کرد. مهمتر از همه، نیاز به یک برنامه Cramp جداگانه حذف شد. نقطه پایانی AJAX در عوض توسط برنامه Rails ارائه شد. کد سمت کلاینت برای پشتیبانی از نظرسنجی jQuery AJAX اصلاح شد:

var fillStream = function(mostRecentActivity) {
  $.getJSON(requestURL, function(data) {
    addToStream(data.reverse());

    setTimeout(function() {
      fillStream(recentActivities.last());
    }, 15000);
  });
};

AJAX polling, though, is not without its downsides. Relying on the HTTP request/response cycle means that the server sees constant load even when there aren't any new updates. And of course, AJAX polling doesn't take advantage of what HTML5 has to offer.

## EventSource: The right tool for the job

Up to this point, a key factor was ignored about the nature of Stream Congress: the app only needs to stream updates one way, from server to client - downstream. It didn't need to be real-time, upstream client-to-server communication. 

In this sense, WebSockets is overkill for Stream Congress. Server-to-client communication is so common that it's been given a general term: push. In fact, many existing solutions for WebSockets, from the hosted [PusherApp](http://pusherapp.com) to the Rails library [Socky](https://github.com/socky), optimize for push and don't support client-to-server communication at all.

Enter EventSource, also called Server-Sent Events. The specification compares favorably to WebSockets in the context to server to client push:

- A similar, simple JavaScript API on the browser side.
- The open connection is HTTP-based, not dropping to the low level of TCP.
- Automatic reconnection when the connection is closed.

### Going Back to Cramp

In recent months, Cramp has added support for EventSource. The code is very similar to the WebSockets implementation:

```ruby
class LiveEvents < Cramp::Action
self.transport = :sse

periodic_timer :latest, :every => 15

def latest
@latest_activity ||= nil
new_activities = find_activities_since(@latest_activity)
@latest_activity = new_activities.first unless new_activities.empty?
render new_activities.to_json
end
end

routes = HttpRouter.new do
add('/').to(LiveEvents)
end
run routes

موضوع مهمی که باید در مورد EventSource در نظر داشت این است که اتصالات بین دامنه ای مجاز نیستند. این بدان معنی است که برنامه Cramp باید از همان دامنه streamcongress.com به عنوان برنامه اصلی Rails ارائه شود. این را می توان با پروکسی در وب سرور انجام داد. با فرض اینکه برنامه Cramp از Thin پشتیبانی می کند و روی پورت 8000 اجرا می شود، پیکربندی آپاچی به این صورت به نظر می رسد:

LoadModule  proxy_module             /usr/lib/apache2/modules/mod_proxy.so
LoadModule  proxy_http_module        /usr/lib/apache2/modules/mod_proxy_http.so
LoadModule  proxy_balancer_module    /usr/lib/apache2/modules/mod_proxy_balancer.so

<VirtualHost *:80>
  ServerName streamcongress.com
  DocumentRoot /projects/streamcongress/www/current/public
  RailsEnv production
  RackEnv production

  <Directory /projects/streamcongress/www/current/public>
    Order allow,deny
    Allow from all
    Options -MultiViews
  </Directory>

  <Proxy balancer://thin>
    BalancerMember http://localhost:8000
  </Proxy>

  ProxyPass /live balancer://thin/
  ProxyPassReverse /live balancer://thin/
  ProxyPreserveHost on
</VirtualHost>

این پیکربندی یک نقطه پایانی EventSource را در streamcongress.com/live تنظیم می‌کند.

پلی پر پایدار

یکی از مهم‌ترین مزایای EventSource نسبت به WebSockets این است که بازگشتی کاملاً مبتنی بر جاوا اسکریپت است و هیچ وابستگی به Flash ندارد. پلی‌فیل رمی شارپ با اجرای طولانی‌نظرسنجی در مرورگرهایی که از EventSource به صورت بومی پشتیبانی نمی‌کنند، این کار را انجام می‌دهد. بنابراین، EventSource امروز بر روی تمام مرورگرهای مدرن با جاوا اسکریپت فعال است.

نتیجه

HTML5 در را به روی بسیاری از امکانات جدید و هیجان انگیز توسعه وب باز می کند. با WebSockets و EventSource، توسعه دهندگان وب اکنون استانداردهای تمیز و کاملاً تعریف شده ای برای فعال کردن برنامه های وب بلادرنگ دارند. اما همه کاربران مرورگرهای مدرن را اجرا نمی کنند. هنگام انتخاب برای پیاده سازی این فناوری ها، باید تخریب برازنده در نظر گرفته شود. و ابزارسازی در سمت سرور برای WebSockets و EventSource هنوز در مراحل اولیه است. مهم است که این عوامل را هنگام توسعه برنامه های HTML5 بلادرنگ در نظر داشته باشید.