[@dalinaum][twitter-dalinaum] - 안드로이드 가이. 최근에는 안드로이드의 3D 그래픽(렌더스크립트, OpenGL ES)에 심취해 있다, dalinaum ad gmail.com
많은 애플리케이션은 프레젠테이션 레이어와 핵심 로직의 언어를 분리하고 있습니다. 트위터의 경우에는 핵심 로직을 스칼라로 처리하며 프레젠테이션 부분을 루비 언어로 처리합니다. 안드로이드 플랫폼은 프레젠테이션 레이어를 자바와 XML 파일로 구성하며 성능이 중요한 부분은 C/C++ 언어를 사용하고 있습니다. 앞으로 소프트웨어 개발에 더 다양한 언어 조합이 등장할 것을 어렵지 않게 예상할 수 있습니다. 펄 언어 만으로 되어 있는 단일한 환경에서는 모듈과 함수 호출로서 대부분의 일을 해결할 수 있습니다. 하지만 더 다양한 언어와 다채로운 개발 환경을 조합할 경우 기존의 방법으로 문제를 해결하기는 쉽지 않습니다.
Thrift는 페이스북이 개발한 규모 가변적인 (scalable) 이종 언어 서비스 개발을 위한 소프트웨어 프레임워크입니다. 이 프레임워크는 2007년에 페이스북에 의해 개발되기 시작하여 2008년 이후 아파치 재단이 유지 보수를 맡고 있습니다. Thrift는 C++
, Java
, Python
, PHP
, Ruby
, Erlang
, Perl
, Haskell
, C#
, Cocoa
, Javascript
, Node.js
, Smalltalk
, OCalm
언어 등의 다양한 환경을 지원하고 있습니다. 우리가 사용하는 Perl
언어를 비롯해 학계나 산업계가 주목하는 언어들의 대부분을 지원하고 있는 셈입니다. Thrift를 이용해여 다양한 환경의 소프트웨어를 쉽게 결합할 수 있습니다.
Thrift 기술을 활용하기 위해 먼저 서버에 Thrift 구현이 설치되어 있어야 합니다. 윈도우즈를 위한 구현의 빌드 버전은 공식 사이트에서 받을 수 있 습니다. 윈도우 환경에서는 다운로드 받아 설치할 수 있습니다.
리눅스 환경과 Mac OS X 환경에서는 빌드를 하여 구축할 수 있습니다.
우분투 환경에서는 아래와 같은 명령어를 입력하여 빌드할 수 있습니다.
./configure
make
make install
일반적인 빌드 과정과 비슷합니다.
맥에서는 커맨드라인에 직접 빌드하기 보다는 Homebrew를 이용하여 설치하는 편이 훨씬 더 편리합니다.
homebrew가 설치된 환경에서는 아래의 명령으로 진행합니다.
brew install thrift
설치가 완료되었으면 thrift 명령으로 수행이 가능합니다.
Thrift가 준비가 되었으면 개별 언어용 라이브러리를 만들어야 합니다. 이 글에서는 Java언어와 Perl 언어를 위한 라이브러리를 빌드합니다. Thrift가 홈 디렉토리의 서브디렉토리 ~/thrift-0.7.0
에 설치되 었다면 아래와 같은 형태로 빌드합니다.
cd ~/thrift-0.7.0/lib/java
ant
Java 언어용 라이브러리 코드를 빌드한 것입니다. Perl 언어용 라이브러리 코드도 만듭시다.
cd ~/thrift-0.7.0/lib/perl
perl Makefile.PL
make
make install
먼저 Thrift가 제대로 동작하는지 튜토리얼 버전을 통해 확인해보겠습니다. ~/thrift-0.7.0
에 thrift
가 설치되었다면 ~/thrift-0.7.0/tutorial
에 튜토리얼 코드가 있습니다.
cd ~/thrift-0.7.0/tutorial
thrift -r --gen perl tutorial.thrift
cd perl
perl PerlServer.pl &
perl PerlClient.pl
제대로 동작하면 fg
명령어를 이용해서 서버로 이동하신 다음 Control + C
(혹은 Command + C
버튼)을 눌러 나오시면 됩니다.
생소한 명령어들이 많기 때문에 낯설 것입니다. 하나씩 짚어가겠습니다. 두번째 줄의 thrift -r --gen perl tutorial.thrift
는 tutorial.thrift
파일을 이용하여 재귀적으로 (-r
) 펄 언어용 코드를 (--gen perl
) 생성하는 것을 의미합니다. Thrift
에서 사용하는 언어는 thrift
확장자를 사용합니다. 여기의 tutorial.thrift
가 그 파일에 해당됩니다. thrift --gen
으로 코드를 생성할 때 thrift
파일이 이동되고 서버용 스켈레톤 코드와 클라이언트용 라이브러리 코드가 만들어져 편리하게 이용할 수 있습니다. 큰 노력없이 클라이언트를 구현할 수 있고 서버측 코드도 스켈레톤 내에 코드를 채우는 형태로 구현할 수 있습니다. 만약 --gen
옵션에 다른 언어를 지정했다면 다른 언어를 위한 코드가 생성됩니다. --gen java
를 입력하면 자바용 코드가 만들어집니다.
이제 tutorial.thrift
파일을 살펴보겠습니다. 전체적으로 주석이 많이 달려있어 꼼꼼이 읽으면 대부분의 내용을 이해할 수 있을 겁니다.
include "shared.thrift"
shared.thrift
파일을 포함하고 있습니다. 그래서 먼저 shared.thrift
부터 보겠습니다.
namespace cpp shared
namespace java shared
namespace perl shared
namespace php shared
struct SharedStruct {
1: i32 key
2: string value
}
service SharedService {
SharedStruct getStruct(1: i32 key)
}
첫 줄부터 네 번째 줄은 thirft
에서 생성할 언어 별 네임스페이스를 지정하는 것입니다. 여기서는 cpp
, java
, perl
, php
언어에서 같은 네임스페이스로 서버 루틴을 사용할 수 있습니다.
struct SharedStruct
는 구조체를 만든 것입니다. 항목이 1:
, 2:
와 같이 콜론이 뒤에 붙은 숫자의 나열로 되어 있는 점을 유의하십시요. 이후에 타입과 이름이 붙습니다. 자료형이 i32
와 string
인데 해당 자료들은 그 언어에 맞게 자동으로 변환됩니다. 아래는 사용할 수 있는 자료형입니다.
- bool - Boolean, one byte
- byte - Signed byte
- i16 - Signed 16-bit integer
- i32 - Signed 32-bit integer
- i64 - Signed 64-bit integer
- double - 64-bit floating point value
- string - String
- map<t1,t2> - Map from one type to another
- list - Ordered list of one type
- set - Set of unique elements of one type
많은 언어에서 기본적으로 제공될만한 자료형들이 제공되고 있습니다. 복잡한 자료형은 직렬화를 하거나 구조체를 만들어 사용해야 합니다.
그 다음으로 service
가 위치하는데 service
안에 우리가 사용할 수 있는 함수들이 위치합니다. service
는 객체 정도로 보시면 됩니다. SharedStruct getStruct(1: i32 key)
를 보시면 매개 변수에도 1:
과 같이 순서대로 번호를 붙인 것을 볼 수 있습니다.
다시 tutorial.thrift
로 돌아가서 아직 모르는 부분들을 찾아봅시다.
php_namespace tutorial
php
의 경우 별도의 namespace
인 php_namespace
를 사용하는 것을 볼 수 있습니다. (구체적인 차이점은 모르겠습니다. php
에 대해 잘 아시는 분이 설명해주셨으면 좋겠네요 :)
typedef i32 MyInteger
C
언어에서 익숙한 형의 typedef
구문입니다.
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
상수 리터럴은 위와 같이 사용합니다.
enum Operation {
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
}
열거형입니다.
exception InvalidOperation {
1: i32 what,
2: string why
}
예외를 정의하는 형태입니다. 아래와 같이 사용합니다.
i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
뒤에 throws를 이용해서 예외를 던질 수 있게 되어 있습니다. 마지막에 ,
가 붙어있는 것을 주의하십시요. service
내에 여러 함수들이 위치하고 ,
로 구별됩니다. 당연한 이야기지만 마지막 항목인 경우에는 뒤에 ,
를 붙이지 않습니다.
oneway void zip(),
oneway
는 호출만 수행시키고 클라이언트가 응답을 기다리지 않는다는 의미입니다. I/O가 많은 작업들에서 더 효과적인 처리를 기대할 수 있습니다.
이 명세를 한번 보는 것만으로는 이해하기 힘든데 다른 thrift
명세들을 보는 것이 도움이 됩니다.
서버를 구축하는 것은 아래와 같은 형태가 됩니다. PerlServer.pl
의 일부 코드만 차용하겠습니다.
use strict;
use lib '../gen-perl';
use Thrift::Socket;
use Thrift::Server;
use tutorial::Calculator;
package CalculatorHandler;
use base qw(tutorial::CalculatorIf);
sub new {
my $classname = shift;
my $self = {};
return bless($self,$classname);
}
sub ping
{
print "ping()\n";
}
위에 정의했던 tutorial
네임스페이스의 Calculator
서비스를 구현하는 것입니다. use base qw(tutorial::CalculatorIf);
로 시작하는데요.
스켈레톤 클래스가 If
가 붙은 형태로 되어 있습니다. 이런 컨벤션은 thrift
가 언어 구현별로 다르게 만들어주는데 샘플 코드를 보고 파악해야 합니다. 나머지 기능들은 sub new
, sub ping
등의 형태로 구현해 주시면 됩니다. 우리는 이 클래스를 확장하여 서버를 구현합니다.
service
의 함수의 작성이 끝나면 서버를 구현합니다.
eval {
my $handler = new CalculatorHandler;
my $processor = new tutorial::CalculatorProcessor($handler);
my $serversocket = new Thrift::ServerSocket(9090);
my $forkingserver = new Thrift::ForkingServer($processor, $serversocket);
print "Starting the server...\n";
$forkingserver->serve();
print "done.\n";
}; if ($@) {
if ($@ =~ m/TException/ and exists $@->{message}) {
my $message = $@->{message};
my $code = $@->{code};
my $out = $code . ':' . $message;
die $out;
} else {
die $@;
}
}
CalculatorHandler
는 방금 저희가 상속받아 구현한 클래스입니다. 여기에서 $processor
에서 사용된 tutorialProcessor
는 thrift
가 생성한 코드입니다. Thrift::ServerSocket
과 Thrift::ForkingServer
는 이미 thrift
내에 포함된 클래스입니다.
Thrift::ServerSocket
에 포트를 지정하는데 서버와 클라이언트 사이에 약속한 포트번호를 여시면 됩니다. 이 번호는 서버내의 thrift
구현이 여러개인 경우 여러 번호가 필요합니다. 예를 들어 서버에서 Hbase
나 Casandra
서버가 같이 돌고 있다면 하나의 thrift
포트를 사용하고 있을 겁니다. 이 경우 다른 thrift
서버는 새 포트 번호가 필요합니다.
아래는 클라이언트 코드의 일부입니다.
my $socket = new Thrift::Socket('localhost',9090);
my $transport = new Thrift::BufferedTransport($socket,1024,1024);
my $protocol = new Thrift::BinaryProtocol($transport);
my $client = new tutorial::CalculatorClient($protocol);
eval{
$transport->open();
$client->ping();
print "ping()\n";
my $sum = $client->add(1,1);
print "1+1=$sum\n";
my $work = new tutorial::Work();
$work->op(tutorial::Operation::DIVIDE);
$work->num1(1);
$work->num2(0);
eval {
$client->calculate(1, $work);
print "Whoa! We can divide by zero?\n";
}; if($@) {
warn "InvalidOperation: ".Dumper($@);
}
}
이제 기본적인 thrift
의 명세, 서버의 구현, 클라이언트 사용법을 훑어보셨습니다. Hbase
나 Casandra
에서 thrift
가 어떻게 사용되시는지를 보시면 더 도움이 되실겁니다.
thrift
를 어떻게 설치하고 어떻게 사용할 수 있는지 튜토리얼 코드를 따라가며 확인을 해보았습니다. 서버의 이종 언어의 백엔드 / 프론트엔드간의 통신, thrift
를 지원하는 다른 서버(hbase
와 casandra
가 대표적입니다.)와의 통신, 서버와의 연결이 필요한 모바일 애플리케이션 등에서 (구글은 이 경우에 자사의 프로토콜버퍼를 사용합니다.) 유용하게 쓰일 수 있을 것입니다. 조금 더 복잡한 내용은 추후에 다루겠습니다.
감사합니다.
PS: thrift
의 tutorial은 현대적이지 못한 펄 코드로 되어 있습니다. 이를 어떻게 개선하는 것이 좋을지도 고민해봅시다.