Thrift -- cross-language RPC framework

Thrift - Cross-language RPC framework

1 Introduction

The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages.

For cross-language service software development, combined with a code generation engine, build C++, Java, Python, PHP, Ruby... to generate multilingual RPC call codes;

With the help of IDL language (you can understand the template language of this framework, which will be described in detail later) to generate calling codes in different languages

2. Download and install

My computer is windows10, so I directly download the win version. The download address is:https://thrift.apache.org/download

Install environment variables,

I made a copy here and removed the version information

insert image description here

C:\Users\sff>thrift  -version
Thrift version 0.16.0

The installation is complete

3. Thrift architecture

  • Interactive architecture CS architecture, the client requests the server; the codes on both sides can be generated by Thrift

  • The technical architecture is (different from the ISO seven-layer or five-layer network)

    • The transport layer defines the specific network connection transport protocol using TCP /IP UDP
    • The serialization and deserialization formats of data during the protocol layer RPC interaction process include JSON, XML, binary data, etc.
    • The interface of each language generated by the processing layer Thrift gen, and then our server implements this interface to provide the calling service of the client
    • The service layer interfaces above and then uses different IO models to process data

insert image description here

4. Advantages of Thrift development

  • Multi-language and cross-language, but you need to learn IDL (Interface Definition Language). This is relatively simple. .

  • As long as the IDL file is maintained, the code generation engine will generate server code and client code in each language, eliminating the need to define each language interface writing, network transmission, server IO model type (a small amount of code), and can focus on realizing your own business code;

Note: learning address

IDL syntax:https://thrift.apache.org/docs/idl

Type definition:https://thrift.apache.org/docs/types

Example:

//各个语言对应的包
namespace java com.sff.study
namespace js com.sff.study
namespace go com.sff.study

struct Demo{
    
    
1: i32 id
2: string desc
}

service DemoService {
    
    

    Demo getDemo(1:i32 id)
    bool setDemo(1:Demo demo)
}
## 生成 java go js cpp
D:\Resource\FrameMiddleware\Thrift\template>thrift -o D:\Resource\FrameMiddleware\Thrift\ --gen java demo.thrift
D:\Resource\FrameMiddleware\Thrift\template>thrift -o D:\Resource\FrameMiddleware\Thrift\ --gen go demo.thrift
D:\Resource\FrameMiddleware\Thrift\template>thrift -o D:\Resource\FrameMiddleware\Thrift\ --gen js demo.thrift
D:\Resource\FrameMiddleware\Thrift\template>thrift -o D:\Resource\FrameMiddleware\Thrift\ --gen cpp demo.thrift

insert image description here

5. Protocol layer protocol

Parent class TProtocol

insert image description here

  • TCompacProtocol: compressed binary coded transmission
  • TbinaryProtocol: Binary encoded transmission
  • TJSONProtocol JSON protocol transport with type information
  • TSimpleJSONProtocol JSON only needs to have its own deserialization tool for data

6. Transport layer protocol

insert image description here

  • TServerSocket: Socket common BIO
    • TNonblockingServerTransport: non-blocking IO Nio;
  • TFramedTransport

7. Quick start

The code based on the java version generated by the previous code

7.1 Create two projects to import generated code

insert image description here

7.2 Add dependencies

 <dependencies>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>0.16.0</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>

7.3 The server implements the interface

public class DemoServiceImpl implements DemoService.Iface {
    
    

	@Override
	public Demo getDemo(int id) throws TException {
    
    
		Demo demo = new Demo();
		int nextInt = new Random().nextInt(100);
		demo.setId(nextInt);
		demo.setDesc("我是demo" + nextInt);
		return null;
	}

	@Override
	public boolean setDemo(Demo demo) throws TException {
    
    
		System.out.println("保存了~~~~~:" + demo);
		return false;
	}
}

Simple mode of the main startup class

package com.sff.study;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;

public class ServerMain {
    
    
	public static void main(String[] args) {
    
    
		try {
    
    
			TServerTransport serverTransport = new TServerSocket(9090);

			TServer.Args args1 = new TServer.Args(serverTransport);
			// 业务处理器
			DemoService.Processor processor = new DemoService.Processor(new DemoServiceImpl());
			args1.processor(processor);
			// 指定序列化工厂
			TBinaryProtocol.Factory factory = new TBinaryProtocol.Factory();
			args1.protocolFactory(factory);

			// 绑定参数
			TSimpleServer server = new TSimpleServer(args1);

			// Use this for a multithreaded server
			// TServer server = new TThreadPoolServer(new
			// TServer server = new TThreadPoolServer(new
			// TThreadPoolServer.Args(serverTransport).processor(processor));

			System.out.println("Starting the simple server...");
			server.serve();
		} catch (Exception e) {
    
    
			e.printStackTrace();
		}
	}
}

