I’ve recently seen some job descriptions that have requirements for thrift:

As a resume-oriented programmer who has used HTTP calls from SpringMVC for many years, I was a little curious about thrift, so the study of thrift was put on the agenda.

Basic concept of Thrift

The definition of Baidu Encyclopedia is as follows:

Thrift is an interface description language and binary communication protocol that is used to define and create cross-language services. It is used as a remote procedure call (RPC) framework developed by Facebook for “large-scale cross-language service development.”

There are no more general concepts in this article, so let’s dive right into thfrit.

Data type of Thrift

The Thrift scripts can define the following types of data:

  • Basic types:

    • Boolean: the Boolean value
    • Byte: an 8-bit signed integer
    • I16: a 16-bit signed integer
    • I32:32-bit signed integer
    • I64:64-bit signed integer
    • Double: 64-bit floating point number
    • String: utF-8 encoded character string
    • Binary: a binary string
  • Structure type:

    • Struct: structure object defined
  • Container type:

    • List: a list of ordered elements
    • Set: Unordered collection of non-repeating elements
    • Map: an ordered set of keys and values
  • Exception type:

    • Exception: indicates the exception type
  • Service type:

    • Service: service class

The agreement of Thrift

Thrift allows the user to select a type of communication protocol between the client and server. It is generally divided into text and binary transport protocols. To save bandwidth and improve transmission efficiency, binary transmission protocols are generally used, and text-based protocols are sometimes used based on the actual requirements of the project or product. Common protocols are as follows:

  • TBinaryProtocol: Data is transmitted in binary encoding format
  • TCompactProtocol: Efficient, dense binary encoding format for data transmission
  • TJSONProtocol: Uses the JSON text data encoding protocol for data transmission
  • TSimpleJSONProtocol: Provides only JSON write-only protocol, suitable for parsing through scripting languages

Thrift’s transport layer

The following transport layers are commonly used:

  • TSocket: Using blocking I/O for transmission is the most common mode
  • TNonblockingTransport: Uses a non-blocking mode for building asynchronous clients
  • TFramedTransport: Transfers by block size in a non-blocking manner, similar to NIO in Java

The server type of Thrift

  • TSimpleServer: single-threaded server side, using standard blocking I/O
  • TThreadPoolServer: Multithreaded server side, using standard blocking I/O
  • TNonblockingServer: single-threaded server side, using non-blocking I/O
  • THsHaServer: semi-synchronous and semi-asynchronous server side, based on non-blocking IO read and write and multi-threaded work task processing
  • TThreadedSelectorServer: Multithreaded selector server side, yesTHsHaServerEnhancements on asynchronous IO models

With a brief overview of the above concepts, we will use an example to demonstrate the use of thrift.

The installation

Before using Thrift, you need to install the Thrift command-line utility, which compiles the thfirt files you write into the specified source file.

Install it on a Mac using the following command:

$ brew install thrift
Copy the code

After the installation is successful, view the version:

$thrift -v thrift version 0.14.1Copy the code

Install other operating systems on your own.

Writing thrift files

Here we write two services.

HelloService.thrift

namespace java com.attempt.thrift02.gen.service

service HelloService {
   string hello(1: string text);
}
Copy the code

Here we define a HelloService with only a Hello (String) method inside

QueryResult.thrift

namespace java com.attempt.thrift02.gen.vo

struct QueryResult {
   1: required i32 code;         // The requested code is mandatory
   2: optional string msg;       // Request to return information, optional
}
Copy the code

Here we define an object entity with two attributes, code and MSG, to receive the return parameters.

QueryService.thrift

namespace java com.attempt.thrift02.gen.service

// Import another file
include "QueryResult.thrift"

service QueryService {
    // QueryResult is in another file, using the filename. Object name
   QueryResult.QueryResult query(1: i32 code);
}
Copy the code

Here’s another Service: QueryService, which has only one method: Query (…) This method returns an object as QueryResult. Since this object is in the QueryResult.thrift file, you need to include it, and when referencing it, you need to use the file name. Object name.

generate

On the command line, run the following command (specifying that the source file is Java) :

$ thrift -r --gen java QueryService.thrift
$ thrift -r --gen java HelloService.thrift
Copy the code

