Performances SSR supplémentaires avec le serveur Nuxt fullstack (partie 2)

Performances SSR supplémentaires avec le serveur Nuxt fullstack


Dans la partie 1, j'ai décrit à quel point il est facile d'organiser un serveur API dans Nuxt . Dans la partie 2, je veux vous dire quels avantages supplémentaires vous pouvez obtenir du serveur Nuxt fullstack .

Partie 2: accélérer le rendu du serveur!


Réfléchissons maintenant au fonctionnement de notre serveur à partir de l'exemple codesandbox.io/s/codesandbox-nuxt-3gzhl

  1. Le client demande la page principale 3gzhl.sse.codesandbox.io
  2. Nuxt commence à afficher sur le serveur la page /pages/index.vue
  3. Vient à

      async fetch() {
        this.users = await this.$api("users", "index");
      },
    
  4. Via axios, il fait une requête http , sur 3gzhl.sse.codesandbox.io/api/users/index i.e. sur moi
  5. Une connexion est établie, une nouvelle session est créée sur le serveur et de la mémoire est allouée pour traiter la demande http
  6. Une demande entrante est acceptée via le protocole http , l'url est analysée, les paramètres sont traités
  7. Exécute le middleware du serveur
  8. Nuxt lance notre serveur API
  9. JSON
  10. users.index(), JSON
  11. JSON http
  12. axios JSON
  13. API

Imaginez maintenant que nous avons 20 composants sur la page qui demandent des données via l' API , donc dans une demande pour la page avec le serveur Nuxt , 20 connexions http internes supplémentaires seront établies et les étapes 4-13 seront exécutées 20 fois. Le serveur HTTP Nuxt peut traiter plus de 55 000 requêtes par seconde, cependant, en créant des requêtes HTTP internes, nous réduisons les ressources potentielles du serveur de dizaines de fois.

Mais lorsque nous rendons la page sur le serveur, nous avons un accès direct à tous les contrôleurs dans le dossier / api / Modifions

la logique de sorte que lors du rendu sur le serveur, le code du contrôleur soit appelé directement, et lorsqu'il est appelé depuis le navigateur, la demande a été envoyée via http

  1. Renommez le fichier /plugins/api-context.js en /plugins/api-context.client.js
  2. changer le nom du fichier dans les paramètres /nuxt.config.js

      plugins: ["~/plugins/api-context.client.js"]
    

    Maintenant, le contexte $ Api n'est disponible que pour le code client
  3. créer ce contexte . $ api pour appeler directement les contrôleurs sur le serveur

    /plugins/api-context.server.js
    export default (context, inject) => {
      inject("api", async (controller, method, params) => {
        try {
          let api = require("../api/" + controller.replace(/^\/+|\/+$|\.+/g, ""));
          return await api[method](params);
        } catch (e) {
          console.error(e);
          throw e;
        }
      });
    };
    
  4. connectez le plugin serveur

    /nuxt.config.js
      plugins: [
        "~/plugins/api-context.client.js",
        "~/plugins/api-context.server.js"
      ]
    

Maintenant, la fonction this. $ Api sur le serveur appellera directement la méthode du contrôleur, et sur le client this. $ Api enverra une requête http via axios .

Le code

  async fetch() {
    this.users = await this.$api("users", "index");
  },

lors du rendu sur le serveur, il n'exécutera pas de requête http pour lui-même, mais connectera simplement le fichier /api/users.js via require et appellera la méthode index () , c'est-à-dire les éléments de 4 à 13 ne seront pas exécutés, mais seulement 10 seront exécutés. Cependant, lorsque le client clique sur le bouton Actualiser dans le navigateur , les mêmes données seront demandées via http . Voici le code complet: codesandbox.io/s/codesandbox-nuxt-pbriw





Test de performance


