Thrift入門及 Java 實現簡單demo

本文示例代碼: github傳送門html

本文並未與spring boot集成,僅實現了demo.能夠將本文中的類做爲spring中的bean使用便可.java

其實一開始是想集成的,後來發現thrift已經夠頭大了,就暫時放棄了,後面單獨寫一篇吧.集成比較簡單一些.git

背景介紹

我終於從一個寫Http接口的轉職到了寫RPC接口的.(微笑)github

因此我須要學習一下RPC框架,至於爲何學習Thrift而不是Dubbo或者其餘,由於工做在用Thrift,因此先學習一下這個咯.spring

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掌管shell

詳細步驟

1. 安裝Thrift

這裏只提供Mac OS的安裝方法,其餘平臺的能夠在網上搜索一下.apache

在shell裏面執行:ruby

brew install thrift.服務器

若是未安裝brew,強烈建議安裝.app

安裝命令:

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

2.新建項目

新建一個Maven項目並在其pom.xml中添加下列依賴項.

<dependency>
      <groupId>org.apache.thrift</groupId>
      <artifactId>libthrift</artifactId>
      <version>0.12.0</version>
    </dependency>
複製代碼

3. 定義接口文件

注: thrift文件的具體語法這裏不作說明,比較簡單,僅在附錄中添加經常使用的一些類型備忘.須要的朋友可直接在文末查看.

這裏寫的是個demo,咱們定義兩個接口,一個是根據id查詢用戶,一個是判斷用戶是否存在.

namespace java thrift_demo

service UserService {
  string getName(1:i32 id)
  bool isExist(1:string name)
}
複製代碼

4. 根據接口定義生成java文件

在命令行中進入user.thrift所在的目錄,而後執行thrift -r -gen java user.thrift.

會發如今當前目錄下生成了gen-java文件夾,將文件夾中的userService文件copy到項目目錄下.

這個文件定義了接口,入參,出參,是客戶端和服務器共同使用的一個文件.也是thrift框架很重要的一部分.

5.編寫服務端的具體實現.

服務端要必須實現UserService中的UserService.Iface接口,爲其提供具體的業務邏輯.

代碼以下:

package thrift_demo.server;

import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import thrift_demo.UserService;

/** * Created by pfliu on 2019/03/28. */
public class UserServiceImpl implements UserService.Iface {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final static String HUYANSHI = "HUYANSHI";

    @Override
    public String getName(int id) throws TException {
        logger.info("received getName, id = {}:", id);
        return HUYANSHI;
    }

    @Override
    public boolean isExist(String name) throws TException {
        logger.info("receive isExist, name = {}", name);
        return HUYANSHI.equals(name);
    }
}

複製代碼

6. 編寫服務器啓動類

服務啓動類不涉及到業務邏輯,只是調用thrift的一些方法,監聽tcp的某個端口.文中是2345端口.

package thrift_demo.server;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import org.apache.thrift.transport.TTransportFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import thrift_demo.UserService;

/** * Created by huyanshi on 2019/03/28. */
public class Server {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private void startServer() {
        UserService.Processor processor = new UserService.Processor<UserService.Iface>(new UserServiceImpl());
        try {
            TServerTransport transport = new TServerSocket(2345);
            TThreadPoolServer.Args tArgs = new TThreadPoolServer.Args(transport);
            tArgs.processor(processor);
            tArgs.protocolFactory(new TBinaryProtocol.Factory());
            tArgs.transportFactory(new TTransportFactory());
            tArgs.minWorkerThreads(10);
            tArgs.maxWorkerThreads(20);
            TServer server = new TThreadPoolServer(tArgs);
            server.serve();
        } catch (Exception e) {
            logger.error("thrift服務啓動失敗", e);
        }
    }

    public static void main(String[] args) {
        Server server = new Server();
        server.startServer();
    }
}
複製代碼

上面實現的是單線程,僅作demo,平常啓動多個線程的狀況比較多.

7. 編寫客戶端代碼

客戶端代碼比較簡單,建立了客戶端調用對應的方法便可.

平時咱們作服務端開發的時候,通常不用開發客戶端,僅在測試類中作簡單實現便可.

package thrift_demo.client;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.*;
import thrift_demo.UserService;

/** * Created by huyanshi on 2019/03/28. */
public class Client {
    private static final String SERVER_IP = "localhost";
    private static final int SERVER_PORT = 2345;//Thrift server listening port
    private static final int TIMEOUT = 3000;

    private void startClient(String userName) {
        TTransport transport = null;
        try {
            transport = new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT);
            // 協議要和服務端一致
            TProtocol protocol = new TBinaryProtocol(transport);
            UserService.Client client = new UserService.Client(protocol);
            transport.open();
            System.out.println(client.getName(1));
            System.out.println(client.isExist("haha"))
        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } finally {
            if (null != transport) {
                transport.close();
            }
        }
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.startClient("Tom");
    }

}
複製代碼

8. 啓動服務端

能夠看到上面的服務端啓動類,就是一個包含main方法的類.直接啓動便可.

9. 啓動客戶端進行測試.

同上,啓動便可.

能夠分別查看兩端日誌是否符合預期.

總結

前文的全部步驟進行了驗證,接下來的就是意識流分析了,因爲我也第一天接觸thrift,不保證正確,看看便可.

RPC是什麼呢?遠程過程調用,咱們但願能夠像調用本地方法同樣調用遠程的方法.

這個過程是這樣的:

2019-03-29-09-36-07

這太麻煩了,因此就有了RPC框架,RPC框架的目的就是封裝除了黃色部分以外的其餘全部步驟,使得除了第一步和最後一步其餘步驟不可見.

怎麼實現呢?(嚴重意識流預警,目前認識,完了打臉了我就回來改)

依我目前對thrift的瞭解:

  1. 定義一個接口文件,thrift生成Java類.這個類客戶端和服務端共同擁有並使用.
  2. 服務端根據文件中定義的接口,作出具體的實現.
  3. 客戶端連接服務端,調用文件中的客戶端的方法,thrift負責序列化反序列化以及反射等等操做.拿到結果.

本文示例代碼: github傳送門

參考文章

www.cnblogs.com/duanxz/p/55…

zh.wikipedia.org/wiki/Thrift

附錄

  • 基本類型:
    • bool:布爾值,true 或 false,對應 Java 的 boolean
    • byte:8 位有符號整數,對應 Java 的 byte
    • i16:16 位有符號整數,對應 Java 的 short
    • i32:32 位有符號整數,對應 Java 的 int
    • i64:64 位有符號整數,對應 Java 的 long
    • double:64 位浮點數,對應 Java 的 double
    • string:utf-8編碼的字符串,對應 Java 的 String
  • 結構體類型:
    • struct:定義公共的對象,相似於 C 語言中的結構體定義,在 Java 中是一個 JavaBean
  • 容器類型:
    • list:對應 Java 的 ArrayList
    • set:對應 Java 的 HashSet
    • map:對應 Java 的 HashMap
  • 異常類型:
    • exception:對應 Java 的 Exception
  • 服務類型:
    • service:對應服務的類




ChangeLog

2019-03-28 完成

以上皆爲我的所思所得,若有錯誤歡迎評論區指正。

歡迎轉載,煩請署名並保留原文連接。

聯繫郵箱:huyanshi2580@gmail.com

更多學習筆記見我的博客------>呼延十

相關文章
相關標籤/搜索