7.4 Client

test connection call

package com.sff.study;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;

public class ClientMain {
    
    

	public static void main(String[] args) throws Exception {
    
    

		// 传输层IO
		TSocket socket = new TSocket("127.0.0.1", 9090);
		// 协议层 序列化
		TProtocol protocol = new TBinaryProtocol(socket);
		DemoService.Client client = new DemoService.Client(protocol);

		// 打开 scoket
		socket.open();

		Demo demo = client.getDemo(11);
		System.out.println(demo);

		// int nextInt = new Random().nextInt(100);
	}
}

7.5 Debugging

first commissioning

Start the server first, then the client, and the client reports an error

Connected to the target VM, address: '127.0.0.1:54713', transport: 'socket'
Exception in thread "main" org.apache.thrift.TApplicationException: getDemo failed: unknown result
	at com.sff.study.DemoService$Client.recv_getDemo(DemoService.java:71)
	at com.sff.study.DemoService$Client.getDemo(DemoService.java:56)
	at com.sff.study.ClientMain.main(ClientMain.java:20)
Disconnected from the target VM, address: '127.0.0.1:54713', transport: 'socket'

It is found that DemoServiceImpl##getDemo() returns null, which can be debugged after modification

Connected to the target VM, address: '127.0.0.1:54809', transport: 'socket'
Demo(id:11, desc:我是demo11)
Disconnected from the target VM, address: '127.0.0.1:54809', transport: 'socket'

8. Server service type

TServer is the parent class of all services

//  主要方法 还是子类去实现的 开启服务 serve
public abstract void serve();
//  主要方法 还是子类去实现的 关闭服务 stop
public void stop() {
    
    }

insert image description here

8.1 TSimpleServer

public class TSimpleServer extends TServer {
    
    
    private static final Logger LOGGER = LoggerFactory.getLogger(TSimpleServer.class.getName());

    public TSimpleServer(AbstractServerArgs args) {
    
    
        super(args);
    }
	//核心方法 开启服务
    public void serve() {
    
    
        try {
    
    
            //开启监听端口
            this.serverTransport_.listen();
        } catch (TTransportException var9) {
    
    
            LOGGER.error("Error occurred during listening.", var9);
            return;
        }

        if (this.eventHandler_ != null) {
    
    
            this.eventHandler_.preServe();
        }

        this.setServing(true);

        // 关闭服务置位 true  停止循环
        while(!this.stopped_) {
    
    
          .....

            try {
    
    
                // 阻塞方法  一直监听请求
                client = this.serverTransport_.accept();
                if (client != null) {
    
    
                    ........
                    while(true) {
    
    
                        if (this.eventHandler_ != null) {
    
    
                            this.eventHandler_.processContext(connectionContext, inputTransport, outputTransport);
                        }
					 //获取我们自己的处理器 处理业务逻辑
                        processor.process(inputProtocol, outputProtocol);
                    }
                }
            }
            catch (TTransportException var10) {
    
    
           ......
            }  
        }

        this.setServing(false);
    }
	//核心方法 关闭服务
    public void stop() {
    
    
        this.stopped_ = true;
        this.serverTransport_.interrupt();
    }
}

The execution process is as follows

insert image description here

Disadvantages : Single-thread blocking; multi-client connections will be blocked, and the next one can not be processed until the previous connection is released

8.2 TThreadPoolServer

public class TThreadPoolServer extends TServer {
    
    
    private static final Logger LOGGER = LoggerFactory.getLogger(TThreadPoolServer.class);
    private ExecutorService executorService_;
    private final TimeUnit stopTimeoutUnit;
    private final long stopTimeoutVal;

    public TThreadPoolServer(TThreadPoolServer.Args args) {
    
    
        super(args);
        this.stopTimeoutUnit = args.stopTimeoutUnit;
        this.stopTimeoutVal = (long)args.stopTimeoutVal;
        // 初始化的时候使用 线程池
        this.executorService_ = args.executorService != null ? args.executorService : createDefaultExecutorService(args);
    }

    // 线程池参数
    private static ExecutorService createDefaultExecutorService(TThreadPoolServer.Args args) {
    
    
        // args.maxWorkerThreads 参数为最大线程数
        return new ThreadPoolExecutor(args.minWorkerThreads, args.maxWorkerThreads, 60L, TimeUnit.SECONDS, new SynchronousQueue(), new ThreadFactory() {
    
    
            final AtomicLong count = new AtomicLong();

            public Thread newThread(Runnable r) {
    
    
                Thread thread = new Thread(r);
                thread.setDaemon(true);
                thread.setName(String.format("TThreadPoolServer WorkerProcess-%d", this.count.getAndIncrement()));
                return thread;
            }
        });
    }
 
