高可用 React 服务端渲染
郭达峰 Strikingly 联合创始人兼CTO
Strikingly / 上线了
十分钟实现你的互联网+
300万网站
小程序发布量行业领先
YC孵化首家中国团队
总融资1750万美金
React SSR
Server-side Rendering
服务端渲染/同构直出
Browser: React Editor
Node.js: SSR HTML
同构:一份JS代码,前端和后端共享
Why do we need React SSR?
1. SEO and faster initial load
2. no code duplication
3,000,000+网站
网站数目庞大,每个网站都不同
Daily Deployment
每次部署都需要短时间内刷新cache
Crawler DDOS!
几分钟内爬虫扫描成千上万网站
高性能
高可用
React SSR 性能
React SSR is slow!
相比于传统模板渲染,SSR慢很多
renderToString
- node is single-threaded
- synchronous rendering
- CPU bound
Useless client code
- Virtual DOM
- Synthetic event
SSR性能优化
测试目标 DivTree
import React from 'react'
// <DivTree depth={5} breadth={5} /> => 5^5 = 3125 React components
class DivTree extends React.Component {
render() {
const { depth, breadth } = this.props
if (depth <= 0) {
return <div>{'content'}</div>
}
let children = []
for (let i = 0; i < breadth; i++) {
children.push(<DivTree key={i} depth={depth - 1} breadth={breadth} />)
}
return <div>{children}</div>
}
}
mean: ~400 ms using out of box React 15
Optimization Tricks #1
use NODE_ENV=production
Mean: 400ms -> 105ms
Optimization Tricks #2
Babel-transform: transform-react-constant-elements
const Hr = () => {
return <hr className="hr" />;
};
const _ref = <hr className="hr" />;
const Hr = () => {
return _ref;
};
Static component only need to call React.createElment once
Optimization Tricks #3
Babel-transform: transform-react-inline-elements
<Baz foo="bar" key="1"></Baz>;
babelHelpers.jsx(Baz, {
foo: "bar"
}, "1");
/**
* Instead of
*
* React.createElement(Baz, {
* foo: "bar",
* key: "1",
* });
*/
Tricks #2 and #3 get us to 70ms
So far
NODE_ENV=production
Babel plugins
We cut from 400ms down to 70ms
Use at your own risk Optimization
Component caching
(state, props) => string
this is essentially what ReactSSR does
(state, props) => string
memoize it!
Typically use LRU cache
https://github.com/electrode-io/electrode-react-ssr-caching
Component caching
3,000,000+网站
每个网站内容和组件都大有不同
400ms -> 70ms
pretty good?
Oversize page!
CPU overload -> Machine died
- node is single-threaded
- synchronous rendering
- CPU bound
User
Web Server
request
response
SSR
High Availability (HA)
高可用集群是指以减少服务中断时间为目的的服务器集群技术
User
Web Server
request
response
SSR
pm2 and autoscaling
Adding SSR Server Layer
- Dedicated to rendering service
- Fallback to client rendering if timeout
- Provide high availability SSR service
User
Web Server
request
response
SSR
Server
Airbnb's Hypernova
https://github.com/airbnb/hypernova
User
Web Server
request
response
SSR
Server
入口Component
const React = require('react')
const renderReact = require('hypernova-react').renderReact
function MyComponent(props) {
return <div>Hello, {props.name}!</div>
}
module.exports = renderReact('MyComponent.js', MyComponent)
Web Server
const Renderer = require('hypernova-client')
const renderer = new Renderer({
url: 'http://localhost:3030/batch',
})
const jobs = {
MyComponent: { name: req.query.name || 'Stranger' },
Component2: { text: 'Hello World' },
}
renderer.render(jobs).then(html => res.send(html))
SSR Server
var hypernova = require('hypernova/server')
hypernova({
devMode: true,
getComponent(name) {
if (name === 'MyComponent.js') {
return require('./app/assets/javascripts/MyComponent.js')
}
return null
},
port: 3030,
})
<div data-hypernova-key="MyComponent.js" data-hypernova-id="684d437b-dda0">
<div data-reactroot="" data-reactid="1" data-react-checksum="255991580">
<p data-reactid="2">Hello, FCC Chengdu</p>
</div>
</div>
<script type="application/json" data-hypernova-key="MyComponent.js" data-hypernova-id="684d437b-dda0">
<!--{ name: "FCC Chengdu" }-->
</script>
const React = require('react')
const renderReact = require('hypernova-react').renderReact
function MyComponent(props) {
return <div>Hello, {props.name}!</div>
}
module.exports = renderReact('MyComponent.js', MyComponent)
Component
SSR Succeed HTML
<div data-hypernova-key="MyComponent.js" data-hypernova-id="684d437b-dda0">
<!-- SSR failed -->
</div>
<script type="application/json" data-hypernova-key="MyComponent.js" data-hypernova-id="684d437b-dda0">
<!--{ name: "FCC Chengdu" }-->
</script>
SSR Failure HTML
hypernova-powered HA
- Dedicated to rendering
- Fallback to client rendering if timeout
- Provide high availability SSR service
...
我们成功的把前端的问题交给了后端处理
Cluster
Cluster of small servers with autoscaling policy
Serverless
Event triggered serverless computing service
AWS Lambda
Alicloud Function Compute
Serverless
Event triggered serverless computing service
Serverless
Cluster
with Autoscaling
总结
React SSR is slow
- node is single-threaded
- synchronous rendering
- CPU bound
- Useless client code
Tricks to Optimize
- NODE_ENV=production
- transform-react-constant-elements
- transform-react-inline-elements
- these tricks get us from 400ms -> 70ms
SSR Service to Provide HA
- Separate SSR into a service
- Graceful fallback
- High availability
- Airbnb's hypernova
- Cluster or Serverless
Any further optimization?
CPU Hogging
Async Rendering
Suspense
Time Slicing
上线了招人
- 帮助中国2000w中小企业上线
- (微信|支付宝|w+)小程序
- 前端性能优化
- 创造出美的产品
- 推荐成功送1000美金红包
jobs@strikingly.com
React SSR
By dfguo
React SSR
- 3,519