天天看點

Apache Thrift入門學習

本文簡單介紹下Apache Thrift。thrift是一個軟體架構,用來進行可擴充且跨語言的服務的開發。它結合了功能強大的軟體堆棧和代碼生成引擎,以建構在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 這些程式設計語言間無縫結合的、高效的服務。

全部代碼下載下傳:Github連結:github連結,點選驚喜;寫文章不易,歡迎大家采我的文章,以及給出有用的評論,當然大家也可以關注一下我的github;多謝;

1.Thrift介紹

Thrift是一種接口描述語言和二進制通訊協定,[1]它被用來定義和建立跨語言的服務。[2]它被當作一個遠端過程調用(RPC)架構來使用,是由Facebook為“大規模跨語言服務開發”而開發的。它通過一個代碼生成引擎聯合了一個軟體棧,來建立不同程度的、無縫的跨平台高效服務,可以使用C#、C++(基于POSIX相容系統[3])、Cappuccino、[4]Cocoa、Delphi、Erlang、Go、Haskell、Java、Node.js、OCaml、Perl、PHP、Python、Ruby和Smalltalk。[5]雖然它以前是由Facebook開發的,但它現在是Apache軟體基金會的開源項目了。該實作被描述在2007年4月的一篇由Facebook發表的技術論文中,該論文現由Apache掌管(摘自維基百科)

2.Thrift安裝

安裝Thrift主要是為了通過IDL檔案生成需要的接口和類。

下載下傳位址:http://archive.apache.org/dist/thrift/0.9.3/

2.1windowx的安裝:

  1. 下載下傳thrift-0.9.3.exe
  2. 建立一個檔案夾如:Thtift

    3.将thrift-0.9.3.exe該名為thrift(可以不改名)

    4.修改系統的環境變量:

    Apache Thrift入門學習
  3. 測試安裝成功否:
    Apache Thrift入門學習

2.2Linux的安裝:

  1. 下載下傳:thrift-0.9.3.tar.gz

    2.tar -xzf thrift-0.9.3.tar.gz

    3.cd thrift-0.9.3

  2. ./configure
  3. make 有點慢

    6.sudo make install

3.Thrift的IDL格式要求

IDL即接口定義語言,主要是為了生成各種語言(例如:java,c/C++等)能夠了解的消息結構、接口定義的描述形式。因為要生成其他語言的類,接口,或者結構體,是以必須遵守一些規範。

3.1檔案支援的基本類型:

  • bool: 布爾值
  • byte: 有符号位元組
  • i16: 16位有符号整型
  • i32: 32位有符号整型
  • i64: 64位有符号整型
  • double: 64位浮點型
  • string: 字元串/字元數組
  • binary: 二進制資料

3.2容器類型:

  • list: 一系列由T類型的資料組成的有序清單,元素可以重複
  • set: 一系列由T類型的資料組成的無序集合,元素不可重複
  • map

3.3結構體:

結構體經過解析後在面向對象語言中,表現為“類定義”;在弱類型語言、動态語言中,表現為“結構/結構體”。

定義格式如下:

struct <結構體名稱> {
        <序号>:[字段性質] <字段類型> <字段名稱> [= <預設值>] [;|,]
}
·結構體名稱:可以按照您的業務需求,給定不同的名稱(區分大小寫)。但是要注意,一組IDL定義檔案中結構體名稱不能重複,且不能使用IDL已經占用的關鍵字(例如required 、struct 等單詞)。

·序号:序号非常重要。正整數,按照順序排列使用。這個屬性在Apache Thrift進行序列化的時候被使用。

·字段性質:包括兩種關鍵字:required 和 optional,如果您不指定,那麼系統會預設為required。required表示這個字段必須有值,并且Apache Thrift在進行序列化時,這個字段都會被序列化;optional表示這個字段不一定有值,且Apache Thrift在進行序列化時,這個字段隻有有值的情況下才會被序列化。

·字段類型:在struct中,字段類型可以是某一個基礎類型,也可以是某一個之前定義好的struct,還可以是某種Apache Thrift支援的容器(set、map、list),還可以是定義好的枚舉。字段的類型是必須指定的。

·字段名稱:字段名稱區分大小寫,不能重複,且不能使用IDL已經占用的關鍵字(例如required 、struct 等單詞)。