   //开启监听
    protected boolean preServe() {
    
    
        ......
         this.serverTransport_.listen();
        ......
        return true;
    }

    public void serve() {
    
    
        //开启监听
        if (this.preServe()) {
    
    
            // 真正执行的方法
            this.execute();
            
            this.executorService_.shutdownNow();
            if (!this.waitForShutdown()) {
    
    
                LOGGER.error("Shutdown is not done after " + this.stopTimeoutVal + this.stopTimeoutUnit);
            }

            this.setServing(false);
        }
    }

    protected void execute() {
    
    
        while(!this.stopped_) {
    
    
            try {
    
    
                //和8.1 的那个一样  会一致处于阻塞 直到新的socket 到来
                TTransport client = this.serverTransport_.accept();
                try {
    
    
                    //来一个连接就交给线程池处理
                    this.executorService_.execute(new TThreadPoolServer.WorkerProcess(client));
                } catch (RejectedExecutionException var3) {
    
    
                    if (!this.stopped_) {
    
    
                        LOGGER.warn("ThreadPool is saturated with incoming requests. Closing latest connection.");
                    }
                    client.close();
                }
            } catch (TTransportException var4) {
    
    
                if (!this.stopped_) {
    
    
                    LOGGER.warn("Transport error occurred during acceptance of message", var4);
                }
            }
        }

    }

    public static class Args extends AbstractServerArgs<TThreadPoolServer.Args> {
    
    
        public int minWorkerThreads = 5;
        public int maxWorkerThreads = 2147483647;
        public ExecutorService executorService;
        public int stopTimeoutVal = 60;
        public TimeUnit stopTimeoutUnit;
       .....
    }
}

Execution flowchart

insert image description here

Advantages: Each Socket will go to the thread pool to take a thread for processing, which is suitable for scenarios with a small number of connections

Disadvantages: When many clients are connected at the same time, when there are many clients, the server needs to have a corresponding number of threads to process, and the upper limit set in the code is still relatively large; thread resources are more important and consume more server performance; May overflow memory;

8.3 AbstractNonblockingServer

The parent class of AbstractNonblockingServer mainly agrees on the main process, and the implementation class can only use TFrameTransport

    public void serve() {
    
    
        if (this.startThreads()) {
    
    
            if (this.startListening()) {
    
    
                this.setServing(true);
                this.waitForShutdown();
                this.setServing(false);
                this.stopListening();
            }
        }
    }

8.4 TNonblockingServer React Model

TNonblockingServer extends AbstractNonblockingServer

Look at the execution process

server  父类
    
==> this.startThreads()
protected boolean startThreads() {
    
    
....
    // TNonblockingServer.SelectAcceptThread
            this.selectAcceptThread_ = new TNonblockingServer.SelectAcceptThread((TNonblockingServerTransport)this.serverTransport_);
            this.selectAcceptThread_.start();
...
}

======>TNonblockingServer.SelectAcceptThread
    protected class SelectAcceptThread extends AbstractSelectThread {
    
    
      ......
        public void run() {
    
    
            try {
    
    
                ....

                while(!TNonblockingServer.this.stopped_) {
    
    
                    // 调用
                    this.select();
                    this.processInterestChanges();
                }
           .....
            }
// 调用
        private void select() {
    
    
           ....
                this.handleRead(key);
            ....
        }
    }
        
=====》  父类    this.handleRead(key);
 protected void handleRead(SelectionKey key) {
    
    
      ///this.requestInvoke(buffer) 调用
      if (buffer.isFrameFullyRead() && !AbstractNonblockingServer.this.requestInvoke(buffer)) {
    
    
}

//this.requestInvoke(buffer) 调用
protected boolean requestInvoke(FrameBuffer frameBuffer) {
    
    
        //调用
        frameBuffer.invoke();
        return true;
}
//frameBuffer.invoke(); 调用
 public void invoke() {
    
    
     ....
 AbstractNonblockingServer.this.processorFactory_.getProcessor(this.inTrans_).process(this.inProt_, this.outProt_);
 }

flow chart

insert image description here

Disadvantages : single-threaded processing, multi-client single time-consuming operation will make the client block;

8.5 TThreadedSelectorServer 单React

TThreadedSelectorServer extends AbstractNonblockingServer

TThreadedSelectorServer.AcceptThread is responsible for the connection

The multi-threaded (selector thread, business logic processing thread) version of TNonblockingServer , an Accpet code is not displayed

The flow chart is as follows

insert image description here

This advantage and disadvantage: it is not very obvious to deal with connection socket status and business logic are all handled by the thread pool

8.6 THsHaServer Single React

THsHaServer extends TNonblockingServer

The code of the multi-threaded (business logic processing thread) version of TNonblockingServer is not shown

insert image description here

Guess you like

Origin blog.csdn.net/weixin_44244088/article/details/126220176