1. 程式人生 > >使用Openresty加快網頁速度

使用Openresty加快網頁速度

全部 解決辦法 fault warn upstream mys tor proxy tps

新年快樂~~~
上一篇文章講到使用多級緩存來減少數據庫的訪問來加快網頁的速度,只是,仍舊沒有“嗖”一下就加載出來的感覺,想再優化一下,優化代碼什麽的已經到了極限。上周無意中看到了openresty,能夠直接在nginx層直接對請求處理,而不需要每次都訪問tomcat,特別是對於自己的首頁http://www.wenzhihuai.com來說,由於首頁內容廣泛,需要涉及的方法太多,每次打開首頁都得耗費大量的時間。

一、整體架構

目前本人總共有三個服務器:
A:119.23.46.71(深圳,Nginx環境,本地Redis,tomcat服務器)
B:119.29.188.224(廣州,tomcat服務器)


C:47.95.10.139(北京,tomact服務器)
為了減少後端的相應時間,之前使用的是在應用裏集成ehcache作為一級緩存,redis作為二級緩存。這種架構存在一種特殊的情況:當Nginx將首頁的請求分發給北京節點的時候,響應將變得極其緩慢,用戶的請求需要從深圳到北京,再從北京回到深圳,光是延時就要耗費40ms(最好的情況),由於網速是1M/s,最壞的情況下,響應用戶的請求也得耗費幾秒。所以,為了減少這種極端情況,設計了這款架構(個人項目隨便整整而已,怎麽來都行啦)。
由於是博客類的項目,對於一致性要求並不高,所以幹脆直接將首頁緩存到Redis即可。
技術分享圖片
步驟:
1.請求到達nginx後,openresty通過lua讀取本地緩存,如果不命中,則回源到tomcat集群。
2.tomcat集群首先從自己的服務器中讀取一級緩存Ehcache,如果沒有命中,則繼續回源到二級緩存。
3.讀取二級緩存Redis,如果依舊沒有命中,則回源到MySQL服務器。

二、配置過程

2.1 Openresty

安裝過程可以直接參考官方文檔:http://openresty.org/cn/download.html,安裝前還需安裝以下開發庫:

yum install pcre-devel openssl-devel gcc curl

然後進行編譯安裝:

tar -xzvf openresty-VERSION.tar.gz
cd openresty-VERSION/
./configure
make
sudo make install

2.2 Nginx相關配置

Openresty自帶了Nginx。所以,只要安裝好了Openresty,即可直接使用nginx來配置。
以下只是部分,需要全部的請查看mynginxconfig

http {
    include       mime.types;
    default_type  application/octet-stream;
    # 需要添加lua的相關庫
    lua_package_path "/opt/openresty/lualib/?.lua;;";
    lua_package_cpath "/opt/openresty/lualib/?.so;;";
    ...

    access_log  logs/access.log  main;
    sendfile        on;
    keepalive_timeout  65;

    upstream backend {
        #consistent_hash was not configured
        hash $uri;
        server 47.95.10.139:8080;
        server 119.23.46.71:8080;
        server 119.29.188.224:8080;
    }
    server {
        listen       80;
        server_name  www.wenzhihuai.com;
        # 精確匹配,打開首頁的時候進入
        location  = / {
            default_type    text/html;
            root   html;
            index  index.html index.htm;
            ...
            # 關閉緩存lua腳本,調試的時候專用
            lua_code_cache off;
            content_by_lua_file /opt/lua/hello.lua;
            # 此處不要proxy_pass了,否則lua腳本沒用
            # proxy_pass http://backend;

        }
        # 如果上面的不符合,則匹配下面的
        location / {
            default_type    text/html;
            root   html;
            index  index.html index.htm;
            # 對請求進行反向代理
            proxy_pass http://backend;
        }
    }
    ...
}

2.3 lua腳本

腳本記得放在/opt/lua/hello.lua目錄下,對應nginx的配置,同時需要引入redis模塊。

local redis = require "resty.redis"
local red = redis:new()

local request_uri = ngx.var.request_uri

if (request_uri == "/" or request_uri == "/index.html") then
    red:set_timeout(1000) -- 1 sec
    red:connect("119.23.46.71", 6340)
    local ok, err = red:auth("root")
    if not ok then
        ngx.say("failed to connect: ", err)
        return
    end
    --緩存的首頁放在key為index裏
    local resp, errr = red:get("index")
    if not resp then
        return
    end
    if resp == ngx.null then
        resp = "<h1>hello world</h1>"
    end
    --如果找到,則輸出內容
    ngx.print(resp)
    red:close()
    return
end
local pagenum = ngx.req.get_uri_args()["pagenum"]
--因為在nginx中設置了proxy_pass_request_headers off,即不講請求頭部傳到lua,所以頭部需要重新設置
ngx.req.set_header("Accept", "text/html,application/xhtml+xml,application/xml;")
--這裏回源到tomcat的時候,Accept-Encoding默認為gzip,即返回來數據已經是gzip壓縮過了的,返回到用戶的時候又被壓縮了一次,會造成一堆亂碼。所以將Accept-Encoding設置為空。
ngx.req.set_header("Accept-Encoding", "")
local respp = ngx.location.capture("/index.do", { method = ngx.HTTP_GET, args = { pagenum = pagenum } })
--打印
ngx.print(respp.body)
return

2.4 更新首頁到redis

每隔20秒直接訪問後端進行首頁的抓取,然後存儲到redis裏面,簡單粗暴。

@Controller
@SuppressWarnings("unchecked")
public class TimeController {
    //logger
    private static final Logger logger = LoggerFactory.getLogger(TimeController.class);
    @Scheduled(cron = "0/20 * * * * ?")
    public void refreshIndex() throws Exception {
        String ip = IPUtils.getServerIp().replaceAll("\n", "");
        if (REGULARIP.equals(ip)) {
            String content = HttpHelper.getInstance().get("http://119.29.188.224:8080");
            JedisUtil.getInstance().set("index", content);
        }
    }
}

碰到的問題

capture報錯

2018/02/10 18:53:51 [error] 2833#0: *3942 lua subrequests cycle while processing "/index.html", client: 113.108.186.130, server: www.wenzhihuai.com, request: "GET /index.html?pagenum=23 HTTP/1.1", subrequest: "/index.html", host: "www.wenzhihuai.com"
2018/02/10 18:53:51 [error] 2833#0: *3942 lua entry thread aborted: runtime error: /opt/lua/hello.lua:25: failed to issue subrequest: -1
stack traceback:
coroutine 0:
    [C]: in function 'capture'
    /opt/lua/hello.lua:25: in function </opt/lua/hello.lua:1>, client: 113.108.186.130, server: www.wenzhihuai.com, request: "GET /index.html?pagenum=23 HTTP/1.1", subrequest: "/index.html", host: "www.wenzhihuai.com"

打開網頁亂碼,用curl正常顯示

查了好久,才發現Openresty如果使用nginx.location.capture是重新請求一次的,如果帶有請求頭並且開啟了gzip壓縮,那麽數據就相當於被再次壓縮。
技術分享圖片

解決辦法:去掉請求頭部
ngx.req.set_header("Accept-Encoding", "");

使用Openresty加快網頁速度