为什么80%的码农都做不了架构师?>>>
Thrift是一个软件框架(远程过程调用框架),用来进行可扩展且跨语言的服务的开发,封装了数据传输格式(二进制、json)和网络通信的服务框架,提供多语言(C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml)的网络服务器端和客户端程序组件
适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输相对于JSON和xml无论在性能、传输大小上有明显的优势。
本文以注册服务接口和登录服务器接口为教程,
Thrift开发的几个概念:
Server 服务模型
Handler 数据处理接口
Processor 数据处理对象
Protocol 数据传输协议
Transport 数据传输方式
(1)支持的传输格式
TBinaryProtocol – 二进制格式.
TCompactProtocol – 压缩格式
TJSONProtocol – JSON格式
TSimpleJSONProtocol –提供JSON只写协议, 生成的文件很容易通过脚本语言解析。
TDebugProtocol – 使用易懂的可读的文本格式,以便于debug
(2) 支持的通信方式(数据传输方式)(Transport)
TFileTransport:文件(日志)传输类,允许client将文件传给server,允许server将收到的数据写到文件中。
THttpTransport:采用Http传输协议进行数据传输
TSocket:采用TCP Socket进行数据传输
TZlibTransport:压缩后对数据进行传输,或者将收到的数据解压
下面几个类主要是对上面几个类地装饰(采用了装饰模式),以提高传输效率。
TBufferedTransport:对某个Transport对象操作的数据进行buffer,即从buffer中读取数据进行传输,或者将数据直接写入buffer
TFramedTransport:以frame为单位进行传输,非阻塞式服务中使用。同TBufferedTransport类似,也会对相关数据进行buffer,同时,它支持定长数据发送和接收。
TMemoryBuffer:从一个缓冲区中读写数据
(3)支持的服务模型
TSimpleServer – 简单的单线程服务模型,常用于测试
TThreadedServer - 多线程服务模型,使用阻塞式IO,每个请求创建一个线程。
TThreadPoolServer – 线程池服务模型,使用标准的阻塞式IO,预先创建一组线程处理请求。
TNonblockingServer – 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式)
处理大量更新的话,主要是在TThreadedServer和TNonblockingServer中进行选择。TNonblockingServer能够使用少量线程处理大量并发连接,但是延迟较高;TThreadedServer的延迟较低。实际中,TThreadedServer的吞吐量可能会比TNonblockingServer高,但是TThreadedServer的CPU占用要比TNonblockingServer高很多。
服务端编写的一般步骤:
1. 创建Handler
2. 基于Handler创建Processor
3. 创建Transport(通信方式)
4. 创建Protocol方式(设定传输格式)
5. 基于Processor, Transport和Protocol创建Server
6. 运行Server
客户端编写的一般步骤:
1. 创建Transport
2. 创建Protocol方式
3. 基于Transport和Protocol创建Client
4. 运行Client的方法
上边概述内容参考自:http://elf8848.iteye.com/blog/1960131
下面开始正式代码教程
服务描述文件test.thrift,定义了login服务和register
/**
* The first thing to know about are types. The available types in Thrift are:
*
* 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
* binary Blob (byte array)
* map Map from one type to another
* list Ordered list of one type
* set Set of unique elements of one type
*
* Did you also notice that Thrift supports C style comments?
*/
namespace java com.penngo
namespace php com.penngo
struct User {
1: i64 id,
2: string name,
3: string password
}
service LoginService{
User login(1:string name, 2:string psw);
}
service RegisterService{
User createUser(1:string name, 2:string psw);
}
使用thrift生成对应平台语言代码
thrift -gen java test.thrift
thrift -gen php test.thrift
如果php需要生成服务器端,需求改为thrift -gen php:server test.thrift
java
实现LoginServiceImpl.java登录接口业务
import org.apache.thrift.TException;
public class LoginServiceImpl implements LoginService.Iface{
public LoginServiceImpl(){
}
public User login(String name, String psw) throws TException{
User user = null;
if(name.equals("penngo") && psw.equals("123")){
user = new User();
user.setId(1);
user.setName("penngo");
}
return user;
}
}
实现RegisterServiceImpl.java注册接口业务
import org.apache.thrift.TException;
public class RegisterServiceImpl implements RegisterService.Iface{
public RegisterServiceImpl(){
}
public User createUser(String name, String psw) throws TException{
User user = new User();
user.setId(2);
user.setName(name);
user.setPassword(psw);
return user;
}
}
服务器端java代码
package com.penngo.main;
import org.apache.thrift.TMultiplexedProcessor;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import com.penngo.LoginService;
import com.penngo.LoginServiceImpl;
import com.penngo.RegisterService;
import com.penngo.RegisterServiceImpl;
public class Server {
private void start() {
try {
TServerSocket serverTransport = new TServerSocket(7911);
// 用户登录
LoginService.Processor loginProcessor = new LoginService.Processor(
new LoginServiceImpl());
// 用户注册
RegisterService.Processor registerProcessor = new RegisterService.Processor(
new RegisterServiceImpl());
// Factory protFactory = new TBinaryProtocol.Factory(true, true);
// TServer server = new TThreadPoolServer(new
// TThreadPoolServer.Args(serverTransport)
// .processor(loginProcessor));
TMultiplexedProcessor processor = new TMultiplexedProcessor();
processor.registerProcessor("LoginService", loginProcessor);
processor.registerProcessor("RegisterService", registerProcessor);
TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(
serverTransport).processor(processor));
System.out.println("Starting server on port 7911 ...");
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
Server srv = new Server();
srv.start();
}
}
客户端java
package com.penngo.main;
import org.apache.thrift.*;
import org.apache.thrift.protocol.*;
import org.apache.thrift.transport.*;
import com.penngo.LoginService;
import com.penngo.RegisterService;
import com.penngo.User;
public class Client {
public static void main(String[] args) {
try {
TTransport transport = new TSocket("localhost", 7911);
TProtocol protocol = new TBinaryProtocol(transport);
TMultiplexedProtocol mp1 = new TMultiplexedProtocol(protocol,
"LoginService");
// TProtocol protocol = new TBinaryProtocol(transport);
// LoginService.Client client = new LoginService.Client(protocol);
LoginService.Client loginClient = new LoginService.Client(mp1);
TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol,
"RegisterService");
RegisterService.Client registerClient = new RegisterService.Client(
mp2);
transport.open();
User user = loginClient.login("penngo", "123");
if (user != null) {
System.out.println("登录成功:" + user.getId() + " "
+ user.getName());
} else {
System.out.println("登录失败");
}
User user2 = registerClient.createUser("test", "123");
if (user2 != null) {
System.out.println("创建用户成功:" + user2.getId() + " "
+ user2.getName());
} else {
System.out.println("创建用户失败");
}
transport.close();
} catch (TException x) {
x.printStackTrace();
}
}
}
客户端php
registerNamespace('Thrift', __DIR__ . '/../../lib');
//$loader->registerDefinition('shared', $GEN_DIR);
$loader->registerDefinition('com', $GEN_DIR);
$loader->register();
if (php_sapi_name() == 'cli') {
ini_set("display_errors", "stderr");
}
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Protocol\TMultiplexedProtocol;
use Thrift\Transport\TSocket;
use Thrift\Transport\THttpClient;
use Thrift\Transport\TBufferedTransport;
use Thrift\Exception\TException;
use com\penngo\RegisterServiceClient;
use com\penngo\LoginServiceClient;
try {
if (array_search('--http', $argv)) {
//$socket = new THttpClient('localhost', 8080, '/php/PhpServer.php');
} else {
$socket = new TSocket('localhost', 7911);
}
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol = new TBinaryProtocol($transport);
$loginProtocol = new TMultiplexedProtocol($protocol, "LoginService");
$registerProtocol = new TMultiplexedProtocol($protocol, "RegisterService");
$loginClient = new LoginServiceClient($loginProtocol);
$registerClient = new RegisterServiceClient($registerProtocol);
$transport->open();
$user = $loginClient->login('penngo', '123');
print "user===={$user->id} {$user->name} \n";
$user = $registerClient->createUser('test', '123456');
print "user===={$user->id} {$user->name} \n";
$transport->close();
} catch (TException $tx) {
print 'TException: '.$tx->getMessage()."\n";
print 'TException: '.$tx->getTraceAsString()."\n";
}
?>