The QueryService.thrift file is referenced in queryService. thrift, so you just need to generate QueryService.thrift, and queryResult. thrift is automatically generated.

The generated file is as follows:

implementation

We copy the Java code generated in the previous step to SRC /main/ Java with the following directory structure:

Importing the Thrift JAR package

In addition to copying the generated files, we also need to introduce the thrift JAR package into the project. The GAV of the JAR package is as follows:

<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.141.</version>
</dependency>
Copy the code

Implementation of HelloService: HelloServiceImpl

package com.attempt.thrift02.serviceImpl;

import com.attempt.thrift02.gen.service.HelloService;
import org.apache.thrift.TException;

/** * {add description here} **@author chengyan
 * @date 2021-05-06 11:50 上午
 */
public class HelloServiceImpl implements HelloService.Iface {

    @Override
    public String hello(String text) throws TException {
        return "hello, " + text + "!"; }}Copy the code

HelloServiceImpl is our own class that implements HelloService.Iface, writing our own business logic in the corresponding method, and HelloService.Iface is a class generated by thrift.

QueryService implementation: QueryServiceImpl

package com.attempt.thrift02.serviceImpl;

import com.attempt.thrift02.gen.service.QueryService;
import com.attempt.thrift02.gen.vo.QueryResult;
import org.apache.thrift.TException;

/** * {add description here} **@author chengyan
 * @date 2021-05-06 11:51 上午
 */
public class QueryServiceImpl implements QueryService.Iface {

    @Override
    public QueryResult query(int code) throws TException {
        QueryResult result = new QueryResult();
        if (code == 1) {
            result.code = 1;
            result.msg = "success";
        } else {
            result.code = 0;
            result.msg = "fail";
        }
        returnresult; }}Copy the code

Similarly, QueryServiceImpl is a class we wrote ourselves that implements QueryService.Iface, writing our own business logic in the corresponding method, whereas QueryService.Iface is a class generated by thrift.

Client and server

The next step is to implement the corresponding client for each service.

HelloService

server:

package com.attempt.thrift02.server;

import com.attempt.thrift02.gen.service.HelloService;
import com.attempt.thrift02.serviceImpl.HelloServiceImpl;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;

/** * {add description here} **@author chengyan
 * @date 2021-05-06 11:53 上午
 */
public class HelloServer {

    private static final int SERVER_PORT = 8090;

