Örnek Olay - Akış Kongresi'nde Gerçek Zamanlı Güncellemeler

Luigi Montanez
Luigi Montanez

Giriş

WebSockets ve EventSource aracılığıyla HTML5, geliştiricilerin bir sunucuyla gerçek zamanlı iletişim kuran web uygulamaları oluşturmasına olanak tanır. Stream Congress (Chrome Web Mağazası'nda bulunabilir), ABD Kongresi'nin çalışmaları hakkında canlı güncellemeler sağlar. Hem Parlamento hem de Senato'dan taban haberleri, alakalı haber güncellemelerini, Kongre üyelerinden tweet'leri ve diğer sosyal medya güncellemelerini canlı yayınlar. Kongre iş dünyası hakkında bilgi verdiği için uygulamanın gün boyu açık kalması amaçlanmıştır.

WebSockets ile başlama

WebSockets spesifikasyonu, sağladığı olanaklar nedeniyle yoğun ilgi gördü: tarayıcı ile sunucu arasında sabit, çift yönlü bir TCP soketi. TCP soketine uygulanan herhangi bir veri biçimi yoktur; geliştirici bir mesajlaşma protokolü tanımlamakta özgürdür. Uygulamada, JSON nesnelerinin dize olarak geçirilmesi en kolay yöntemdir. Canlı güncellemeleri dinleyecek istemci tarafı JavaScript kodu açık ve basittir:

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

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

WebSockets için tarayıcı desteği basit olsa da, sunucu tarafı desteği hâlâ geliştirme aşamasındadır. Node.js'deki Socket.IO, sunucu tarafı en olgun ve güçlü çözümlerden birini sunar. WebSockets için Node.js gibi etkinlik odaklı bir sunucu uygundur. Alternatif uygulamalar için Python geliştiricileri Twisted ve Tornado'yu kullanırken, Ruby geliştiricileri EventMachine'i kullanabilir.

Cramp ile tanışın

Cramp, EventMachine üzerinde çalışan eşzamansız bir Ruby web çerçevesidir. Senaryosunu, Ruby on Rails çekirdek ekibinin üyelerinden Pratik Naik yazdı. Gerçek zamanlı web uygulamaları için alana özgü bir dil (DSL) sağlayan Cramp, Ruby web geliştiricileri için ideal bir seçimdir. Ruby on Rails'da denetleyici yazmayı bilenler Cramp'ın stilini tanır:

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

Cramp, engellemeyen EventMachine'ın üzerine geldiğinden unutulmaması gereken birkaç nokta vardır:

  • MySQLPlus ve em-mongo gibi engellemeyen veritabanı sürücüleri kullanılmalıdır.
  • Olay odaklı web sunucuları kullanılmalıdır. Thin ve Rainbows için yerleşik destek sunulmaktadır.
  • Cramp uygulaması, Stream Congress'i destekleyen ana Rails uygulamasından ayrı olarak çalıştırılmalı, yeniden başlatılmalı ve bağımsız olarak izlenmelidir.

Mevcut Sınırlamalar

8 Aralık 2010 tarihinde bir güvenlik açığının duyurulmasıyla birlikte WebSockets bir kesinti yaşadı. Hem Firefox hem de Opera, WebSockets için tarayıcı desteğini kaldırdı. Sadece JavaScript çoklu dolgusu olmasa da yaygın şekilde benimsenen bir Flash yedeği vardır. Ancak, Flash'tan yararlanmak ideal bir çözüm değildir. Chrome ve Safari, WebSockets'i desteklemeye devam etse de, Flash'a güvenmeden tüm modern tarayıcıları desteklemek için WebSockets'in değiştirilmesi gerektiği ortadaydı.

AJAX yoklamasına geri dönme

Bu karar, WebSockets'i bırakıp "eski tarz" AJAX yoklama yöntemine dönmeye karar verildi. Disk ve ağ G/Ç açısından çok daha az verimli olsa da AJAX yoklaması Stream Congress'in teknik uygulamasını basitleştirdi. En önemlisi, ayrı bir Cramp uygulamasına duyulan ihtiyaç ortadan kalktı. AJAX uç noktası bunun yerine Rails uygulaması tarafından sağlanmıştır. İstemci tarafı kodu, jQuery AJAX yoklamayı destekleyecek şekilde değiştirilmiştir:

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 ile ilgili unutulmaması gereken önemli bir sorun, web alanları arası bağlantılara izin verilmemesidir. Bu, Cramp uygulamasının ana Rails uygulamasıyla aynı streamcongress.com alan adından yayınlanması gerektiği anlamına gelir. Bu işlem, web sunucusunda proxy yaparak gerçekleştirilebilir. Cramp uygulamasının Thin tarafından desteklendiği ve bağlantı noktası 8000'de çalıştığı varsayıldığında, Apache yapılandırması aşağıdaki gibi görünür:

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>

Bu yapılandırma, streamcongress.com/live adresinde bir EventSource uç noktası ayarlar.

Kararlı Polyfill

EventSource'un WebSockets'e göre en önemli avantajlarından biri, yedeğin tamamen JavaScript tabanlı olması ve Flash'a bağlı olmamasıdır. Remy Sharp'ın polyfill'i, bunu EventSource'u yerel olarak desteklemeyen tarayıcılarda uzun yoklama uygulayarak yapar. Bu nedenle, EventSource şu anda JavaScript'in etkin olduğu tüm modern tarayıcılarda çalışmaktadır.

Sonuç

HTML5, birçok yeni ve heyecan verici web geliştirme olanağının kapısını açar. WebSockets ve EventSource ile web geliştiricileri artık gerçek zamanlı web uygulamalarını etkinleştirmek için net ve iyi tanımlanmış standartlara sahip. Ancak tüm kullanıcılar modern tarayıcı çalıştırmaz. Kontrollü azalma, bu teknolojilerin uygulanması seçiminde dikkate alınmalıdır. WebSockets ve EventSource için sunucu tarafında araçlar henüz ilk aşamalardadır. Gerçek zamanlı HTML5 uygulamaları geliştirirken bu faktörleri göz önünde bulundurmak önemlidir.