Studi Kasus - Pembaruan Real-time di Kongres Streaming

Luigi Montanez
Luigi Montanez

Pengantar

Melalui WebSockets dan EventSource, HTML5 memungkinkan developer membuat aplikasi web yang berkomunikasi secara real time dengan server. Streaming Kongres (tersedia di Chrome Web Store) memberikan info terbaru langsung tentang cara kerja Kongres Amerika Serikat. Konferensi ini melakukan streaming pembaruan dari Dewan Perwakilan dan Senat, kabar berita yang relevan, tweet dari anggota Kongres, dan pembaruan media sosial lainnya. Aplikasi ini dimaksudkan untuk dibiarkan terbuka sepanjang hari karena menampilkan urusan Kongres.

Memulai dengan WebSockets

Spesifikasi WebSockets mendapat cukup banyak perhatian terkait fungsi yang diaktifkannya: soket TCP dua arah yang stabil antara browser dan server. Tidak ada format data yang diberlakukan pada soket TCP; pengembang bebas menentukan protokol pengiriman pesan. Dalam praktiknya, meneruskan objek JSON sebagai string adalah cara yang paling mudah. Kode JavaScript sisi klien untuk memproses update langsung sangatlah bersih dan sederhana:

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

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

Meskipun dukungan browser untuk WebSockets sangat mudah, dukungan sisi server masih dalam tahap formatif. Socket.IO di Node.js menyediakan salah satu solusi sisi server yang paling andal dan andal. Server yang terpicu oleh peristiwa seperti Node.js sangat cocok untuk WebSockets. Untuk implementasi alternatif, developer Python dapat menggunakan Twisted dan Tornado, sedangkan developer Ruby memiliki EventMachine.

Memperkenalkan Cramp

Cramp adalah framework web Ruby asinkron yang berjalan di atas EventMachine. Buku ini ditulis oleh Pratik Naik, anggota tim inti Ruby on Rails. Menyediakan bahasa khusus domain (DSL) untuk aplikasi web real-time, Cramp adalah pilihan ideal bagi developer web Ruby. Mereka yang terbiasa menulis pengontrol di Ruby on Rails akan mengenali gaya Cramp:

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

Karena Cramp berada di atas EventMachine non-pemblokiran, ada beberapa pertimbangan yang perlu diingat:

  • Driver database yang tidak memblokir harus digunakan, seperti MySQLPlus dan em-mongo.
  • Server web berbasis peristiwa harus digunakan. Dukungan sudah tersedia untuk Thin dan Rainbows.
  • Aplikasi Cramp harus dijalankan terpisah dari aplikasi Rails utama yang mendukung Stream Kongres, dimulai ulang, dan dipantau secara independen.

Batasan Saat Ini

WebSockets mengalami kemunduran pada tanggal 8 Desember 2010 ketika kerentanan keamanan dipublikasikan. Firefox dan Opera menghapus dukungan browser untuk WebSockets. Meskipun tidak ada polyfill JavaScript murni, ada pengganti Flash yang telah digunakan secara luas. Namun, mengandalkan Flash tidak ideal. Meskipun Chrome dan Safari terus mendukung WebSockets, menjadi jelas bahwa untuk mendukung semua browser modern tanpa mengandalkan Flash, WebSockets harus diganti.

Melakukan roll back ke polling AJAX

Keputusan ini dibuat untuk meninggalkan WebSockets dan kembali ke polling AJAX "lama". Meskipun jauh lebih efisien dari perspektif I/O disk dan jaringan, polling AJAX menyederhanakan implementasi teknis Stream Kongres. Yang paling signifikan, kebutuhan akan aplikasi Cramp terpisah telah dihilangkan. Endpoint AJAX disediakan oleh aplikasi Rails. Kode sisi klien telah dimodifikasi untuk mendukung polling AJAX jQuery:

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

Masalah penting yang perlu diingat pada EventSource adalah koneksi lintas-domain tidak diizinkan. Artinya, aplikasi Cramp harus disalurkan dari domain streamcongress.com yang sama dengan aplikasi Rails utama. Hal ini dapat dilakukan dengan membuat proxy di server web. Dengan asumsi aplikasi Cramp didukung oleh Thin dan berjalan pada port 8000, konfigurasi Apache akan terlihat seperti ini:

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>

Konfigurasi ini menetapkan endpoint EventSource di streamcongress.com/live.

Polyfill Stabil

Salah satu keuntungan paling signifikan EventSource dibandingkan WebSockets adalah penggantian sepenuhnya berbasis JavaScript, tanpa ketergantungan pada Flash. polyfill Remy Sharp dapat melakukannya dengan menerapkan polling panjang di browser yang tidak mendukung EventSource secara native. Dengan demikian, EventSource saat ini berfungsi di semua browser modern yang mengaktifkan JavaScript.

Kesimpulan

HTML5 membuka pintu untuk banyak kemungkinan pengembangan web yang baru dan menarik. Dengan WebSockets dan EventSource, developer web kini memiliki standar yang bersih dan ditetapkan dengan baik untuk mengaktifkan aplikasi web real-time. Namun, tidak semua pengguna menjalankan browser modern. {i>Graceful degradation <i}harus dipertimbangkan saat memilih untuk menerapkan teknologi ini. Selain itu, alat di sisi server untuk WebSockets dan EventSource masih dalam tahap awal. Penting untuk mengingat faktor-faktor ini saat mengembangkan aplikasi HTML5 real-time.