Rendimiento adicional de SSR con el servidor Fullstack de Nuxt (Parte 2)

Rendimiento SSR adicional con el servidor Nuxt fullstack


En la Parte 1, hablé sobre lo fácil que es organizar un Servidor API en Nuxt . En la Parte 2, quiero decirle qué beneficios adicionales puede obtener del servidor Fullstack de Nuxt .

Parte 2: ¡acelerar el renderizado del servidor!


Pensemos ahora cómo funciona nuestro servidor a partir de los códigos de ejemploandbox.io/s/codesandbox-nuxt-3gzhl

  1. El cliente solicita la página principal 3gzhl.sse.codesandbox.io
  2. Nuxt comienza a procesar en el servidor la página /pages/index.vue
  3. Viene a

      async fetch() {
        this.users = await this.$api("users", "index");
      },
    
  4. A través de axios, realiza una solicitud http , en 3gzhl.sse.codesandbox.io/api/users/index, es decir. en mí mismo
  5. Se establece una conexión, se crea una nueva sesión en el servidor y se asigna memoria para procesar la solicitud http
  6. Se acepta una solicitud entrante a través del protocolo http , se analiza el URL, se procesan los parámetros
  7. Ejecuta el middleware del servidor
  8. Nuxt lanza nuestro servidor API
  9. JSON
  10. users.index(), JSON
  11. JSON http
  12. axios JSON
  13. API

Ahora imagine que tenemos 20 componentes en la página que solicitan datos a través de la API , por lo que en una solicitud para la página con el servidor Nuxt se establecerán 20 conexiones http internas adicionales y los pasos 4-13 se realizarán 20 veces. El servidor HTTP de Nuxt puede procesar más de 55 mil solicitudes por segundo, sin embargo, al crear solicitudes HTTP internas, reducimos los recursos potenciales del servidor en decenas de veces.

Pero cuando procesamos la página en el servidor, tenemos acceso directo a todos los controladores en la carpeta / api /

Cambiemos la lógica para que cuando se procese en el servidor, el código del controlador se llame directamente, y cuando se llame desde el navegador, la solicitud se envió a través de http

  1. Cambie el nombre del archivo /plugins/api-context.js a /plugins/api-context.client.js
  2. cambiar el nombre del archivo en la configuración /nuxt.config.js

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

    Ahora este contexto . $ Api está disponible solo para el código del cliente
  3. cree este contexto . $ api para llamar directamente a los controladores en el servidor

    /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. conecte el complemento del servidor

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

Ahora la función this. $ Api en el servidor llamará directamente al método del controlador, y en el cliente esto. $ Api enviará una solicitud http a través de axios .

El código

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

cuando se procesa en el servidor, no realizará una solicitud http para sí mismo, sino que simplemente conectará el archivo /api/users.js a través de require y llamará al método index () , es decir los elementos del 4 al 13 no se ejecutarán, pero solo se ejecutarán 10. Sin embargo, cuando el cliente hace clic en el botón Actualizar en el navegador , se solicitarán los mismos datos a través de http . Aquí está el código completo: codesandbox.io/s/codesandbox-nuxt-pbriw





Pruebas de rendimiento


codesandbox.io/s/codesandbox-nuxt-rzdyw

  1. Para eliminar la influencia de la velocidad de las conexiones externas, reemplacé la recepción de datos con datos estáticos:

    /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. Ahora compare el tiempo de ejecución de rzdyw.sse.codesandbox.io Tiempo de recuperación de llamadas de

    API interna del servidor: 1 ms de
    tiempo de 0 ms a un máximo de 2 ms

    rzdyw.sse.codesandbox.io/httpcall Tiempo de
    recuperación de llamadas de API http del servidor:
    tiempo de 71 ms de 46 ms a un máximo de 1059 ms
    y varias veces el servidor generalmente fallaba con un error

    RangeError
    Maximum call stack size exceeded

Aquí hay un ejemplo completo: codesandbox.io/s/codesandbox-nuxt-rzdyw

Total parte 2


  • Con cambios mínimos, puede acelerar la representación del servidor más de 50 veces, en un ejemplo en vivo, la representación de mi página se aceleró ~ 1.7 veces
  • Los recursos del servidor HTTP del nodo se redujeron significativamente
  • De manera optimizada, la única instancia de Nuxt debería soportar la carga de proyectos pequeños y medianos

All Articles