Thrift objective C应用

Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目。Thrift通过一个中间语言(IDL, 接口定义语言)来定义RPC的接口和数据类型,然后通过一个编译器生成不同语言的代码(目前支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),并由生成的代码负责RPC协议层和传输层的实现。

Thrift官方网站英文

Thrift支持Mac OS X10.5及以上版本,iOS4及以上版本。iOS5和Mac OS10.7及以上OC开始支持ARC,自动管理Retain/release.

Thrift0.8.0不支持ARC,如果你的应用使用了ARC模式,你可以在Xcode的Targets-》BuildPhases-》COmpilSource把Thrift相关文件设置-fno-objc-arc。Thrift0.9.0开始支出ARC和非ARC,他自动适应程序的ARC和非ARC模式,不需要手动转换。

安装Thrift

如果没有安装brew可以去官网安装,直接在终端上运行下面一句命令就可以。

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
$ brew --version

可以查看brew版本信息,如果没有那么你应该先安装一个brew了。
如果想升级一下:

brew update

然后安装Thrift

brew install thrift

项目中引入Thrift库

在此我使用pod引入Thrift库,

pod 'thrift', '~> 0.9.3'

很方便快捷,如果不会,请自行补Pod方面知识。

Thrift文件

Thrift实际上是实现了C/S模式,通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。用户在Thirft描述文件中声明自己的服务,这些服务经过编译后会生成相应语言的代码文件,然后用户实现服务(客户端调用服务,服务器端提服务)便可以了。其中protocol(协议层, 定义数据传输格式,可以为二进制或者XML等)和transport(传输层,定义数据传输方式,可以为TCP/IP传输,内存共享或者文件共享等)被用作运行时库。

我们这里是服务器工程师提供,一定要注意文件中格式问题,面向OC的要特别注意,结构体中变量名不能首字母大写不然取不到值,最好thrift只生产一个文件,如果接口特别多,特别需要生产多个文件,函数名称,变量名称,都要加个前缀。因为OC是不允许同名属性名,和同名方法名的。

生成相应的oc文件,首先cd到thrift的当前文件夹内。

thrift --gen cocoa ****.thrift

会在gen-cocoa文件夹内生成两个相对应的.h.m文件。

以后文件如果有改动,也安照以上步骤进行,会自动生成两个文件覆盖以前的两个文件。所以覆盖前一定要询问服务器工程师,够修改了什么地方,自己也要备份好以前的thrift文件以方便以后对照找bug。以防止变动不足以引起编译运行时报错,但使用时就是crash了。

项目内编写代码

#####(1)支持的传输格式

TBinaryProtocol – 二进制格式.

TCompactProtocol – 压缩格式

TJSONProtocol – JSON格式

TSimpleJSONProtocol –提供JSON只写协议, 生成的文件很容易通过脚本语言解析。

TDebugProtocol – 使用易懂的可读的文本格式,以便于debug

#####(2) 支持的数据传输方式

TSocket -阻塞式socker

TFramedTransport – 以frame为单位进行传输,非阻塞式服务中使用。

TFileTransport – 以文件形式进行传输。

TMemoryTransport – 将内存用于I/O. java实现时内部实际使用了简单的ByteArrayOutputStream。

TZlibTransport – 使用zlib进行压缩, 与其他传输方式联合使用。当前无java实现。

#####(3)支持的服务模型

TSimpleServer – 简单的单线程服务模型,常用于测试

TThreadPoolServer – 多线程服务模型,使用标准的阻塞式IO。

TNonblockingServer – 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式)

需要注意的是,thrift生成的oc代码,是同步的,所以在请求的时候,需要自己开辟一个线程进行异步数据请求。

文件中导入

#import "TBinaryProtocol.h"
#import "TFramedTransport.h"
#import "TSocketClient.h"
#import "TMemoryBuffer.h"

代码实现

- (IpadTeamLeaderClient *)setupTeamLeaderClient {

TSocketClient *transport = [[TSocketClient alloc] initWithHostname:ServerName port:ServerPort];
TFramedTransport *ftransport = [[TFramedTransport alloc] initWithTransport:transport];
TBinaryProtocol *protocol = [[HHBinaryProtocol alloc] initWithTransport:ftransport];
return [[IpadTeamLeaderClient alloc] initWithProtocol:protocol];

}  

时也可以采用http传输方式

 #import <THTTPClient.h>
#import <TBinaryProtocol.h>

- (void) connect {
NSURL *url = [NSURL URLWithString:@"http://localhost:8082"];
// url = [NSURL URLWithString:@"https://myapp145454.appspot.com"];

// Talk to a server via HTTP, using a binary protocol
THTTPClient *transport = [[THTTPClient alloc] initWithURL:url];
TBinaryProtocol *protocol = [[TBinaryProtocol alloc] 
                             initWithTransport:transport 
                             strictRead:YES 
                             strictWrite:YES];

server = [[TestServiceClient alloc] initWithProtocol:protocol];

数据请求需要异步。官方demo

1
2
3
4
5
6
7
@interface ThriftTestViewController : UIViewController {
IBOutlet UILabel *msg;
}
@property (nonatomic, retain) UILabel *msg;
@property (nonatomic, strong) TestServiceClient *server;
@property (nonatomic, strong) NSOperationQueue *asyncQueue;
@property (nonatomic, strong) NSOperationQueue *mainQueue;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- (void )viewDidLoad {
asyncQueue = [[NSOperationQueue alloc] init];
[asyncQueue setMaxConcurrentOperationCount:1]; // serial
mainQueue = [NSOperationQueue mainQueue]; // for GUI, DB
NSURL *url = [NSURL URLWithString:@"http://localhost:8082"];
// Talk to a server via HTTP, using a binary protocol
THTTPClient *transport = [[THTTPClient alloc] initWithURL:url];
TBinaryProtocol *protocol = [[TBinaryProtocol alloc]
initWithTransport:transport
strictRead:YES
strictWrite:YES];
// Use the service defined in profile.thrift
server = [[TestServiceClient alloc] initWithProtocol:protocol];
NSLog(@"Client init done %@", url);
}
-(void)doCalc {
__unsafe_unretained ThriftTestViewController *weakSelf = self;
[asyncQueue addOperationWithBlock:^(void) {
__unsafe_unretained ThriftTestViewController *weakSelf2 = weakSelf;
@try {
weakSelf.msg.text = nil;
int result = [weakSelf.server calc:24 :32];
[weakSelf.mainQueue addOperationWithBlock:^(void) {
weakSelf2.msg.text = [NSString stringWithFormat:@"%d", result];
}];
}
@catch (TException *e) {
NSString *errorMsg = e.description;
NSLog(@"Error %@", errorMsg);
[weakSelf.mainQueue addOperationWithBlock:^(void) {
weakSelf2.msg.text = errorMsg;
}];
}
}];
}