2016年8月

页面静态化

页面内容的拆分

Artboard 1.png

  1. 静态内容
  2. 非状态相关的动态内容
  3. 状态相关的动态内容

HInclude CSI

用于pc或者h5页面

<hx:include src="/_fragment?path="></hx:include>

Varnish ESI

多用于app api的接口返回

vcl 4.0;

import directors;

backend server1 {
    .host = "10.162.110.168";
    .port = "80";
}

acl local {
    "localhost";
}

sub vcl_init {
    new director = directors.round_robin();
    director.add_backend(server1);
}

sub vcl_recv {
    set req.backend_hint = director.backend();

    if (req.method == "PURGE") {
        if (client.ip ~ local) {
            return (purge);
        } else {
            return (synth(405));
        }
    }

    if (req.http.Accept == "text/event-stream") {
        return (pipe);
    }

    if (req.http.upgrade ~ "(?i)websocket") {
        return (pipe);
    }

    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }

    set req.http.Surrogate-Capability = "abc=ESI/1.0";
    if (req.http.X-Forward-For) {
        set req.http.X-Forward-For = req.http.X-Forward-For + "," + client.ip;
    } else {
        set req.http.X-Forward-For = client.ip;
    }
    return (hash);
}

sub vcl_backend_response {
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
        unset beresp.http.Surrogate-Control;
        set beresp.do_esi = true;
    }

    if (bereq.uncacheable) {
        return (deliver);
    }

    set beresp.grace = 30s;

    # 302 5xx no cache
    if (beresp.status == 302 || beresp.status >= 500) {
        set beresp.uncacheable = true;
        set beresp.ttl = 120s;
        return (deliver);
    }

    if (beresp.status == 301 || (beresp.status >= 400 && beresp.status < 500)) {
        set beresp.ttl = 120s;
    } else {
        set beresp.ttl = 2400s;
    }

    unset beresp.http.Set-Cookie;
    return (deliver);
}

sub vcl_pipe {
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
    }
    return (pipe);
}

sub vcl_deliver {
    set resp.http.X-Age = resp.http.Age;
    unset resp.http.Age;
    unset resp.http.Via;
    unset resp.http.X-Powered-By;
    unset resp.http.X-Varnish;

    if (obj.hits > 0) {
        set resp.http.X-Cache="HIT from " + server.hostname;
    } else {
        set resp.http.X-Cache="MISS";
    }

    return (deliver);
}

workflow

  1. varnish 接收到请求后,增加 Surrogate-Capability:abc=ESI/1.0 头,打到后端
  2. 后端判断Surrogate-Capability决定是否要使用esi标签来返回 (即判断这个请求是不是从varnish过来的)
  3. 返回内容时判断是否含有 <esi:include,增加 Surrogate-Control:content="ESI/1.0"
  4. varnish 判断 Surrogate-Control 头,决定是否要启用 esi 替换

后端应用中的处理

  1. 生成路由url /_fragment?path=

    • <hx:include src="/_fragment?path="></hx:include>
    • <esi:include src="/_fragment?path="></esi:include>
  2. 注册处理 /_fragment 路由

wrk中的lua脚本

wrk是一款现代化的http压测工具,提供lua脚本的功能可以满足每个请求或部分请求的差异化。

wrk中执行http请求的时候,调用lua分为3个阶段,setup,running,done,每个wrk线程中都有独立的脚本环境。

wrk.png

wrk的全局属性

wrk = {
  scheme  = "http",
  host    = "localhost",
  port    = nil,
  method  = "GET",
  path    = "/",
  headers = {},
  body    = nil,
  thread  = <userdata>,
}

wrk的全局方法

-- 生成整个request的string,例如:返回
-- GET / HTTP/1.1
-- Host: tool.lu
function wrk.format(method, path, headers, body)

-- 获取域名的IP和端口,返回table,例如:返回 `{127.0.0.1:80}`
function wrk.lookup(host, service)

-- 判断addr是否能连接,例如:`127.0.0.1:80`,返回 true 或 false
function wrk.connect(addr)

Setup阶段

setup是在线程创建之后,启动之前。

function setup(thread)

-- thread提供了1个属性,3个方法
-- thread.addr 设置请求需要打到的ip
-- thread:get(name) 获取线程全局变量
-- thread:set(name, value) 设置线程全局变量
-- thread:stop() 终止线程

Running阶段

function init(args)
-- 每个线程仅调用1次,args 用于获取命令行中传入的参数, 例如 --env=pre

function delay()
-- 每个线程调用多次,发送下一个请求之前的延迟, 单位为ms

function request()
-- 每个线程调用多次,返回http请求

function response(status, headers, body)
-- 每个线程调用多次,返回http响应

Done阶段

可以用于自定义结果报表,整个过程中只执行一次

function done(summary, latency, requests)


latency.min              -- minimum value seen
latency.max              -- maximum value seen
latency.mean             -- average value seen
latency.stdev            -- standard deviation
latency:percentile(99.0) -- 99th percentile value
latency(i)               -- raw value and count

summary = {
  duration = N,  -- run duration in microseconds
  requests = N,  -- total completed requests
  bytes    = N,  -- total bytes received
  errors   = {
    connect = N, -- total socket connection errors
    read    = N, -- total socket read errors
    write   = N, -- total socket write errors
    status  = N, -- total HTTP status codes > 399
    timeout = N  -- total request timeouts
  }
}

例子

表单的提交

wrk.method = "POST"
wrk.body = "" -- 直接写死,如果不需要请求数据的差异化
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

-- 如果要实现每次都不一样的表单内容
local queries = {
    "language=php",
    "language=java",
    "language=lua"
}
local i = 0
request = function()
    local body = wrk.format(nil, nil, nil, queries[i % #queries + 1])
    i = i + 1
    return body
end