Although tools like Varnish can improve performance and scalability for static sites, when user-specific content is needed, a hit to the PHP/Ruby/Python/.Net backend is still required, causing scalability issues. We’ll look at a brand-new Nginx module which implements an ultra-fast and scalable solution to this problem, changing the way developers think about designing sites with user-specific content.
12. Who am I ?
Wim Godden (@wimgtr)
Founder of Cu.be Solutions (http://cu.be)
Open Source developer since 1997
Developer of OpenX, PHPCompatibility, PHPConsistent, ...
Speaker at Open Source conferences
13. Who are you ?
Developers ?
System/network engineers ?
Managers ?
33. ESI – how it works
<html>
...
<esi:include src="/top"/>
<esi:include src="/nav"/>
<div id=”something”>
<esi:include src="/latest-news"/>
</div>
<esi:include src="/article/id/732"/>
...
</html>
GET /pageGET /page
34. ESI – how it works
<div id=”top-part”>
<a href=”/login”>Login</a>
</div>
GET /top
35. ESI – how it works
<html>
...
<esi:include src="/top"/>
<esi:include src="/nav"/>
<div id=”something”>
<esi:include src="/latest-news"/>
</div>
<esi:include src="/article/id/732"/>
...
</html>
GET /pageGET /page
36. ESI – how it works
<html>
...
<div id=”top-part”>
<a href=”/login”>Login</a>
</div>
<esi:include src="/nav"/>
<div id=”something”>
<esi:include src="/latest-news"/>
</div>
<esi:include src="/article/id/732"/>
...
</html>
GET /pageGET /page
37. Varnish - what can/can't be cached ?
Can :
Static pages
Images, js, css
Static parts of pages that don't change often (ESI)
Can't :
POST requests
Very large files (it's not a file server !)
Requests with Set-Cookie
User-specific content
38. ESI → no caching on user-specific content ?
Logged in as : Wim Godden
5 messages
TTL = 5minTTL=1h
TTL = 0s ?
40. The semi-functional Varnish way
Use VCL to attach session id or UUID to cache entries
if( req.http.Cookie ~ "myapp_unique_user_id" ) {
set req.http.X-Varnish-Hashed-On =
regsub( req.http.Cookie, "^.*?
myapp_unique_user_id=([^;]*);*.*$", "1" );
}
Painful, messy configuration
Inefficient and messy way to update data
POST /sendmessagePOST /sendmessage
PURGE /top?UUID
GET /page GET /top?UUID
DB
41. The almost functional Varnish way
Varnish module : VMOD-Memcached
Connects Varnish directly to Memcached
Advantage :
It works
It will speed a few things up
Disadvantage :
A pain to set up
No plug&play → DIY !
Only works on info stored in session
48. SLIC on Nginx
<slic:include key="article732" src="/article/732" />
<slic:include
key="nav"
src="/nav" />
<slic:include key="top" src="/top" session="true" />
Logged in as : Wim Godden
5 messages ???
49. New message is sent...
POST /send
DB
insert into...
set(...)
top (in SLIC session)
50. Advantages
No repeated GET hits to webserver anymore !
At login : POST → warm up the cache !
No repeated hits for user-specific content
Not even for non-specific content
52. First release : ESI
Part of the ESI 1.0 spec
Only relevant features implemented
Extension for dynamic session support
But : unavailable for copyright reasons
53. Rebuilt from scratch : SLIC
Control structures : if/else, switch/case, foreach
Variable handling
Strings : concatenation, substring, …
Exception handling, header manipulation, …
JSON support !
57. Approaches – full block
<p id=”LoggedInAs”>
You are logged in as : Wim Godden
</p>
<p id=”MessageCount”>
5 messages
</p>
Logged in as : Wim Godden
5 messages
top_432
58. Approaches – individual variables
<p id=”LoggedInAs”>
You are logged in as : <slic:session_var("person_name") />
</p>
<p id=”MessageCount”>
<slic:session_var(“messages”) /> messages
</p>
Logged in as : Wim Godden
5 messages
59. Approaches – JSON
<p id=”LoggedInAs”>
You are logged in as : <slic:session_var("userData").person_name />
</p>
<p id=”MessageCount”>
<slic:session_var(“userData”).message_count /> messages
</p>
Logged in as : Wim Godden
5 messages
60. Identifying the user
In Nginx configuration :
slic_session_cookie <name> → Defined by language (or configurable)
slic_session_identifier <string> → Defined by you
Example for PHP :
slic_session_cookie PHPSESSID
slic_session_identifier UserID
61. Identifying the user
Nginx + SLIC
Cookie :
PHPSESSID =
jpsidc1po35sq9q3og4f3hi6e2
get UserID_jpsidc1po35sq9q3og4f3hi6e2432
62. Retrieving user specific content
Nginx + SLIC
get userData_432
Cookie :
PHPSESSID =
jpsidc1po35sq9q3og4f3hi6e2
64. Figures
2nd
customer :
No. of web servers : 72 → 8
No. of db servers : 15 → 4
Total : 87 → 12 (86% reduction !)
Last customer :
No. of total servers : +/- 1350
Expected reduction : 1350 → 380
Expected savings : €1.5 Million per year
67. Availability
Good news :
The concept is solid : ESI version stable at 4 customers
Open Source
Bad news :
First customer holds copyrights
Total rebuild
March : v0.1 (include, variable handling, …)
Final release : Q2-3 2015 (?)
Site : http://slic.cu.be
Code : Github
68. Some technical details
Written in Lua (with LuaJIT)
Each SLIC:include is a subrequest
Groups cache key requests together for multiget
Shares cache results across all subrequests
Template compilation (!!)
Memcached implemented
Redis and others in the pipeline
Not RFC compliant yet
Unit tested
Future (1.0 or beyond) :
Translation functionality
...