One, foreword

In the previous several articles, I believe that we have a certain understanding of the front-end architecture evolution of Vivo official website mall, from the steady advance of the front and back end separation to small program multi-terminal exploration practice, the team is constantly trying to innovate.

In this article, we will share the actual experience of Vivo official website mall in Server Side Rendering (SSR). This paper mainly focuses on the following aspects:

  • Comparison of CSR and SSR
  • Performance optimization
  • Automated deployment
  • Disaster recovery and degradation
  • Logging and monitoring

Second, the background

At present, the front and back ends of Vivo official website mall are separated by SPA single-page mode. SPA will package all JS as a whole, but the problem that cannot be ignored is that the file is too large, leading to a long wait before rendering. Especially when there is a difference in network speed, it is not a good experience for users to wait for a blank screen to end. Therefore, the front end team of Vivo official website mall tried to introduce SSR technology to speed up the access speed of the first screen of the page, so as to improve user experience.

Three, SSR introduction

3.1 What is SSR?

Page Rendering is mainly divided into Client Side Rendering and Server Side Rendering:

  • Client Side Rendering (CSR)

The server only returns a basic HTML template, the browser according to the HTML content to load JS, get data, render the page content;

  • Server Side Rendering (SSR)

The content of the page is rendered on the server and returned to the browser for direct display.

3.2 Why SSR is used?

Compared with traditional SPA (single-page Application), SSR has the following advantages:

  • Better search engine optimization (SEO). SPA applications initially display loading chrysanthemum graphs and then fetch content via Ajax. Search engines do not wait for asynchronism to complete before fetching page content.
  • Faster time-to-content, especially for slow network situations or slow-running devices, enables users to see fully rendered pages more quickly without waiting for all JavaScript to download and execute before displaying server-rendered markup, improving the user experience. The picture below shows the loading effect more intuitively.

Comparison of CSR and SSR page rendering:

Fourth, SSR practice

The technology stack of Vivo official website mall project is Vue. Considering the complexity of building a server side rendering application from scratch, we chose the Nuxt.js framework officially recommended by Vue, which is a higher-level framework based on Vue ecology and provides extremely convenient development experience for developing Vue application rendered by the server side.

There is no sharing of basic usage here. If you are interested, you can go to the official website of nuxt.js to learn basic usage. We will focus on some of the main challenges encountered during the entire practice:

  • Performance: How to optimize performance, improve QPS and save server resources?
  • Disaster Recovery (Dr) : How to perform a DISASTER recovery (Dr) process to implement automatic degradation?
  • Log: How do I access logs to facilitate fault locating?
  • Monitoring: How do I monitor Node services?
  • Deployment: How to get through the CI/CD process to achieve automated deployment?

4.1 Performance Optimization

Although Vue SSR rendering speed has been very fast, due to the overhead of creating component instances and virtual DOM nodes, the performance of the template engine based on string stitching is very different. In the case of high concurrency, the server response will be slow, greatly affecting the user experience, so the performance optimization must be carried out.

4.1.1 Solution 1 Enable caching

A. Page Cache: LRU-cache is used to Cache rendered HTML when creating render instance, and the HTML string in Cache is returned directly when there is another request to access the page.

Nuxt.config.js adds configuration:

serverMiddleware: ["~/serverMiddleware/pageCache.js"]
Copy the code

The root directory to create serverMiddleware/pageCache. Js

B. Component cache: store the rendered component DOM in the cache, refresh periodically, and take the DOM from the cache within the validity period. This applies primarily to reusable components, mostly to lists, such as lists of items.

Config file nuxt.config.js:

