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, foreach
Variable handling
Strings : concatenation, substring, …
Exception handling, header manipulation, …
JSON support
Much (much) faster
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
top_slic_ss (session-specific)
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
top_slic_ss (session-specific)
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
63. Template system – find the last template
Nginx
SLIC
Shared memory
Template ?
Request
Last updated ?
64. Template system – new/changed template
Nginx
SLIC
LuaJIT
Shared memory
Fetch template
Convert to LUA
Compile
Store
65. Template system – unchanged template
Nginx
SLIC
Shared memory
Template ?LuaJIT
Last updated ?
70. Availability
Good news :
The concept is solid : ESI version stable at 4 customers
Open Source
Bad news :
First customer holds copyrights
Total rebuild
Beta 1 almost ready (include, variable handling, …)
Final release : Q3 2015 (?)
Site : http://slic.cu.be
Code : Github (after beta 1 release)
71. 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
Future (1.0 or beyond) :
Translation functionality
Integration with LuaJIT FFI → direct C calls
Any suggestions ?