本文是多篇文章的转载,但是经过本人的亲自实践,多谢大神的分享,有幸学的好多的东西
thrift的安装
安装镜像下载地址
http://archive.apache.org/dist/thrift/0.12.0/
安装
1.在C盘新建一个Thtift文件夹,将下载的thrift-0.9.1.exe重新命名为thrift.exe后放到Thtift文件夹下
2.配置环境变量path
3、测试安装是否成功
4、编写thrift文件
namespace java com.thrifttest.generate
enum RequestType {
SAY_HELLO, //问好
QUERY_TIME, //询问时间
}
struct Request {
1: required RequestType type; // 请求的类型,必选
2: required string name; // 发起请求的人的名字,必选
3: optional i32 age; // 发起请求的人的年龄,可选
}
exception RequestException {
1: required i32 code;
2: optional string reason;
}
// 服务名
service HelloWordService {
string doAction(1: Request request) throws (1:RequestException qe); // 可能抛出异常。
}
5、运行命令生成代码
应用
1、test.thrift
namespace java com.thrifttest.generate
enum RequestType {
SAY_HELLO, //问好
QUERY_TIME, //询问时间
}
struct Request {
1: required RequestType type; // 请求的类型,必选
2: required string name; // 发起请求的人的名字,必选
3: optional i32 age; // 发起请求的人的年龄,可选
}
exception RequestException {
1: required i32 code;
2: optional string reason;
}
// 服务名
service HelloWordService {
string doAction(1: Request request) throws (1:RequestException qe); // 可能抛出异常。
}
2、执行命令:
thrift -gen java test.thrift
3、生成java文件
4、放到项目的对应的namespace下
namespace java com.thrifttest.generate
包为 com.thrifttest.generate
5、创建接口HelloWordService的服务端的实现:
package com.thrifttest.server;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.thrift.TException;
import com.thrifttest.generate.HelloWordService;
import com.thrifttest.generate.Request;
import com.thrifttest.generate.RequestException;
public class HelloWordServiceImpl implements HelloWordService.Iface{
Logger LOG= LogManager.getLogger(HelloWordServiceImpl.class);
@Override
public String doAction(Request request) throws RequestException, TException {
LOG.info("服务端接收到消息: request.age={}, request.name={}, request.type={}", request.getAge(), request.getName(), request.getType());
return null;
}
}
6、创建服务端
server的创建socket的几个类:
server的几个实现类:
TServer的构造函数:
代码来了:
ThriftServer.java`
public class ThriftServer {
Logger LOG=LogManager.getLogger(ThriftServer.class);
private void Main() throws TTransportException {
//设置传输方式
TServerTransport serverTransport = createTserverTransport(165,false);
//接口实现
Processor<Iface> processor = new Processor<>(new HelloWordServiceImpl());
//启用多线程服务端
TServer server =createTServer(serverTransport,processor,false,false);
//启动
server.serve();
System.out.println("服务端启动!");
}
/**
* 设置传输的协议
* @param port
* @param isblock
* @return
* @throws TTransportException
*/
private TServerTransport createTserverTransport(int port, boolean isblock) throws TTransportException {
if(isblock) {
//阻塞传输方式,不推荐,性能比TNonblockingServerSocket 差
return new TServerSocket(port);
}
return new TNonblockingServerSocket(port);
}
/**
* 创建服务端
* @param serverTransport
* @param processor
* @param singleThread
* @param isBlock
* @return
*/
public TServer createTServer(TServerTransport serverTransport,Processor processor,boolean singleThread,boolean isBlock ) {
if (isBlock) {
if(singleThread) {
// 同步阻塞单线程服务端,性能很差,一般做测试用,禁止在正式环境中使用,协议服务端和客户端必须保持一致。
return new TSimpleServer(new TSimpleServer
.Args(serverTransport)
.processor(processor)
.transportFactory(new TFramedTransport.Factory())
.protocolFactory(new TCompactProtocol.Factory()));
}
// 同步阻塞多线程服务端,客户端如果连接一直不释放,服务端会一直有个线程与之对应,仅适合客户端极少或是没有常连接的情况,协议服务端和客户端必须保持一致。
return new TThreadPoolServer(new TThreadPoolServer
.Args(serverTransport)
.processor(processor)
.transportFactory(new TFramedTransport.Factory())
.protocolFactory(new TCompactProtocol.Factory()));
}
if(singleThread) {
// 半同步半异步非阻塞单线程服务端,serverTransport必须为TNonblockingServerTransport类型,客户端传输对象必须为TFramedTransport,协议服务端和客户端必须保持一致
return new THsHaServer(new THsHaServer
.Args((TNonblockingServerTransport)serverTransport)
.processor(processor)
.transportFactory(new TFramedTransport.Factory())
.protocolFactory(new TCompactProtocol.Factory()));
}
// 半同步半异步非阻塞多线程服务端,强烈推荐,serverTransport必须为TNonblockingServerTransport类型,客户端传输对象必须为TFramedTransport,协议服务端和客户端必须保持一致。
return new TThreadedSelectorServer(new TThreadedSelectorServer
.Args((TNonblockingServerTransport)serverTransport)
.processor(processor)
.transportFactory(new TFramedTransport.Factory())
.protocolFactory(new TCompactProtocol.Factory()));
}
}
7、创建客户端:
public class ThriftClient {
private static final Logger LOG = LogManager.getLogger(ThriftClient.class);
public static void main(String[] args) throws RequestException, TException {
LOG.info("send with pool");
rpcWithPool();
LOG.info("send simple");
rpcSimple();
}
/**
* 使用连接池进行RPC调用, 强烈推荐
* @throws TException
* @throws RequestException
*/
private static void rpcWithPool() {
//创建一个代理发送服务请求
HelloWordService.Iface helloWordService= new HelloWordServiceProxy(createPool());
String result;
try {
result = helloWordService.doAction(createRequest("小明", 16));
LOG.info("result is {}", result);
result = helloWordService.doAction(createRequest("小白", 20));
} catch (RequestException e) {
LOG.error("reason is " + e.getReason());
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建请求参数
* @param string
* @param i
* @return
*/
private static Request createRequest(String name, int age) {
Request request = new Request();
request.setAge(age);
request.setName(name);
request.setType(RequestType.SAY_HELLO);
return request;
}
//创建连接池
public static GenericObjectPool<TProtocol> createPool() {
// 初始化连接池
GenericObjectPool<TProtocol> pool = new GenericObjectPool<>(new TProtocolFactory("127.0.0.1", 168));
// 连接池设置
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
// 最大空闲连接数
genericObjectPoolConfig.setMaxIdle(2);
// 最大连接数
genericObjectPoolConfig.setMaxTotal(5);
// 从连接池获取连接的时候需要测试
genericObjectPoolConfig.setTestOnBorrow(true);
// 创建连接的时候需要测试
genericObjectPoolConfig.setTestOnCreate(true);
// 归还连接的时候需要测试
genericObjectPoolConfig.setTestOnReturn(true);
// 连接空闲的时候需要测试
genericObjectPoolConfig.setTestWhileIdle(true);
// 使设置生效
pool.setConfig(genericObjectPoolConfig);
return pool;
}
/**
* 简单RPC请求,不带重连和连接池
*
* @throws IOException
*/
private static void rpcSimple() {
TTransport transport=null;
try {
//创建传输的方式
transport =createSocket(true,"127.0.0.1",80);
transport=createWrapperTransport(transport,false);
transport.open();
//设置传输的协议
TProtocol protocol=createTProtocol(transport,true);
HelloWordService.Iface client=new HelloWordService.Client(protocol);
String result = client.doAction(createRequest("小明", 16));
LOG.info("result is {}", result);
result = client.doAction(createRequest("小白", 20));
} catch (IOException e) {
LOG.error("catch IOException: ", e);
}catch (RequestException e) {
LOG.error("reason is " + e.getReason());
}catch (Exception e) {
LOG.error("catch exception: ", e);
}finally {
if(null !=transport) {
transport.close();
}
}
}
//包装
private static TTransport createWrapperTransport(TTransport socket, boolean isZip) {
if (isZip) {
// 读取时按1K为单位将数据读出并调用JDK的zip函数进行解压再放到Buffer,写入时,在flush时先zip再写入。
return new TZlibTransport(socket);
}
// 强烈推荐,性能或许稍有不如TZlibTransport和TFastFramedTransport,但是兼容性更好
// 按Frame读写数据。
// 每Frame的前4字节会记录Frame的长度(少于16M)。读的时候按长度预先将整Frame数据读入Buffer,
// 再从Buffer慢慢读取。写的时候,每次flush将Buffer中的所有数据写成一个Frame。
// 有长度信息的TFramedTransport是后面NonBlockingServer粘包拆包的基础。
return new TFramedTransport(socket);
// TFastFramedTransport 与TFramedTransport相比,始终使用相同的Buffer,提高了内存的使用率。
// TFramedTransport的ReadBuffer每次读入Frame时都会创建新的byte[],WriteBuffer每次flush时如果大于初始1K也会重新创建byte[]。
// 而TFastFramedTransport始终使用相同的ReadBuffer和WriteBuffer,都是1K起步,不够时自动按1.5倍增长,和NIO的ByteBuffer一样,加上limit/pos这样的指针,每次重复使用时设置一下它们。
// return new TFastFramedTransport(socket);
}
/**
* 创建协议连接
*
* @return
*/
private static TProtocol createTProtocol(TTransport transport, boolean isJson) {
if(isJson) {
// JSON传输协议, JSON协议还有一种是
// TSimpleJSONProtocol,不过这种只能写,而且不是标准JSON,给脚本语言解析用,不推荐
return new TJSONProtocol(transport);
}
// 压缩二进制传输协议,强烈推荐!传输带宽占用最小,
// 二进制协议还有一种是TBinaryProtocol,这种带宽占用比TCompactProtocol大,不推荐
return new TCompactProtocol(transport);
}
/**
*
* @param isSyn
* @param ip
* @param port
* @return
* @throws IOException
*/
private static TTransport createSocket(boolean isSyn,String ip, int port) throws IOException {
if(isSyn) {
//同步
return new TSocket(ip,port);
}
//异步
return new TNonblockingSocket(ip,port);
}
}
8、创建代理
public class HelloWordServiceProxy implements HelloWordService.Iface{
private static final Logger LOG = LogManager.getLogger(HelloWordServiceProxy.class);
/**
* thrift连接池
*/
private GenericObjectPool<TProtocol> pool;
/**
* 初始化代理
*
* @param pool
*/
public HelloWordServiceProxy(GenericObjectPool<TProtocol> pool) {
this.pool=pool;
}
@Override
public String doAction(Request request) throws RequestException, TException {
TProtocol protocol=null;
try {
try {
protocol = pool.borrowObject();
} catch (Exception e) {
e.printStackTrace();
}
HelloWordService.Iface client=ReconnectingThriftClient.wrap(new HelloWordService.Client(protocol),HelloWordService.Iface.class);
LOG.info("xxxxxxx borrow protocol in pool");
return client.doAction(request);
} finally {
if(null!=protocol) {
pool.returnObject(protocol);
}
}
}
}
9、创建协议工厂:
public class TProtocolFactory extends BasePooledObjectFactory<TProtocol> {
private Logger LOG = LogManager.getLogger(TProtocolFactory.class);
private String host;
private int port;
private boolean keepAlive = true;
public TProtocolFactory(String host, int port, boolean keepAlive) {
super();
this.host = host;
this.port = port;
this.keepAlive = keepAlive;
}
public TProtocolFactory(String host, int port) {
super();
this.host = host;
this.port = port;
}
@Override
public TProtocol create() throws Exception {
TSocket socket=new TSocket(host, port);
TTransport transport=new TFramedTransport(socket);
transport.open();
LOG.info("xxxxxxx create a tprotocol and open ttransport");
TProtocol protocol=new TCompactProtocol(transport);
return protocol;
}
@Override
public PooledObject<TProtocol> wrap(TProtocol obj) {
return new DefaultPooledObject<TProtocol>(obj);
}
/**
* 对象钝化(即:从激活状态转入非激活状态,returnObject时触发)
*
* @param pooledObject
* @throws TTransportException
*/
@Override
public void passivateObject(PooledObject<TProtocol> pooledObject) throws TTransportException {
if(!keepAlive) {
LOG.info("XXXXXX close ttransport");
pooledObject.getObject().getTransport().flush();
pooledObject.getObject().getTransport().close();
}else {
validateObject(pooledObject);
}
}
/**
* 对象激活(borrowObject时触发)
*
* @param pooledObject
* @throws TTransportException
*/
@Override
public void activateObject(PooledObject<TProtocol> pooledObject) throws TTransportException {
if (!pooledObject.getObject().getTransport().isOpen()) {
pooledObject.getObject().getTransport().open();
}
}
/**
* 对象销毁(clear时会触发)
* @param pooledObject
* @throws TTransportException
*/
@Override
public void destroyObject(PooledObject<TProtocol> pooledObject) throws TTransportException {
passivateObject(pooledObject);
pooledObject.markAbandoned();
}
/**
* 验证对象有效性
*
* @param p
* @return
*/
@Override
public boolean validateObject(PooledObject<TProtocol> p) {
if(p.getObject()!=null) {
if(p.getObject().getTransport().isOpen()) {
return true;
}
try {
p.getObject().getTransport().open();
return true;
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
}
10、自动重连机制:
这段代码来自:http://liveramp.com/engineering/reconnecting-thrift-client/
/**
* thrift 自动断线重连
* @author mmm
*
*/
public final class ReconnectingThriftClient {
private static final Logger LOG = LoggerFactory.getLogger(ReconnectingThriftClient.class);
/**
* List of causes which suggest a restart might fix things (defined as constants in {@link org.apache.thrift.transport.TTransportException}).
*/
private static final Set<Integer> RESTARTABLE_CAUSES = Sets.newHashSet(
TTransportException.NOT_OPEN,
TTransportException.END_OF_FILE,
TTransportException.TIMED_OUT,
TTransportException.UNKNOWN);
public static class Options {
private int numRetries;
private long timeBetweenRetries;
/**
* @param numRetries the maximum number of times to try reconnecting before giving up and throwing an
* exception
* @param timeBetweenRetries the number of milliseconds to wait in between reconnection attempts.
*/
public Options(int numRetries, long timeBetweenRetries) {
this.numRetries = numRetries;
this.timeBetweenRetries = timeBetweenRetries;
}
private int getNumRetries() {
return numRetries;
}
private long getTimeBetweenRetries() {
return timeBetweenRetries;
}
public Options withNumRetries(int numRetries) {
this.numRetries = numRetries;
return this;
}
public Options withTimeBetweenRetries(long timeBetweenRetries) {
this.timeBetweenRetries = timeBetweenRetries;
return this;
}
public static Options defaults() {
return new Options(5, 10000L);
}
}
/**
* Reflectively wraps a thrift client so that when a call fails due to a networking error, a reconnect is attempted.
*
* @param baseClient the client to wrap
* @param clientInterface the interface that the client implements (can be inferred by using
* {@link #wrap(org.apache.thrift.TServiceClient, intellif.mining.thrift.ReconnectingMqttClient.spruce_lib.singletons.ReconnectingThriftClient.Options)}
* @param options options that control behavior of the reconnecting client
* @param <T>
* @param <C>
* @return
*/
public static <T extends TServiceClient, C> C wrap(T baseClient, Class<C> clientInterface, Options options) {
Object proxyObject = Proxy.newProxyInstance(clientInterface.getClassLoader(),
new Class<?>[]{clientInterface},
new ReconnectingClientProxy<T>(baseClient, options.getNumRetries(), options.getTimeBetweenRetries()));
return (C) proxyObject;
}
/**
* Reflectively wraps a thrift client so that when a call fails due to a networking error, a reconnect is attempted.
*
* @param baseClient the client to wrap
* @param options options that control behavior of the reconnecting client
* @param <T>
* @param <C>
* @return
*/
public static <T extends TServiceClient, C> C wrap(T baseClient, Options options) {
Class<?>[] interfaces = baseClient.getClass().getInterfaces();
for (Class<?> iface : interfaces) {
if (iface.getSimpleName().equals("Iface") && iface.getEnclosingClass().equals(baseClient.getClass().getEnclosingClass())) {
return (C) wrap(baseClient, iface, options);
}
}
throw new RuntimeException("Class needs to implement Iface directly. Use wrap(TServiceClient, Class) instead.");
}
/**
* Reflectively wraps a thrift client so that when a call fails due to a networking error, a reconnect is attempted.
*
* @param baseClient the client to wrap
* @param clientInterface the interface that the client implements (can be inferred by using
* {@link #wrap(org.apache.thrift.TServiceClient, intellif.mining.thrift.ReconnectingMqttClient.spruce_lib.singletons.ReconnectingThriftClient.Options)}
* @param <T>
* @param <C>
* @return
*/
public static <T extends TServiceClient, C> C wrap(T baseClient, Class<C> clientInterface) {
return wrap(baseClient, clientInterface, Options.defaults());
}
/**
* Reflectively wraps a thrift client so that when a call fails due to a networking error, a reconnect is attempted.
*
* @param baseClient the client to wrap
* @param <T>
* @param <C>
* @return
*/
public static <T extends TServiceClient, C> C wrap(T baseClient) {
return wrap(baseClient, Options.defaults());
}
/**
* Helper proxy class. Attempts to call method on proxy object wrapped in try/catch. If it fails, it attempts a
* reconnect and tries the method again.
*
* @param <T>
*/
private static class ReconnectingClientProxy<T extends TServiceClient> implements InvocationHandler {
private final T baseClient;
private final int maxRetries;
private final long timeBetweenRetries;
public ReconnectingClientProxy(T baseClient, int maxRetries, long timeBetweenRetries) {
this.baseClient = baseClient;
this.maxRetries = maxRetries;
this.timeBetweenRetries = timeBetweenRetries;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return method.invoke(baseClient, args);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof TTransportException) {
TTransportException cause = (TTransportException) e.getTargetException();
if (RESTARTABLE_CAUSES.contains(cause.getType())) {
reconnectOrThrowException(baseClient.getInputProtocol().getTransport(), maxRetries, timeBetweenRetries);
return method.invoke(baseClient, args);
}
}
throw null == e.getCause() ? e : e.getCause();
}
}
private static void reconnectOrThrowException(TTransport transport, int maxRetries, long timeBetweenRetries) throws TTransportException {
int errors = 0;
transport.close();
while (errors < maxRetries) {
try {
LOG.info("Attempting to reconnect...");
transport.open();
LOG.info("Reconnection successful");
break;
} catch (TTransportException e) {
LOG.error("Error while reconnecting:", e);
errors++;
if (errors < maxRetries) {
try {
LOG.info("Sleeping for {} milliseconds before retrying", timeBetweenRetries);
Thread.sleep(timeBetweenRetries);
} catch (InterruptedException e2) {
throw new RuntimeException(e);
}
}
}
}
if (errors >= maxRetries) {
throw new TTransportException("Failed to reconnect");
}
}
}
}