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 類:
    struct AModel {
    	1: optional i64 id; // 註釋
    }
    
    一般來講我們都用optional, 避免用required,以免後續有更新的時候遇到兼容問題
  • 定義枚舉:
    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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章