微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从 Lambda 执行 SSR 时,如何缩短冷启动时间

如何解决从 Lambda 执行 SSR 时,如何缩短冷启动时间

我有一个用 CRA、ConnectedRouter、Redux 和 Saga 制作的 React.JS 应用程序的 SSR 服务器。我正在尝试使用以下代码在 AWS Lambda 下托管此服务器:

const serverlessExpress = require('@vendia/serverless-express');
const app = require('./index');
const binaryMimeTypes = [
  'application/javascript',...
  'text/xml'
];
exports.handler = serverlessExpress({
  app,binaryMimeTypes
}).handler;

这个包装器提取下面的设置代码

const md5File = require('md5-file');
const fs = require('fs');
const path = require('path');

// CSS styles will be imported on load and that complicates matters... ignore those bad boys!
const ignoreStyles = require('ignore-styles');
const register = ignoreStyles.default;

// We also want to ignore all image requests
// When running locally these will load from a standard import
// When running on the server,we want to load via their hashed version in the build folder
const extensions = ['.gif','.jpeg','.jpg','.png','.svg'];

// Override the default style ignorer,also modifying all image requests
register(ignoreStyles.DEFAULT_EXTENSIONS,(mod,filename) => {
  if (!extensions.find(f => filename.endsWith(f))) {
    // If we find a style
    return ignoreStyles.noOp();
  }
  // for images that less than 10k,CRA will turn it into Base64 string,but here we have to do it again
  const stats = fs.statSync(filename);
  const fileSizeInBytes = stats.size / 1024;
  if (fileSizeInBytes <= 10) {
    mod.exports = `data:image/${mod.filename
      .split('.')
      .pop()};base64,${fs.readFileSync(mod.filename,{
      encoding: 'base64'
    })}`;
    return ignoreStyles.noOp();
  }

  // If we find an image
  const hash = md5File.sync(filename).slice(0,8);
  const bn = path.basename(filename).replace(/(\.\w{3})$/,`.${hash}$1`);

  mod.exports = `/static/media/${bn}`;
});

// Set up babel to do its thing... env for the latest toys,react-app for CRA
// Notice three plugins: the first two allow us to use import rather than require,the third is for code splitting
// polyfill is required for Babel 7,polyfill includes a custom regenerator runtime and core-js
require('@babel/polyfill');
require('@babel/register')({
  ignore: [/\/(build|node_modules)\//],presets: ['@babel/preset-env','@babel/preset-react'],plugins: [
    '@babel/plugin-Syntax-dynamic-import','@babel/plugin-proposal-class-properties','dynamic-import-node','react-loadable/babel'
  ]
});

// Now that the nonsense is over... load up the server entry point
const app = require('./server');
module.exports = app;

然后我的 server.js 中有常规的 express 服务器

// Express requirements
import bodyParser from 'body-parser';
import compression from 'compression';
import express from 'express';
import morgan from 'morgan';
import path from 'path';
import forceDomain from 'forcedomain';
import Loadable from 'react-loadable';
import cookieParser from 'cookie-parser';

// Our loader - this basically acts as the entry point for each page load
import loader from './loader';

// Create our express app using the port optionally specified
const main = () => {
  const app = express();
  const PORT = process.env.PORT || 3000;
  // Compress,parse,log,and raid the cookie jar
  app.use(compression());
  app.use(bodyParser.json());
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use(morgan('dev'));
  app.use(cookieParser());

  // Set up homepage,static assets,and capture everything else
  app.use(express.Router().get('/',loader));
  const favicon = require('serve-favicon');
  app.use(favicon(path.resolve(__dirname,'../build/icons/favicon.ico')));
  app.use(express.static(path.resolve(__dirname,'../build')));
  app.use(loader);

  // We tell React Loadable to load all required assets and start listening - ROCK AND ROLL!
  Loadable.preloadAll().then(() => {
    app.listen(PORT,console.log(`App listening on port ${PORT}!`));
  });

  // Handle the bugs somehow
  app.on('error',error => {
    if (error.syscall !== 'listen') {
      throw error;
    }

    const bind = typeof PORT === 'string' ? 'Pipe ' + PORT : 'Port ' + PORT;

    switch (error.code) {
      case 'EACCES':
        console.error(bind + ' requires elevated privileges');
        process.exit(1);
        break;
      case 'EADDRINUSE':
        console.error(bind + ' is already in use');
        process.exit(1);
        break;
      default:
        throw error;
    }
  });

  return app;
};

module.exports = main();

然后,我让加载程序执行:

// Express requirements
import path from 'path';
import fs from 'fs';

// React requirements
import React from 'react';
import { renderToString } from 'react-dom/server';
import Helmet from 'react-helmet';
import { Provider } from 'react-redux';
import { StaticRouter } from 'react-router-dom';
import { Frontload,frontloadServerRender } from 'react-frontload';
import Loadable from 'react-loadable';

// Our store,entrypoint,and manifest
import createStore from '../src/configureStore';
import App from '../src/containers/app';
import manifest from '../build/asset-manifest.json';

// Some optional Redux functions related to user authentication
//import { setCurrentUser,logoutUser } from '../src/modules/auth';

// LOADER
export default (req,res) => {
  /*
    A simple helper function to prepare the HTML markup. This loads:
      - Page title
      - SEO Meta tags
      - Preloaded state (for Redux) depending on the current route
      - Code-split script tags depending on the current route
  */
  const injectHTML = (data,{ html,title,Meta,body,scripts,state }) => {
    data = data.replace('<html>',`<html ${html}>`);
    data = data.replace(/<title>.*?<\/title>/g,title);
    data = data.replace('</head>',`${Meta}</head>`);
    data = data.replace(
      '<div id="root"></div>',`<div id="root">${body}</div><script>window.__PRELOADED_STATE__ = ${state}</script>${scripts.join(
        ''
      )}`
    );

    return data;
  };

  // Load in our HTML file from our build
  fs.readFile(
    path.resolve(__dirname,'../build/index.html'),'utf8',(err,htmlData) => {
      ...

整个过程,包括转译,以及启动 express 服务器所需的时间需要相当长的时间。对于温暖的 lambda,我的延迟可能在 100 毫秒之间,一直到大约 3 秒。

是否有一些简单的改进可以应用到我的代码中?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?