Bootstrap

SpringBoot集成Thrift

前言:编写一个微服务,即能提供http调用,也可提供rpc调用,并且在rpc调用时采用pb进行序列化。

一、项目结构

thrift-test
  - .idea
  - pom.xml
  - thrift-client
  - src
  - thrift-server

server端提供服务,client端进行调用

二、Server端

2.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>thrift-test</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>thrift-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.googlecode.protobuf-java-format</groupId>
            <artifactId>protobuf-java-format</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>0.9.3</version>
        </dependency>
        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-runtime</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.5.0:exe:${os.detected.classifier}</protocArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.thrift.tools</groupId>
                <artifactId>maven-thrift-plugin</artifactId>
                <version>0.1.11</version>
                <configuration>
                    <thriftExecutable>/usr/local/bin/thrift</thriftExecutable>
                </configuration>
                <executions>
                    <execution>
                        <id>thrift-sources</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>thrift-test-sources</id>
                        <phase>generate-test-sources</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.2 集成thrift、pb

2.2.1 定义接口函数

test.thrift

namespace java com.thrift.test.server.contract

// 定义服务
service DataService{
    // 类型  方法   参数
    ThriftPredictResponse predict(1:ThriftPredictRequest request)
}

// 结构体
struct ThriftPredictRequest {
   1: required i64 requestId,
   2: required string signature,
   3: required i64 unused,
   4: required binary inputs,
}

struct ThriftPredictResponse{
  1: required i32 status,
  2: required binary outputs,
}

这里面定义了rpc调用的接口,predict函数

2.2.2 定义消息结构

test.proto

syntax = "proto3";

option java_package = "com.thrift.test.server.protobuf";
option java_outer_classname = "Messages";

message Message {
    string access_token = 1;
    string username = 2;
}

2.3 实现DataService

实现通过test.thrift定义的函数接口,处理逻辑如下:

1、从客户端发送的请求(ThriftPredictRequest)中解析数据

2、进行数据处理

3、构造ThriftPredictResponse返回给客户端

package com.thrift.test.server.service;

import com.thrift.test.server.contract.DataService;
import com.thrift.test.server.contract.ThriftPredictRequest;
import com.thrift.test.server.contract.ThriftPredictResponse;
import com.thrift.test.server.protobuf.Messages;
import com.thrift.test.server.util.ProtoBufUtil;
import org.apache.thrift.TException;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class DataCoreService implements DataService.Iface {

    @Override
    public ThriftPredictResponse predict(ThriftPredictRequest request) throws TException {
        ThriftPredictResponse response = new ThriftPredictResponse();
        long requestId = request.getRequestId();
        String signature = request.getSignature();
        byte[] inputs = request.getInputs();
        long unused = request.getUnused();
        System.out.println("requestId:" + requestId);
        System.out.println("signature:" + signature);
        System.out.println("unused:" + unused);
        Messages.Message input = ProtoBufUtil.deserializer(inputs, Messages.Message.class);
        System.out.println("input:" + input.toString());
        Messages.Message.Builder builder = input.toBuilder();
        builder.setAccessToken(UUID.randomUUID().toString()+"_server");
        builder.setUsername("abcd");
        response.setOutputs(ProtoBufUtil.serializer(builder.build()));
        response.setStatus(0);
        return response;
    }
}

使用到pb序列化工具如下:

package com.thrift.test.server.util;
 
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;
 
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
 
/**
 * ProtoBufUtil 转换工具类
 * 
 * @author XRQ
 *
 */
public class ProtoBufUtil {
	private static Logger log = LoggerFactory.getLogger(ProtoBufUtil.class);
	private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();
 
	private static Objenesis objenesis = new ObjenesisStd(true);
 
	@SuppressWarnings("unchecked")
	private static <T> Schema<T> getSchema(Class<T> cls) {
		Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
		if (schema == null) {
			schema = RuntimeSchema.createFrom(cls);
			if (schema != null) {
				cachedSchema.put(cls, schema);
			}
		}
		return schema;
	}
 
	public ProtoBufUtil() {
	}
 
	@SuppressWarnings({ "unchecked" })
	public static <T> byte[] serializer(T obj) {
		Class<T> cls = (Class<T>) obj.getClass();
		LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
		try {
			Schema<T> schema = getSchema(cls);
			return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
		} catch (Exception e) {
			log.error("protobuf序列化失败");
			throw new IllegalStateException(e.getMessage(), e);
		} finally {
			buffer.clear();
		}
	}
 
