6
votes

Quelle est la bonne façon de mettre en œuvre des méthodes de synchronisation et d'asynchronisation dans une bibliothèque?

Je dois faire une bibliothèque dans laquelle j'aurai une fonctionnalité synchrone et asynchrone.

  • exécutesynchrone () code> - attend jusqu'à ce que je reçois un résultat, renvoie le résultat. LI>
  • exécutéasynchrone () code> - retourne un avenir immédiatement pouvant être traité après d'autres choses terminées, si nécessaire. li> ul>

    Logique principale de ma bibliothèque forte> p>

    Le client utilisera notre bibliothèque et ils l'appelleront en passant à l'objet code> Datakey code> Builder. Nous construirons ensuite une URL en utilisant cet objet dakeyy code> et faire appel à cette URL en l'exécutant et après avoir reçu la réponse en tant que chaîne JSON, nous enverrons cette chaîne JSON Retourner à Notre client tel qu'il est en créant un objet dataResponse code>. Certains clients appelleront exécutesynchrones () code> et certains peuvent appeler exécutantasynchronique () code> méthode de sorte que j'ai besoin de fournir deux méthodes séparément dans ma bibliothèque. P>

    Interface: p> xxx pré>

    et j'ai mon DataClient code> qui implémente l'interface code> Client p> p>

    // if they are calling `executeSynchronous()` method
    DataResponse response = DataClientFactory.getInstance().executeSynchronous(dataKey);
    
    // and if they want to call `executeAsynchronous()` method
    Future<DataResponse> response = DataClientFactory.getInstance().executeAsynchronous(dataKey);
    


0 commentaires

5 Réponses :


4
votes

pour synchrone appel, exécution dans un thread séparé n'est définitivement pas une bonne idée. Vous encourez des coûts supplémentaires et des ressources pour un fil avec le coût du commutateur de contexte de threads dans ce cas.

S'il y a beaucoup d'appels synchrones , vous bloquez inutilement les threads pour appels asynchrones comme votre exécuteur est de threads de taille fixe. Le débit total du système sera moins dans ce cas.

Par exemple: S'il y a 10 clients appelant chacun des appels et asynchrones asynchrones , dans votre implémentation, seuls les threads fonctionnent réellement. Toutefois, si vous utilisiez également les threads du client et ne pas faire Synchronous appelé comme asynchrone et attendre, tous les 20 appels seront traités en même temps.


0 commentaires

1
votes

Je pense que c'est mieux:

@Override
public DataResponse executeSynchronous(DataKey key) {
    Task task = new Task(key, restTemplate);
    return task.call();
}


3 commentaires

J'ai pensé à ça. Alors comment vais-je mettre en œuvre les trucs de délai d'attente? J'ai toujours besoin de faire ça à droite afin que je puisse faire du temps si le serveur prend trop de temps pour répondre.


J'ai remarqué le délai d'attente après avoir donné ma réponse. J'ai également fait une édition à la réponse sans lire votre commentaire. Je ne pense pas (basé sur le code que vous montrez) Le délai d'attente doit être sur le dakey , et si le délai d'attente est un doit alors c'est une bonne implémentation . Pour mettre en œuvre un délai d'attente, vous aurez besoin d'un deuxième fil qui compte le temps et tue le fil d'origine et lent.


Mais jetez un coup d'œil à: Stackoverflow.com/Questtions/13837012/... . Il est préférable d'utiliser correctement les paramètres de la bibliothèque sous-jacente et d'écrire votre code aussi simple que possible, comme dans ma réponse d'origine (je vais le modifier pour inclure ce bit)



1
votes

Je ne me dérangerais pas avec cette classe de tâches. Faites simplement que votre méthode synchrone effectue tout le travail et appelez-le de manière asynchrone à partir de la méthode asynchrone.

public class DataClient implements Client {

    private RestTemplate restTemplate = new RestTemplate();
    private ExecutorService executor = Executors.newFixedThreadPool(10);