·預設值:您可以為某一個字段指定預設值(也可以不指定)。

·結束符:在struct中,支援兩種結束符,您可以使用“;”或者“,”。當然您也可以不使用結束符(Apache Thrift代碼生成程式,會自己識别到)
           

3.4枚舉:

枚舉的定義形式和C的Enum定義差不多

enum <枚舉名稱> {
        <枚舉字段名> = <枚舉值>[;|,]
}           

3.5常量定義:

IDL允許定義常量。常量的關鍵字為“const”,與C語言類似,常量可以是基礎類型,也可以是定義的Struct。

如:

const i32 MAX_AREA = 60

3.6異常定義:

IDL允許定義異常,在定義服務接口使用。其定義方法類似于上面的Struct。因為exception也是類,定義時隻需要将struct換成exception就行,如下:

exception <異常名稱> {
        <序号>:[字段性質] <字段類型> <字段名稱> [= <預設值>] [;|,]
}           

3.7服務接口:

服務接口在生成代碼過程中,生成的接口類是我們需要實作的提供給用戶端調用的。service服務接口定義如下:

service <服務名稱> {
    <void | 傳回指類型> <服務方法名>([<入參序号>:[required | optional] <參數類型> <參數名> ...]) [throws ([<異常序号>:[required | optional] <異常類型> <異常參數名>...])]
}
·服務名稱:服務名可以按照您的業務需求自行制定,注意服務名是區分大小寫的。IDL中服務名稱隻有兩個限制,就是不能重複使用相同的名稱,不能使用IDL已經占用的關鍵字(例如required 、struct 等單詞)。

·傳回值類型:如果這個調用方法沒有傳回類型,那麼可以關鍵字“void”; 可以是Apache Thrift的基礎類型,也可以是某一個之前定義好的struct,還可以是某種Apache Thrift支援的容器(set、map、list),還可以是定義好的枚舉。

·服務方法名:服務方法名可以根據您的業務需求自定制定,注意區分大小寫。在同一個服務中,不能重複使用一個服務方法名命名多個方法(一定要注意),不能使用IDL已經占用的關鍵字。

·服務方法參數:<入參序号>:[required | optional] <參數類型> <參數名>。注意和struct中的字段定義相似,可以指定required或者optional;如果不指定則系統預設為required 。如果一個服務方法中有多個參數名,那麼這些參數名稱不能重複。

