2010-09-12 6 views
3

J'ai envisagé une interface générique/annulable générique pour les demandes/réponses asynchrones. Les exigences sont les suivantes, il doit:Conception d'une interface d'opération asynchrone annulable générique

  • appels asynchrones Support
  • Soyez résiliable
  • générique
  • Demande de support/réponse
  • support soit de retour dans le thread courant ou traitement de la réponse en une autre réponse

Alors, voici mon premier coup:

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

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

Utilisation:

// 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(); 
} 

Inconvénients

  • Il est compliqué
  • Il ne supporte qu'un paramètre de requête unique - pas trop préoccupé par celui-ci
  • Le single 'done' signifie que les exceptions doivent être communiquées via le 'done', cela pourrait être résolu en ayant un onSuccess et onException (et onFin ? Allié) dans AsyncCallback mais il serait encore plus bavard

Pour un certain contexte, les méthodes de service Google Protocol Buffers suivent un modèle relativement similaire:

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

une meilleure idée?

Répondre

1

Avez-vous besoin du paramètre de type INPUT? Ne serait-il plus facile des objets de fonctionnement détenus l'entrée en état, comme dans:

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

De cette façon, un appelant peut passer autant de paramètres comme il aime dans un type de manière sûre. De plus, invoquer un rappel et renvoyer un futur semble une utilisation étrange. Es-tu sûr de l'avoir besoin? Vous pouvez fournir deux méthodes d'exécution, l'une renvoyant le futur et l'autre invoquant le rappel.

+0

Vous êtes à droite, prendre un rappel et renvoyer un futur est un peu étrange mais c'est pour soutenir le cas d'utilisation d'annulation ainsi que le cas où vous pourriez vouloir dire attendre 5 secondes, si l'opération est terminée retourner le résultat de l'opération, si ce n'est pas le cas, retournez simplement que c'est un 'traitement occupé'. Le rappel effectue ensuite le traitement effectif du résultat. –

+0

Plan astucieux pour se débarrasser de la restriction des paramètres d'entrée.Le cas d'utilisation que j'ai à l'esprit est de pouvoir créer des implémentations génériques de AsyncOperation qui limitent/limitent le nombre de threads utilisés, etc. –

0

Jetez un oeil à jetlang. Il prend en charge les opérations asynchrones et le modèle requête-réponse. Voici un exemple de leurs tests:

@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(); 
} 
+0

Cool. Merci, je vais vérifier. J'aime le modèle Acteur. Cela semble un peu compliqué pour la demande/réponse et l'annulation devrait être ajoutée en plus? –

+0

Jetlang ne fournit pas de modèle d'acteur, bien que l'on puisse en construire un au dessus des canaux, des rappels et du système de requête-réponse qu'il fournit. –

+0

Ah, ok, juste regardé encore maintenant et je vois ce que vous voulez dire. Je dois regarder de plus près Jetlang. –

0

Ok, suce pour répondre à votre question, mais je suis venu avec un ensemble de classes qui fonctionne très bien pour cela:

// 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; 
     } 
    } 
} 
Questions connexes