    // for synchronous call
    @Override
    public DataResponse executeSynchronous(DataKey key) {
        DataResponse dataResponse = null;
        String response = null;

        try {
            String url = createURL();
            response = restTemplate.getForObject(url, String.class);

            // it is a successful response
            dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
        } catch (RestClientException ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
            dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
        } catch (Exception ex) {
            PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
            dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
        }

        return dataResponse;
    }

    //for asynchronous call
    @Override
    public Future<DataResponse> executeAsynchronous(final DataKey key) {
        return executor.submit(new Callable<DataResponse>() {
            @Override
            public DataResponse call() throws Exception {
                return executeSynchronous(key);
            }
        });
    }
}


1 commentaires

J'ai seulement écrit le code ci-dessus de cette façon parce que je pensais que ça avait l'air plus jolie et plus simple. Mais votre version est absolument fine si ce que vous voulez faire est d'étrangler le nombre de demandes simultanées au service de repos. Je ne peux pas dire à l'information que vous avez donnée qui fonctionnerait mieux. Cela dépend de la manière dont le service de repos fonctionne et sur ce que font les clients de votre bibliothèque. La meilleure chose à faire serait d'essayer l'une des autres et la mesure qui fonctionne mieux. Je ne sais rien à propos de l'asyncresttemplate, désolé.



1
votes

Le code ci-dessus d'exécution de la tâche synchrone via ASYNC est la même chose que tout comme ASYNC. Si tel est l'exigence, je vous suggérerais d'utiliser l'écoute de Google GuiVa. Je ne suis pas un avocat, mais il a des méthodes pour gérer le délai d'attente des tâches, des rappels bien écrits pour gérer les scénarios onsuccès, onFailure. https://code.google.com/p/guava-libries/wiki/listenablefuTureExploigne


0 commentaires

2
votes

Si vous créez un nouveau thread, même en cas d'opération synchrone (lorsqu'il n'est pas requis), cela conduira à une performance touchée. Vous créez essentiellement un nouveau thread (lire comme gaspillage de ressources) sans même en avoir des avantages. Cela étant dit, je pense que le meilleur moyen serait d'envelopper la partie HTTP d'une classe différente. De cette façon, vous utiliseriez le code de l'accès HTTP dans un boîtier synchrone et asynchrone.

public class DataClient implements Client {

    private ExecutorService executor = Executors.newFixedThreadPool(10);
    private RestTemplate restTemplate;
    private void initRestClient(DataKey key){
    if(restTemplate == null)
        restTemplate = new RestTemplate(clientHttpRequestFactory(key));
    }

    private ClientHttpRequestFactory clientHttpRequestFactory(DataKey key) {
        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
        factory.setReadTimeout(key.getTimeout());
        factory.setConnectTimeout(key.getTimeout());
        //if you need to set otherparams this is the place we can do it extracting from DataKey obj
        return factory;
    }

    // for synchronous call
    @Override
    public DataResponse executeSynchronous(DataKey key) {
        initRestClient(key);
        DataResponse dataResponse = new HTTPAccess(key).performRequest();
        return dataResponse;
    }

    //for asynchronous call
    @Override
    public Future<DataResponse> executeAsynchronous(final DataKey key) {
        return executor.submit(new Callable<DataResponse>() {
            @Override
            public DataResponse call() throws Exception {
                return executeSynchronous(key);
            }
        });
    }
}


3 commentaires

Veuillez noter que le délai d'attente sera pris en tant que RestClientException dans la classe HTTPAccess. Donc, peut-être que vous voudriez changer le code où nous avons attrapé la reseClientException avec quelque chose de plus spécifique à votre besoin.


Merci de suggestion. Apprécié votre aide. J'ai peu de doutes. Avec cette approche, ne crée-nous pas Resttemplate à chaque fois pour chaque demande ici? Et aussi, je suppose que nous créons une usine pour chaque demande et chaque usine a une connexion et une piscine de fil et un objet assez lourd, je suppose. Y a-t-il un moyen de les réutiliser? Je suppose que BCOZ de cette raison pour laquelle j'espérais passer une plaque de repos de ma classe de DataClien en utilisant di.


Oui, votre suggestion est géniale. Nous pouvons réutiliser l'objet restontemplate . Ont édité le code pour refléter les modifications.