Thrift ile ölçeklenebilir Web
Bu belgede anlatılan tüm kodları http://github.com/yuxel/thrift-examples/ adresinden inceleyebilirsiniz.
Nedir?
Thrift, Facebook tarafından geliştirilen, C++, C# , Cocoa , Erlang , Haskell , Java, OCaml, Perl, PHP , Python, Ruby, Smalltalk dilleri ile istekler yapıp cevaplar alabileceğiniz bir RPC framework'üdür. Örneğin, PHP kodu içinde, PHP methodlarını kullanarak arka tarafta çalışan bir Python uygulamasına istek gönderip cevap alabilirsiniz.
Binary bir protokol kullandığı için SOAP gibi alternatiflerinden daha performanslı bir iletişim katmanı sunar.
Ayrıca en güzel özelliklerinden birisi de asenkron işlemlere izin veren yapısıdır.
Kullanım alanları
Thrift, desteklediği diller arasındaki esnek geçişlerle, bir dil ile karşılaştığımız kısıtları başka bir dil ile yazdığımız arka plan servislerinin çözmesine imkan veriyor. Örneğin, PHP'nin en zayıf olduğu multithread gerektiren bir işlem düşünün. 10 farklı veritabanından veri çekmeniz gerekiyor ve her verinin gelmesi 1'er saniye sürüyor. Tek thread üzerinden bu işlemi yapmanız 10*1 = 10 saniye sürecektir. Ama bu işlemi thread desteği daha iyi olan bir dil ile 10 thread'de paralel olarak 1 saniyede yapabiliriz. Thrift'i bu tip işlemler için kullanabilirsiniz.
Thrift aynı zamanda "oneway", yani asenkron işlemlere de izin vermektedir. Asenkron görevler ise zaman zaman çok işimize yarar. Örneğin, kullanıcının yüklediği bir resmi 10 farklı boyutta, üzerlerine filigran basark, 10 farklı sunucuya yedekleyerek ve bu yükleme işlemini belli yerlere e-posta atarak bildiren bir servisiniz olsun. Yaptığınız her optimizasyona rağmen de bu işlemin 10 saniye sürdüğünü düşünün. Böyle bir işlemde kullanıcı resmi gönderdikten sonra 10 saniye beklemek zorunda kalır. Ama aslında kullanıcının, resmi gönderdikten sonra ve belki resmin ilk kopyası bir sunucuya gönderildikten sonra beklemesine gerek yoktur. Diğer işlem arka planda çalışabilir. Thrift bu tip işlemler için askenkron işlem desteğini sunar.
Kurulum
http://www.yuxel.net/?module=my&id=12 adresinden Debian GNU/Linux için anlatımı bulabilirsiniz.
Tanımlama Dosyası
Thrift kod üretme araçları, tanımlama dosyalarını kullanarak yukarıda belirtilen diller ile ilgili dosyaları oluşturmanıza yardımcı olur. Bu dosya ile ilgili kodları implemente edeceğiniz PHP, Python, C++, Java interface'lerini oluşturabilirsiniz.
Tanımlama dosyalarında, http://wiki.apache.org/thrift/ThriftTypes adresinde belirtilen tiplerdeki verilerle, servis arayüzünüzü yazabilirsiniz.
Kod oluşturma
Yukarıda da bahsettiğim gibi, Thrift aslında bir "kod oluşturma kütüphanesi" olarak da tanımlanabilir. Hazırladığınız Thrift tanımlama dosyasından, istediğiniz dil ile sunucu ve istemci tarafını implemente edebileceğiniz kodları oluşturabilirsiniz.
Örnek Uygulama
Az laf çok kod :) Şimdi de basit bir Thrift servisi oluşturalım. Servisin 2 tane methodu olsun. Birinci method senkron olarak mevcut zamanı unix time stamp formatında geri döndürsün. İkinci method da alt tarafta çok uzun sürecek bir işlemi (örneğin bir resmin çeşitli boyutlarda 10 kopyasını oluşturup her birini farklı sunuculara upload eden, 10 saniye süren bir işlem) asenkron olarak yapsın. Bunu da bir PHP istemci(client) ve Python sunucu(server) ile yapsın.
Öncelikle bu servisi thrift dosyamızda hazırlamalıyız.
namespace php Example service Example { string showCurrentTimestamp() oneway void asynchronousJob() }
Bu dosyada Example isimli bir servisimiz var. showCurrentTimestamp methodu string türünden bir veri dönüyor. asynchronousJob methodu da askenkron (oneway) olarak bir islem yapıyor.
Asenkron methodların bir veri döndürmeyeceğini unutmayın. Dolayısıyla her oneway method void türünden veri dönmelidir.
Bu dosyayı Example.thrift olarak kaydettikten sonra PHP ve Python arayüzlerini oluşturmalıyız.
thrift --gen php Example.thrift thrift --gen py Example.thrift
Bu komutlardan sonra gen-php ve gen-py dizinleri içinde Example sınıfları oluşmuş olmalı.
Oluşturulan php dosyasında (gen-php/Example/Example.php) Example_types.php dosyasının "include_once" ile cagirildigi yolun dogru olduguna emin olun veya o satırı yorum satırı haline getirin.
Bundan sonra ise artık istemci ve sunucu kodlarımızı implemente etmemiz gerekiyor. Bir Python sunucu ve PHP istemci kod örneği görelim.
PythonServer.py
#!/usr/bin/env python
port = 9090
import sys # Burada gen-py dizininizi belirtin sys.path.append('../gen-py')
import time
# Olusturulan dosyalari import et from Example import * from Example.ttypes import *
# Thrift kutuphanesi from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol from thrift.server import TServer
# Burada sunucu implementasyonumuz basliyor class ExampleHandler: # mevcut zamani donen def showCurrentTimestamp(self): timeStamp = time.time() return str(timeStamp)
# sunucuda 10 saniye suren bir islem # asenkron olarak calisacak def asynchronousJob(self): print 'Burada sunucuda 10 saniye suren bir islem basliyor' time.sleep(10) print 'Ama istemci 10 saniye beklemiyor'
# Sunucuyu baslat handler = ExampleHandler()
processor = Example.Processor(handler) transport = TSocket.TServerSocket(port) tfactory = TTransport.TBufferedTransportFactory() pfactory = TBinaryProtocol.TBinaryProtocolFactory()
server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
print 'Sunucu baslatiliyor' server.serve()
PhpClient.php
// Thrift kutuphanesi dosyalari $GLOBALS['THRIFT_ROOT'] = '../lib/';
require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php'; require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php'; require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php'; require_once $GLOBALS['THRIFT_ROOT'].'/transport/THttpClient.php'; require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
// gen-php dizini $GEN_DIR = '../gen-php';
// Example dosyalarimiz require_once $GEN_DIR . '/Example/Example.php'; require_once $GEN_DIR . '/Example/Example_types.php';
// Baglanilacak sunucu host ve portu $host = "localhost"; $port = 9090;
try {
//Thrift baglantisi $socket = new TSocket( $host , $port ); $transport = new TBufferedTransport($socket, 1024, 1024); $protocol = new TBinaryProtocol($transport);
// Istemciyi baslat $client = new ExampleClient($protocol); $transport->open();
// Sunucudan mevcut zamani al $currentTimeStamp = $client->showCurrentTimestamp(); echo $currentTimeStamp;
// Sunucuda 10 saniye surecek bir islemi asenkron olarak cagir $client->asynchronousJob();
$transport->close();
} catch (TException $tx) { print 'Hata: '.$tx->getMessage()."n"; }
Simdi olusan Python ve PHP arayuzleri ile istemci ve sunucumuzu implemente ettik. python PythonServer.py komutu ile Python ile hazirladigimiz sunucuyu baslattiktan ve PhpClient.php dosyamizi tarayicimizla açtıktan sonra, ekranda mevcut zamanı göreceğiz ve askenkron isteğimizi sunucuya göndermiş olacağız.
Kaynakça
http://wiki.apache.org/thrift/ |