本文组织结构如下: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 java 例子 thrift架构_数据传输

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代码(主要是想借用别人画的这张图)

thrift java 例子 thrift架构_数据传输_02

在client端,用户自定义CalculatorClient类型的对象(用户在.thrift文件中声明的服务名称是Calculator, 则生成的中间代码中的主类为CalculatorClient), 该对象中封装了各种服务,可以直接调用(如client->ping()), 然后thrift会通过封装的rpc调用server端同名的函数。

在server端,需要实现在.thrift文件中声明的服务中的所有功能,以便处理client发过来的请求。