网站数据统计分析项目之后端脚本解读
后端脚本要完成的功能
1、解析HTTP请求参数
2、从服务器获取客户端无法获取的信息(访客ip)
3、将采集到的数据写入log
4、生成一副1×1的空gif图片作为响应内容(响应头Content-type=image/gif)
5、在响应头中通过Set-cookie为客户端植入cookie
编写后端脚本
编写数据采集的后端脚本nginx.conf文件。
[hadoop@master conf] pwd
/home/hadoop/work/conf
[hadoop@master conf]$ vi nginx.conf
worker_processes 1;
error_log logs/error.log;
events{
worker_connections 1024;
}
http{
log_format myformat "$msec^A$remote_addr^A$u_domain^A$u_url^A$u_title^A$u_referrer^A$u_sh^A$u_sw^A$u_cd^A$u_lang^A$http_user_agent^A$u_utrace^A$u_account";
server{
listen 8080;
location /1.gif {
#伪装成gif文件
default_type image/gif;
#本身关闭access_log,通过subrequest记录log
access_log off;
access_by_lua "
-- 用户跟踪cookie名为utrace
local uid = ngx.var.cookie_utrace
if not uid then
-- 如果没有则生成一个跟踪cookie,算法为md5(时间戳+IP+客户端信息)
uid = ngx.md5(ngx.now() .. ngx.var.remote_addr .. ngx.var.http_user_agent)
end
ngx.header['Set-Cookie'] = {'utrace=' .. uid .. '; path=/'}
ngx.location.capture('/save-log?' .. ngx.var.args .. '&utrace=' .. uid)
--if ngx.var.arg_domain then
-- 通过subrequest到/save-log记录日志,将参数和用户跟踪cookie带过去
--ngx.location.capture('/save-log?' .. ngx.var.args .. '&utrace=' .. uid)
--end
";
#此请求不缓存
add_header Expires "Fri, 01 Jan 1980 00:00:00 GMT";
add_header Pragma "no-cache";
add_header Cache-Control "no-cache, max-age=0, must-revalidate";
#返回一个1×1的空gif图片
empty_gif;
}
location /save-log {
#内部location,不允许外部直接访问
internal;
#定义变量,注意需要unescape
set_unescape_uri $u_domain $arg_domain;
set_unescape_uri $u_url $arg_url;
set_unescape_uri $u_title $arg_title;
set_unescape_uri $u_referrer $arg_referrer;
set_unescape_uri $u_sh $arg_sh;
set_unescape_uri $u_sw $arg_sw;
set_unescape_uri $u_cd $arg_cd;
set_unescape_uri $u_lang $arg_lang;
set_unescape_uri $u_utrace $arg_utrace;
set_unescape_uri $u_account $arg_account;
#打开日志
log_subrequest on;
#记录日志到ma.log,实际应用中最好加buffer,格式为tick
access_log logs/my.log myformat;
#输出空字符串
echo '';
}
}
}
下面我们对后端脚本重难点分析:
log_format vs access_lognginx服务器日志相关指令主要有两条:
1)log_format,用来设置日志格式
2)access_log,用来指定日志文件的存放路径、格式和缓存大小
举例:创建一个名字叫tick的日志格式
log_format tick "$msec $remote_addr $u_domain $u_url $u_title $u_referrer $u_sh $u_sw $u_cd $u_lang $http_user_agent $u_utrace $u_account";
subrequest
Nginx 世界里有两种类型的“请求”:
1)main request(主请求)
由 HTTP 客户端从 Nginx 外部发起的请求。比如,从浏览器访问Nginx就是一个“主请求”。
2)subrequest(子请求)
“子请求”则是由 Nginx 正在处理的请求在 Nginx 内部发起的一种级联请求。可以有多级,可递归。
模拟Http接口,并没有额外的Http或者Tcp传输开销,就像函数调用。
ngx.location.capture用于发出一个同步的,非阻塞的Nginxsubrequest(子请求)。
location = /other {
ehco 'Hello, world!';
}
# Lua非阻塞IO
location = /lua {
content_by_lua '
local res = ngx.location.capture("/other")
if res.status == 200 then
ngx.print(res.body)
end
';
}
ngx.var.arg vs ngx.req.get_uri_args
两者都是为了获取请求uri中的参数,例如:
http://pureage.info?id=1
为了获取输入参数id,以下两种方法都可以:
id = ngx.var.arg_id
id = ngx.req.get_uri_args[“id"]
它们的区别在于:当请求uri中有多个同名参数时, ngx.var.arg_xx的做法是取第一个出现的值,ngx.req_get_uri_args["xx"]的做法是返回一个table
set_unescape_uringx.var.arg与ngx.req.get_uri_args获取的请求参数是没解码的。
如果你想对 URI 参数值中的 %XX 这样的编码序列进行解码,可以使用第三方 ngx_set_misc 模块提供的 set_unescape_uri 配置指令:
location /test {
set_unescape_uri $name $arg_name;
set_unescape_uri $class $arg_class;
echo "name: $name";
echo "class: $class";
}