设计通用的可取消异步操作接口

时间:2010-09-12 19:08:24

标签: java architecture asynchronous

我一直在考虑用于异步请求/响应的通用通用/可取消接口。要求如下,必须:

  • 支持异步调用
  • 可取消
  • 是通用的
  • 支持请求/响应
  • 支持在当前线程中返回或在另一个响应中处理响应

所以这是我的第一次尝试:

interface AsyncOperation<INPUT, OUTPUT> {
    Future<OUTPUT> execute(INPUT in, AsyncCallback<OUTPUT> callback);
}

interface AsyncCallback<OUTPUT> {
    void done(OUTPUT output);
}

用法:

// completely async operation
operation.execute("Test", new AsyncCallback<String> {
    public void done(String output) {
        // process result...
    }
});

// sync operation with cancellation after timeout
Future<String> future = operation.execute("Test", null);
try {
    String result = future.get(1000);
} catch(TimeoutException ex) {
    future.cancel();
}

缺点

  • 这很复杂
  • 它只支持一个请求参数 - 不太关心这个
  • 单个'done'意味着必须通过'done'传递异常,这可以通过在AsyncCallback中使用onSuccess和onException(以及onFinally?)来解决,但它会使它更加冗长

对于某些情况,Google Protocol Buffers服务方法遵循相对类似的模式:

void [methodname](RpcController controller, 
    [RequestClass] request, RpcCallback<[ResponseClass]> callback);

有更好的想法吗?

3 个答案:

答案 0 :(得分:1)

您需要INPUT类型参数吗?将输入作为状态保存的操作对象不会更容易,如:

void greet(final String name) {
    new AsyncOperation<Object>() {
        @Override Object doIt() {
            System.out.println("Hello " + name + "!");
        }
    }.execute(null);
}

这样,调用者可以以类型安全的方式传递任意数量的参数。

此外,调用回调并返回未来似乎是一种奇怪的用法。你确定需要吗?您可以提供两个执行方法,一个返回将来,另一个调用回调。

答案 1 :(得分:0)

看看jetlang。它支持异步操作和请求 - 响应模型。以下是他们测试的一个例子:

@Test
public void simpleRequestResponse() throws InterruptedException {
    Fiber req = new ThreadFiber();
    Fiber reply = new ThreadFiber();
    req.start();
    reply.start();
    RequestChannel<String, Integer> channel = new MemoryRequestChannel<String, Integer>();
    Callback<Request<String, Integer>> onReq = new Callback<Request<String, Integer>>() {
        public void onMessage(Request<String, Integer> message) {
            assertEquals("hello", message.getRequest());
            message.reply(1);
        }
    };
    channel.subscribe(reply, onReq);

    final CountDownLatch done = new CountDownLatch(1);
    Callback<Integer> onReply = new Callback<Integer>() {
        public void onMessage(Integer message) {
            assertEquals(1, message.intValue());
            done.countDown();
        }
    };
    AsyncRequest.withOneReply(req, channel, "hello", onReply);
    assertTrue(done.await(10, TimeUnit.SECONDS));
    req.dispose();
    reply.dispose();
}

答案 2 :(得分:0)

好的,很难回答你自己的问题,但我已经提出了一套适合这个问题的课程:

// the main Async operation interface
public interface AsyncOperation<OUTPUT, INPUT> {
    public AsyncController execute(INPUT input, 
        AsyncCallback<OUTPUT> callback);
}

// the callback that gets called when the operation completes
public interface AsyncCallback<OUTPUT> {
    public void done(AsyncResult<OUTPUT> output);
}

// provides the ability to cancel operations
public interface AsyncController {
    void cancel();
}

// this provides a convenient way to manage a response that is either a
// value or an exception
public class AsyncResult<VALUE> {

    private final VALUE value;
    private final Throwable throwable;

    private AsyncResult(VALUE value, Throwable throwable) {
        this.value = value;
        this.throwable = throwable;
    }

    public AsyncResult(VALUE value) {
        this(value, null);
    }

    public AsyncResult(Throwable throwable) {
        this((VALUE) null, throwable);
    }

    public VALUE value() throws Throwable {
        if(throwable != null) {
            throw throwable;
        } else {
            return value;
        }
    }
}