本文组织结构如下: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发过来的请求。