thrift框架介绍
本文组织结构如下:1)引言 2)架构3)支持的数据传输格式、数据传输方式和服务模型 4)Thrift安装 5)利用Thift部署服务
1、引言
Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目。Thrift通过一个中间语言(IDL, 接口定义语言)来定义RPC的接口和数据类型,然后通过一个编译器生成不同语言的代码(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),并由生成的代码负责RPC协议层和传输层的实现。官网地址为:http://thrift.apache.org/ github地址为: https://github.com/packaged/thrift
2、架构

thrift实际上是实现了C/S模式,通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。用户在Thirft描述文件中声明自己的服务,这些服务经过编译后会生成相应语言的代码文件,然后用户实现服务(客户端调用服务,服务器端提服务)便可以了。其中protocol(协议层, 定义数据传输格式,可以为二进制或者XML等)和transport(传输层,定义数据传输方式,可以为TCP/IP传输,内存共享或者文件共享等)被用作运行时库。
3、 支持的数据传输格式、数据传输方式和服务模型
(1)支持的传输格式
TBinaryProtocol – 二进制格式.
TCompactProtocol – 压缩格式
TJSONProtocol – JSON格式
(2) 支持的数据传输方式
TBufferedTransport--缓冲区(常用)
THttpClient---http
TMemoryBuffer ----内存用于I/O
TPhpStream.php---php流的方式
TSocket.php--阻塞式socket
TSocketPool--socket池
(3) 支持的服务模型
TSimpleServer 简单的单线程模型
TServerSocket
4、 Thrift安装
下载:http://thrift.apache.org/download
安装要求:
Unix/linux 系统,windows+cygwin
C++语言:g++、boost
java 语言:JDK、Apache Ant
其他语言:Python、PHP、Perl, etc…
编译安装:./configure –》make –》make install
1、 利用Thrift部署服务
主要流程:编写服务说明,保存到.thrift文件–》根据需要, 编译.thrift文件,生成相应的语言源代码–》根据实际需要, 编写client端和server端代码。
(1).thrift文件编写
一般将服务放到一个.thrift文件中,服务的编写语法与C语言语法基本一致,在.thrift文件中有主要有以下几个内容:变量声明、数据声明(struct)和服务接口声明(service, 可以继承其他接口)。
下面分析Thrift的tutorial中带的例子tutorial.thrift(源码链接地址:https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=tree;f=tutorial;hb=HEAD)
1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20 # Thrift Tutorial 21 # Mark Slee (mcslee@facebook.com) 22 # 23 # This file aims to teach you how to use Thrift, in a .thrift file. Neato. The 24 # first thing to notice is that .thrift files support standard shell comments. 25 # This lets you make your thrift file executable and include your Thrift build 26 # step on the top line. And you can place comments like this anywhere you like. 27 # 28 # Before running this file, you will need to have installed the thrift compiler 29 # into /usr/local/bin. 30 31 /** 32 * The first thing to know about are types. The available types in Thrift are: 33 * 34 * bool Boolean, one byte 35 * byte Signed byte 36 * i16 Signed 16-bit integer 37 * i32 Signed 32-bit integer 38 * i64 Signed 64-bit integer 39 * double 64-bit floating point value 40 * string String 41 * binary Blob (byte array) 42 * map<t1,t2> Map from one type to another 43 * list<t1> Ordered list of one type 44 * set<t1> Set of unique elements of one type 45 * 46 * Did you also notice that Thrift supports C style comments? 47 */ 48 49 // Just in case you were wondering... yes. We support simple C comments too. 50 51 /** 52 * Thrift files can reference other Thrift files to include common struct 53 * and service definitions. These are found using the current path, or by 54 * searching relative to any paths specified with the -I compiler flag. 55 * 56 * Included objects are accessed using the name of the .thrift file as a 57 * prefix. i.e. shared.SharedObject 58 */ 59 include "shared.thrift" 60 61 /** 62 * Thrift files can namespace, package, or prefix their output in various 63 * target languages. 64 */ 65 namespace cpp tutorial 66 namespace d tutorial 67 namespace java tutorial 68 namespace php tutorial 69 namespace perl tutorial 70 namespace haxe tutorial 71 72 /** 73 * Thrift lets you do typedefs to get pretty names for your types. Standard 74 * C style here. 75 */ 76 typedef i32 MyInteger 77 78 /** 79 * Thrift also lets you define constants for use across languages. Complex 80 * types and structs are specified using JSON notation. 81 */ 82 const i32 INT32CONSTANT = 9853 83 const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} 84 85 /** 86 * You can define enums, which are just 32 bit integers. Values are optional 87 * and start at 1 if not supplied, C style again. 88 */ 89 enum Operation { 90 ADD = 1, 91 SUBTRACT = 2, 92 MULTIPLY = 3, 93 DIVIDE = 4 94 } 95 96 /** 97 * Structs are the basic complex data structures. They are comprised of fields 98 * which each have an integer identifier, a type, a symbolic name, and an 99 * optional default value. 100 * 101 * Fields can be declared "optional", which ensures they will not be included 102 * in the serialized output if they aren't set. Note that this requires some 103 * manual management in some languages. 104 */ 105 struct Work { 106 1: i32 num1 = 0, 107 2: i32 num2, 108 3: Operation op, 109 4: optional string comment, 110 } 111 112 /** 113 * Structs can also be exceptions, if they are nasty. 114 */ 115 exception InvalidOperation { 116 1: i32 what, 117 2: string why 118 } 119 120 /** 121 * Ahh, now onto the cool part, defining a service. Services just need a name 122 * and can optionally inherit from another service using the extends keyword. 123 */ 124 service Calculator extends shared.SharedService { 125 126 /** 127 * A method definition looks like C code. It has a return type, arguments, 128 * and optionally a list of exceptions that it may throw. Note that argument 129 * lists and exception lists are specified using the exact same syntax as 130 * field lists in struct or exception definitions. 131 */ 132 133 void ping(), 134 135 i32 add(1:i32 num1, 2:i32 num2), 136 137 i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), 138 139 /** 140 * This method has a oneway modifier. That means the client only makes 141 * a request and does not listen for any response at all. Oneway methods 142 * must be void. 143 */ 144 oneway void zip() 145 146 } 147 148 /** 149 * That just about covers the basics. Take a look in the test/ folder for more 150 * detailed examples. After you run this file, your generated code shows up 151 * in folders with names gen-<language>. The generated code isn't too scary 152 * to look at. It even has pretty indentation. 153 */
要生成php代码:./thrift --gen php tutorial.thrift,结果代码存放在gen-php目录下
要生成java代码:./thrift --gen java tutorial.thrift,结果代码存放在gen-java目录下
(2) client端和server端代码编写
client端和sever端代码要调用编译.thrift生成的中间文件。
在https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=tree;f=tutorial;hb=HEAD 中有各种语言写的client和server;
下面以php为例;
PhpClient.php
1#!/usr/bin/env php 2 <?php 3 4 namespace tutorial\php; 5 6 error_reporting(E_ALL); 7 8 require_once __DIR__.'/../../lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php'; 9 10 use Thrift\ClassLoader\ThriftClassLoader; 11 12 $GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php'; 13 14 $loader = new ThriftClassLoader(); 15 $loader->registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); 16 $loader->registerDefinition('shared', $GEN_DIR); 17 $loader->registerDefinition('tutorial', $GEN_DIR); 18 $loader->register(); 19 20 /* 21 * Licensed to the Apache Software Foundation (ASF) under one 22 * or more contributor license agreements. See the NOTICE file 23 * distributed with this work for additional information 24 * regarding copyright ownership. The ASF licenses this file 25 * to you under the Apache License, Version 2.0 (the 26 * "License"); you may not use this file except in compliance 27 * with the License. You may obtain a copy of the License at 28 * 29 * http://www.apache.org/licenses/LICENSE-2.0 30 * 31 * Unless required by applicable law or agreed to in writing, 32 * software distributed under the License is distributed on an 33 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 34 * KIND, either express or implied. See the License for the 35 * specific language governing permissions and limitations 36 * under the License. 37 */ 38 39 use Thrift\Protocol\TBinaryProtocol; 40 use Thrift\Transport\TSocket; 41 use Thrift\Transport\THttpClient; 42 use Thrift\Transport\TBufferedTransport; 43 use Thrift\Exception\TException; 44 45 try { 46 if (array_search('--http', $argv)) { 47 $socket = new THttpClient('localhost', 8080, '/php/PhpServer.php'); 48 } else { 49 $socket = new TSocket('localhost', 9090); 50 } 51 $transport = new TBufferedTransport($socket, 1024, 1024); 52 $protocol = new TBinaryProtocol($transport); 53 $client = new \tutorial\CalculatorClient($protocol); 54 55 $transport->open(); 56 57 $client->ping(); 58 print "ping()\n"; 59 60 $sum = $client->add(1,1); 61 print "1+1=$sum\n"; 62 63 $work = new \tutorial\Work(); 64 65 $work->op = \tutorial\Operation::DIVIDE; 66 $work->num1 = 1; 67 $work->num2 = 0; 68 69 try { 70 $client->calculate(1, $work); 71 print "Whoa! We can divide by zero?\n"; 72 } catch (\tutorial\InvalidOperation $io) { 73 print "InvalidOperation: $io->why\n"; 74 } 75 76 $work->op = \tutorial\Operation::SUBTRACT; 77 $work->num1 = 15; 78 $work->num2 = 10; 79 $diff = $client->calculate(1, $work); 80 print "15-10=$diff\n"; 81 82 $log = $client->getStruct(1); 83 print "Log: $log->value\n"; 84 85 $transport->close(); 86 87 } catch (TException $tx) { 88 print 'TException: '.$tx->getMessage()."\n"; 89 } 90 91 ?>
PhpServer.php
1#!/usr/bin/env php 2 <?php 3 4 namespace tutorial\php; 5 6 error_reporting(E_ALL); 7 8 require_once __DIR__.'/../../lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php'; 9 10 use Thrift\ClassLoader\ThriftClassLoader; 11 12 $GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php'; 13 14 $loader = new ThriftClassLoader(); 15 $loader->registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); 16 $loader->registerDefinition('shared', $GEN_DIR); 17 $loader->registerDefinition('tutorial', $GEN_DIR); 18 $loader->register(); 19 20 /* 21 * Licensed to the Apache Software Foundation (ASF) under one 22 * or more contributor license agreements. See the NOTICE file 23 * distributed with this work for additional information 24 * regarding copyright ownership. The ASF licenses this file 25 * to you under the Apache License, Version 2.0 (the 26 * "License"); you may not use this file except in compliance 27 * with the License. You may obtain a copy of the License at 28 * 29 * http://www.apache.org/licenses/LICENSE-2.0 30 * 31 * Unless required by applicable law or agreed to in writing, 32 * software distributed under the License is distributed on an 33 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 34 * KIND, either express or implied. See the License for the 35 * specific language governing permissions and limitations 36 * under the License. 37 */ 38 39 /* 40 * This is not a stand-alone server. It should be run as a normal 41 * php web script (like through Apache's mod_php) or as a cgi script 42 * (like with the included runserver.py). You can connect to it with 43 * THttpClient in any language that supports it. The PHP tutorial client 44 * will work if you pass it the argument "--http". 45 */ 46 47 if (php_sapi_name() == 'cli') { 48 ini_set("display_errors", "stderr"); 49 } 50 51 use Thrift\Protocol\TBinaryProtocol; 52 use Thrift\Transport\TPhpStream; 53 use Thrift\Transport\TBufferedTransport; 54 55 class CalculatorHandler implements \tutorial\CalculatorIf { 56 protected $log = array(); 57 58 public function ping() { 59 error_log("ping()"); 60 } 61 62 public function add($num1, $num2) { 63 error_log("add({$num1}, {$num2})"); 64 return $num1 + $num2; 65 } 66 67 public function calculate($logid, \tutorial\Work $w) { 68 error_log("calculate({$logid}, {{$w->op}, {$w->num1}, {$w->num2}})"); 69 switch ($w->op) { 70 case \tutorial\Operation::ADD: 71 $val = $w->num1 + $w->num2; 72 break; 73 case \tutorial\Operation::SUBTRACT: 74 $val = $w->num1 - $w->num2; 75 break; 76 case \tutorial\Operation::MULTIPLY: 77 $val = $w->num1 * $w->num2; 78 break; 79 case \tutorial\Operation::DIVIDE: 80 if ($w->num2 == 0) { 81 $io = new \tutorial\InvalidOperation(); 82 $io->what = $w->op; 83 $io->why = "Cannot divide by 0"; 84 throw $io; 85 } 86 $val = $w->num1 / $w->num2; 87 break; 88 default: 89 $io = new \tutorial\InvalidOperation(); 90 $io->what = $w->op; 91 $io->why = "Invalid Operation"; 92 throw $io; 93 } 94 95 $log = new \shared\SharedStruct(); 96 $log->key = $logid; 97 $log->value = (string)$val; 98 $this->log[$logid] = $log; 99 100 return $val; 101 } 102 103 public function getStruct($key) { 104 error_log("getStruct({$key})"); 105 // This actually doesn't work because the PHP interpreter is 106 // restarted for every request. 107 //return $this->log[$key]; 108 return new \shared\SharedStruct(array("key" => $key, "value" => "PHP is stateless!")); 109 } 110 111 public function zip() { 112 error_log("zip()"); 113 } 114 115 }; 116 117 header('Content-Type', 'application/x-thrift'); 118 if (php_sapi_name() == 'cli') { 119 echo "\r\n"; 120 } 121 122 $handler = new CalculatorHandler(); 123 $processor = new \tutorial\CalculatorProcessor($handler); 124 125 $transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W)); 126 $protocol = new TBinaryProtocol($transport, true, true); 127 128 $transport->open(); 129 $processor->process($protocol, $protocol); 130 $transport->close();
下面分析cpp文件下面的CppClient.cpp和CppServer.cpp代码(主要是想借用别人画的这张图)

在client端,用户自定义CalculatorClient类型的对象(用户在.thrift文件中声明的服务名称是Calculator, 则生成的中间代码中的主类为CalculatorClient), 该对象中封装了各种服务,可以直接调用(如client->ping()), 然后thrift会通过封装的rpc调用server端同名的函数。
在server端,需要实现在.thrift文件中声明的服务中的所有功能,以便处理client发过来的请求。
浙公网安备 33010602011771号