    public static void main(String[] args) {
        try {
            HelloService.Processor processor = new HelloService.Processor<>(
                new HelloServiceImpl());
            TServerTransport transport = new TServerSocket(SERVER_PORT);
            TServer server = new TSimpleServer(new TServer.Args(transport)
                .processor(processor));
            System.out.println("Starting the simple server...");
            server.serve();
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

client:

package com.attempt.thrift02.client;

import com.attempt.thrift02.gen.service.HelloService;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;

/** * {add description here} **@author chengyan
 * @date 2021-05-06 11:52 上午
 */
public class HelloClient {
    private static final int SERVER_PORT = 8090;

    public static void main(String[] args) {
        TTransport transport = null;
        try {
            transport = new TSocket("localhost", SERVER_PORT);
            transport.open();

            TProtocol protocol = new TBinaryProtocol(transport);
            HelloService.Client client = new HelloService.Client(protocol);

            String result = client.hello("thrift world");
            System.out.println("result=" + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(null! = transport) { transport.close(); }}}}Copy the code

Start the server:

Running the client.

QueryService

Server:

package com.attempt.thrift02.server;

import com.attempt.thrift02.gen.service.QueryService;
import com.attempt.thrift02.serviceImpl.QueryServiceImpl;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;

/** * {add description here} **@author chengyan
 * @date 2021-05-06 11:53 上午
 */
public class QueryServer {
    private static final int SERVER_PORT = 8091;

    public static void main(String[] args) {
        try {
            QueryService.Processor processor = new QueryService.Processor<>(
                new QueryServiceImpl());
            TServerTransport transport = new TServerSocket(SERVER_PORT);
            TServer server = new TSimpleServer(new TServer.Args(transport)
                .processor(processor));
            System.out.println("Starting the simple server...");
            server.serve();
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

Client:

package com.attempt.thrift02.client;

import com.attempt.thrift02.gen.service.QueryService;
import com.attempt.thrift02.gen.vo.QueryResult;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;

/** * {add description here} **@author chengyan
 * @date 2021-05-06 11:53 上午
 */
public class QueryClient {
    private static final int SERVER_PORT = 8091;

    public static void main(String[] args) {
        TTransport transport = null;
        try {
            transport = new TSocket("localhost", SERVER_PORT);
            transport.open();

            TProtocol protocol = new TBinaryProtocol(transport);
            QueryService.Client client = new QueryService.Client(protocol);

            QueryResult result = client.query(1);
            System.out.println("query result=" + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(null! = transport) { transport.close(); }}}}Copy the code

Start the server:

Running the client.

The composite service

There are two thrift services in the same project. We open two ports to provide services. In actual production environment, there may be more thrift services. Thrift provides TMultiplexedProcessor to solve this problem, using the following method:

Server:

package com.attempt.thrift02.server;

import com.attempt.thrift02.gen.service.HelloService;
import com.attempt.thrift02.gen.service.QueryService;
import com.attempt.thrift02.serviceImpl.HelloServiceImpl;
import com.attempt.thrift02.serviceImpl.QueryServiceImpl;
import org.apache.thrift.TMultiplexedProcessor;
import org.apache.thrift.TProcessor;
import org.apache.thrift.server.THsHaServer;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import org.apache.thrift.transport.TTransportException;

/** * {add description here} **@author chengyan
 * @date 2021-05-06 12:45 下午
 */
public class MultipleServer {

    private static final int SERVER_PORT = 8093;

    public static void main(String[] args) {
        try {
            TMultiplexedProcessor processor = new TMultiplexedProcessor();
            / / helloService registration
            processor.registerProcessor("helloService".new HelloService.Processor<>(new HelloServiceImpl()));
            / / register queryService
            processor.registerProcessor("queryService".new QueryService.Processor<>(new QueryServiceImpl()));
            TServer server = getSimpleServer(SERVER_PORT, processor);
            System.out.println("Starting the simple server...");
            server.serve();
        } catch(Exception e) { e.printStackTrace(); }}/** Simple single-threaded service model, generally used for testing */
    public static TServer getSimpleServer(int port, TProcessor processor) 
            throws TTransportException {
        TServerTransport transport = new TServerSocket(port);
        TServer server = new TSimpleServer(new TServer.Args(transport)
            .processor(processor));
        returnserver; }}Copy the code

Client:

Multiplexed server uses TMultiplexedProcessor, client also needs to use:

package com.attempt.thrift02.client;

import com.attempt.thrift02.gen.service.HelloService;
import com.attempt.thrift02.gen.service.QueryService;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TMultiplexedProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;

/** * {add description here} **@author chengyan
 * @date 2021-05-06 12:46 下午
 */
public class MultipleClient {
    private static final int SERVER_PORT = 8093;
    public static void main(String[] args) {
        TTransport transport = null;
        try {
            transport = new TSocket("localhost", SERVER_PORT);
            transport.open();
            TProtocol protocol = new TBinaryProtocol(transport);

            // helloService
            TMultiplexedProtocol helloService = new TMultiplexedProtocol(
                    protocol, "helloService");
            HelloService.Client client = new HelloService.Client(helloService);
            System.out.println(client.hello("thrift world"));

            // queryService
            TMultiplexedProtocol helloProtocol = new TMultiplexedProtocol(
                    protocol, "queryService");
            QueryService.Client queryClient = new QueryService.Client(helloProtocol);
            System.out.println(queryClient.query(1));

        } catch (TException e) {
            e.printStackTrace();
        } finally{ transport.close(); }}}Copy the code

Start the server:

Start the client:

So much for the use of Thrift, next we’ll look at the source code implementation.

Reference:

  • Apache Thrift – Overview and Introduction: juejin.cn/post/684490…

Limited to the author’s personal level, there are inevitable mistakes in the article, welcome to correct! Original is not easy, commercial reprint please contact the author to obtain authorization, non-commercial reprint please indicate the source.

This article was first published in the wechat public number Java technology exploration, if you like this article, welcome to pay attention to the public number, let us explore together in the world of technology!