codesandbox.io/s/codesandbox-nuxt-rzdyw

  1. Pour éliminer l'influence de la vitesse des connexions externes, j'ai remplacé la réception de données par des données statiques:

    /api/users.js
    // we can get data from any DB
    async function getDataFromDB() {
      return {
        page: 1,
        per_page: 6,
        total: 12,
        total_pages: 2,
        data: [
          {
            id: 1,
            email: "george.bluth@reqres.in",
            first_name: "George",
            last_name: "Bluth",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
          },
          {
            id: 2,
            email: "janet.weaver@reqres.in",
            first_name: "Janet",
            last_name: "Weaver",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
          },
          {
            id: 3,
            email: "emma.wong@reqres.in",
            first_name: "Emma",
            last_name: "Wong",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"
          },
          {
            id: 4,
            email: "eve.holt@reqres.in",
            first_name: "Eve",
            last_name: "Holt",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/marcoramires/128.jpg"
          },
          {
            id: 5,
            email: "charles.morris@reqres.in",
            first_name: "Charles",
            last_name: "Morris",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/stephenmoon/128.jpg"
          },
          {
            id: 6,
            email: "tracey.ramos@reqres.in",
            first_name: "Tracey",
            last_name: "Ramos",
            avatar:
              "https://s3.amazonaws.com/uifaces/faces/twitter/bigmancho/128.jpg"
          }
        ],
        ad: {
          company: "StatusCode Weekly",
          url: "http://statuscode.org/",
          text:
            "A weekly newsletter focusing on software development, infrastructure, the server, performance, and the stack end of things."
        }
      };
      /*
      return (await require("axios").get(`https://reqres.in/api/users?page=1`))
        .data;
      */
    }
    ....
    
  2. api , http

    /plugins/api-context.server.js
    export default (context, inject) => {
      inject("server", () => true);
      inject("api", async (controller, method, params) => {
        try {
          if (params && params.httpcall) {
            return await context.$axios["$" + (params ? "post" : "get")](
              "/api/" + controller + "/" + method,
              params
            );
          }
          let api = require("../api/" + controller.replace(/^\/+|\/+$|\.+/g, ""));
          return await api[method](params);
        } catch (e) {
          console.error(e);
          throw e;
        }
      });
    };
    
  3. index.vue fetch api 50

    /pages/index.vue
      async fetch() {
        let start = new Date();
        let promises = [];
        let callNum = 50;
        for (let i = 0; i < callNum; i++) {
          promises.push(this.$api("users", "index"));
        }
    
        let arr = await Promise.all(
          promises.map(async p => {
            return await p;
          })
        );
    
        let res = [];
        for (let r of arr) {
          res = res.concat(r);
        }
    
        this.users = res;
        this.fetchType =
          (this.$server && this.$server() ? "Server internal" : "Client http") +
          " API call";
        this.fetchTime = new Date() - start;
      },
    
  4. httpcall.vue fetch api 50 http

    /pages/httpcall.vue
    ...
          promises.push(this.$api("users", "index", { httpcall: true }));
    ...
        this.fetchType =
          (this.$server && this.$server() ? "Server http" : "Client http") +
          " API call";
    ...
    
  5. Maintenant , comparez le temps d'exécution de rzdyw.sse.codesandbox.io

    rendu d'appel API Server interne: chercher temps 1ms
    temps de 0ms à un maximum de 2 ms

    de
    serveur http appel API de rendu temps: aller chercher 71ms
    temps de 46ms à un maximum de 1059ms
    et plusieurs fois le serveur s'est généralement écrasé avec une erreur

    RangeError
    Maximum call stack size exceeded

Voici un exemple complet - codesandbox.io/s/codesandbox-nuxt-rzdyw

Total partie 2


  • Avec des changements minimes, vous pouvez accélérer le rendu du serveur de plus de 50 fois, sur un exemple en direct, le rendu de ma page a été accéléré d'environ 1,7 fois
  • Les ressources du serveur HTTP du nœud sont considérablement réduites
  • De manière optimisée, la seule instance de Nuxt devrait supporter la charge des petits et moyens projets

All Articles