thrift介紹與簡單應用
0 本文目的
本文簡單介紹thrift概念和基礎用法,爲想簡單瞭解的人提供一個快速的入門,決定是否要使用以及基礎的使用方式。同時,對於有在使用的人(比如我自己),提供一個快速的基礎的回顧。
本文使用的thrift版本爲0.5。
1 爲什麼要使用thrift
1.1 應用場景在哪兒
thrift 可以用來實現跨語言的rpc調用,讓我們像使用本地接口一樣地去使用遠程接口。這樣的rpc服務調用也是微服務架構的最基礎的部分。由facebook最早開源出來,比較成熟。
1.2 其他方案比較
- http rest實際上也可以作爲跨語言的遠程調用。一般來說是文本形式的數據交換,當然也可以自己定義二進制形式的,不過這樣一來相當於自己定義了rpc框架。另外協議當然是使用的http了。
- thrift可以有多種protocol和transport供選擇,一般來說使用二進制,基於tcp socket,更加高效。
- gRPC是由谷歌開源,較thrift來說更晚開源,目前來看在協議豐富度和語言豐富度上不及thrift,在帶寬敏感時相對錶現更好。
1.3 不支持什麼
- 不能返回null
- 不支持繼承,多態
2 thrift 基本概念
2.1 層級
層級 | 組件 |
---|---|
Server | single-threaded, event-driven etc |
Processor | compiler generated |
Protocol | JSON, compact etc |
Transport | raw TCP, HTTP etc |
2.2 Server
類 | 描述 |
---|---|
TSimplerServer | 接受一個連接,處理連接請求,直到客戶端關閉了連接,它纔回去接受一個新的連接。 |
TNonblockingServer | 使用非阻塞的I/O解決了TSimpleServer一個客戶端阻塞其他所有客戶端的問題。 |
THsHaServer | (半同步/半異步的server)它使用一個單獨的線程來處理網絡I/O,一個獨立的worker線程池來處理消息。這樣,只要有空閒的worker線程,消息就會被立即處理,因此多條消息能被並行處理。 |
TThreadPoolServer | 有一個專用的線程用來接受連接。一旦接受了一個連接,它就會被放入ThreadPoolExecutor中的一個worker線程裏處理。 |
2.3 Processor
自己實現的處理邏輯
2.4 Protocol
類 | 描述 |
---|---|
TBinaryProtocol | 二進制格式 |
TCompactProtocol | 高效和壓縮的二進制格式 |
TDenseProtocoal | 與TCompactProtocol相比,meta信息略有不同 |
TJSONProtocoal | JSON |
TDebugProtocoal | text 格式 方便調試 |
2.5 Transport
TTransport主要處理服務器端和客戶端的網絡讀寫。主要的接口
- open
- close
- read
- write
- flush
例如常用的客戶端是TSocket就是TTransport的一個實現
在服務器端當有請求來的時候,通過TServerTransport可以創建TTransport對象,然後通過TTransport發送數據給客戶端,主要的接口
- open
- listen
- accept
- close
例如TNonblockingTransport,TSocket等。ServerTransport可以理解爲主要實現了accept以後要做的事情,到底是採用何種方式(同步異步,阻塞非阻塞)把請求交給processor處理。
3 簡單應用
3.1 idl
官方文檔可以參考link.
常用的幾個點:
- 文件是 .thrift 結尾 ,裏面可以定義多個類,枚舉
- namespace *****
- include “xxx.thrift”
- 定義model 類:
一般來講我們都用optional, 避免用required,以免後續有更新的時候遇到兼容問題struct AModel { 1: optional i64 id; // 註釋 }
- 定義枚舉:
enum TAttribute { BASIC = 1; // 基本屬性 DATETIME = 2; // 日期屬性 }
- 定義服務
service BService extends A.AService { A.AModel getA(1: A.AModel model); BModel getB(1: BModel model, 2: string name); }
例子:
- model
namespace java com.tx.thrift.model
enum TGreetingType {
EAT = 1; // 問候吃飯
MORNING = 2; // 早
}
struct TGreeting {
1: optional string name; // 名字
2: optional TGreetingType greetingType; // 寒暄類型
}
- service, 引用model
namespace java com.tx.thrift.service
include "hello_model.thrift"
service HelloService {
string hello(1: hello_model.TGreeting greeting);
}
3.2 生成接口
對於java,可以使用maven-thrift-plugin插件,配置後可以在編譯時生成代碼jar包,在server端和client分別引用。
<plugin>
<groupId>org.apache.thrift.tools</groupId>
<artifactId>maven-thrift-plugin</artifactId>
<version>0.1.15-mdt0.0.2</version>
<configuration>
<thriftExecutable>thrift</thriftExecutable>
<generator>java</generator>
<compilerVersion>0.5.0</compilerVersion>
</configuration>
<executions>
<execution>
<id>thrift-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
3.3 實現服務
- 服務實現
package com.tx.thrift.server.impl;
import com.tx.thrift.model.TGreeting;
import com.tx.thrift.service.HelloService;
import org.apache.thrift.TException;
import org.springframework.stereotype.Service;
@Service
public class HelloServiceImpl implements HelloService.Iface {
@Override
public String hello(TGreeting tGreeting) throws TException {
String hello = tGreeting.name;
switch (tGreeting.getGreetingType()) {
case EAT:
hello += ", 吃了嗎";
break;
case MORNING:
hello += ", 早";
break;
default:
break;
}
return hello;
}
}
- 服務啓動
package com.tx.thrift.server;
import com.tx.thrift.server.impl.HelloServiceImpl;
import com.tx.thrift.service.HelloService;
import lombok.extern.slf4j.Slf4j;
import org.apache.thrift.TProcessor;
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@Slf4j
public class Main {
private static final int MAX_READ_BUFFER_BYTES = 16 * 1024 * 1024;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
HelloServiceImpl impl = context.getBean(HelloServiceImpl.class);
System.out.println("bean is loaded.......");
startThriftServerImpl(impl, 9000);
System.out.println("Mifi Loan Api server is started.......");
}
private static void startThriftServerImpl(HelloServiceImpl impl, final int port) {
try {
TProcessor processor = new HelloService.Processor(impl);
THsHaServer.Options options = new THsHaServer.Options();
options.maxReadBufferBytes = MAX_READ_BUFFER_BYTES;
final TNonblockingServerSocket socket = new TNonblockingServerSocket(port);
final TProtocolFactory protocolFactory = new TBinaryProtocol.Factory();
final TServer server = new THsHaServer(new TProcessorFactory(processor), socket,
new TFramedTransport.Factory(), protocolFactory, protocolFactory, options);
System.out.println("STARTING thrift service on port " + port);
new Thread(() -> {
server.serve();
log.info("Thrift server stopped.");
System.exit(-1);
}).start();
System.out.println("STARTED thrift service on port " + port);
} catch (Throwable e) {
System.out.println("Exit the process with unexpected error." + e);
throw new IllegalArgumentException("Start Thrift service failed.", e);
}
}
}
3.4 實現調用
- thrift client
package com.tx.thrift.client;
import com.tx.thrift.service.HelloService;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransportException;
public class HelloClient {
private HelloService.Client client;
private TBinaryProtocol protocol;
private TSocket transport;
public HelloClient(String host, int port) {
transport = new TSocket(host, port);
protocol = new TBinaryProtocol(new TFramedTransport(transport));
client = new HelloService.Client(protocol);
}
public HelloService.Client getClient() {
return client;
}
public void open() throws TTransportException {
transport.open();
}
public void close() {
transport.close();
}
}
- 調用測試
package com.tx.thrift.client;
import com.tx.thrift.model.TGreeting;
import com.tx.thrift.model.TGreetingType;
import org.apache.thrift.TException;
public class Main {
public static void main(String[] args) throws TException {
TGreeting greeting = new TGreeting();
greeting.setName("XXX");
greeting.setGreetingType(TGreetingType.MORNING);
HelloClient client = new HelloClient("localhost", 9000);
client.open();
System.out.println(client.getClient().hello(greeting));
greeting.setGreetingType(TGreetingType.EAT);
System.out.println(client.getClient().hello(greeting));
client.close();
}
}
注意對於TNonblockingServerSocket,client和server都要用TNonblockingServerSocket。
3.5 代碼與thrift概念映射
server層我們使用的是THsHaServer,processor是用自己是實現邏輯實例化的,protocol用的TBinaryProtocol,transport用的TNonblockingTransport和TFramedTransport。
4 比較swift(*)
todo