程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 給nginx lua模塊添加sendfile函數可代替X-Accel-Redirect

給nginx lua模塊添加sendfile函數可代替X-Accel-Redirect

編輯:C++入門知識

nginx發送靜態文件,速度極快,Nginx中的x-sendfile機制需要依靠 X-Accel-Redirect 特性實現,不過經過我的測試,不能滿足我的需求,我 要用lua來處理業務邏輯, 然後發送文件內容,一開始用如下方式來實現, 這種方式如果文件小, 到無所謂, 但是當文件很大時, 對性能的影響非常大。   [cpp]  local file = io.open(filePath, "rb")         local size = file:seek("end")   ngx.header["Content-Length"] = size   file:seek("set", 0)    data = file:read("*a")      ngx.print(data)   ngx.flush(true)      file:close()     眾所周知, linux內核裡有sendfile函數,可以0拷貝發送文件,於是在網上找資料,找到的都是同樣的一個文章,都是介紹Nginx的 X-Accel-Redirect 如何使用的, 經過測試 X-Accel-Redirect ,不能滿足我的需求。   介紹 X-Accel-Redirect 的官方文章地址為 : http://wiki.nginx.org/XSendfile       最後沒辦法, 只能從源碼入手了。       參考了 ngx_http_static_module.c  這個發送靜態文件的模塊源碼, 決定實現一個lua的接口, 可以讓lua直接調用sendfile函數, 發送文件內容。       print 函數在 ngx_http_lua_log.c 中實現, 我也把sendfile函數放這裡吧, 直接用 ngx_lua的框架。   [cpp]   void   ngx_http_lua_inject_log_api(lua_State *L)   {       ngx_http_lua_inject_log_consts(L);          lua_pushcfunction(L, ngx_http_lua_ngx_log);       lua_setfield(L, -2, "log");          lua_pushcfunction(L, ngx_http_lua_print);       lua_setglobal(L, "print");          lua_pushcfunction(L, ngx_http_lua_sendfile); //添加的內容       lua_setglobal(L, "sendfile");//添加的內容   }     上面的代碼裡 lua_pushcfunction 就是把函數的指針壓入堆棧,lua_setglobal 就是用來把 "sendfile"壓入堆棧的, 並且設置的是全局函數,全局函數的話, 在lua裡調用就是直接 sendfile(),  如果是用 lua_setfield 來壓入堆棧的話, 那在lua裡就得用  ngx.sendfile () 這樣的形式來調用了。  反正是兩種都可以, 隨便你了。     下面貼出 ngx_http_lua_sendfile 函數的實現 :   [cpp]   static int ngx_http_lua_sendfile(lua_State *L)   {       u_char                    *last, *location;       size_t                     root, len;       ngx_http_request_t        *r;       ngx_str_t                  path;       ngx_int_t                  rc;       ngx_uint_t                 level;       ngx_log_t                 *log;       ngx_buf_t                 *b;       ngx_chain_t                out;       ngx_open_file_info_t       of;       ngx_http_core_loc_conf_t  *clcf;       int                        offset;       int                        bytes;       char                      *filename;       int                        nargs;          lua_pushlightuserdata(L, &ngx_http_lua_request_key);       lua_rawget(L, LUA_GLOBALSINDEX);       r = lua_touserdata(L, -1);       lua_pop(L, 1);          if (r == NULL)        {           luaL_error(L, "no request object found");       return 1;       }              nargs = lua_gettop(L);          filename = (char *) lua_tolstring(L, 1, &len);       offset   = lua_tonumber(L, 2);       bytes    = lua_tonumber(L, 3);          log = r->connection->log;          path.len = ngx_strlen(filename);          path.data = ngx_pnalloc(r->pool, path.len + 1);       if (path.data == NULL) {           return 0;       }          (void) ngx_cpystrn(path.data, (u_char *) filename, path.len + 1);          ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "ngx send lua filename: \"%s\"", filename);          clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);          ngx_memzero(&of, sizeof(ngx_open_file_info_t));          of.read_ahead = clcf->read_ahead;       of.directio = clcf->directio;       of.valid = clcf->open_file_cache_valid;       of.min_uses = clcf->open_file_cache_min_uses;       of.errors = clcf->open_file_cache_errors;       of.events = clcf->open_file_cache_events;          if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK)        {           return 0;//NGX_HTTP_INTERNAL_SERVER_ERROR;       }          if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK)       {           switch (of.err)        {              case 0:               return 0;//NGX_HTTP_INTERNAL_SERVER_ERROR;              case NGX_ENOENT:           case NGX_ENOTDIR:           case NGX_ENAMETOOLONG:                  level = NGX_LOG_ERR;               rc = NGX_HTTP_NOT_FOUND;               break;              case NGX_EACCES:   #if (NGX_HAVE_OPENAT)           case NGX_EMLINK:           case NGX_ELOOP:   #endif               level = NGX_LOG_ERR;               rc = NGX_HTTP_FORBIDDEN;               break;              default:                  level = NGX_LOG_CRIT;               rc = NGX_HTTP_INTERNAL_SERVER_ERROR;               break;           }              if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found)        {               ngx_log_error(level, log, of.err, "%s \"%s\" failed", of.failed, path.data);           }              return 0;//rc;       }          r->root_tested = !r->error_page;          ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);          if (offset < 0) {           offset = 0;       }          if (bytes <= 0) {           bytes = of.size - offset;       }         #if !(NGX_WIN32) /* the not regular files are probably Unix specific */          if (!of.is_file)        {           ngx_log_error(NGX_LOG_CRIT, log, 0, "\"%s\" is not a regular file", path.data);              return 0;//NGX_HTTP_NOT_FOUND;       }      #endif          if (r->method & NGX_HTTP_POST)        {           return 0;//NGX_HTTP_NOT_ALLOWED;       }          rc = ngx_http_discard_request_body(r);          if (rc != NGX_OK)        {           return 0;//rc;       }          log->action = "sending response to client";          len = (offset + bytes) >= of.size ? of.size : (offset + bytes);          r->headers_out.status = NGX_HTTP_OK;       r->headers_out.content_length_n = len - offset;       r->headers_out.last_modified_time = of.mtime;          if (ngx_http_set_content_type(r) != NGX_OK)        {           return 0;//NGX_HTTP_INTERNAL_SERVER_ERROR;       }          if (r != r->main && of.size == 0)        {            ngx_http_send_header(r);            return 0;//       }          r->allow_ranges = 1;          /* we need to allocate all before the header would be sent */          b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));       if (b == NULL)        {           return 0;//NGX_HTTP_INTERNAL_SERVER_ERROR;       }          b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));       if (b->file == NULL)        {           return 0;//NGX_HTTP_INTERNAL_SERVER_ERROR;       }          rc = ngx_http_send_header(r);          if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)        {           return 0;//rc;       }          b->file_pos = offset;       b->file_last = (offset + bytes) >= of.size ? of.size : (offset + bytes);          b->in_file = 1;       b->last_buf = (r == r->main) ? 1: 0;       b->last_in_chain = 1;          b->file->fd = of.fd;       b->file->name = path;       b->file->log = log;       b->file->directio = of.is_directio;          out.buf = b;       out.next = NULL;          ngx_http_output_filter(r, &out);       return 0;//   }   sendfile函數的參數共有三個, 第一個參數為文件名filename。 第二個參數為文件偏移量offset, offset<0代表從文件頭開始發送。 第三個參數為bytes, 要發送的字節數,如果bytes<0, 代表發送到文件尾。       這樣在 lua 腳本裡就可以這樣調用了:   sendfile("/opt/f1.ts", -1,-1) 發送整個文件   或者   sendfile("/opt/f1.ts", 104857600,104857600)  從100MB開始的地方發送100MB的數據       經過測試, 速度和直接nginx發送靜態文件的速度一致。   在添加該功能時,碰到過一些小問題,記錄下來。   ngx_lua裡的C函數返回值代表壓入堆棧的返回值的個數, 看我上面的代碼, 基本都返回0, 代表我沒有給lua的堆棧壓入任何參數, 而下面的代碼   [cpp]   luaL_error(L, "no request object found");   return 1;   就代表壓入了一個參數, 所以返回值為1, 要不然會出現段錯誤。  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved