web-dev-qa-db-fra.com

Comment utiliser le servlet 3.1 dans Spring MVC?

Deux fonctionnalités différentes sont disponibles:

  1. servlet 3. permet de traiter la requête dans un thread différent du thread du conteneur.

  2. servlet 3.1 permet de lire/écrire dans le socket sans bloquer le thread de lecture/écriture

Il existe de nombreux exemples sur Internet concernant la fonction servlet 3.0. Nous pouvons l'utiliser très facilement au printemps. Il suffit de retourner DefferedResult ou CompletableFuture

Mais je ne trouve pas d'exemple d'utilisation du servlet 3.1 au printemps. Pour autant que je sache, nous devons enregistrer WriteListener et ReadListener et faire du sale boulot à l'intérieur. Mais je ne trouve pas l'exemple de ce Listeners. Je pense que ce n'est pas très facile.

Pourriez-vous s'il vous plaît fournir un exemple de fonctionnalité de servlet 3.1 au printemps avec une explication de l'implémentation de l'écouteur?

15
gstackoverflow

Cela ne devrait pas être trop difficile de chasser quelques exemples. J'en ai trouvé un chez IBM à WASdev/sample.javaee7.servlet.nonblocking . L'utilisation de l'API javax.servlet Dans Spring ou Spring Boot consiste simplement à demander à Spring d'injecter HttpServletRequest ou HttpServletResponse. Ainsi, un exemple simple pourrait être:

@SpringBootApplication
@Controller
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RequestMapping(path = "")
    public void writeStream(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletOutputStream output = response.getOutputStream();
        AsyncContext context = request.startAsync();
        output.setWriteListener(new WriteListener() {
            @Override
            public void onWritePossible() throws IOException {
                if ( output.isReady() ) {
                    output.println("WriteListener:onWritePossible() called to send response data on thread : " + Thread.currentThread().getName());
                }
                context.complete();
            }
            @Override
            public void onError(Throwable t) {
                context.complete();
            }
        });
    }
}

Cela crée simplement un WriteListener et le joint au flux de sortie de la demande, puis retourne. Rien d'extraordinaire.

EDITS: Le fait est que le conteneur de servlet, par exemple Tomcat, appelle onWritePossible lorsque les données peuvent être écrites sans blocage. Plus d'informations à ce sujet sur E/S non bloquantes utilisant Servlet 3.1: Applications évolutives utilisant Java EE 7 (TOTD # 188). .

Les écouteurs (et les rédacteurs) ont des méthodes de rappel qui sont invoquées lorsque le contenu est disponible pour être lu ou peut être écrit sans blocage.

Par conséquent, onWritePossible n'est appelé que lorsque out.println Peut être appelé sans blocage.

L'appel des méthodes setXXXListener indique que des E/S non bloquantes sont utilisées à la place des E/S traditionnelles.

Vraisemblablement ce que vous avez à faire, vérifiez output.isReady Pour savoir si vous pouvez continuer à écrire des octets. Il semble que vous deviez avoir une sorte d'accord implicite avec l'expéditeur/récepteur sur la taille des blocs. Je ne l'ai jamais utilisé, donc je ne sais pas, mais vous avez demandé un exemple de cela dans le framework Spring et c'est ce qui est fourni.

Donc onWritePossible n'est appelé que lorsque out.println peut être appelé sans blocage. Cela semble correct, mais comment puis-je comprendre combien d'octets peuvent être écrits? Comment dois-je contrôler cela?

EDIT 2: C'est une très bonne question à laquelle je ne peux pas vous donner de réponse exacte. Je suppose que onWritePossible est appelé lorsque le serveur exécute le code dans un thread séparé (asynchrone) de la servlet principale. À partir de l'exemple, vous vérifiez input.isReady() ou output.isReady() et je suppose que bloque votre thread jusqu'à ce que l'expéditeur/destinataire soit prêt pour plus. Comme cela se fait de manière asynchrone, le serveur lui-même n'est pas bloqué et peut gérer d'autres requêtes. Je ne l'ai jamais utilisé donc je ne suis pas un expert.

Quand j'ai dit qu'il y aurait une sorte d'accord implicite avec l'expéditeur/récepteur sur la taille des blocs, ce qui signifie que si le récepteur est capable d'accepter des blocs de 1024 octets, vous écririez ce montant lorsque output.isReady Est vrai. Vous devriez avoir des connaissances à ce sujet en lisant la documentation, rien dans l'api à ce sujet. Sinon, vous pourriez écrire des octets simples, mais l'exemple d'Oracle utilise des blocs de 1024 octets. Les blocs de 1024 octets sont une taille de bloc assez standard pour le streaming des E/S. L'exemple ci-dessus devrait être développé pour écrire des octets dans une boucle while comme il est montré dans l'exemple Oracle. C'est un exercice laissé au lecteur.

Le réacteur de projet et Spring Webflux ont le concept de backpressure qui peut traiter cela plus attentivement. Ce serait une question distincte et je n'ai pas examiné de près comment les couples émetteurs et récepteurs (ou vice versa).

0
K.Nicholas