<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Nginx 中運(yùn)行 JavaScript

          共 9628字,需瀏覽 20分鐘

           ·

          2021-09-16 22:11

          點(diǎn)擊上方 前端瓶子君,關(guān)注公眾號(hào)

          回復(fù)算法,加入前端編程面試算法每日一題群

          引言

          Nginx 作為市場(chǎng)占有率最高的Web服務(wù)器,主打高性能、可擴(kuò)展。自帶了很多核心功能模塊,并且也有大量的第三方模塊。

          Web 服務(wù)中灰度方案的實(shí)現(xiàn),很多會(huì)采用 Nginx + Lua + Redis 方案。Lua 是一個(gè)輕量級(jí)的腳本語(yǔ)言,體積小、啟動(dòng)速度快、性能高。通過(guò) lua-nginx-module 模塊將 Lua 語(yǔ)言嵌入到 Nginx 中,可以使用 Lua 腳本擴(kuò)展 Nginx 功能,并可以訪問(wèn) MySQL、Redis 等數(shù)據(jù)庫(kù)。

          Lua 雖然是個(gè)強(qiáng)大的腳本語(yǔ)言,但過(guò)于小眾。Nginx 團(tuán)隊(duì)選擇非常流行的 JavaScript 研發(fā) NGINX JavaScript 模塊 (njs),讓更多工程師可以使用 JavaScript 來(lái)擴(kuò)展 Nginx 功能,從而更好的發(fā)展 Nginx 社區(qū)生態(tài)。

          NGINX JavaScript 簡(jiǎn)介

          NGINX JavaScript 簡(jiǎn)稱 njs,是 JavaScript 語(yǔ)言的子集,實(shí)現(xiàn)了部分 ECMAScript 5.1(strict mode)規(guī)范和 ECMAScript 6 規(guī)范,可以使用 njs 來(lái)擴(kuò)展 Nginx 功能。

          njs 與 Node.js、JavaScript 的區(qū)別

          一、運(yùn)行時(shí)不同

          Node.js 使用 V8 引擎,njs 是專門(mén)為 Nginx 定制設(shè)計(jì)的運(yùn)行時(shí)。Node.js 使用 V8 引擎在內(nèi)存中有一個(gè)持久化的 JavaScript 虛擬機(jī) (VM) 并執(zhí)行垃圾收集以進(jìn)行內(nèi)存管理;而 njs 是專門(mén)為 Nginx 設(shè)計(jì),非常輕量,會(huì)為每個(gè)請(qǐng)求初始化一個(gè)新的 JavaScript VM 和必要的內(nèi)存,并在請(qǐng)求完成時(shí)釋放內(nèi)存。

          二、語(yǔ)言規(guī)范差異

          JavaScript 的規(guī)范是由 ECMAScript 標(biāo)準(zhǔn)定義,隨著標(biāo)準(zhǔn)版本的更新迭代,會(huì)支持更多的語(yǔ)言功能;njs 自研的服務(wù)端運(yùn)行時(shí),更多的優(yōu)先支撐服務(wù)于 Nginx,只實(shí)現(xiàn)了 ECMAScript 5.1 和部分 ECMAScript 6,實(shí)現(xiàn)更多標(biāo)準(zhǔn)規(guī)范的同時(shí),更多會(huì)考慮是否是 Nginx 所需要的。

          njs 安裝&配置

          安裝 nginx-module-njs 動(dòng)態(tài)模塊,需要 Nginx 版本為 1.9.11 之后支持動(dòng)態(tài)模塊的載入。

          yum install nginx-module-njs

          安裝后,在配置文件 nginx.conf 中需要使用 load_module 指令加載 njs 動(dòng)態(tài)模塊。

          load_module modules/ngx_http_js_module.so;

          njs 基本使用

          Hello World

          nginx.conf:

          http {
          js_import http.js;
          # or js_import http from http.js;

          server {
          listen 8000;

          location / {
          js_content http.hello;
          }
          }
          }

          http.js:

          function hello(r{
              r.return(200"Hello world!");
          }

          export default { hello };

          js_import : 導(dǎo)入一個(gè) njs 模塊,沒(méi)有指定模塊名稱則默認(rèn)為文件名稱。

          js_content : 使用 njs 模塊里導(dǎo)出的方法處理這個(gè)請(qǐng)求。

          HTTP Proxying

          使用 njs 模塊處理 HTTP 請(qǐng)求,并使用 subrequest 發(fā)起子請(qǐng)求。

          nginx.conf:

          js_import http.js;

          location /start {
          js_content http.content;
          }

          location /foo {
          proxy_pass <http://backend1>;
          }

          location /bar {
          proxy_pass <http://backend2>;
          }

          http.js:

          function content(r{
              r.subrequest('/api/5/foo', {
                    method'POST',
                    bodyJSON.stringify({ foo'foo'bar"bar" })
              }, function(res{
                      if (res.status != 200) {
                          r.return(res.status, res.responseBody);
                          return;
                      }
                      var json = JSON.parse(res.responseBody);
                      r.return(200, json.content);
              });
          }

          export default { content };

          r.subrequest : 可以去請(qǐng)求內(nèi)部的其他 API ,headers 和該請(qǐng)求相同,并且可以在 location 塊里使用 proxy_set_header 來(lái)設(shè)置或覆蓋原來(lái)的 header。

          自定義日志輸出格式

          使用 njs 定制 Nginx 日志的輸出格式。

          nginx.js:

          js_import  logging.js;
          js_set $access_log_headers logging.kvAccess;
          log_format kvpairs $access_log_headers;

          server {
          listen 80;
          root /usr/share/nginx/html;
          access_log /var/log/nginx/access.log kvpairs;
          }

          logging.js:

          function kvAccess(r{
              var log = `${r.variables.time_iso8601} client=${r.remoteAddress} method=${r.method} uri=${r.uri} status=${r.status}`;
              r.rawHeadersIn.forEach(h => log += ` in.${h[0]}=${h[1]}`);
              r.rawHeadersOut.forEach(h => log += ` out.${h[0]}=${h[1]}`);
              return log;
          }

          export default { kvAccess }

          js_set : 將 njs 模塊里的 kvAccess 方法執(zhí)行后,執(zhí)行結(jié)果放到 $access_log_headers 變量中。但如果只被引用在 log_format 中,則只會(huì)在日志記錄階段被執(zhí)行。

          r : HTTP request 對(duì)象。屬性列表:http://nginx.org/en/docs/njs/reference.html#http

          訪問(wèn)數(shù)據(jù)庫(kù)

          一、訪問(wèn) Redis

          使用 redis2-nginx-module 動(dòng)態(tài)模塊,結(jié)合 subrequest 來(lái)訪問(wèn) Redis 數(shù)據(jù)。

          nginx.conf:

          js_import http.js;

          # GET /redis_get?key=some_key
          location = /redis_get {
          # 解碼 uri 中的參數(shù) key,賦值到變量 $key
          set_unescape_uri $key $arg_key;
          redis2_query get $key;
          redis2_pass 127.0.0.1:6379;
          }

          # GET /redis_set?key=one&val=first%20value
          location = /redis_set {
          set_unescape_uri $key $arg_key;
          set_unescape_uri $val $arg_val;
          redis2_query set $key $val;
          redis2_pass 127.0.0.1:6379;
          }

          # GET /get_redis_data?key=some_key
          location /get_redis_data {
          js_content http.get_redis_data;
          }

          http.js:

          function serialize(obj{
              var str = [];
              for (var p in obj) {
                  if (obj.hasOwnProperty(p)) {
                      str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
                  }
              }
              return str.join("&");
          };

          function get_redis_data(r{
              r.subrequest('/redis_get', {
                    args: serialize(r.args),
                    method'GET'
              }, function(res{
                      if (res.status != 200) {
                          r.return(res.status, res.responseBody);
                          return;
                      }

                      r.return(200, res.responseBody);
              });
              return log;
          }

          export default { get_redis_data }

          set_unescape_uri :解碼 uri 中參數(shù)的 %XX 編碼。

          redis2_query : 執(zhí)行的 Redis 命令。

          redis2_pass : Redis 后端服務(wù)。

          redis2_pass 返回值為類似 redis-cli 執(zhí)行后的返回值,需要有一個(gè) parser 來(lái)解析是否執(zhí)行成。

          二、訪問(wèn) MySQL

          使用 drizzle-nginx-module 動(dòng)態(tài)模塊,結(jié)合 subrequest 來(lái)訪問(wèn) MySQL 數(shù)據(jù)。

          nginx.conf:

          upstream backend {
          drizzle_server 127.0.0.1:3306 dbname=test
          password=some_pass user=monty protocol=mysql;
          }

          server {
          js_import http.js;

          location /mysql {
          set_unescape_uri $name $arg_name;
          # 為防止 SQL 注入攻擊,使用 set_quote_sql_str 來(lái)設(shè)置 sql 語(yǔ)句中的變量
          set_quote_sql_str $quoted_name $name;

          drizzle_query "select * from cats where name = $quoted_name";
          drizzle_pass backend;

          drizzle_connect_timeout 500ms; # default 60s
          drizzle_send_query_timeout 2s; # default 60s
          drizzle_recv_cols_timeout 1s; # default 60s
          drizzle_recv_rows_timeout 1s; # default 60s
          }

          # GET /get_mysql_data?name=cat_name
          location /get_mysql_data {
          js_content http.get_mysql_data;
          }
          }

          http.js:

          function serialize(obj{
              var str = [];
              for (var p in obj) {
                  if (obj.hasOwnProperty(p)) {
                      str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
                  }
              }
              return str.join("&");
          };

          function get_mysql_data(r{
              r.subrequest('/mysql', {
                    args: serialize(r.args),
                    method'GET'
              }, function(res{
                      if (res.status != 200) {
                          r.return(res.status, res.responseBody);
                          return;
                      }

                      r.return(200, res.responseBody);
              });
              return log;
          }

          export default { get_mysql_data }

          set_quote_sql_str : 為防止 SQL 注入攻擊,來(lái)設(shè)置 sql 語(yǔ)句中的變量。

          drizzle_query : 執(zhí)行的 SQL 語(yǔ)句。

          drizzle_pass : Drizzle 或 MySQL 服務(wù)的 upstream。

          結(jié)語(yǔ)

          在 njs 之前,Nginx+Lua 生態(tài)雖然已日趨成熟,但 Nginx 畢竟是一個(gè) Web 服務(wù)器,JavaScript 作為 Web 開(kāi)發(fā)的最流行的語(yǔ)言,可以使用 JavaScript 生態(tài)來(lái)擴(kuò)展 Nginx 的功能,可能會(huì)更加的有一些想象力做更多的事情。

          參考文獻(xiàn)

          • 2021年06月 Web 服務(wù)器排行榜 https://news.netcraft.com/archives/2021/06/29/june-2021-web-server-survey.html
          • njs scripting language https://nginx.org/en/docs/njs/
          • NJS Learning Materials https://github.com/soulteary/njs-learning-materials
          • Harnessing the Power and Convenience of JavaScript for Each Request with the NGINX JavaScript Module https://www.nginx.com/blog/harnessing-power-convenience-of-javascript-for-each-request-with-nginx-javascript-module
          • Introducing Nginx NJS https://www.mywaiting.com/weblogs/introducing-nginx-njs/

          關(guān)于本文

          來(lái)源:whilefor

          https://zhuanlan.zhihu.com/p/393788937


          最后

          歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
          回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會(huì)很認(rèn)真的解答喲!
          回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
          回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
          如果這篇文章對(duì)你有幫助,在看」是最大的支持
           》》面試官也在看的算法資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持


          瀏覽 44
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天撸在线不卡 | 亚洲免费三级 | 黄片视频在线观看 | 一本久久精品一区二区 | 午夜男女羞羞影院 |