Asynchronous example Thrift in Java
上一篇试了下同步的例子,这篇来玩玩异步调用是怎样滴~直接上代码~
1. 例子代码:
异步调用的服务端:
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import tutorial.MultiplicationService;
import tutorial.MultiplicationServiceImpl;
public class AsyncServer {
public static final int SERVER_PORT = 8090;
public static final int CLIENT_TIMEOUT = 30000;
public void startServer() {
try {
System.out.println("AsyncServer start ....");
TProcessor tprocessor = new MultiplicationService.Processor(new MultiplicationServiceImpl());
TNonblockingServerSocket tnbSocketTransport = new TNonblockingServerSocket(SERVER_PORT, CLIENT_TIMEOUT);
TNonblockingServer.Args tnbArgs = new TNonblockingServer.Args(tnbSocketTransport);
tnbArgs.processor(tprocessor);
tnbArgs.transportFactory(new TFramedTransport.Factory());
tnbArgs.protocolFactory(new TCompactProtocol.Factory());
// 使用非阻塞式IO,服务端和客户端需要指定TFramedTransport数据传输的方式
TServer server = new TNonblockingServer(tnbArgs);
server.serve();
} catch (Exception e) {
System.out.println("Server start error!!!");
e.printStackTrace();
}
}
public static void main(String[] args) {
AsyncServer server = new AsyncServer();
server.startServer();
}
}
异步调用的客户端:
import org.apache.thrift.TException;
import org.apache.thrift.async.AsyncMethodCallback;
import org.apache.thrift.async.TAsyncClientManager;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.transport.TNonblockingSocket;
import org.apache.thrift.transport.TNonblockingTransport;
import tutorial.MultiplicationService;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class AsynClient {
public static final String SERVER_IP = "localhost";
public static final int SERVER_PORT = 8090;
public static final int TIMEOUT = 30;
public static final int THREAD_NUM = 8;
public void startClient() {
try {
System.out.println("Client start .....");
CountDownLatch latch = new CountDownLatch(THREAD_NUM);
// We only need one protocol factory
TProtocolFactory tprotocol = new TCompactProtocol.Factory();
long start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUM; i++) {
TNonblockingTransport transport = new TNonblockingSocket(SERVER_IP, SERVER_PORT, TIMEOUT);
MultiplicationService.AsyncClient asyncClient = new MultiplicationService.AsyncClient(tprotocol,
new TAsyncClientManager(), transport);
new Thread(new AsynSendTask(asyncClient, new AsynCallback(latch))).start();
}
latch.await(300, TimeUnit.SECONDS);
long elapsedTime = System.currentTimeMillis() - start;
System.out.println("elapsed time is (s): " + elapsedTime / 1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("startClient end.");
}
public class AsynSendTask implements Runnable {
private MultiplicationService.AsyncClient asyncClient;
private AsynCallback callBack;
public AsynSendTask(MultiplicationService.AsyncClient asyncClient, AsynCallback callBack) {
this.asyncClient = asyncClient;
this.callBack = callBack;
}
@Override
public void run() {
try {
//for (int i = 0; i < 10; i++) {
asyncClient.multiply(2, 3, callBack);
//}
} catch (TException e) {
e.printStackTrace();
}
}
}
public class AsynCallback implements AsyncMethodCallback<MultiplicationService.AsyncClient.multiply_call> {
private CountDownLatch latch;
public AsynCallback(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void onComplete(MultiplicationService.AsyncClient.multiply_call response) {
System.out.println("onComplete");
try {
//TimeUnit.SECONDS.sleep(10);
System.out.println("AsynCall result =:" + response.getResult());
} catch (TException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}
@Override
public void onError(Exception exception) {
System.out.println("onError :" + exception.getMessage());
latch.countDown();
}
}
public static void main(String[] args) {
AsynClient client = new AsynClient();
client.startClient();
}
}
从上面代码可以发现有两个值得关注的问题:
1. for (int i = 0; i < THREAD_NUM; i++) {} 这里面,每个循环都得新构造一个TNonblockingSocket,新构造一个AsyncClient。尝试过每个线程都只使用一个socket与client,表示公用一条链路,会抛出异常:java.lang.IllegalStateException: Client is currently executing another method: tutorial.MultiplicationService AsyncClient multiply_call
由此可知调用时只能一条请求线程使用一条链路..
2. 为了测试异步,将代码修改下,对于同个client调用多次看看结果:
for (int i = 0; i < n; i++) {
asyncClient.multiply(2, 3, callBack);
}
然后却是抛出跟上面同样的异常,说好的异步呢?我哪里认知错了?按我的理解同一个线程client应该可以调用多次,调用的结果异步通过callback可取。
2. 封装给多线程调用
例子从Stack Overflow中一个回答改造而来,给大家参考下,服务端代码还是以上的,客户代码改造如下:~
import org.apache.thrift.async.TAsyncClientManager;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.transport.TNonblockingSocket;
import tutorial.MultiplicationService.AsyncClient;
import tutorial.MultiplicationService.AsyncClient.Factory;
import java.io.IOException;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
public class Thrift {
// This is the request
public static abstract class ThriftRequest {
private void go(final Thrift thrift, final AsyncClient cli) {
on(cli);
thrift.ret(cli);
}
public abstract void on(AsyncClient cli);
}
// Holds all of our Async Clients
private final ConcurrentLinkedQueue<AsyncClient> instances = new ConcurrentLinkedQueue<AsyncClient>();
// Holds all of our postponed requests
private final ConcurrentLinkedQueue<ThriftRequest> requests = new ConcurrentLinkedQueue<ThriftRequest>();
// Holds our executor, if any
private Executor exe = null;
/**
* This factory runs in thread bounce mode, meaning that if you call it from
* many threads, execution bounces between calling threads depending on when
* execution is needed.
*/
public Thrift(
final int clients,
final int clients_per_message_processing_thread,
final String host,
final int port) throws IOException {
// We only need one protocol factory
TProtocolFactory proto_fac = new TCompactProtocol.Factory();
// Create our clients
Factory fac = null;
for (int i = 0; i < clients; i++) {
if (fac == null || i % clients_per_message_processing_thread == 0) {
fac = new AsyncClient.Factory(new TAsyncClientManager(), proto_fac);
}
instances.add(fac.getAsyncClient(new TNonblockingSocket(host, port)));
}
}
/**
* This factory runs callbacks in whatever mode the executor is setup for,
* not on calling threads.
*/
public Thrift(Executor exe,
final int clients,
final int clients_per_message_processing_thread,
final String host,
final int port) throws IOException {
this(clients, clients_per_message_processing_thread, host, port);
this.exe = exe;
}
// Call this to grab an instance
public void req(final ThriftRequest req) {
final AsyncClient cli;
synchronized (instances) {
cli = instances.poll();
}
if (cli != null) {
if (exe != null) {
// Executor mode
exe.execute(new Runnable() {
@Override
public void run() {
req.go(Thrift.this, cli);
}
});
} else {
// Thread bounce mode
req.go(this, cli);
}
return;
}
// No clients immediately available
requests.add(req);
}
private void ret(final AsyncClient cli) {
final ThriftRequest req;
synchronized (requests) {
req = requests.poll();
}
if (req != null) {
if (exe != null) {
// Executor mode
exe.execute(new Runnable() {
@Override
public void run() {
req.go(Thrift.this, cli);
}
});
} else {
// Thread bounce mode
req.go(this, cli);
}
return;
}
// We did not need this immediately, hold onto it
instances.add(cli);
}
}
调用的代码:
import org.apache.thrift.TException;
import org.apache.thrift.async.AsyncMethodCallback;
import tutorial.MultiplicationService.AsyncClient;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class Client {
public static final int THREAD_NUM = 8;
private static final CountDownLatch latch = new CountDownLatch(THREAD_NUM);
public static void main(String[] args) throws IOException, InterruptedException {
// Make the pool
Thrift t = new Thrift(THREAD_NUM, THREAD_NUM, "localhost", 8090);
long start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUM; i++) {
t.req(new MyThriftRequest());// Use the pool
}
latch.await(60, TimeUnit.SECONDS);
long elapsedTime = System.currentTimeMillis() - start;
System.out.println("elapsed time is (s): " + elapsedTime / 1000);
}
static class MyThriftRequest extends Thrift.ThriftRequest {
@Override
public void on(AsyncClient cli) {
try {
cli.multiply(2, 5, new AsyncMethodCallback<AsyncClient.multiply_call>() {
@Override
public void onComplete(AsyncClient.multiply_call response) {
try {
System.out.println("AsynCall result =:" + response.getResult());
} catch (TException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}
@Override
public void onError(Exception e) {
System.out.println("onError :" + e.getMessage());
latch.countDown();
}
});
} catch (TException e) {
e.printStackTrace();
}
}
}
}