const LRU = require('lru-cache') module.exports = { render: { bundleRenderer: { cache: LRU({ max: MaxAge: 1000 * 60 * 5 // Cache 5 minutes})}}}Copy the code

The cache component adds name and serverCacheKey as unique keys:

export default { 
    name: 'productList', 
    props: ['productId'], 
    serverCacheKey: props => props.productId
}
Copy the code

C. API cache: Node server needs to call the background interface first to obtain data before rendering. The speed of obtaining the interface directly affects the time of rendering. The cache of the interface can speed up the processing of each request and release the request faster, thus improving performance. API caching is mainly used for interfaces where the data remains largely unchanged, changes are infrequent and have nothing to do with the user’s personal data.

4.1.2 Scheme 2 Interface Concurrent Requests

For the same page, multiple interfaces may be called at the Node layer at the same time. If the call is serial, the waiting time will be longer. If the request is concurrent, the waiting time will be reduced.

Such as:

Let data1 = await $axios.get(' interface 1') let data2 = await $axios.get(' interface 2') let data3 = await $axios.get(' interface 3')Copy the code

Can be changed to:

Let {data1,data2,data3} = await Promise. All ([$axios.get(' interface 1'), $axios.get(' interface 2')])Copy the code

4.1.3 Solution 3 Minimize the first screen

The user experience is mainly affected by the white screen time of the first screen, while the second screen, the third screen… Does not need to be displayed immediately. Take the product details page as an example, as shown below:

The page structure can be split. SSR is used for first-screen elements, and CSR is used for non-first-screen elements. SSR data can be obtained using asyncData, and CSR data can be obtained in Mounted.

CSR is written as follows:

<client-only> Client render dom </client-only>Copy the code

4.1.4 CSR is used on some pages of Solution 4

Not all pages face the experience, SEO requirements are very high, such as the mall business, can only do SSR to the home page, product details page and other core pages, which can greatly reduce the pressure on the server.

4.1.5 Comparison of performance pressure measurement before and after optimization

Before optimization:

After the optimization:

As can be seen from the figure above, QPS before optimization is only 125, but after a series of optimization, QPS reaches 6000, which is nearly 50 times higher.

The bottleneck of using Node to do SSR lies in CPU and memory. Under the condition of high concurrency, IT is easy to cause THE CPU to soar and the time for users to access the page becomes longer. If the Node server is down, the page cannot be accessed directly. Therefore, to ensure smooth operation of the project after it is launched, a Dr And downgrade plan must be provided.

Nuxt.js can support both CSR and SSR. When we package, we generate both SSR and CSR packages and deploy them separately.

The following downgrades were used in the project:

4.2 Downgrade Policy

4.2.1 Monitoring System Degradation

Start a service on the Node server to monitor the CPU and memory usage of the Node process and set a threshold. When the threshold is reached, stop the SSR and directly return the CSR entry file index.html to achieve degradation.

4.2.2 Nginx degrade Policy

4.2.2.1 Platform Degradation

For example, during 618, Double 11, etc., we know that the traffic will be heavy in advance, so we can change the Nginx configuration in advance to forward the request to the static server, return index.html, and switch to CSR.

4.2.2.2 Single Access Degraded

When the Node server returns a 5xx error code, or the Node server directly hangs, we can use the following Nginx configuration to automatically switch to CSR to ensure normal user access.

Nginx configuration is as follows:

Location / {proxy_pass Node server address; proxy_intercept_errors on; error_page 408 500 501 502 503 504 =200 @spa_page; } location @spa_page { rewrite ^/* /spa/200.html break; Proxy_pass static server; }Copy the code

4.2.2.3 Specifying the Rendering Mode

Add parameter isCsr=true to url, Nginx layer intercepts parameter isCsr. If this parameter is used, it points to CSR, otherwise, it points to SSR. In this way, you can use URL parameter configuration to distribute pages and reduce the pressure on Node servers.

4.3 Automatic CI/CD Deployment

Based on CI/CD of the company, we have realized two automatic deployment schemes of Docker deployment and Shell script deployment.

4.3.1 Solution 1 Shell script Construction and deployment

For Shell scripting, we mainly solve the problem of how to install the specified Node version from the script. Here we can divide into two steps:

1. Install NVM. Install_nvm () {echo "env $app_env install NVM ..." wget --header='Authorization:Basic dml2b2Rldm9wczp4TFFidmtMbW9ZKn4x' -nv -P .nvm http://xxx/download/nvm-master.zip unzip -qo .nvm/nvm-master.zip mv nvm-master/* $NVM_DIR rm -rf .nvm rm -rf nvm-master . "$NVM_DIR/nvm.sh" if [[ $? = 1 ]]. Then echo "install NVM fail" else echo "install NVM success" fi} # define install Node method install_node() {# Command_args Specifies a user-defined Node version. Local USE_NODEVER=$command_args echo "will install NodeJs $USE_NODEVER" NVM install $USE_NODEVER >/dev/null echo "success change to NodeJs version" $(node -v)} # Prepare () {if [[-s "$NVM_DIR/nvm.sh" ]]; then . "$NVM_DIR/nvm.sh" else install_nvm fi echo "nvm version $(nvm --version)" install_node }Copy the code

4.3.2 Scheme 2 Docker construction and deployment

Docker is an open source application container engine that allows developers to package their applications and dependencies into a portable image that can then be distributed to any popular Linux or Windows machine, as well as virtualization. Containers are completely sandboxed and have no interface with each other.

RUN mkdir -p /home/docker-docker-workdir /home/docker-demo COPY. /home/docker-demo # RUN yarn install RUN YARN prod # Static resource deployment CDN RUN yarn deploy # port number EXPOSE 3000 # Project start command CMD NPM startCopy the code

In comparison, Docker deployment has major advantages:

  • Easier to build and deploy
  • Consistent runtime environment “This code works fine on my machine”
  • Elastic scaling
  • More efficient use of system resources
  • Fast – Administrative actions (start, stop, start, restart, etc.) are measured in seconds or milliseconds

4.4 Monitoring and Alarm

Monitoring is a very important part of the whole product life cycle. It provides early warning and detailed data to track down and locate problems.

When an application fails, it is necessary to have a proper tool chain to support problem location and repair. We introduce the open source enterprise Node.js application performance monitoring and online fault location solution Easy-Monitor, which can better Monitor the Node.js application status to face the performance and stability challenges. We deployed this system on the Intranet, and carried out secondary development, integrated the Intranet domain login, and can push alarm information through the internal chat tool.

4.5 log

Once an application goes live, the first thing to do is to figure out what happened, such as how the user acted and how the data responded. In this case, the log information provides us with first-hand information. So we need access to the company’s log system.

4.5.1 implementation

Log components are encapsulated in Log4JS and interconnect with the log center of the company

Add to nuxt.config.js:

export default { // ... modules: [ "@vivo/nuxt-vivo-logger" ], vivoLog: { logPath:process.env.NODE_ENV === "dev"?" ./logs":"/data/logs/", logName:'aa.log' } }Copy the code

4.5.2 using

async asyncData({ $axios,$vivoLog }) {
    try {
      const resData =  await $axios.$get('/api/aaa')
      if (process.server) $vivoLog.info(resData)
    } catch (e) {
      if (process.server) $vivoLog.error(e)
    }
},
Copy the code

4.5.3 results

Write at the end

The improvement of user experience is a permanent topic. The front-end team of Vivo has been committed to the continuous innovation of technology, hoping to bring better experience to users through the exploration of technology; The above is the front end team of Vivo official website mall in SSR technology practice some experience, share out hope to learn and discuss with you.

  • Vivo mall front-end architecture upgrade – multi – terminal unified exploration, practice and prospects

  • Vivo mall front-end architecture upgrade – front and back end separation

  • Vivo mall front-end architecture upgrade – overview

  • Vivo global Mall: the integration of consignment business from 0 to 1

  • Vivo Global Mall: The Evolution of architecture

Author: Front-end team of Vivo official website mall