前言:编写一个微服务,即能提供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、服务端信息