·服務方法異常:throws ([<異常序号>:[required | optional] <異常類型> <異常參數名>。throws關鍵字是服務方法異常定義的開始點。在throws關鍵字後面,可以定義1個或者多個不同的異常類型。
           

3.8命名空間:

thrift的命名空間相當于Java中的package的意思,主要目的是組織代碼。thrift使用關鍵字namespace定義命名空間,例如:

namespace java cn.wpeace.thrift           

3.9其他特性:

1.注釋:

支援下面三種方式:
/*
* 注釋方式1:
**/// 注釋方式2# 注釋方式3           
  1. IDL檔案包含

    thrift也支援檔案包含,相當于C/C++中的include,Java中的import。使用關鍵字include定義,例 如:

include "wpeace.thrift"           

4.Hello world Thrift

下面講解下編寫一個Hello world工程的步驟:

1. 編寫IDL檔案,以及編譯

2.下載下傳jar包建立服務端代碼:實作接口,和啟動服務

3.建立用戶端代碼進行通路

4.1編寫IDL檔案和編譯

  1. 按照第三節的規範,編寫IDL檔案。如下:我命名為hello.thrift
//命名空間定義:java包
namespace java cn.wpeace.thrift
//結構體定義:轉化java中的實體類
struct Request{
      1:required string userName;
      2:required string password;
}
//定義傳回類型
struct Student{
        1:required string naem;
        2:required i32 age;
}
//異常描述定義
exception HelloException{
       1:required string msg;
}
//服務定義,生成接口用
service StudentService{
                                 list<Student> getAllStudent(1:Request request)throws (1:HelloException e);
}           
  1. 打開終端輸入指令:thrift -gen java ./hello.thrift 後會在目前目錄生成gen-java檔案夾,裡面包含了我們定義的類和接口。按namespace路徑存放。
    Apache Thrift入門學習

4.2下載下傳jar包建立服務端代碼

1.需要下載下傳的jar包:

org.slf4j.api.jar
org.slf4j.simple.jar
libthrift-0.9.1.jar           

點選下載下傳

2.建立服務端代碼,需要實作StudnetService類中的Iface接口,代碼如下:詳見注釋

package cn.wpeace.thrift;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.server.TThreadPoolServer.Args;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import cn.wpeace.thrift.StudentService.Iface;
import cn.wpeace.thrift.StudentService.Processor;
public class StudentServiceImpl implements Iface {// 實作的是StudentService類下面的接口
    @Override
    public List<Student> getAllStudent(Request request) throws HelloException, TException {
        System.out.println(request.getUserName());
        System.out.println(request.getPassword());
        List<Student> students = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Student student = new Student();
            student.setNaem("peace" + i);
            student.setAge(22 + i);
            students.add(student);
        }
        return students;
    }   
}           

3.啟動伺服器:

public static void main(String[] args) {
        try {
            System.out.println("服務啟動");
            // 非阻塞式
            TNonblockingServerSocket serverSocket=new TNonblockingServerSocket(8081);
            // 服務執行控制器,類似于rmi中的bind
            Processor<Iface> processor = new StudentService.Processor<Iface>(new StudentServiceImpl());
            // 為伺服器設定對應的IO網絡模型
            TNonblockingServer.Args tArgs = new TNonblockingServer.Args(serverSocket);
            // 設定控制器
            tArgs.processor(processor);
            //設定傳輸方式
            // tArgs.transportFactory(new TFramedTransport.Factory());
            // 設定消息封裝格式
            tArgs.protocolFactory(new TBinaryProtocol.Factory());//Thrift特有的一種二進制描述格式
            // 啟動Thrift服務
            TNonblockingServer server = new TNonblockingServer(tArgs);
            server.serve();//啟動後,程式就停在這裡了。
            System.out.println("服務結束");
        } catch (TTransportException e) {
            e.printStackTrace();
        }
    }
  @Test
  public  void test(String[] args) {
        try {
            System.out.println("服務啟動");
            // 阻塞式同步socket
            TServerSocket serverSocket = new TServerSocket(8081);
            // 服務執行控制器,類似于rmi中的bind
            Processor<Iface> processor = new StudentService.Processor<Iface>(new StudentServiceImpl());
            // 為伺服器設定對應的IO網絡模型
            Args tArgs = new Args(serverSocket);
            // 設定控制器
            tArgs.processor(processor);
            // 設定消息封裝格式
            tArgs.protocolFactory(new TBinaryProtocol.Factory());//Thrift特有的一種二進制描述格式
            // 設定線程池參數
            tArgs.executorService(Executors.newFixedThreadPool(10));//線程池排程器,由于是阻塞模式需要設定線程池。
            // 啟動Thrift服務
            TThreadPoolServer server = new TThreadPoolServer(tArgs);
            server.serve();//啟動後,程式就停在這裡了。
            System.out.println("服務結束");
        } catch (TTransportException e) {
            e.printStackTrace();
        }
    }             

4.3建立客戶段代碼:

注意:用戶端和服務端的傳輸協定需要一至,同時阻塞和非阻塞傳輸也有差別。詳細見代碼

public static void main(String[] args) {
        try {
            //建立socket連接配接
            //TSocket tSocket = new TSocket("192.168.1.118",8081);
            //如果是非阻塞型  需要使用
            TTransport tSocket = new TFramedTransport(new TSocket("192.168.1.118",8081,  30000));  
            //設定封裝協定
            TBinaryProtocol protocol = new TBinaryProtocol(tSocket);
            //建立調用client
            StudentService.Client client=new StudentService.Client(protocol);
            //設定調用參數:
            Request request=new Request().setUserName("peace").setPassword("123456");
            //準備傳輸
            tSocket.open();
            //正式調用接口
            List<Student> allStudent = client.getAllStudent(request);
            //請求結束,斷開連接配接
            tSocket.close();
            for(Student student:allStudent)

            {
                System.out.println(student.getNaem()+":"+student.getAge());
            }

        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (HelloException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }

    }             

4.4測試:

1.啟動伺服器

2.啟動用戶端

用戶端結果:

Apache Thrift入門學習

服務端結果:

Apache Thrift入門學習

本文來自伊豚(blog.wpeace.cn)