当前位置: 首页 > 科技观察

你可以在Nginx中运行JavaScript,太棒了!

时间:2023-03-17 13:53:54 科技观察

简介Nginx作为市场占有率最高的Web服务器,主打高性能和可扩展性。自带很多核心功能模块,还有大量的第三方模块。Web服务中很多灰度方案的实现都会使用Nginx+Lua+Redis方案。Lua是一种体积小、启动速度快、性能高的轻量级脚本语言。通过lua-nginx-module模块将Lua语言嵌入到Nginx中,可以使用Lua脚本扩展Nginx功能,访问MySQL、Redis等数据库。Lua虽然是一门强大的脚本语言,但是太小众了。Nginx团队选择了非常流行的JavaScript来开发NGINXJavaScript模块(njs),让更多的工程师可以使用JavaScript来扩展Nginx的功能,从而更好的发展Nginx社区生态。图片NGINXJavaScript简介NGINXJavaScript,简称njs,是JavaScript语言的子集,实现了部分ECMAScript5.1(严格模式)规范和ECMAScript6规范。你可以使用njs来扩展Nginx的功能。njs、Node.js和JavaScript的区别1.运行时不同Node.js使用V8引擎,njs是专门为Nginx设计的运行时。Node.js使用V8引擎在内存中拥有一个持久化的JavaScript虚拟机(VM),并进行垃圾回收进行内存管理;而njs是专门为Nginx设计的,非常轻量级,每次请求都会初始化一个新的JavaScriptVM和必要的内存,请求完成后释放内存。2.语言规范的差异JavaScript规范由ECMAScript标准定义。随着标准版本的更新迭代,将支持更多的语言功能;njs自研服务器在运行时,更多的支持服务优先考虑Nginx,并且只实现了ECMAScript5.1和部分ECMAScript6,在实现更多标准规范的同时,会更多的考虑Nginx是否需要。njs安装&配置安装nginx-module-njs动态模块,Nginx版本需要为1.9.11或更高版本才能支持加载动态模块。yuminstallnginx-module-njs安装完成后,需要使用load_module命令加载配置文件nginx.conf中的njs动态模块。load_modulemodules/ngx_http_js_module.so;njs基本上使用HelloWorldnginx.conf:http{js_importhttp.js;#orjs_importhttpfromhttp.js;server{listen8000;location/{js_contenthttp.hello;}}}http.js:functionhello(r){r.return(200,"Helloworld!");}exportdefault{hello};js_import:导入一个njs模块,如果不指定模块名,默认为文件名。js_content:使用njs模块中导出的方法来处理此请求。HTTPProxying使用njs模块处理HTTP请求,使用subrequest发起子请求。nginx.conf:js_importhttp.js;location/start{js_contenthttp.content;}location/foo{proxy_pass;}location/bar{proxy_pass;}http.js:functioncontent(r){r.subrequest('/api/5/foo',{method:'POST',body:JSON.stringify({foo:'foo',bar:"bar"})},函数(res){if(res.status!=200){r.return(res.status,res.responseBody);return;}varjson=JSON.parse(res.responseBody);r.return(200,json.content);});}exportdefault{content};r.subrequest:可以请求其他内部API,headers与本次请求相同,可以在location块中使用proxy_set_header设置或覆盖原来的header。自定义日志输出格式使用njs自定义Nginx日志的输出格??式。nginx.js:js_importlogging.js;js_set$access_log_headerslogging.kvAccess;log_formatkvpairs$access_log_headers;server{listen80;root/usr/share/nginx/html;access_log/var/log/nginx/access.logkvpairs;}logging.js:functionkvAccess(r){varlog=`${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]}`);returnlog;}exportdefault{kvAccess}js_set:njs模块中的kvAccess方法执行后,将执行结果放入$access_log_headers变量中。但如果只在log_format中引用,则只会在logging阶段执行。r:HTTP请求对象。属性列表:http://nginx.org/en/docs/njs/reference.html#http访问数据库1.访问Redis使用redis2-nginx-module动态模块,结合subrequest访问Redis数据。nginx.conf:js_importhttp.js;#GET/redis_get?key=some_keylocation=/redis_get{#解码uri中的参数key,赋值给变量$keyset_unescape_uri$key$arg_key;redis2_queryget$key;redis2_pass127.0.0.1:6379;}#GET/redis_set?key=one&val=first%20valuelocation=/redis_set{set_unescape_uri$key$arg_key;set_unescape_uri$val$arg_val;redis2_queryset$key$val;redis2_pass127.0.0.1:6379;}#GET/get_redis_data?key=some_keylocation/get_redis_data{js_contenthttp.get_redis_data;}http.js:functionserialize(obj){varstr=[];for(varpinobj){if(obj.hasOwnProperty(p)){str.push(encodeURIComponent(p)+"="+encodeURIComponent(obj[p]));}}returnstr.join("&");};functionget_redis_data(r){r.subrequest('/redis_get',{args:serialize(r.args),方法:'GET'},function(res){if(res.status!=200){r.return(res.status,res.responseBody);return;}r.return(200,res.responseBody);});returnlog;}exportdefault{get_redis_data}set_unescape_uri:解码uri中参数的%XX编码。redis2_query:要执行的Redis命令。redis2_pass:Redis后端服务。redis2_pass的返回值类似于redis-cli执行后的返回值,需要解析器解析是否执行成功。2、访问MySQL使用drizzle-nginx-module动态模块,结合subrequest访问MySQL数据。nginx.conf:upstreambackend{drizzle_server127.0.0.1:3306dbname=testpassword=some_passuser=montyprotocol=mysql;}server{js_importhttp.js;location/mysql{set_unescape_uri$name$arg_name;#为防止SQL注册攻击,使用set_quote_sql_str来设置sql语句中的变量set_quote_sql_str$quoted_name$name;drizzle_query"select*fromcatswherename=$quoted_name";drizzle_passbackend;drizzle_connect_timeout500ms;#default60sdrizzle_send_query_timeout2s;#default60sdrizzle_recv_cols_timeout1s;#default60sdrizzle_recv_rows_timeout1s;#default60s}#GET/get_mysql_data?name=cat_namelocation/get_mysql_data{js_contenthttp.get_mysql_data;}}http.js:functionserialize(obj){varstr=[];for(varpinobj){if(obj.hasOwnProperty(p)){str.push(encodeURIComponent(p)+"="+encodeURIComponent(obj[p]));}}returnstr.join("&");};functionget_mysql_data(r){r.subrequest('/mysql',{args:serialize(r.args),method:'GET'},函数(res){if(res.status!=200){r.return(res.status,res.responseBody);返回;}r.return(200,res.responseBody);});returnlog;}exportdefault{get_mysql_data}set_quote_sql_str:为了防止SQL注入攻击,在sql语句中设置变量drizzle_query:执行的SQL语句。drizzle_pass:Drizzle或MySQL服务的上游。结束语在njs之前,虽然Nginx+Lua生态已经日趋成熟,但Nginx毕竟是一个web服务器。JavaScript是最流行的Web开发语言。可以利用JavaScript生态来扩展Nginx的功能,可能会更有想象力,做的更多。