/**
*
*/
package com.ganji.as.thrift.protocol.service;
import java.nio.ByteBuffer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TransferQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import org.apache.thrift.TException;
import org.apache.thrift.async.AsyncMethodCallback;
import org.apache.thrift.async.TAsyncClient;
import org.apache.thrift.async.TAsyncClientManager;
import org.apache.thrift.async.TAsyncMethodCall;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.transport.TNonblockingTransport;
import org.slf4j.Logger;
import com.ganji.as.thrift.protocol.builder.ClientBuildingConfig;
import com.ganji.as.thrift.protocol.client.future.Future;
import com.ganji.as.thrift.protocol.client.intf.ThriftProtocolClientRetryPolicy;
import com.ganji.as.thrift.protocol.client.request.ThriftClientRequest;
import com.ganji.as.thrift.protocol.client.socket.async.pool.SocketConnection;
import com.ganji.as.thrift.protocol.client.socket.async.pool.SocketConnectionPool;
import com.ganji.as.thrift.protocol.client.socket.async.pool.SocketConnectionPoolFactory;
import com.ganji.as.thrift.protocol.service.intf.ThriftProtocolFunction;
import com.ganji.as.thrift.protocol.service.intf.ThriftProtocolService;
import com.ganji.as.thrift.protocol.client.request.ThriftClientInvocation;
import com.ganji.as.thrift.protocol.cluster.load.balance.LoadBalance;
/**
* @author yikangfeng
* @date 2015年7月20日
* @copyright yikangfeng
*/
public class ThriftProtocolServe<REQ, REP> implements
ThriftProtocolService<REQ, REP> {
static final private TransferQueue<RunnableFuture<?>> concurrentSessionQueue_ = new LinkedTransferQueue<>();
static final private int threadPoolCoreSize_ = (int) Math.pow(Runtime
.getRuntime().availableProcessors(), 2);// io busy.
static final private ExecutorService executor = new ThreadPoolExecutor(
threadPoolCoreSize_, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new LinkedTransferQueue<Runnable>());
final private TAsyncClientManager asyncClientManager_ = new TAsyncClientManager();// thread
// safe
final private ClientBuildingConfig clientBuildingConfig_;
final private LoadBalance loadBalance_;
final private ThriftProtocolClientRetryPolicy retryPolicy_;
final private SocketConnectionPool clientSocketConnectionPool_;
final private TProtocolFactory protocolFactory_;
final private Logger LOGGER;
static {// first one init.
int threadPoolCoreSize = threadPoolCoreSize_;
do {
executor.submit(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (!Thread.currentThread().isInterrupted()) {
while (concurrentSessionQueue_.isEmpty()) {
LockSupport.parkNanos(1000000L);
}
final RunnableFuture<?> runnableTask = concurrentSessionQueue_
.poll();
runnableTask.run();
}
}
});
} while (--threadPoolCoreSize > 0);// false exit.
}
public ThriftProtocolServe(final ClientBuildingConfig clientBuildingConfig)
throws Throwable {
this.clientBuildingConfig_ = clientBuildingConfig;
LOGGER = clientBuildingConfig_.getLogger();
clientSocketConnectionPool_ = SocketConnectionPoolFactory.factory()
.createSocketConnectionPool(clientBuildingConfig);
protocolFactory_ = clientBuildingConfig.getCodec();
loadBalance_ = clientBuildingConfig.getLoadBalancePolicy();
this.retryPolicy_ = clientBuildingConfig.getRetryPolicy();
}
@SuppressWarnings("rawtypes")
static private TAsyncClientFactoryEx<TAsyncClientExtender> ASYNC_CLIENT_FACTORY = new TAsyncClientFactoryEx<TAsyncClientExtender>() {
@Override
public TAsyncClientExtender getAsyncClient(
final TProtocolFactory protocolFactory,
final TAsyncClientManager asyncClientManager,
final TNonblockingTransport transport) {
return new TAsyncClientExtender(protocolFactory,
asyncClientManager, transport);
}
};
static private class TAsyncMethodCallEx extends
TAsyncMethodCall<TAsyncMethodCallEx> {
final private ThriftClientRequest __request__;
protected TAsyncMethodCallEx(final ThriftClientRequest _request_,
TAsyncClient client, TProtocolFactory protocolFactory,
TNonblockingTransport transport,
AsyncMethodCallback<TAsyncMethodCallEx> callback,
boolean isOneway) {
super(client, protocolFactory, transport, callback, isOneway);
this.__request__ = _request_;
}
@Override
protected void write_args(TProtocol protocol) throws TException {
// TODO Auto-generated method stub
final ThriftClientRequest clientRequest = (ThriftClientRequest) __request__;
if (clientRequest.message == null
|| clientRequest.message.length <= 0)
throw new TException("The request data is not legal.");
for (final byte _byte : clientRequest.message) {
protocol.writeByte(_byte);
}
}
@Override
protected ByteBuffer getFrameBuffer() {
// TODO Auto-generated method stub
return super.getFrameBuffer();
}
}
static private class TAsyncClientExtender<REQ, REP> extends TAsyncClient
implements TAsyncClientEx<REQ> {
private TProtocolFactory protocolFactory_;
private TNonblockingTransport transport_;
public TAsyncClientExtender(TProtocolFactory protocolFactory,
TAsyncClientManager manager, TNonblockingTransport transport) {
// TODO Auto-generated constructor stub
super(protocolFactory, manager, transport);
this.protocolFactory_ = protocolFactory;
this.transport_ = transport;
}
@Override
protected void checkReady() {
// TODO Auto-generated method stub
super.checkReady();
}
@Override
public Exception getError() {
// TODO Auto-generated method stub
return super.getError();
}
@SuppressWarnings("unchecked")
@Override
public void sendRequest(final REQ __request__,
final AsyncMethodCallback<?> asyncMethodCallback)
throws Throwable {
// TODO Auto-generated method stub
this.checkReady();
___manager
.call(new TAsyncMethodCallEx(
(ThriftClientRequest) __request__,
this,
this.protocolFactory_,
this.transport_,
(AsyncMethodCallback<TAsyncMethodCallEx>) asyncMethodCallback,
false));
}
}
@SuppressWarnings("unchecked")
@Override
public Future<REP> apply(final REQ clientRequest) {
// TODO Auto-generated method stub
return new Future<REP>() {
private ThriftProtocolFunction<REQ, ?> callbackFunction_;
private REQ clientRequest_ = clientRequest;
private java.util.concurrent.RunnableFuture<REQ> futureSession_;
private long startRequestTime_;
@Override
public ThriftProtocolFunction<?, ?> getCallbackFunction() {
// TODO Auto-generated method stub
return this.callbackFunction_;
}
@Override
public java.util.concurrent.RunnableFuture<REQ> getFutureSession() {
return this.futureSession_;
}
@Override
public <INPUT, OUTPUT> OUTPUT flatMap(
final ThriftProtocolFunction<INPUT, OUTPUT> function) {
// TODO Auto-generated method stub
this.callbackFunction_ = (ThriftProtocolFunction<REQ, ?>) function;
this.futureSession_ = createFutureSession(clientRequest_);
this.startRequestTime_ = System.currentTimeMillis();
try {
concurrentSessionQueue_.put(futureSession_);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
// logger
if (!(LOGGER == null)) {
if (LOGGER.isInfoEnabled())
LOGGER.info(String
.format("Failed to apply the client [%s] request, the reasons:%s",
(clientBuildingConfig_
.getClientName() == null ? ""
: clientBuildingConfig_
.getClientName()),
e));
}
throw new IllegalStateException(e);
}
return (OUTPUT) this;
}
@Override
public REP get() throws Throwable {
// TODO Auto-generated method stub
return getValue(-1, null);
}
@Override
public REP get(final long timeout, final TimeUnit unit)
throws Throwable {
// TODO Auto-generated method stub
if (unit == null)
throw new NullPointerException();
return getValue(timeout, unit);
}
public REP getValue(final long timeout, final TimeUnit unit)
throws Throwable {
if (this.callbackFunction_ == null)
throw new NullPointerException(
"The callback function not registered.");
REQ response = null;
try {
response = ((timeout == -1 && unit == null) ? futureSession_
.get() : futureSession_.get(timeout, unit));
} catch (final Throwable t) {
if (LOGGER != null) {
if (LOGGER.isInfoEnabled())
LOGGER.info(String
.format("By async method call service error,response time is:%d (ms)",
(System.currentTimeMillis() - this.startRequestTime_)));
}
// retry
retryPolicy_.retry(this,
clientBuildingConfig_.getRetries(), timeout, unit);
try {
response = this.futureSession_.get();
} catch (final Throwable _t) {
}
if (LOGGER != null) {
if (LOGGER.isInfoEnabled())
LOGGER.info("Retry end.");
}
}
final Object ret = this.callbackFunction_.apply(response);
if (ret == null)
throw new NullPointerException(
"The remote server response result is null.");
if (Future.class.isInstance(ret)) {// Nested future
final Future<Throwable> futureThrowable = ((Future<Throwable>) ret);
if (!(futureThrowable.get() == null)
&& Throwable.class
.isInstance(futureThrowable.get()))
throw futureThrowable.get();
return (REP) ((Future<?>) ret).get();
}
return (REP) ret;
}
};
}
@SuppressWarnings("unchecked")
private FutureTask<REQ> createFutureSession(final REQ clientRequest) {
return new FutureTask<REQ>(new Callable<REQ>() {
@Override
public REQ call() throws Exception {
// TODO Auto-generated method stub
SocketConnection socketConnectionProxy = null;
try {
// Because it is a multi host
socketConnectionProxy = clientSocketConnectionPool_
.getSocketConnection(loadBalance_,
(ThriftClientInvocation) clientRequest);
final TAsyncClientEx<REQ> client = ASYNC_CLIENT_FACTORY
.getAsyncClient(protocolFactory_,
asyncClientManager_,
socketConnectionProxy.get());
AsyncMethodCallbackEx<TAsyncMethodCallEx> asyncMethodCallback;
client.sendRequest(
clientRequest,
asyncMethodCallback = new AsyncMethodCallbackEx<TAsyncMethodCallEx>() {
private TAsyncMethodCallEx response_;
private Throwable exception_;
private final AtomicBoolean hasError_ = new AtomicBoolean(
false);
@Override
public void onComplete(
TAsyncMethodCallEx response) {
// TODO Auto-generated method stub
synchronized (this) {
this.response_ = response;
this.notify();
}
}
@Override
public void onError(Exception exception) {
// TODO Auto-generated method stub
hasError_.getAndSet(true);
synchronized (this) {
this.notify();
}
this.exception_ = exception;
this.response_ = null;
}
@Override
public Throwable getException() {
// TODO Auto-generated method stub
return this.exception_;
}
@Override
public TAsyncMethodCallEx getResponse() {
if (this.response_ == null) {
synchronized (this) {
while (this.response_ == null
&& !hasError_.get()) {
try {
this.wait();
} catch (InterruptedException ignored) {
// TODO
// Auto-generated
// catch block
ignored.printStackTrace();
onError(ignored);
}
}
}
}
return this.response_;
}
});
if (client.getError() != null)
throw client.getError();
return (REQ) asyncMethodCallback.getResponse()
.getFrameBuffer().array();// byte array.
} catch (final Throwable e) {
if (!(LOGGER == null)) {
if (LOGGER.isInfoEnabled())
LOGGER.info(String
.format("Gets the server response to the error, reasons:%s",
e));
}
if (!(socketConnectionProxy == null)) {
socketConnectionProxy.setAlive(false);
socketConnectionProxy.close();
clientSocketConnectionPool_
.removeSocketConnection(socketConnectionProxy);
socketConnectionProxy = null;
}
throw (Exception) e;
} finally {
if (socketConnectionProxy != null)// Must be close
{
socketConnectionProxy.close();
clientSocketConnectionPool_
.returnSocketConnection(socketConnectionProxy);
}
}
}
});
}
@Override
public void close() throws Exception {
// TODO Auto-generated method stub
if (this.clientSocketConnectionPool_ != null)
this.clientSocketConnectionPool_.destory();
if (executor != null)
executor.shutdownNow();
if (concurrentSessionQueue_ != null)
concurrentSessionQueue_.clear();
if (this.asyncClientManager_ != null)
this.asyncClientManager_.stop();
}
}