How Suning Supercharged Node.js Performance: From CSS Merging to 75% TPS Gains

This article details Suning's step‑by‑step Node.js performance improvements—including CSS/JS registration, EJS template caching, static route optimization, and TPS enhancements—that reduced request latency, cut resource loads, and boosted throughput by up to 75 percent.

Suning Technology
Suning Technology
Suning Technology
How Suning Supercharged Node.js Performance: From CSS Merging to 75% TPS Gains

Node.js Project Background

Since 2016 Suning has been running large‑scale Node.js projects using an Nginx + Node.js + PM2 stack, upgrading from Node 6 to 8 and migrating from Express to Koa2, with performance optimization as a core focus.

Initial Optimization – CSS/JS Registration and Merging

EJS Template Related Optimization

Suning initially used Express, later switching to Koa after Node.js 8 LTS, and employed EJS as the template language.

Impact of Merging CSS and JS

Common parts were extracted into layout.ejs, and pages included it via EJS include. This separated layout from business logic but placed static resource tags inside the body, causing many <link> and <script> tags to appear in the body.

//layout.ejs
<link type="text/css" rel="stylesheet" href="public.css" />
<script src="public.js"></script>
...
include(page1);

//page1.ejs
<link type="text/css" rel="stylesheet" href="page1.css" />
<script src="page1.js"></script>
<h1>hello</h1>

After rendering, the page showed resource tags inside the body:

...
<link type="text/css" rel="stylesheet" href="public.css" />
<script src="public.js"></script>
</header>
<body>
<div class="header"></div>
<link type="text/css" rel="stylesheet" href="page1.css" />
<script src="page1.js"></script>
<h1>hello</h1>
</body>
...

Suning introduced an EJS static‑resource registration mechanism using getResource() placeholders and a register() method to control where resources are injected.

...
{{{CSS_PLACEHOLDER}}}
</header>
<body>
<div class="header"></div>
<h1>hello</h1>
</body>
{{{JS_PLACEHOLDER}}}
...

After replacement, CSS and JS tags appear in the correct locations, and registered resources are merged into combined URLs, reducing the number of HTTP requests.

…
<link type="text/css" rel="stylesheet" href="public.css,page1.css" />
</header>
<body>
<div class="header"></div>
<h1>hello</h1>
</body>
<script src="public.js, page1.js"></script>
...

Cache Mechanism

Because each request performed string replacement, Suning added a cache keyed by request pathname; if a page was cached, getResource() returned the cached content and the registration step was skipped, shaving 4–8 ms off response time on repeated accesses.

Advanced Optimization – Massive Route Matching

Suning’s Hong Kong site had 173 static routes and 11 dynamic routes. Express matches routes by converting each rule to a regular expression and testing them sequentially, leading to O(n) complexity.

Static routes were re‑implemented as a hash map (Object) for O(1) lookup, while dynamic routes still use regex. This change dramatically reduced matching time, though developers must ensure route order is respected.

High‑Level Optimization – TPS Improvement

Initial stress tests showed very low TPS despite using Node.js 8.9.1. The bottleneck was disabled EJS template caching, causing disk reads on every render. Enabling cache yielded a ten‑fold performance boost.

Further gains required reducing synchronous operations. Profiling revealed CPU consumption inside the EJS engine: shallow copies and repeated fs.existsSync checks for includes. Caching include existence and using prototype‑based object creation eliminated unnecessary copies.

The function getIncludePath still performed absolute‑path resolution on every include, invoking file‑system calls. Suning introduced a per‑template map to cache these mappings, avoiding repeated path calculations.

After these optimizations, local benchmarks showed a 50 % performance increase, and online stress testing raised TPS from ~2000 to over 3500—a 75 % improvement, surpassing the Java baseline.

Conclusion

Node.js excels at asynchronous I/O, so performance bottlenecks usually stem from synchronous code. Suning’s optimizations—resource registration, caching, route‑matching redesign, and deep EJS engine tweaks—demonstrate how targeted backend improvements can dramatically boost throughput.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendoptimizationcachingnodejsejs
Suning Technology
Written by

Suning Technology

Official Suning Technology account. Explains cutting-edge retail technology and shares Suning's tech practices.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.