	public static <T> T deserializer(byte[] bytes, Class<T> clazz) {
		try {
			T message = (T) objenesis.newInstance(clazz);
			Schema<T> schema = getSchema(clazz);
			ProtostuffIOUtil.mergeFrom(bytes, message, schema);
			return message;
		} catch (Exception e) {
			log.error("protobuf反序列化失败");
			throw new IllegalStateException(e.getMessage(), e);
		}
	}
 
}

2.4 编写ThriftServer监听RPC请求

package com.thrift.test.server;

import com.thrift.test.server.contract.DataService;
import com.thrift.test.server.service.DataCoreService;
import org.apache.thrift.TProcessor;
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.TTransportException;
import org.springframework.stereotype.Component;

@Component
public class ThriftServer {

    public void start() {
        try {
            System.out.println("服务端开启....");
            TProcessor tprocessor = new DataService.Processor<DataService.Iface>(new DataCoreService());
            // 简单的单线程服务模型
            TServerSocket serverTransport = new TServerSocket(9898);
            TServer.Args tArgs = new TServer.Args(serverTransport);
            tArgs.processor(tprocessor);
            tArgs.protocolFactory(new TBinaryProtocol.Factory());
            TServer server = new TSimpleServer(tArgs);
            server.serve();
        } catch (TTransportException e) {
            e.printStackTrace();
        }
    }
}

2.5 编写Controller

提供http服务

package com.thrift.test.server.controller;

import com.thrift.test.server.protobuf.Messages;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

@RestController
@RequestMapping("thrift")
public class DataCoreController {

    @GetMapping("/test")
    public String test() {
        return "thrift test";
    }

    @GetMapping(value = "/pb", produces = "application/x-protobuf")
    public Messages.Message getProto() {
        Messages.Message.Builder builder =  Messages.Message.newBuilder();
        builder.setAccessToken(UUID.randomUUID().toString()+"_res");
        builder.setUsername("abc");
        return builder.build();
    }
}

2.6 启动类

package com.thrift.test.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class ThriftServerApplication {

    private static ThriftServer thriftServer;

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(ThriftServerApplication.class, args);
        try {
            thriftServer = context.getBean(ThriftServer.class);
            thriftServer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在main函数中同时启动http server和thrift server,这样就同时支持http和rpc调用

2.7 application.yaml

server:
  port: 8067

三、Client端

3.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>thrift-test</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>thrift-client</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>com.googlecode.protobuf-java-format</groupId>
            <artifactId>protobuf-java-format</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>0.9.3</version>
        </dependency>
        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-runtime</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.5.0.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.5.0:exe:${os.detected.classifier}</protocArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.thrift.tools</groupId>
                <artifactId>maven-thrift-plugin</artifactId>
                <version>0.1.11</version>
                <configuration>
                    <thriftExecutable>/usr/local/bin/thrift</thriftExecutable>
                </configuration>
                <executions>
                    <execution>
                        <id>thrift-sources</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>thrift-test-sources</id>
                        <phase>generate-test-sources</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

3.2 集成thrift、pb

与server端一致

3.3 调用

package com.thrift.test.client;

import com.thrift.test.client.protobuf.Messages;
import com.thrift.test.client.util.ProtoBufUtil;
import com.thrift.test.server.contract.DataService;
import com.thrift.test.server.contract.ThriftPredictRequest;
import com.thrift.test.server.contract.ThriftPredictResponse;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ThriftClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(ThriftClientApplication.class, args);
        System.out.println("客户端启动....");
        TTransport transport = null;
        try {
            transport = new TSocket("localhost", 9898, 30000);
            // 协议要和服务端一致
            TProtocol protocol = new TBinaryProtocol(transport);
            DataService.Client client = new DataService.Client(protocol);
            transport.open();
            ThriftPredictRequest request = new ThriftPredictRequest();
            Messages.Message.Builder builder = Messages.Message.newBuilder();
            builder.setAccessToken("client test");
            builder.setUsername("zzc");
            request.setInputs(ProtoBufUtil.serializer(builder.build()));
            request.setSignature("client test");
            request.setRequestId(1L);
            request.setUnused(0L);
            ThriftPredictResponse response = client.predict(request);
            byte[] outputs = response.getOutputs();
            int status = response.getStatus();
            System.out.println("response, status:" + status);
            System.out.println("response, outputs:" + ProtoBufUtil.deserializer(outputs, Messages.Message.class));
        } catch (TException e) {
            e.printStackTrace();
        } finally {
            if (null != transport) {
                transport.close();
            }
        }
    }
}

四、测试

1、启动服务端
在这里插入图片描述

2、服务端http调用
在这里插入图片描述

3、启动服务端,rpc调用
在这里插入图片描述
4、服务端信息
在这里插入图片描述

;